• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1### CHRE Simulation Test Framework
2
3#### Background
4
5Simulation tests are written for the CHRE linux (i.e. simulation) platform, and
6can be useful in validating higher level CHRE behavior. By "higher level", we
7mean:
8
9* More coverage than a module-level unit test.
10* But smaller in scope compared to a full end-to-end integration test.
11
12You can think of a simulation test as treating the core CHRE framework as a
13black box, and is able to validate its output.
14
15#### Running the tests
16
17You can run simulation tests through `atest`:
18
19```
20atest --host chre_simulation_tests
21```
22
23#### How to write a test
24
25The simulation test framework encourages writing self contained tests as follow:
26
27```cpp
28// Use the same unique prefix for all the tests in a single file
29TEST_F(TestBase, <PrefixedTestName>) {
30  // 1. Create tests event to trigger code in the Nanoapp context.
31  CREATE_CHRE_TEST_EVENT(MY_TEST_EVENT, 0);
32
33  // 2. Create a test Nanpoapp by inheriting TestNanoapp.
34  struct App : public TestNanoapp {
35    decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType,
36                                                   const void *eventData) {
37      switch (eventType) {
38        // 3. Handle system events.
39        case CHRE_EVENT_WIFI_ASYNC_RESULT: {
40          // ...
41          // 4. Send event back to the test.
42          TestEventQueueSingleton::get()->pushEvent(
43            CHRE_EVENT_WIFI_ASYNC_RESULT)
44          break;
45        }
46
47        case CHRE_EVENT_TEST_EVENT: {
48          auto event = static_cast<const TestEvent *>(eventData);
49          switch (event->type) {
50            // 5. Handle test events to execute code in the context the Nanoapp.
51            case MY_TEST_EVENT:
52              // ...
53              break;
54          }
55        }
56      }
57    };
58  };
59
60  // 6. Load the app and add initial expectations.
61  auto app = loadNanoapp<App>();
62  EXPECT_TRUE(...);
63
64  // 7. Send test events to the Nanoapp to execute some actions and add
65  //    expectations about the result.
66  sendEventToNanoapp(app, MY_TEST_EVENT);
67  waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT);
68  EXPECT_TRUE(...);
69
70  // 8. Optionally unload the Nanoapp
71  unloadNanoapp(app);
72}
73```
74
75##### Test app (#2, #6, #8)
76
77Inherit from `TestNanoapp` to create a test nanoapp. The following
78properties oif a nanoapp can be overridden `name`, `id`, `version`, `perms`,
79`start`, `handleEvent`, and `end`.
80
81Typical tests only override of few of the above properties:
82
83* `perms` to set the permissions required for the test,
84* `start` to put the system in a known state before each test,
85* `handleEvent` is probably the most important function where system and test
86   events are handled. See the sections below for more details.
87
88##### Test events (#1)
89
90The test events are local to a single test and created using the
91`CREATE_CHRE_TEST_EVENT(name, id)` macro. The id must be unique in a single
92test and in the range [0, 0xfff].
93
94##### System event (#3)
95
96Add code to `handleEvent` to handle the system events you are interested in for
97the test:
98
99```cpp
100decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType,
101                                               const void *eventData) {
102  switch (eventType) {
103    case CHRE_EVENT_WIFI_ASYNC_RESULT: {
104      // ...
105      break;
106    }
107  }
108};
109```
110
111The handler would typically send an event back to the nanoapp, see the next
112section for more details.
113
114##### Send event from the nanoapp (#4)
115
116You can send an event from the nanoapp (typically inside `handleEvent`):
117
118```cpp
119// Sending a system event.
120TestEventQueueSingleton::get()->pushEvent(CHRE_EVENT_WIFI_ASYNC_RESULT);
121
122// Sending a test event.
123TestEventQueueSingleton::get()->pushEvent(MY_TEST_EVENT);
124```
125
126Use `waitForEvent` to wait for an event in your test code:
127
128```cpp
129// Wait for a system event.
130waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT);
131
132// Wait for a test event.
133waitForEvent(MY_TEST_EVENT);
134```
135
136Waiting for an event as described above is sufficient to express a boolean
137expectation. For example the status of an event:
138
139```cpp
140  decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType,
141                                                 const void *eventData) {
142    switch (eventType) {
143      case CHRE_EVENT_WIFI_ASYNC_RESULT: {
144        auto *event = static_cast<const chreAsyncResult *>(eventData);
145        if (event->success) {
146          TestEventQueueSingleton::get()->pushEvent(
147              CHRE_EVENT_WIFI_ASYNC_RESULT);
148        }
149        break;
150      }
151    }
152  };
153};
154```
155
156With the above snippet `waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT)` will timeout
157if the nanoapp did not receive a successful status.
158
159Sometimes you want to attach additional data alongside the event. Simply pass
160the data as the second argument to pushEvent:
161
162```cpp
163    decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType,
164                                                   const void *eventData) {
165      switch (eventType) {
166        case CHRE_EVENT_WIFI_ASYNC_RESULT: {
167          auto *event = static_cast<const chreAsyncResult *>(eventData);
168          if (event->success) {
169            TestEventQueueSingleton::get()->pushEvent(
170                CHRE_EVENT_WIFI_ASYNC_RESULT,
171                *(static_cast<const uint32_t *>(event->cookie)));
172          }
173          break;
174        }
175      }
176    };
177```
178
179The data must be trivially copyable (a scalar or a struct of scalar are safe).
180
181Use the second argument of `waitForEvent` to retrieve the data in your test
182code:
183
184```cpp
185uint32_t cookie;
186waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT, &cookie);
187EXPECT_EQ(cookie, ...);
188```
189
190##### Send event to the nanoapp (#5)
191
192To execute the code in the nanoapp context, you will need to create a test
193event and send it to the nanoapp as follow:
194
195```cpp
196CREATE_CHRE_TEST_EVENT(MY_TEST_EVENT, 0);
197
198// ...
199
200sendEventToNanoapp(app, MY_TEST_EVENT);
201```
202
203The code to be executed in the context of the nanoapp should be added to its
204`handleEvent` function:
205
206```cpp
207decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType,
208                                               const void *eventData) {
209  switch (eventType) {
210    // Test event are received with a CHRE_EVENT_TEST_EVENT type.
211    case CHRE_EVENT_TEST_EVENT: {
212      auto event = static_cast<const TestEvent *>(eventData);
213      switch (event->type) {
214        // Create a case for each of the test events.
215        case MY_TEST_EVENT:
216          // Code running in the context of the nanoapp.
217          break;
218      }
219    }
220  }
221};
222```
223
224It is possible to send data alongside a test event:
225
226```cpp
227bool enable = true;
228sendEventToNanoapp(app, MY_TEST_EVENT, &enable);
229```
230
231The data should be a scalar type or a struct of scalars. Be careful not to send
232a pointer to a memory block that might be released before the data is consumed
233in `handleEvent`. This would result in a use after free error and flaky tests.
234
235The `handleEvent` function receives a copy of the data in the `data` field of
236the `TestEvent`:
237
238```cpp
239decltype(nanoappHandleEvent) *handleEvent = [](uint32_t, uint16_t eventType,
240                                               const void *eventData) {
241  switch (eventType) {
242    // Test event are received with a CHRE_EVENT_TEST_EVENT type.
243    case CHRE_EVENT_TEST_EVENT: {
244      auto event = static_cast<const TestEvent *>(eventData);
245      switch (event->type) {
246        // Create a case for each of the test events.
247        case MY_TEST_EVENT:
248          chreFunctionTakingABool(*(bool*(event->data)));
249          break;
250      }
251    }
252  }
253};
254```
255