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