furi_record_test.c 13 KB

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