furi_record_test.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  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) {
  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 = furi_open(
  28. "test/pipe", false, false, pipe_record_cb, NULL
  29. );
  30. if(pipe_record == NULL) {
  31. fuprintf(log, "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. fuprintf(log, "cannot write to record\n");
  38. return false;
  39. }
  40. // 4. check that subscriber get data
  41. if(pipe_record_value != WRITE_VALUE) {
  42. fuprintf(log, "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. fuprintf(log, "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. fuprintf(log, "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 test_furi_holding_data(FuriRecordSubscriber* log) {
  75. // 1. Create holding record
  76. uint8_t holder = 0;
  77. if(!furi_create("test/holding", (void*)&holder, sizeof(holder))) {
  78. fuprintf(log, "cannot create record\n");
  79. return false;
  80. }
  81. // 2. Open/Subscribe on it
  82. FuriRecordSubscriber* holding_record = furi_open(
  83. "test/holding", false, false, holding_record_cb, NULL
  84. );
  85. if(holding_record == NULL) {
  86. fuprintf(log, "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. fuprintf(log, "cannot write to record\n");
  93. return false;
  94. }
  95. // 4. check that subscriber get data
  96. if(holding_record_value != WRITE_VALUE) {
  97. fuprintf(log, "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. fuprintf(log, "cannot read from record\n");
  104. return false;
  105. }
  106. if(read_value != WRITE_VALUE) {
  107. fuprintf(log, "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. fuprintf(log, "overflowed write not allowed\n");
  113. return false;
  114. }
  115. if(furi_read(holding_record, &read_value, 100)) {
  116. fuprintf(log, "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. FuriRecordSubscriber* log = (FuriRecordSubscriber*)p;
  135. FuriRecordSubscriber* holding_record = furi_open(
  136. "test/concurrent", false, false, NULL, NULL
  137. );
  138. if(holding_record == NULL) {
  139. fuprintf(log, "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. fuprintf(log, "cannot take record\n");
  146. furi_give(holding_record);
  147. furiac_exit(NULL);
  148. }
  149. // emulate read-modify-write broken by context switching
  150. uint8_t a = value->a;
  151. uint8_t b = value->b;
  152. a++;
  153. b++;
  154. delay(2); // this is only for test, do not add delay between take/give in prod!
  155. value->a = a;
  156. value->b = b;
  157. furi_give(holding_record);
  158. }
  159. furiac_exit(NULL);
  160. }
  161. bool test_furi_concurrent_access(FuriRecordSubscriber* log) {
  162. // 1. Create holding record
  163. ConcurrentValue holder = {.a = 0, .b = 0};
  164. if(!furi_create("test/concurrent", (void*)&holder, sizeof(ConcurrentValue))) {
  165. fuprintf(log, "cannot create record\n");
  166. return false;
  167. }
  168. // 2. Open it
  169. FuriRecordSubscriber* holding_record = furi_open(
  170. "test/concurrent", false, false, NULL, NULL
  171. );
  172. if(holding_record == NULL) {
  173. fuprintf(log, "cannot open record\n");
  174. return false;
  175. }
  176. // 3. Create second app for interact with it
  177. FuriApp* second_app = furiac_start(
  178. furi_concurent_app, "furi concurent app", (void*)log
  179. );
  180. // 4. multiply ConcurrentValue::a
  181. for(size_t i = 0; i < 4; i++) {
  182. ConcurrentValue* value = (ConcurrentValue*)furi_take(holding_record);
  183. if(value == NULL) {
  184. fuprintf(log, "cannot take record\n");
  185. furi_give(holding_record);
  186. return false;
  187. }
  188. // emulate read-modify-write broken by context switching
  189. uint8_t a = value->a;
  190. uint8_t b = value->b;
  191. a++;
  192. b++;
  193. value->a = a;
  194. delay(10); // this is only for test, do not add delay between take/give in prod!
  195. value->b = b;
  196. furi_give(holding_record);
  197. }
  198. delay(20);
  199. if(second_app->handler != NULL) {
  200. fuprintf(log, "second app still alive\n");
  201. return false;
  202. }
  203. if(holder.a != holder.b) {
  204. fuprintf(log, "broken integrity: a=%d, b=%d\n", holder.a, holder.b);
  205. return false;
  206. }
  207. return true;
  208. }
  209. /*
  210. TEST: non-existent data
  211. 1. Try to open non-existent record
  212. 2. Check for NULL handler
  213. 3. Try to write/read, get error
  214. TODO: implement this test
  215. */
  216. bool test_furi_nonexistent_data(FuriRecordSubscriber* log) {
  217. return true;
  218. }
  219. /*
  220. TEST: mute algorithm
  221. 1. Create "parent" application:
  222. 1. Create pipe record
  223. 2. Open watch handler: no_mute=false, solo=false, subscribe to data.
  224. 2. Open handler A: no_mute=false, solo=false, NULL subscriber. Subscribe to state.
  225. Try to write data to A and check subscriber.
  226. 3. Open handler B: no_mute=true, solo=true, NULL subscriber.
  227. Check A state cb get FlipperRecordStateMute.
  228. Try to write data to A and check that subscriber get no data. (muted)
  229. Try to write data to B and check that subscriber get data.
  230. TODO: test 3 not pass beacuse state callback not implemented
  231. 4. Open hadler C: no_mute=false, solo=true, NULL subscriber.
  232. Try to write data to A and check that subscriber get no data. (muted)
  233. Try to write data to B and check that subscriber get data. (not muted because open with no_mute)
  234. Try to write data to C and check that subscriber get data.
  235. 5. Open handler D: no_mute=false, solo=false, NULL subscriber.
  236. Try to write data to A and check that subscriber get no data. (muted)
  237. Try to write data to B and check that subscriber get data. (not muted because open with no_mute)
  238. Try to write data to C and check that subscriber get data. (not muted because D open without solo)
  239. Try to write data to D and check that subscriber get data.
  240. 6. Close C, close B.
  241. Check A state cb get FlipperRecordStateUnmute
  242. Try to write data to A and check that subscriber get data. (unmuted)
  243. Try to write data to D and check that subscriber get data.
  244. TODO: test 6 not pass beacuse cleanup is not implemented
  245. TODO: test 6 not pass because mute algorithm is unfinished.
  246. 7. Exit "parent application"
  247. Check A state cb get FlipperRecordStateDeleted
  248. TODO: test 7 not pass beacuse cleanup is not implemented
  249. */
  250. static uint8_t mute_last_value = 0;
  251. static FlipperRecordState mute_last_state = 255;
  252. void mute_record_cb(const void* value, size_t size) {
  253. // hold value to static var
  254. mute_last_value = *((uint8_t*)value);
  255. }
  256. void mute_record_state_cb(FlipperRecordState state) {
  257. mute_last_state = state;
  258. }
  259. void furi_mute_parent_app(void* p) {
  260. FuriRecordSubscriber* log = (FuriRecordSubscriber*)p;
  261. // 1. Create pipe record
  262. if(!furi_create("test/mute", NULL, 0)) {
  263. fuprintf(log, "cannot create record\n");
  264. furiac_exit(NULL);
  265. }
  266. // 2. Open watch handler: solo=false, no_mute=false, subscribe to data
  267. FuriRecordSubscriber* watch_handler = furi_open(
  268. "test/mute", false, false, mute_record_cb, NULL
  269. );
  270. if(watch_handler == NULL) {
  271. fuprintf(log, "cannot open watch handler\n");
  272. furiac_exit(NULL);
  273. }
  274. while(1) {
  275. // TODO we don't have thread sleep
  276. delay(100000);
  277. }
  278. }
  279. bool test_furi_mute_algorithm(FuriRecordSubscriber* log) {
  280. // 1. Create "parent" application:
  281. FuriApp* parent_app = furiac_start(
  282. furi_mute_parent_app, "parent app", (void*)log
  283. );
  284. delay(2); // wait creating record
  285. // 2. Open handler A: solo=false, no_mute=false, NULL subscriber. Subscribe to state.
  286. FuriRecordSubscriber* handler_a = furi_open(
  287. "test/mute", false, false, NULL, mute_record_state_cb
  288. );
  289. if(handler_a == NULL) {
  290. fuprintf(log, "cannot open handler A\n");
  291. return false;
  292. }
  293. uint8_t test_counter = 1;
  294. // Try to write data to A and check subscriber
  295. if(!furi_write(handler_a, &test_counter, sizeof(uint8_t))) {
  296. fuprintf(log, "write to A failed\n");
  297. return false;
  298. }
  299. if(mute_last_value != test_counter) {
  300. fuprintf(log, "value A mismatch: %d vs %d\n", mute_last_value, test_counter);
  301. return false;
  302. }
  303. // 3. Open handler B: solo=true, no_mute=true, NULL subscriber.
  304. FuriRecordSubscriber* handler_b = furi_open(
  305. "test/mute", true, true, NULL, NULL
  306. );
  307. if(handler_b == NULL) {
  308. fuprintf(log, "cannot open handler B\n");
  309. return false;
  310. }
  311. // Check A state cb get FlipperRecordStateMute.
  312. if(mute_last_state != FlipperRecordStateMute) {
  313. fuprintf(log, "A state is not FlipperRecordStateMute: %d\n", mute_last_state);
  314. return false;
  315. }
  316. test_counter = 2;
  317. // Try to write data to A and check that subscriber get no data. (muted)
  318. if(furi_write(handler_a, &test_counter, sizeof(uint8_t))) {
  319. fuprintf(log, "A not muted\n");
  320. return false;
  321. }
  322. if(mute_last_value == test_counter) {
  323. fuprintf(log, "value A must be muted\n");
  324. return false;
  325. }
  326. test_counter = 3;
  327. // Try to write data to B and check that subscriber get data.
  328. if(!furi_write(handler_b, &test_counter, sizeof(uint8_t))) {
  329. fuprintf(log, "write to B failed\n");
  330. return false;
  331. }
  332. if(mute_last_value != test_counter) {
  333. fuprintf(log, "value B mismatch: %d vs %d\n", mute_last_value, test_counter);
  334. return false;
  335. }
  336. // 4. Open hadler C: solo=true, no_mute=false, NULL subscriber.
  337. FuriRecordSubscriber* handler_c = furi_open(
  338. "test/mute", true, false, NULL, NULL
  339. );
  340. if(handler_c == NULL) {
  341. fuprintf(log, "cannot open handler C\n");
  342. return false;
  343. }
  344. // TODO: Try to write data to A and check that subscriber get no data. (muted)
  345. // TODO: Try to write data to B and check that subscriber get data. (not muted because open with no_mute)
  346. // TODO: Try to write data to C and check that subscriber get data.
  347. // 5. Open handler D: solo=false, no_mute=false, NULL subscriber.
  348. FuriRecordSubscriber* handler_d = furi_open(
  349. "test/mute", false, false, NULL, NULL
  350. );
  351. if(handler_d == NULL) {
  352. fuprintf(log, "cannot open handler D\n");
  353. return false;
  354. }
  355. // TODO: Try to write data to A and check that subscriber get no data. (muted)
  356. // TODO: Try to write data to B and check that subscriber get data. (not muted because open with no_mute)
  357. // TODO: Try to write data to C and check that subscriber get data. (not muted because D open without solo)
  358. // TODO: Try to write data to D and check that subscriber get data.
  359. // 6. Close C, close B.
  360. // TODO: Check A state cb get FlipperRecordStateUnmute
  361. // TODO: Try to write data to A and check that subscriber get data. (unmuted)
  362. // TODO: Try to write data to D and check that subscriber get data.
  363. // 7. Exit "parent application"
  364. if(!furiac_kill(parent_app)) {
  365. fuprintf(log, "kill parent_app fail\n");
  366. return false;
  367. }
  368. // TODO: Check A state cb get FlipperRecordStateDeleted
  369. return true;
  370. }