1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef CHRE_SIMULATION_TEST_EVENT_QUEUE_H_
18 #define CHRE_SIMULATION_TEST_EVENT_QUEUE_H_
19 
20 #include <gtest/gtest.h>
21 
22 #include <cinttypes>
23 
24 #include "chre/platform/memory.h"
25 #include "chre/util/fixed_size_blocking_queue.h"
26 #include "chre/util/memory.h"
27 #include "chre/util/non_copyable.h"
28 #include "chre/util/singleton.h"
29 #include "test_event.h"
30 
31 namespace chre {
32 
33 //! A test event type to indicate the test nanoapp has loaded.
34 #define CHRE_EVENT_SIMULATION_TEST_NANOAPP_LOADED \
35   CHRE_SIMULATION_TEST_EVENT_ID(0)
36 
37 //! A test event type to indicate the test has timed out, and should abort.
38 #define CHRE_EVENT_SIMULATION_TEST_TIMEOUT CHRE_SIMULATION_TEST_EVENT_ID(1)
39 
40 //! A test event type to indicate the test nanoapp has unloaded.
41 #define CHRE_EVENT_SIMULATION_TEST_NANOAPP_UNLOADED \
42   CHRE_SIMULATION_TEST_EVENT_ID(2)
43 
44 /**
45  * A class that monitors events for the test to consume.
46  *
47  * This class can be used as an execution barrier for the test, i.e. waiting
48  * for a specific event to occur. The barrier is done through the semantics of
49  * CHRE events, and can be used e.g. for nanoapps to redirect incoming events
50  * using pushEvent().
51  *
52  * The main test thread can then wait for this event using waitForEvent().
53  *
54  * Note 1) pushEvent() can also be invoked outside the nanoapp, for instance
55  * using deferred system callbacks.
56  * Note 2) The CHRE_EVENT_SIMULATION_TEST_TIMEOUT event type can be used to
57  * abort the test due to a timeout (this usage is recommended in order to avoid
58  * the test framework from stalling).
59  */
60 class TestEventQueue : public NonCopyable {
61  public:
62   //! Push an event to the queue.
pushEvent(uint16_t eventType)63   void pushEvent(uint16_t eventType) {
64     mQueue.push({eventType});
65   }
66 
67   /**
68    * Push an event with data to the queue.
69    *
70    * Note: The data passed to this method must be trivially copyable. It is
71    * recommended to pass a scalar or a struct composed of scalars only. If this
72    * method is used in the test nanoapp handleEvent be careful not to forward
73    * pointers to memory that could be freed by the CHRE framework before the
74    * data is received using @ref waitForEvent.
75    *
76    * @param eventType The type of event.
77    * @param eventData The data to send together with the event, which must not
78    *        contain references to dynamically allocated memory.
79    */
80   template <class T>
pushEvent(uint16_t eventType,T eventData)81   void pushEvent(uint16_t eventType, T eventData) {
82     static_assert(std::is_trivial<T>::value);
83     auto ptr = memoryAlloc<T>();
84     ASSERT_NE(ptr, nullptr);
85     *ptr = eventData;
86     mQueue.push({eventType, static_cast<void *>(ptr)});
87   }
88 
89   //! Block until the event happens.
waitForEvent(uint16_t eventType)90   void waitForEvent(uint16_t eventType) {
91     LOGD("Waiting for event type 0x%" PRIx16, eventType);
92     while (true) {
93       auto event = mQueue.pop();
94       LOGD("Got event type 0x%" PRIx16, event.type);
95       ASSERT_NE(event.type, CHRE_EVENT_SIMULATION_TEST_TIMEOUT)
96           << "Timeout waiting for event " << eventType;
97       memoryFree(event.data);
98       if (event.type == eventType) {
99         break;
100       }
101     }
102   }
103 
104   //! Block until the event happens and populate the event data.
105   template <class T>
waitForEvent(uint16_t eventType,T * data)106   void waitForEvent(uint16_t eventType, T *data) {
107     static_assert(std::is_trivial<T>::value);
108     LOGD("Waiting for event type 0x%" PRIx16, eventType);
109     while (true) {
110       auto event = mQueue.pop();
111       LOGD("Got event type 0x%" PRIx16, event.type);
112       ASSERT_NE(event.type, CHRE_EVENT_SIMULATION_TEST_TIMEOUT)
113           << "Timeout waiting for event " << eventType;
114       if (event.type == eventType) {
115         *data = *(static_cast<T *>(event.data));
116         memoryFree(event.data);
117         break;
118       }
119       memoryFree(event.data);
120     }
121   }
122 
123   //! Flush the queue.
flush()124   void flush() {
125     while (!mQueue.empty()) {
126       auto event = mQueue.pop();
127       memoryFree(event.data);
128     }
129   }
130 
131  private:
132   static const size_t kQueueCapacity = 64;
133   FixedSizeBlockingQueue<TestEvent, kQueueCapacity> mQueue;
134 };
135 
136 //! Provide an alias to the TestEventQueue singleton.
137 typedef Singleton<TestEventQueue> TestEventQueueSingleton;
138 
139 //! Extern the explicit TestEventQueueSingleton to force non-inline method
140 //! calls.
141 extern template class Singleton<TestEventQueue>;
142 
143 }  // namespace chre
144 
145 #endif  // CHRE_SIMULATION_TEST_EVENT_QUEUE_H_
146