furi_record_test.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include "flipper.h"
  4. #include "log.h"
  5. /*
  6. TEST: pipe record
  7. 1. create pipe record
  8. 2. Open/subscribe to it
  9. 3. write data
  10. 4. check that subscriber get data
  11. 5. try to read, get error
  12. 6. close record
  13. 7. try to write, get error
  14. */
  15. static uint8_t pipe_record_value = 0;
  16. void pipe_record_cb(const void* value, size_t size, void* ctx) {
  17. // hold value to static var
  18. pipe_record_value = *((uint8_t*)value);
  19. }
  20. bool test_furi_pipe_record(FuriRecordSubscriber* log) {
  21. // 1. create pipe record
  22. if(!furi_create("test/pipe", NULL, 0)) {
  23. fuprintf(log, "cannot create record\n");
  24. return false;
  25. }
  26. // 2. Open/subscribe to it
  27. FuriRecordSubscriber* pipe_record =
  28. furi_open("test/pipe", false, false, pipe_record_cb, NULL, NULL);
  29. if(pipe_record == NULL) {
  30. fuprintf(log, "cannot open record\n");
  31. return false;
  32. }
  33. const uint8_t WRITE_VALUE = 1;
  34. // 3. write data
  35. if(!furi_write(pipe_record, &WRITE_VALUE, sizeof(uint8_t))) {
  36. fuprintf(log, "cannot write to record\n");
  37. return false;
  38. }
  39. // 4. check that subscriber get data
  40. if(pipe_record_value != WRITE_VALUE) {
  41. fuprintf(log, "wrong value (get %d, write %d)\n", pipe_record_value, WRITE_VALUE);
  42. return false;
  43. }
  44. // 5. try to read, get error
  45. uint8_t read_value = 0;
  46. if(furi_read(pipe_record, &read_value, sizeof(uint8_t))) {
  47. fuprintf(log, "reading from pipe record not allowed\n");
  48. return false;
  49. }
  50. // 6. close record
  51. furi_close(pipe_record);
  52. // 7. try to write, get error
  53. if(furi_write(pipe_record, &WRITE_VALUE, sizeof(uint8_t))) {
  54. fuprintf(log, "writing to closed record not allowed\n");
  55. return false;
  56. }
  57. return true;
  58. }
  59. /*
  60. TEST: holding data
  61. 1. Create holding record
  62. 2. Open/Subscribe on it
  63. 3. Write data
  64. 4. Check that subscriber get data
  65. 5. Read and check data
  66. 6. Try to write/read wrong size of data
  67. */
  68. static uint8_t holding_record_value = 0;
  69. void holding_record_cb(const void* value, size_t size, void* ctx) {
  70. // hold value to static var
  71. holding_record_value = *((uint8_t*)value);
  72. }
  73. bool test_furi_holding_data(FuriRecordSubscriber* log) {
  74. // 1. Create holding record
  75. uint8_t holder = 0;
  76. if(!furi_create("test/holding", (void*)&holder, sizeof(holder))) {
  77. fuprintf(log, "cannot create record\n");
  78. return false;
  79. }
  80. // 2. Open/Subscribe on it
  81. FuriRecordSubscriber* holding_record =
  82. furi_open("test/holding", false, false, holding_record_cb, NULL, NULL);
  83. if(holding_record == NULL) {
  84. fuprintf(log, "cannot open record\n");
  85. return false;
  86. }
  87. const uint8_t WRITE_VALUE = 1;
  88. // 3. write data
  89. if(!furi_write(holding_record, &WRITE_VALUE, sizeof(uint8_t))) {
  90. fuprintf(log, "cannot write to record\n");
  91. return false;
  92. }
  93. // 4. check that subscriber get data
  94. if(holding_record_value != WRITE_VALUE) {
  95. fuprintf(log, "wrong sub value (get %d, write %d)\n", holding_record_value, WRITE_VALUE);
  96. return false;
  97. }
  98. // 5. Read and check data
  99. uint8_t read_value = 0;
  100. if(!furi_read(holding_record, &read_value, sizeof(uint8_t))) {
  101. fuprintf(log, "cannot read from record\n");
  102. return false;
  103. }
  104. if(read_value != WRITE_VALUE) {
  105. fuprintf(log, "wrong read value (get %d, write %d)\n", read_value, WRITE_VALUE);
  106. return false;
  107. }
  108. // 6. Try to write/read wrong size of data
  109. if(furi_write(holding_record, &WRITE_VALUE, 100)) {
  110. fuprintf(log, "overflowed write not allowed\n");
  111. return false;
  112. }
  113. if(furi_read(holding_record, &read_value, 100)) {
  114. fuprintf(log, "overflowed read not allowed\n");
  115. return false;
  116. }
  117. return true;
  118. }
  119. /*
  120. TEST: concurrent access
  121. 1. Create holding record
  122. 2. Open it twice
  123. 3. Change value simultaneously in two app and check integrity
  124. */
  125. // TODO this test broke because mutex in furi is not implemented
  126. typedef struct {
  127. // a and b must be equal
  128. uint8_t a;
  129. uint8_t b;
  130. } ConcurrentValue;
  131. void furi_concurent_app(void* p) {
  132. FuriRecordSubscriber* log = (FuriRecordSubscriber*)p;
  133. FuriRecordSubscriber* holding_record =
  134. furi_open("test/concurrent", false, false, NULL, NULL, NULL);
  135. if(holding_record == NULL) {
  136. fuprintf(log, "cannot open record\n");
  137. furiac_exit(NULL);
  138. }
  139. for(size_t i = 0; i < 10; i++) {
  140. ConcurrentValue* value = (ConcurrentValue*)furi_take(holding_record);
  141. if(value == NULL) {
  142. fuprintf(log, "cannot take record\n");
  143. furi_give(holding_record);
  144. furiac_exit(NULL);
  145. }
  146. // emulate read-modify-write broken by context switching
  147. uint8_t a = value->a;
  148. uint8_t b = value->b;
  149. a++;
  150. b++;
  151. delay(2); // this is only for test, do not add delay between take/give in prod!
  152. value->a = a;
  153. value->b = b;
  154. furi_give(holding_record);
  155. }
  156. furiac_exit(NULL);
  157. }
  158. bool test_furi_concurrent_access(FuriRecordSubscriber* log) {
  159. // 1. Create holding record
  160. ConcurrentValue holder = {.a = 0, .b = 0};
  161. if(!furi_create("test/concurrent", (void*)&holder, sizeof(ConcurrentValue))) {
  162. fuprintf(log, "cannot create record\n");
  163. return false;
  164. }
  165. // 2. Open it
  166. FuriRecordSubscriber* holding_record =
  167. furi_open("test/concurrent", false, false, NULL, NULL, NULL);
  168. if(holding_record == NULL) {
  169. fuprintf(log, "cannot open record\n");
  170. return false;
  171. }
  172. // 3. Create second app for interact with it
  173. FuriApp* second_app = furiac_start(furi_concurent_app, "furi concurent app", (void*)log);
  174. // 4. multiply ConcurrentValue::a
  175. for(size_t i = 0; i < 4; i++) {
  176. ConcurrentValue* value = (ConcurrentValue*)furi_take(holding_record);
  177. if(value == NULL) {
  178. fuprintf(log, "cannot take record\n");
  179. furi_give(holding_record);
  180. return false;
  181. }
  182. // emulate read-modify-write broken by context switching
  183. uint8_t a = value->a;
  184. uint8_t b = value->b;
  185. a++;
  186. b++;
  187. value->a = a;
  188. delay(10); // this is only for test, do not add delay between take/give in prod!
  189. value->b = b;
  190. furi_give(holding_record);
  191. }
  192. delay(50);
  193. if(second_app->handler != NULL) {
  194. fuprintf(log, "second app still alive\n");
  195. return false;
  196. }
  197. if(holder.a != holder.b) {
  198. fuprintf(log, "broken integrity: a=%d, b=%d\n", holder.a, holder.b);
  199. return false;
  200. }
  201. return true;
  202. }
  203. /*
  204. TEST: non-existent data
  205. 1. Try to open non-existent record
  206. 2. Check for NULL handler
  207. 3. Try to write/read, get error
  208. TODO: implement this test
  209. */
  210. bool test_furi_nonexistent_data(FuriRecordSubscriber* log) {
  211. return true;
  212. }
  213. /*
  214. TEST: mute algorithm
  215. 1. Create "parent" application:
  216. 1. Create pipe record
  217. 2. Open watch handler: no_mute=false, solo=false, subscribe to data.
  218. 2. Open handler A: no_mute=false, solo=false, NULL subscriber. Subscribe to state.
  219. Try to write data to A and check subscriber.
  220. 3. Open handler B: no_mute=true, solo=true, NULL subscriber.
  221. Check A state cb get FlipperRecordStateMute.
  222. Try to write data to A and check that subscriber get no data. (muted)
  223. Try to write data to B and check that subscriber get data.
  224. TODO: test 3 not pass beacuse state callback not implemented
  225. 4. Open hadler C: no_mute=false, solo=true, NULL subscriber.
  226. Try to write data to A and check that subscriber get no data. (muted)
  227. Try to write data to B and check that subscriber get data. (not muted because open with no_mute)
  228. Try to write data to C and check that subscriber get data.
  229. 5. Open handler D: no_mute=false, solo=false, NULL subscriber.
  230. Try to write data to A and check that subscriber get no data. (muted)
  231. Try to write data to B and check that subscriber get data. (not muted because open with no_mute)
  232. Try to write data to C and check that subscriber get data. (not muted because D open without solo)
  233. Try to write data to D and check that subscriber get data.
  234. 6. Close C, close B.
  235. Check A state cb get FlipperRecordStateUnmute
  236. Try to write data to A and check that subscriber get data. (unmuted)
  237. Try to write data to D and check that subscriber get data.
  238. TODO: test 6 not pass beacuse cleanup is not implemented
  239. TODO: test 6 not pass because mute algorithm is unfinished.
  240. 7. Exit "parent application"
  241. Check A state cb get FlipperRecordStateDeleted
  242. TODO: test 7 not pass beacuse cleanup is not implemented
  243. */
  244. static uint8_t mute_last_value = 0;
  245. static FlipperRecordState mute_last_state = 255;
  246. void mute_record_cb(const void* value, size_t size, void* ctx) {
  247. // hold value to static var
  248. mute_last_value = *((uint8_t*)value);
  249. }
  250. void mute_record_state_cb(FlipperRecordState state, void* ctx) {
  251. mute_last_state = state;
  252. }
  253. void furi_mute_parent_app(void* p) {
  254. FuriRecordSubscriber* log = (FuriRecordSubscriber*)p;
  255. // 1. Create pipe record
  256. if(!furi_create("test/mute", NULL, 0)) {
  257. fuprintf(log, "cannot create record\n");
  258. furiac_exit(NULL);
  259. }
  260. // 2. Open watch handler: solo=false, no_mute=false, subscribe to data
  261. FuriRecordSubscriber* watch_handler =
  262. furi_open("test/mute", false, false, mute_record_cb, NULL, NULL);
  263. if(watch_handler == NULL) {
  264. fuprintf(log, "cannot open watch handler\n");
  265. furiac_exit(NULL);
  266. }
  267. while(1) {
  268. // TODO we don't have thread sleep
  269. delay(100000);
  270. }
  271. }
  272. bool test_furi_mute_algorithm(FuriRecordSubscriber* log) {
  273. // 1. Create "parent" application:
  274. FuriApp* parent_app = furiac_start(furi_mute_parent_app, "parent app", (void*)log);
  275. delay(2); // wait creating record
  276. // 2. Open handler A: solo=false, no_mute=false, NULL subscriber. Subscribe to state.
  277. FuriRecordSubscriber* handler_a =
  278. furi_open("test/mute", false, false, NULL, mute_record_state_cb, NULL);
  279. if(handler_a == NULL) {
  280. fuprintf(log, "cannot open handler A\n");
  281. return false;
  282. }
  283. uint8_t test_counter = 1;
  284. // Try to write data to A and check subscriber
  285. if(!furi_write(handler_a, &test_counter, sizeof(uint8_t))) {
  286. fuprintf(log, "write to A failed\n");
  287. return false;
  288. }
  289. if(mute_last_value != test_counter) {
  290. fuprintf(log, "value A mismatch: %d vs %d\n", mute_last_value, test_counter);
  291. return false;
  292. }
  293. // 3. Open handler B: solo=true, no_mute=true, NULL subscriber.
  294. FuriRecordSubscriber* handler_b = furi_open("test/mute", true, true, NULL, NULL, NULL);
  295. if(handler_b == NULL) {
  296. fuprintf(log, "cannot open handler B\n");
  297. return false;
  298. }
  299. // Check A state cb get FlipperRecordStateMute.
  300. if(mute_last_state != FlipperRecordStateMute) {
  301. fuprintf(log, "A state is not FlipperRecordStateMute: %d\n", mute_last_state);
  302. return false;
  303. }
  304. test_counter = 2;
  305. // Try to write data to A and check that subscriber get no data. (muted)
  306. if(furi_write(handler_a, &test_counter, sizeof(uint8_t))) {
  307. fuprintf(log, "A not muted\n");
  308. return false;
  309. }
  310. if(mute_last_value == test_counter) {
  311. fuprintf(log, "value A must be muted\n");
  312. return false;
  313. }
  314. test_counter = 3;
  315. // Try to write data to B and check that subscriber get data.
  316. if(!furi_write(handler_b, &test_counter, sizeof(uint8_t))) {
  317. fuprintf(log, "write to B failed\n");
  318. return false;
  319. }
  320. if(mute_last_value != test_counter) {
  321. fuprintf(log, "value B mismatch: %d vs %d\n", mute_last_value, test_counter);
  322. return false;
  323. }
  324. // 4. Open hadler C: solo=true, no_mute=false, NULL subscriber.
  325. FuriRecordSubscriber* handler_c = furi_open("test/mute", true, false, NULL, NULL, NULL);
  326. if(handler_c == NULL) {
  327. fuprintf(log, "cannot open handler C\n");
  328. return false;
  329. }
  330. // TODO: Try to write data to A and check that subscriber get no data. (muted)
  331. // TODO: Try to write data to B and check that subscriber get data. (not muted because open with no_mute)
  332. // TODO: Try to write data to C and check that subscriber get data.
  333. // 5. Open handler D: solo=false, no_mute=false, NULL subscriber.
  334. FuriRecordSubscriber* handler_d = furi_open("test/mute", false, false, NULL, NULL, NULL);
  335. if(handler_d == NULL) {
  336. fuprintf(log, "cannot open handler D\n");
  337. return false;
  338. }
  339. // TODO: Try to write data to A and check that subscriber get no data. (muted)
  340. // TODO: Try to write data to B and check that subscriber get data. (not muted because open with no_mute)
  341. // TODO: Try to write data to C and check that subscriber get data. (not muted because D open without solo)
  342. // TODO: Try to write data to D and check that subscriber get data.
  343. // 6. Close C, close B.
  344. // TODO: Check A state cb get FlipperRecordStateUnmute
  345. // TODO: Try to write data to A and check that subscriber get data. (unmuted)
  346. // TODO: Try to write data to D and check that subscriber get data.
  347. // 7. Exit "parent application"
  348. if(!furiac_kill(parent_app)) {
  349. fuprintf(log, "kill parent_app fail\n");
  350. return false;
  351. }
  352. // TODO: Check A state cb get FlipperRecordStateDeleted
  353. return true;
  354. }