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