furi_record_test.c 13 KB

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