Add minunit test framework (#168)
* add minunit tests * fix logging * ignore unexisting time service on embedded targets * fix warning, issue with printf * add exitcode * migrate to printf * indicate test by leds * add testing description * redesigned minunit tests to allow testing in separate files * add test step for local target * add failure test * add restore test_check * testing description Co-authored-by: rusdacent <rusdacentx0x08@gmail.com> Co-authored-by: DrZlo13 <who.just.the.doctor@gmail.com>
This commit is contained in:
		
							parent
							
								
									aa3ac5b242
								
							
						
					
					
						commit
						176e608c6d
					
				
							
								
								
									
										5
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @ -51,6 +51,11 @@ jobs: | |||||||
|         with: |         with: | ||||||
|           run: make -C firmware TARGET=local |           run: make -C firmware TARGET=local | ||||||
| 
 | 
 | ||||||
|  |       - name: Run local tests | ||||||
|  |         uses: ./.github/actions/docker | ||||||
|  |         with: | ||||||
|  |           run: make -C firmware TARGET=local APP_TEST=1 run | ||||||
|  | 
 | ||||||
|       - name: Build F2 firmware in docker |       - name: Build F2 firmware in docker | ||||||
|         uses: ./.github/actions/docker |         uses: ./.github/actions/docker | ||||||
|         with: |         with: | ||||||
|  | |||||||
| @ -15,6 +15,7 @@ CFLAGS		+= -DAPP_TEST | |||||||
| C_SOURCES	+= $(APP_DIR)/tests/furiac_test.c | C_SOURCES	+= $(APP_DIR)/tests/furiac_test.c | ||||||
| C_SOURCES	+= $(APP_DIR)/tests/furi_record_test.c | C_SOURCES	+= $(APP_DIR)/tests/furi_record_test.c | ||||||
| C_SOURCES	+= $(APP_DIR)/tests/test_index.c | C_SOURCES	+= $(APP_DIR)/tests/test_index.c | ||||||
|  | C_SOURCES	+= $(APP_DIR)/tests/minunit_test.c | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| APP_EXAMPLE_BLINK ?= 0 | APP_EXAMPLE_BLINK ?= 0 | ||||||
|  | |||||||
| @ -22,10 +22,10 @@ void pipe_record_cb(const void* value, size_t size, void* ctx) { | |||||||
|     pipe_record_value = *((uint8_t*)value); |     pipe_record_value = *((uint8_t*)value); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool test_furi_pipe_record(FuriRecordSubscriber* log) { | bool test_furi_pipe_record() { | ||||||
|     // 1. create pipe record
 |     // 1. create pipe record
 | ||||||
|     if(!furi_create("test/pipe", NULL, 0)) { |     if(!furi_create("test/pipe", NULL, 0)) { | ||||||
|         fuprintf(log, "cannot create record\n"); |         printf("cannot create record\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -33,27 +33,27 @@ bool test_furi_pipe_record(FuriRecordSubscriber* log) { | |||||||
|     FuriRecordSubscriber* pipe_record = |     FuriRecordSubscriber* pipe_record = | ||||||
|         furi_open("test/pipe", false, false, pipe_record_cb, NULL, NULL); |         furi_open("test/pipe", false, false, pipe_record_cb, NULL, NULL); | ||||||
|     if(pipe_record == NULL) { |     if(pipe_record == NULL) { | ||||||
|         fuprintf(log, "cannot open record\n"); |         printf("cannot open record\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const uint8_t WRITE_VALUE = 1; |     const uint8_t WRITE_VALUE = 1; | ||||||
|     // 3. write data
 |     // 3. write data
 | ||||||
|     if(!furi_write(pipe_record, &WRITE_VALUE, sizeof(uint8_t))) { |     if(!furi_write(pipe_record, &WRITE_VALUE, sizeof(uint8_t))) { | ||||||
|         fuprintf(log, "cannot write to record\n"); |         printf("cannot write to record\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // 4. check that subscriber get data
 |     // 4. check that subscriber get data
 | ||||||
|     if(pipe_record_value != WRITE_VALUE) { |     if(pipe_record_value != WRITE_VALUE) { | ||||||
|         fuprintf(log, "wrong value (get %d, write %d)\n", pipe_record_value, WRITE_VALUE); |         printf("wrong value (get %d, write %d)\n", pipe_record_value, WRITE_VALUE); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // 5. try to read, get error
 |     // 5. try to read, get error
 | ||||||
|     uint8_t read_value = 0; |     uint8_t read_value = 0; | ||||||
|     if(furi_read(pipe_record, &read_value, sizeof(uint8_t))) { |     if(furi_read(pipe_record, &read_value, sizeof(uint8_t))) { | ||||||
|         fuprintf(log, "reading from pipe record not allowed\n"); |         printf("reading from pipe record not allowed\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -62,7 +62,7 @@ bool test_furi_pipe_record(FuriRecordSubscriber* log) { | |||||||
| 
 | 
 | ||||||
|     // 7. try to write, get error
 |     // 7. try to write, get error
 | ||||||
|     if(furi_write(pipe_record, &WRITE_VALUE, sizeof(uint8_t))) { |     if(furi_write(pipe_record, &WRITE_VALUE, sizeof(uint8_t))) { | ||||||
|         fuprintf(log, "writing to closed record not allowed\n"); |         printf("writing to closed record not allowed\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -87,11 +87,11 @@ void holding_record_cb(const void* value, size_t size, void* ctx) { | |||||||
|     holding_record_value = *((uint8_t*)value); |     holding_record_value = *((uint8_t*)value); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool test_furi_holding_data(FuriRecordSubscriber* log) { | bool test_furi_holding_data() { | ||||||
|     // 1. Create holding record
 |     // 1. Create holding record
 | ||||||
|     uint8_t holder = 0; |     uint8_t holder = 0; | ||||||
|     if(!furi_create("test/holding", (void*)&holder, sizeof(holder))) { |     if(!furi_create("test/holding", (void*)&holder, sizeof(holder))) { | ||||||
|         fuprintf(log, "cannot create record\n"); |         printf("cannot create record\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -99,43 +99,43 @@ bool test_furi_holding_data(FuriRecordSubscriber* log) { | |||||||
|     FuriRecordSubscriber* holding_record = |     FuriRecordSubscriber* holding_record = | ||||||
|         furi_open("test/holding", false, false, holding_record_cb, NULL, NULL); |         furi_open("test/holding", false, false, holding_record_cb, NULL, NULL); | ||||||
|     if(holding_record == NULL) { |     if(holding_record == NULL) { | ||||||
|         fuprintf(log, "cannot open record\n"); |         printf("cannot open record\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const uint8_t WRITE_VALUE = 1; |     const uint8_t WRITE_VALUE = 1; | ||||||
|     // 3. write data
 |     // 3. write data
 | ||||||
|     if(!furi_write(holding_record, &WRITE_VALUE, sizeof(uint8_t))) { |     if(!furi_write(holding_record, &WRITE_VALUE, sizeof(uint8_t))) { | ||||||
|         fuprintf(log, "cannot write to record\n"); |         printf("cannot write to record\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // 4. check that subscriber get data
 |     // 4. check that subscriber get data
 | ||||||
|     if(holding_record_value != WRITE_VALUE) { |     if(holding_record_value != WRITE_VALUE) { | ||||||
|         fuprintf(log, "wrong sub value (get %d, write %d)\n", holding_record_value, WRITE_VALUE); |         printf("wrong sub value (get %d, write %d)\n", holding_record_value, WRITE_VALUE); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // 5. Read and check data
 |     // 5. Read and check data
 | ||||||
|     uint8_t read_value = 0; |     uint8_t read_value = 0; | ||||||
|     if(!furi_read(holding_record, &read_value, sizeof(uint8_t))) { |     if(!furi_read(holding_record, &read_value, sizeof(uint8_t))) { | ||||||
|         fuprintf(log, "cannot read from record\n"); |         printf("cannot read from record\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(read_value != WRITE_VALUE) { |     if(read_value != WRITE_VALUE) { | ||||||
|         fuprintf(log, "wrong read value (get %d, write %d)\n", read_value, WRITE_VALUE); |         printf("wrong read value (get %d, write %d)\n", read_value, WRITE_VALUE); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // 6. Try to write/read wrong size of data
 |     // 6. Try to write/read wrong size of data
 | ||||||
|     if(furi_write(holding_record, &WRITE_VALUE, 100)) { |     if(furi_write(holding_record, &WRITE_VALUE, 100)) { | ||||||
|         fuprintf(log, "overflowed write not allowed\n"); |         printf("overflowed write not allowed\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(furi_read(holding_record, &read_value, 100)) { |     if(furi_read(holding_record, &read_value, 100)) { | ||||||
|         fuprintf(log, "overflowed read not allowed\n"); |         printf("overflowed read not allowed\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -159,12 +159,10 @@ typedef struct { | |||||||
| } ConcurrentValue; | } ConcurrentValue; | ||||||
| 
 | 
 | ||||||
| void furi_concurent_app(void* p) { | void furi_concurent_app(void* p) { | ||||||
|     FuriRecordSubscriber* log = (FuriRecordSubscriber*)p; |  | ||||||
| 
 |  | ||||||
|     FuriRecordSubscriber* holding_record = |     FuriRecordSubscriber* holding_record = | ||||||
|         furi_open("test/concurrent", false, false, NULL, NULL, NULL); |         furi_open("test/concurrent", false, false, NULL, NULL, NULL); | ||||||
|     if(holding_record == NULL) { |     if(holding_record == NULL) { | ||||||
|         fuprintf(log, "cannot open record\n"); |         printf("cannot open record\n"); | ||||||
|         furiac_exit(NULL); |         furiac_exit(NULL); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -172,7 +170,7 @@ void furi_concurent_app(void* p) { | |||||||
|         ConcurrentValue* value = (ConcurrentValue*)furi_take(holding_record); |         ConcurrentValue* value = (ConcurrentValue*)furi_take(holding_record); | ||||||
| 
 | 
 | ||||||
|         if(value == NULL) { |         if(value == NULL) { | ||||||
|             fuprintf(log, "cannot take record\n"); |             printf("cannot take record\n"); | ||||||
|             furi_give(holding_record); |             furi_give(holding_record); | ||||||
|             furiac_exit(NULL); |             furiac_exit(NULL); | ||||||
|         } |         } | ||||||
| @ -190,11 +188,11 @@ void furi_concurent_app(void* p) { | |||||||
|     furiac_exit(NULL); |     furiac_exit(NULL); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool test_furi_concurrent_access(FuriRecordSubscriber* log) { | bool test_furi_concurrent_access() { | ||||||
|     // 1. Create holding record
 |     // 1. Create holding record
 | ||||||
|     ConcurrentValue holder = {.a = 0, .b = 0}; |     ConcurrentValue holder = {.a = 0, .b = 0}; | ||||||
|     if(!furi_create("test/concurrent", (void*)&holder, sizeof(ConcurrentValue))) { |     if(!furi_create("test/concurrent", (void*)&holder, sizeof(ConcurrentValue))) { | ||||||
|         fuprintf(log, "cannot create record\n"); |         printf("cannot create record\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -202,19 +200,19 @@ bool test_furi_concurrent_access(FuriRecordSubscriber* log) { | |||||||
|     FuriRecordSubscriber* holding_record = |     FuriRecordSubscriber* holding_record = | ||||||
|         furi_open("test/concurrent", false, false, NULL, NULL, NULL); |         furi_open("test/concurrent", false, false, NULL, NULL, NULL); | ||||||
|     if(holding_record == NULL) { |     if(holding_record == NULL) { | ||||||
|         fuprintf(log, "cannot open record\n"); |         printf("cannot open record\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // 3. Create second app for interact with it
 |     // 3. Create second app for interact with it
 | ||||||
|     FuriApp* second_app = furiac_start(furi_concurent_app, "furi concurent app", (void*)log); |     FuriApp* second_app = furiac_start(furi_concurent_app, "furi concurent app", NULL); | ||||||
| 
 | 
 | ||||||
|     // 4. multiply ConcurrentValue::a
 |     // 4. multiply ConcurrentValue::a
 | ||||||
|     for(size_t i = 0; i < 4; i++) { |     for(size_t i = 0; i < 4; i++) { | ||||||
|         ConcurrentValue* value = (ConcurrentValue*)furi_take(holding_record); |         ConcurrentValue* value = (ConcurrentValue*)furi_take(holding_record); | ||||||
| 
 | 
 | ||||||
|         if(value == NULL) { |         if(value == NULL) { | ||||||
|             fuprintf(log, "cannot take record\n"); |             printf("cannot take record\n"); | ||||||
|             furi_give(holding_record); |             furi_give(holding_record); | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
| @ -232,12 +230,12 @@ bool test_furi_concurrent_access(FuriRecordSubscriber* log) { | |||||||
|     delay(50); |     delay(50); | ||||||
| 
 | 
 | ||||||
|     if(second_app->handler != NULL) { |     if(second_app->handler != NULL) { | ||||||
|         fuprintf(log, "second app still alive\n"); |         printf("second app still alive\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(holder.a != holder.b) { |     if(holder.a != holder.b) { | ||||||
|         fuprintf(log, "broken integrity: a=%d, b=%d\n", holder.a, holder.b); |         printf("broken integrity: a=%d, b=%d\n", holder.a, holder.b); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -252,7 +250,7 @@ TEST: non-existent data | |||||||
| 
 | 
 | ||||||
| TODO: implement this test | TODO: implement this test | ||||||
| */ | */ | ||||||
| bool test_furi_nonexistent_data(FuriRecordSubscriber* log) { | bool test_furi_nonexistent_data() { | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -310,11 +308,9 @@ void mute_record_state_cb(FlipperRecordState state, void* ctx) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_mute_parent_app(void* p) { | void furi_mute_parent_app(void* p) { | ||||||
|     FuriRecordSubscriber* log = (FuriRecordSubscriber*)p; |  | ||||||
| 
 |  | ||||||
|     // 1. Create pipe record
 |     // 1. Create pipe record
 | ||||||
|     if(!furi_create("test/mute", NULL, 0)) { |     if(!furi_create("test/mute", NULL, 0)) { | ||||||
|         fuprintf(log, "cannot create record\n"); |         printf("cannot create record\n"); | ||||||
|         furiac_exit(NULL); |         furiac_exit(NULL); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -322,7 +318,7 @@ void furi_mute_parent_app(void* p) { | |||||||
|     FuriRecordSubscriber* watch_handler = |     FuriRecordSubscriber* watch_handler = | ||||||
|         furi_open("test/mute", false, false, mute_record_cb, NULL, NULL); |         furi_open("test/mute", false, false, mute_record_cb, NULL, NULL); | ||||||
|     if(watch_handler == NULL) { |     if(watch_handler == NULL) { | ||||||
|         fuprintf(log, "cannot open watch handler\n"); |         printf("cannot open watch handler\n"); | ||||||
|         furiac_exit(NULL); |         furiac_exit(NULL); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -332,9 +328,9 @@ void furi_mute_parent_app(void* p) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { | bool test_furi_mute_algorithm() { | ||||||
|     // 1. Create "parent" application:
 |     // 1. Create "parent" application:
 | ||||||
|     FuriApp* parent_app = furiac_start(furi_mute_parent_app, "parent app", (void*)log); |     FuriApp* parent_app = furiac_start(furi_mute_parent_app, "parent app", NULL); | ||||||
| 
 | 
 | ||||||
|     delay(2); // wait creating record
 |     delay(2); // wait creating record
 | ||||||
| 
 | 
 | ||||||
| @ -342,7 +338,7 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { | |||||||
|     FuriRecordSubscriber* handler_a = |     FuriRecordSubscriber* handler_a = | ||||||
|         furi_open("test/mute", false, false, NULL, mute_record_state_cb, NULL); |         furi_open("test/mute", false, false, NULL, mute_record_state_cb, NULL); | ||||||
|     if(handler_a == NULL) { |     if(handler_a == NULL) { | ||||||
|         fuprintf(log, "cannot open handler A\n"); |         printf("cannot open handler A\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -350,25 +346,25 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { | |||||||
| 
 | 
 | ||||||
|     // Try to write data to A and check subscriber
 |     // Try to write data to A and check subscriber
 | ||||||
|     if(!furi_write(handler_a, &test_counter, sizeof(uint8_t))) { |     if(!furi_write(handler_a, &test_counter, sizeof(uint8_t))) { | ||||||
|         fuprintf(log, "write to A failed\n"); |         printf("write to A failed\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(mute_last_value != test_counter) { |     if(mute_last_value != test_counter) { | ||||||
|         fuprintf(log, "value A mismatch: %d vs %d\n", mute_last_value, test_counter); |         printf("value A mismatch: %d vs %d\n", mute_last_value, test_counter); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // 3. Open handler B: solo=true, no_mute=true, NULL subscriber.
 |     // 3. Open handler B: solo=true, no_mute=true, NULL subscriber.
 | ||||||
|     FuriRecordSubscriber* handler_b = furi_open("test/mute", true, true, NULL, NULL, NULL); |     FuriRecordSubscriber* handler_b = furi_open("test/mute", true, true, NULL, NULL, NULL); | ||||||
|     if(handler_b == NULL) { |     if(handler_b == NULL) { | ||||||
|         fuprintf(log, "cannot open handler B\n"); |         printf("cannot open handler B\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Check A state cb get FlipperRecordStateMute.
 |     // Check A state cb get FlipperRecordStateMute.
 | ||||||
|     if(mute_last_state != FlipperRecordStateMute) { |     if(mute_last_state != FlipperRecordStateMute) { | ||||||
|         fuprintf(log, "A state is not FlipperRecordStateMute: %d\n", mute_last_state); |         printf("A state is not FlipperRecordStateMute: %d\n", mute_last_state); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -376,12 +372,12 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { | |||||||
| 
 | 
 | ||||||
|     // Try to write data to A and check that subscriber get no data. (muted)
 |     // Try to write data to A and check that subscriber get no data. (muted)
 | ||||||
|     if(furi_write(handler_a, &test_counter, sizeof(uint8_t))) { |     if(furi_write(handler_a, &test_counter, sizeof(uint8_t))) { | ||||||
|         fuprintf(log, "A not muted\n"); |         printf("A not muted\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(mute_last_value == test_counter) { |     if(mute_last_value == test_counter) { | ||||||
|         fuprintf(log, "value A must be muted\n"); |         printf("value A must be muted\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -389,19 +385,19 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { | |||||||
| 
 | 
 | ||||||
|     // Try to write data to B and check that subscriber get data.
 |     // Try to write data to B and check that subscriber get data.
 | ||||||
|     if(!furi_write(handler_b, &test_counter, sizeof(uint8_t))) { |     if(!furi_write(handler_b, &test_counter, sizeof(uint8_t))) { | ||||||
|         fuprintf(log, "write to B failed\n"); |         printf("write to B failed\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(mute_last_value != test_counter) { |     if(mute_last_value != test_counter) { | ||||||
|         fuprintf(log, "value B mismatch: %d vs %d\n", mute_last_value, test_counter); |         printf("value B mismatch: %d vs %d\n", mute_last_value, test_counter); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // 4. Open hadler C: solo=true, no_mute=false, NULL subscriber.
 |     // 4. Open hadler C: solo=true, no_mute=false, NULL subscriber.
 | ||||||
|     FuriRecordSubscriber* handler_c = furi_open("test/mute", true, false, NULL, NULL, NULL); |     FuriRecordSubscriber* handler_c = furi_open("test/mute", true, false, NULL, NULL, NULL); | ||||||
|     if(handler_c == NULL) { |     if(handler_c == NULL) { | ||||||
|         fuprintf(log, "cannot open handler C\n"); |         printf("cannot open handler C\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -412,7 +408,7 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { | |||||||
|     // 5. Open handler D: solo=false, no_mute=false, NULL subscriber.
 |     // 5. Open handler D: solo=false, no_mute=false, NULL subscriber.
 | ||||||
|     FuriRecordSubscriber* handler_d = furi_open("test/mute", false, false, NULL, NULL, NULL); |     FuriRecordSubscriber* handler_d = furi_open("test/mute", false, false, NULL, NULL, NULL); | ||||||
|     if(handler_d == NULL) { |     if(handler_d == NULL) { | ||||||
|         fuprintf(log, "cannot open handler D\n"); |         printf("cannot open handler D\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -428,7 +424,7 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { | |||||||
| 
 | 
 | ||||||
|     // 7. Exit "parent application"
 |     // 7. Exit "parent application"
 | ||||||
|     if(!furiac_kill(parent_app)) { |     if(!furiac_kill(parent_app)) { | ||||||
|         fuprintf(log, "kill parent_app fail\n"); |         printf("kill parent_app fail\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -23,26 +23,26 @@ void create_kill_app(void* p) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool test_furi_ac_create_kill(FuriRecordSubscriber* log) { | bool test_furi_ac_create_kill() { | ||||||
|     uint8_t counter = 0; |     uint8_t counter = 0; | ||||||
| 
 | 
 | ||||||
|     uint8_t value_a = counter; |     uint8_t value_a = counter; | ||||||
| 
 | 
 | ||||||
|     FuriApp* widget = furiac_start(create_kill_app, "create_kill_app", (void*)&counter); |     FuriApp* widget = furiac_start(create_kill_app, "create_kill_app", (void*)&counter); | ||||||
|     if(widget == NULL) { |     if(widget == NULL) { | ||||||
|         fuprintf(log, "create widget fail\n"); |         printf("create widget fail\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     delay(10); |     delay(10); | ||||||
| 
 | 
 | ||||||
|     if(!furiac_kill(widget)) { |     if(!furiac_kill(widget)) { | ||||||
|         fuprintf(log, "kill widget fail\n"); |         printf("kill widget fail\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(value_a == counter) { |     if(value_a == counter) { | ||||||
|         fuprintf(log, "counter unchanged\n"); |         printf("counter unchanged\n"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -51,7 +51,7 @@ bool test_furi_ac_create_kill(FuriRecordSubscriber* log) { | |||||||
|     delay(10); |     delay(10); | ||||||
| 
 | 
 | ||||||
|     if(value_a != counter) { |     if(value_a != counter) { | ||||||
|         fuprintf(log, "counter changes after kill (counter = %d vs %d)\n", value_a, counter); |         printf("counter changes after kill (counter = %d vs %d)\n", value_a, counter); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -111,7 +111,7 @@ void task_b(void* p) { | |||||||
|     furiac_exit(p); |     furiac_exit(p); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool test_furi_ac_switch_exit(FuriRecordSubscriber* log) { | bool test_furi_ac_switch_exit() { | ||||||
|     // init sequence
 |     // init sequence
 | ||||||
|     TestSwitchSequence seq; |     TestSwitchSequence seq; | ||||||
|     seq.count = 0; |     seq.count = 0; | ||||||
| @ -124,7 +124,7 @@ bool test_furi_ac_switch_exit(FuriRecordSubscriber* log) { | |||||||
|     seq.sequence[seq.count] = '\0'; |     seq.sequence[seq.count] = '\0'; | ||||||
| 
 | 
 | ||||||
|     if(strcmp(seq.sequence, "ABA/") != 0) { |     if(strcmp(seq.sequence, "ABA/") != 0) { | ||||||
|         fuprintf(log, "wrong sequence: %s\n", seq.sequence); |         printf("wrong sequence: %s\n", seq.sequence); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										562
									
								
								applications/tests/minunit.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										562
									
								
								applications/tests/minunit.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,562 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2012 David Siñuela Pastor, siu.4coders@gmail.com | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining | ||||||
|  |  * a copy of this software and associated documentation files (the | ||||||
|  |  * "Software"), to deal in the Software without restriction, including | ||||||
|  |  * without limitation the rights to use, copy, modify, merge, publish, | ||||||
|  |  * distribute, sublicense, and/or sell copies of the Software, and to | ||||||
|  |  * permit persons to whom the Software is furnished to do so, subject to | ||||||
|  |  * the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be | ||||||
|  |  * included in all copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||||
|  |  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||||
|  |  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||||||
|  |  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||||||
|  |  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||||||
|  |  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||||||
|  |  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||||
|  |  */ | ||||||
|  | #ifndef MINUNIT_MINUNIT_H | ||||||
|  | #define MINUNIT_MINUNIT_H | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | 	extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #if defined(_WIN32) | ||||||
|  | #include <Windows.h> | ||||||
|  | #if defined(_MSC_VER) && _MSC_VER < 1900 | ||||||
|  |   #define snprintf _snprintf | ||||||
|  |   #define __func__ __FUNCTION__ | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) | ||||||
|  | 
 | ||||||
|  | /* Change POSIX C SOURCE version for pure c99 compilers */ | ||||||
|  | #if !defined(_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 200112L | ||||||
|  | #undef _POSIX_C_SOURCE | ||||||
|  | #define _POSIX_C_SOURCE 200112L | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #include <unistd.h>	/* POSIX flags */ | ||||||
|  | #include <time.h>	/* clock_gettime(), time() */ | ||||||
|  | #include <sys/time.h>	/* gethrtime(), gettimeofday() */ | ||||||
|  | #include <sys/resource.h> | ||||||
|  | #include <sys/times.h> | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #if defined(__MACH__) && defined(__APPLE__) | ||||||
|  | #include <mach/mach.h> | ||||||
|  | #include <mach/mach_time.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #if __GNUC__ >= 5 && !defined(__STDC_VERSION__) | ||||||
|  | #define __func__ __extension__ __FUNCTION__ | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #else | ||||||
|  | 
 | ||||||
|  | // #error "Unable to define timers for an unknown OS."
 | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <math.h> | ||||||
|  | 
 | ||||||
|  | /*  Maximum length of last message */ | ||||||
|  | #define MINUNIT_MESSAGE_LEN 1024 | ||||||
|  | /*  Accuracy with which floats are compared */ | ||||||
|  | #define MINUNIT_EPSILON 1E-12 | ||||||
|  | 
 | ||||||
|  | #include "minunit_vars_ex.h" | ||||||
|  | 
 | ||||||
|  | /*  Test setup and teardown function pointers */ | ||||||
|  | static void (*minunit_setup)(void) = NULL; | ||||||
|  | static void (*minunit_teardown)(void) = NULL; | ||||||
|  | 
 | ||||||
|  | /*  Definitions */ | ||||||
|  | #define MU_TEST(method_name) static void method_name(void) | ||||||
|  | #define MU_TEST_SUITE(suite_name) static void suite_name(void) | ||||||
|  | 
 | ||||||
|  | #define MU__SAFE_BLOCK(block) do {\ | ||||||
|  | 	block\ | ||||||
|  | } while(0) | ||||||
|  | 
 | ||||||
|  | /*  Run test suite and unset setup and teardown functions */ | ||||||
|  | #define MU_RUN_SUITE(suite_name) MU__SAFE_BLOCK(\ | ||||||
|  | 	suite_name();\ | ||||||
|  | 	minunit_setup = NULL;\ | ||||||
|  | 	minunit_teardown = NULL;\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | /*  Configure setup and teardown functions */ | ||||||
|  | #define MU_SUITE_CONFIGURE(setup_fun, teardown_fun) MU__SAFE_BLOCK(\ | ||||||
|  | 	minunit_setup = setup_fun;\ | ||||||
|  | 	minunit_teardown = teardown_fun;\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | /*  Test runner */ | ||||||
|  | #define MU_RUN_TEST(test) MU__SAFE_BLOCK(\ | ||||||
|  | 	if (minunit_real_timer==0 && minunit_proc_timer==0) {\ | ||||||
|  | 		minunit_real_timer = mu_timer_real();\ | ||||||
|  | 		minunit_proc_timer = mu_timer_cpu();\ | ||||||
|  | 	}\ | ||||||
|  | 	if (minunit_setup) (*minunit_setup)();\ | ||||||
|  | 	minunit_status = 0;\ | ||||||
|  | 	test();\ | ||||||
|  | 	minunit_run++;\ | ||||||
|  | 	if (minunit_status) {\ | ||||||
|  | 		minunit_fail++;\ | ||||||
|  | 		printf("F");\ | ||||||
|  | 		printf("\n%s\n", minunit_last_message);\ | ||||||
|  | 	}\ | ||||||
|  | 	fflush(stdout);\ | ||||||
|  | 	if (minunit_teardown) (*minunit_teardown)();\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | /*  Report */ | ||||||
|  | #define MU_REPORT() MU__SAFE_BLOCK(\ | ||||||
|  | 	double minunit_end_real_timer;\ | ||||||
|  | 	double minunit_end_proc_timer;\ | ||||||
|  | 	printf("\n\n%d tests, %d assertions, %d failures\n", minunit_run, minunit_assert, minunit_fail);\ | ||||||
|  | 	minunit_end_real_timer = mu_timer_real();\ | ||||||
|  | 	minunit_end_proc_timer = mu_timer_cpu();\ | ||||||
|  | 	printf("\nFinished in %.8f seconds (real) %.8f seconds (proc)\n\n",\ | ||||||
|  | 		minunit_end_real_timer - minunit_real_timer,\ | ||||||
|  | 		minunit_end_proc_timer - minunit_proc_timer);\ | ||||||
|  | ) | ||||||
|  | #define MU_EXIT_CODE minunit_fail | ||||||
|  | 
 | ||||||
|  | /*  Assertions */ | ||||||
|  | #define mu_check(test) MU__SAFE_BLOCK(\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	if (!(test)) {\ | ||||||
|  | 		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %s", __func__, __FILE__, __LINE__, #test);\ | ||||||
|  | 		minunit_status = 1;\ | ||||||
|  | 		return;\ | ||||||
|  | 	} else {\ | ||||||
|  | 		printf(".");\ | ||||||
|  | 	}\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | #define mu_fail(message) MU__SAFE_BLOCK(\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %s", __func__, __FILE__, __LINE__, message);\ | ||||||
|  | 	minunit_status = 1;\ | ||||||
|  | 	return;\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | #define mu_assert(test, message) MU__SAFE_BLOCK(\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	if (!(test)) {\ | ||||||
|  | 		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %s", __func__, __FILE__, __LINE__, message);\ | ||||||
|  | 		minunit_status = 1;\ | ||||||
|  | 		return;\ | ||||||
|  | 	} else {\ | ||||||
|  | 		printf(".");\ | ||||||
|  | 	}\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | #define mu_assert_int_eq(expected, result) MU__SAFE_BLOCK(\ | ||||||
|  | 	int minunit_tmp_e;\ | ||||||
|  | 	int minunit_tmp_r;\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	minunit_tmp_e = (expected);\ | ||||||
|  | 	minunit_tmp_r = (result);\ | ||||||
|  | 	if (minunit_tmp_e != minunit_tmp_r) {\ | ||||||
|  | 		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %d expected but was %d", __func__, __FILE__, __LINE__, minunit_tmp_e, minunit_tmp_r);\ | ||||||
|  | 		minunit_status = 1;\ | ||||||
|  | 		return;\ | ||||||
|  | 	} else {\ | ||||||
|  | 		printf(".");\ | ||||||
|  | 	}\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | #define mu_assert_int_not_eq(expected, result) MU__SAFE_BLOCK(\ | ||||||
|  | 	int minunit_tmp_e;\ | ||||||
|  | 	int minunit_tmp_r;\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	minunit_tmp_e = (expected);\ | ||||||
|  | 	minunit_tmp_r = (result);\ | ||||||
|  | 	if (minunit_tmp_e == minunit_tmp_r) {\ | ||||||
|  | 		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: expected different results but both were %d", __func__, __FILE__, __LINE__, minunit_tmp_e);\ | ||||||
|  | 		minunit_status = 1;\ | ||||||
|  | 		return;\ | ||||||
|  | 	} else {\ | ||||||
|  | 		printf(".");\ | ||||||
|  | 	}\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | #define mu_assert_int_greater_than(val, result) MU__SAFE_BLOCK(\ | ||||||
|  | 	int minunit_tmp_e;\ | ||||||
|  | 	int minunit_tmp_r;\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	minunit_tmp_e = (val);\ | ||||||
|  | 	minunit_tmp_r = (result);\ | ||||||
|  | 	if (val >= minunit_tmp_r) {\ | ||||||
|  | 		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %d <= %d", __func__, __FILE__, __LINE__, minunit_tmp_r, minunit_tmp_e);\ | ||||||
|  | 		minunit_status = 1;\ | ||||||
|  | 		return;\ | ||||||
|  | 	} else {\ | ||||||
|  | 		printf(".");\ | ||||||
|  | 	}\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | #define mu_assert_int_less_than(val, result) MU__SAFE_BLOCK(\ | ||||||
|  | 	int minunit_tmp_e;\ | ||||||
|  | 	int minunit_tmp_r;\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	minunit_tmp_e = (val);\ | ||||||
|  | 	minunit_tmp_r = (result);\ | ||||||
|  | 	if (val <= minunit_tmp_r) {\ | ||||||
|  | 		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %d >= %d", __func__, __FILE__, __LINE__, minunit_tmp_r, minunit_tmp_e);\ | ||||||
|  | 		minunit_status = 1;\ | ||||||
|  | 		return;\ | ||||||
|  | 	} else {\ | ||||||
|  | 		printf(".");\ | ||||||
|  | 	}\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | #define mu_assert_int_between(expected_lower, expected_upper, result) MU__SAFE_BLOCK(\ | ||||||
|  | 	int minunit_tmp_e;\ | ||||||
|  | 	int minunit_tmp_m;\ | ||||||
|  | 	int minunit_tmp_r;\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	minunit_tmp_e = (expected_lower);\ | ||||||
|  | 	minunit_tmp_m = (expected_upper);\ | ||||||
|  | 	minunit_tmp_r = (result);\ | ||||||
|  | 	if (result < minunit_tmp_e || result > minunit_tmp_m) {\ | ||||||
|  | 		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %d was not between (inclusive) %d and %d", __func__, __FILE__, __LINE__,  minunit_tmp_e, minunit_tmp_r, minunit_tmp_m);\ | ||||||
|  | 		minunit_status = 1;\ | ||||||
|  | 		return;\ | ||||||
|  | 	} else {\ | ||||||
|  | 		printf(".");\ | ||||||
|  | 	}\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | #define mu_assert_int_in(expected, array_length, result) MU__SAFE_BLOCK(\ | ||||||
|  | 	int minunit_tmp_r;\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	minunit_tmp_r = (result);\ | ||||||
|  | 	int t = 0;\ | ||||||
|  | 	int i;\ | ||||||
|  | 	for (i = 0; i < array_length; i++) {\ | ||||||
|  | 		if (expected[i] == minunit_tmp_r)\ | ||||||
|  | 			t = 1;\ | ||||||
|  | 	}\ | ||||||
|  | 	if (t == 0) {\ | ||||||
|  | 		char tmp[500] = {0};\ | ||||||
|  | 		tmp[0] = '[';\ | ||||||
|  | 		for (i = 0; i < array_length; i++) {\ | ||||||
|  | 			sprintf(tmp + strlen(tmp), "%d, ", expected[i]);\ | ||||||
|  | 		}\ | ||||||
|  | 		int len = strlen(tmp);\ | ||||||
|  | 		tmp[len - 2] = ']';\ | ||||||
|  | 		tmp[len - 1] = '\0';\ | ||||||
|  | 		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: expected to be one of %s but was %d", __func__, __FILE__, __LINE__, tmp, minunit_tmp_r);\ | ||||||
|  | 		minunit_status = 1;\ | ||||||
|  | 		return;\ | ||||||
|  | 	} else {\ | ||||||
|  | 		printf(".");\ | ||||||
|  | 	}\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | #define mu_assert_double_eq(expected, result) MU__SAFE_BLOCK(\ | ||||||
|  | 	double minunit_tmp_e;\ | ||||||
|  | 	double minunit_tmp_r;\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	minunit_tmp_e = (expected);\ | ||||||
|  | 	minunit_tmp_r = (result);\ | ||||||
|  | 	if (fabs(minunit_tmp_e-minunit_tmp_r) > MINUNIT_EPSILON) {\ | ||||||
|  | 		int minunit_significant_figures = 1 - log10(MINUNIT_EPSILON);\ | ||||||
|  | 		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %.*g expected but was %.*g", __func__, __FILE__, __LINE__, minunit_significant_figures, minunit_tmp_e, minunit_significant_figures, minunit_tmp_r);\ | ||||||
|  | 		minunit_status = 1;\ | ||||||
|  | 		return;\ | ||||||
|  | 	} else {\ | ||||||
|  | 		printf(".");\ | ||||||
|  | 	}\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | #define mu_assert_double_greater_than(val, result) MU__SAFE_BLOCK(\ | ||||||
|  | 	double minunit_tmp_e;\ | ||||||
|  | 	double minunit_tmp_r;\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	minunit_tmp_e = (val);\ | ||||||
|  | 	minunit_tmp_r = (result);\ | ||||||
|  | 	if (val >= minunit_tmp_r) {\ | ||||||
|  | 		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %f <= %f", __func__, __FILE__, __LINE__, minunit_tmp_r, minunit_tmp_e);\ | ||||||
|  | 		minunit_status = 1;\ | ||||||
|  | 		return;\ | ||||||
|  | 	} else {\ | ||||||
|  | 		printf(".");\ | ||||||
|  | 	}\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | #define mu_assert_double_less_than(val, result) MU__SAFE_BLOCK(\ | ||||||
|  | 	double minunit_tmp_e;\ | ||||||
|  | 	double minunit_tmp_r;\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	minunit_tmp_e = (val);\ | ||||||
|  | 	minunit_tmp_r = (result);\ | ||||||
|  | 	if (val <= minunit_tmp_r) {\ | ||||||
|  | 		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %f >= %f", __func__, __FILE__, __LINE__, minunit_tmp_r, minunit_tmp_e);\ | ||||||
|  | 		minunit_status = 1;\ | ||||||
|  | 		return;\ | ||||||
|  | 	} else {\ | ||||||
|  | 		printf(".");\ | ||||||
|  | 	}\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | #define mu_assert_double_between(expected_lower, expected_upper, result) MU__SAFE_BLOCK(\ | ||||||
|  | 	double minunit_tmp_e;\ | ||||||
|  | 	double minunit_tmp_m;\ | ||||||
|  | 	double minunit_tmp_r;\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	minunit_tmp_e = (expected_lower);\ | ||||||
|  | 	minunit_tmp_m = (expected_upper);\ | ||||||
|  | 	minunit_tmp_r = (result);\ | ||||||
|  | 	if (result < minunit_tmp_e || result > minunit_tmp_m) {\ | ||||||
|  | 		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %f was not between (inclusive) %f and %f", __func__, __FILE__, __LINE__,  minunit_tmp_e, minunit_tmp_r, minunit_tmp_m);\ | ||||||
|  | 		minunit_status = 1;\ | ||||||
|  | 		return;\ | ||||||
|  | 	} else {\ | ||||||
|  | 		printf(".");\ | ||||||
|  | 	}\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | #define mu_assert_string_eq(expected, result) MU__SAFE_BLOCK(\ | ||||||
|  | 	const char* minunit_tmp_e = expected;\ | ||||||
|  | 	const char* minunit_tmp_r = result;\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	if (!minunit_tmp_e) {\ | ||||||
|  | 		minunit_tmp_e = "<null pointer>";\ | ||||||
|  | 	}\ | ||||||
|  | 	if (!minunit_tmp_r) {\ | ||||||
|  | 		minunit_tmp_r = "<null pointer>";\ | ||||||
|  | 	}\ | ||||||
|  | 	if(strcmp(minunit_tmp_e, minunit_tmp_r)) {\ | ||||||
|  | 		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: '%s' expected but was '%s'", __func__, __FILE__, __LINE__, minunit_tmp_e, minunit_tmp_r);\ | ||||||
|  | 		minunit_status = 1;\ | ||||||
|  | 		return;\ | ||||||
|  | 	} else {\ | ||||||
|  | 		printf(".");\ | ||||||
|  | 	}\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | #define mu_assert_null(result) MU__SAFE_BLOCK(\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	if (result == NULL) {\ | ||||||
|  | 		printf(".");\ | ||||||
|  | 	} else {\ | ||||||
|  | 		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: Expected result was not NULL", __func__, __FILE__, __LINE__);\ | ||||||
|  | 		minunit_status = 1;\ | ||||||
|  | 		return;\ | ||||||
|  | 	}\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | #define mu_assert_not_null(result) MU__SAFE_BLOCK(\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	if (result != NULL) {\ | ||||||
|  | 		printf(".");\ | ||||||
|  | 	} else {\ | ||||||
|  | 		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: Expected result was not NULL", __func__, __FILE__, __LINE__);\ | ||||||
|  | 		minunit_status = 1;\ | ||||||
|  | 		return;\ | ||||||
|  | 	}\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | #define mu_assert_pointers_eq(pointer1, pointer2) MU__SAFE_BLOCK(\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	if (pointer1 == pointer2) {\ | ||||||
|  | 		printf(".");\ | ||||||
|  | 	} else {\ | ||||||
|  | 		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: Expected the pointers to point to the same memory location", __func__, __FILE__, __LINE__);\ | ||||||
|  | 		minunit_status = 1;\ | ||||||
|  | 		return;\ | ||||||
|  | 	}\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | #define mu_assert_pointers_not_eq(pointer1, pointer2) MU__SAFE_BLOCK(\ | ||||||
|  | 	minunit_assert++;\ | ||||||
|  | 	if (pointer1 != pointer2) {\ | ||||||
|  | 		printf(".");\ | ||||||
|  | 	} else {\ | ||||||
|  | 		snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: Expected the pointers to point to the same memory location", __func__, __FILE__, __LINE__);\ | ||||||
|  | 		minunit_status = 1;\ | ||||||
|  | 		return;\ | ||||||
|  | 	}\ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * The following two functions were written by David Robert Nadeau | ||||||
|  |  * from http://NadeauSoftware.com/ and distributed under the
 | ||||||
|  |  * Creative Commons Attribution 3.0 Unported License | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns the real time, in seconds, or -1.0 if an error occurred. | ||||||
|  |  * | ||||||
|  |  * Time is measured since an arbitrary and OS-dependent start time. | ||||||
|  |  * The returned real time is only useful for computing an elapsed time | ||||||
|  |  * between two calls to this function. | ||||||
|  |  */ | ||||||
|  | static double mu_timer_real(void) | ||||||
|  | { | ||||||
|  | #if defined(_WIN32) | ||||||
|  | 	/* Windows 2000 and later. ---------------------------------- */ | ||||||
|  | 	LARGE_INTEGER Time; | ||||||
|  | 	LARGE_INTEGER Frequency; | ||||||
|  | 
 | ||||||
|  | 	QueryPerformanceFrequency(&Frequency); | ||||||
|  | 	QueryPerformanceCounter(&Time); | ||||||
|  | 
 | ||||||
|  | 	Time.QuadPart *= 1000000; | ||||||
|  | 	Time.QuadPart /= Frequency.QuadPart; | ||||||
|  | 
 | ||||||
|  | 	return (double)Time.QuadPart / 1000000.0; | ||||||
|  | 
 | ||||||
|  | #elif (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__))) | ||||||
|  | 	/* HP-UX, Solaris. ------------------------------------------ */ | ||||||
|  | 	return (double)gethrtime( ) / 1000000000.0; | ||||||
|  | 
 | ||||||
|  | #elif defined(__MACH__) && defined(__APPLE__) | ||||||
|  | 	/* OSX. ----------------------------------------------------- */ | ||||||
|  | 	static double timeConvert = 0.0; | ||||||
|  | 	if ( timeConvert == 0.0 ) | ||||||
|  | 	{ | ||||||
|  | 		mach_timebase_info_data_t timeBase; | ||||||
|  | 		(void)mach_timebase_info( &timeBase ); | ||||||
|  | 		timeConvert = (double)timeBase.numer / | ||||||
|  | 			(double)timeBase.denom / | ||||||
|  | 			1000000000.0; | ||||||
|  | 	} | ||||||
|  | 	return (double)mach_absolute_time( ) * timeConvert; | ||||||
|  | 
 | ||||||
|  | #elif defined(_POSIX_VERSION) | ||||||
|  | 	/* POSIX. --------------------------------------------------- */ | ||||||
|  | 	struct timeval tm; | ||||||
|  | #if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) | ||||||
|  | 	{ | ||||||
|  | 		struct timespec ts; | ||||||
|  | #if defined(CLOCK_MONOTONIC_PRECISE) | ||||||
|  | 		/* BSD. --------------------------------------------- */ | ||||||
|  | 		const clockid_t id = CLOCK_MONOTONIC_PRECISE; | ||||||
|  | #elif defined(CLOCK_MONOTONIC_RAW) | ||||||
|  | 		/* Linux. ------------------------------------------- */ | ||||||
|  | 		const clockid_t id = CLOCK_MONOTONIC_RAW; | ||||||
|  | #elif defined(CLOCK_HIGHRES) | ||||||
|  | 		/* Solaris. ----------------------------------------- */ | ||||||
|  | 		const clockid_t id = CLOCK_HIGHRES; | ||||||
|  | #elif defined(CLOCK_MONOTONIC) | ||||||
|  | 		/* AIX, BSD, Linux, POSIX, Solaris. ----------------- */ | ||||||
|  | 		const clockid_t id = CLOCK_MONOTONIC; | ||||||
|  | #elif defined(CLOCK_REALTIME) | ||||||
|  | 		/* AIX, BSD, HP-UX, Linux, POSIX. ------------------- */ | ||||||
|  | 		const clockid_t id = CLOCK_REALTIME; | ||||||
|  | #else | ||||||
|  | 		const clockid_t id = (clockid_t)-1;	/* Unknown. */ | ||||||
|  | #endif /* CLOCK_* */ | ||||||
|  | 		if ( id != (clockid_t)-1 && clock_gettime( id, &ts ) != -1 ) | ||||||
|  | 			return (double)ts.tv_sec + | ||||||
|  | 				(double)ts.tv_nsec / 1000000000.0; | ||||||
|  | 		/* Fall thru. */ | ||||||
|  | 	} | ||||||
|  | #endif /* _POSIX_TIMERS */ | ||||||
|  | 
 | ||||||
|  | 	/* AIX, BSD, Cygwin, HP-UX, Linux, OSX, POSIX, Solaris. ----- */ | ||||||
|  | 	gettimeofday( &tm, NULL ); | ||||||
|  | 	return (double)tm.tv_sec + (double)tm.tv_usec / 1000000.0; | ||||||
|  | #else | ||||||
|  | 	return -1.0;		/* Failed. */ | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns the amount of CPU time used by the current process, | ||||||
|  |  * in seconds, or -1.0 if an error occurred. | ||||||
|  |  */ | ||||||
|  | static double mu_timer_cpu(void) | ||||||
|  | { | ||||||
|  | #if defined(_WIN32) | ||||||
|  | 	/* Windows -------------------------------------------------- */ | ||||||
|  | 	FILETIME createTime; | ||||||
|  | 	FILETIME exitTime; | ||||||
|  | 	FILETIME kernelTime; | ||||||
|  | 	FILETIME userTime; | ||||||
|  | 
 | ||||||
|  | 	/* This approach has a resolution of 1/64 second. Unfortunately, Windows' API does not offer better */ | ||||||
|  | 	if ( GetProcessTimes( GetCurrentProcess( ), | ||||||
|  | 		&createTime, &exitTime, &kernelTime, &userTime ) != 0 ) | ||||||
|  | 	{ | ||||||
|  | 		ULARGE_INTEGER userSystemTime; | ||||||
|  | 		memcpy(&userSystemTime, &userTime, sizeof(ULARGE_INTEGER)); | ||||||
|  | 		return (double)userSystemTime.QuadPart / 10000000.0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) | ||||||
|  | 	/* AIX, BSD, Cygwin, HP-UX, Linux, OSX, and Solaris --------- */ | ||||||
|  | 
 | ||||||
|  | #if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) | ||||||
|  | 	/* Prefer high-res POSIX timers, when available. */ | ||||||
|  | 	{ | ||||||
|  | 		clockid_t id; | ||||||
|  | 		struct timespec ts; | ||||||
|  | #if _POSIX_CPUTIME > 0 | ||||||
|  | 		/* Clock ids vary by OS.  Query the id, if possible. */ | ||||||
|  | 		if ( clock_getcpuclockid( 0, &id ) == -1 ) | ||||||
|  | #endif | ||||||
|  | #if defined(CLOCK_PROCESS_CPUTIME_ID) | ||||||
|  | 			/* Use known clock id for AIX, Linux, or Solaris. */ | ||||||
|  | 			id = CLOCK_PROCESS_CPUTIME_ID; | ||||||
|  | #elif defined(CLOCK_VIRTUAL) | ||||||
|  | 			/* Use known clock id for BSD or HP-UX. */ | ||||||
|  | 			id = CLOCK_VIRTUAL; | ||||||
|  | #else | ||||||
|  | 			id = (clockid_t)-1; | ||||||
|  | #endif | ||||||
|  | 		if ( id != (clockid_t)-1 && clock_gettime( id, &ts ) != -1 ) | ||||||
|  | 			return (double)ts.tv_sec + | ||||||
|  | 				(double)ts.tv_nsec / 1000000000.0; | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #if defined(RUSAGE_SELF) | ||||||
|  | 	{ | ||||||
|  | 		struct rusage rusage; | ||||||
|  | 		if ( getrusage( RUSAGE_SELF, &rusage ) != -1 ) | ||||||
|  | 			return (double)rusage.ru_utime.tv_sec + | ||||||
|  | 				(double)rusage.ru_utime.tv_usec / 1000000.0; | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #if defined(_SC_CLK_TCK) | ||||||
|  | 	{ | ||||||
|  | 		const double ticks = (double)sysconf( _SC_CLK_TCK ); | ||||||
|  | 		struct tms tms; | ||||||
|  | 		if ( times( &tms ) != (clock_t)-1 ) | ||||||
|  | 			return (double)tms.tms_utime / ticks; | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #if defined(CLOCKS_PER_SEC) | ||||||
|  | 	{ | ||||||
|  | 		clock_t cl = clock( ); | ||||||
|  | 		if ( cl != (clock_t)-1 ) | ||||||
|  | 			return (double)cl / (double)CLOCKS_PER_SEC; | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	return -1;		/* Failed. */ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #endif /* MINUNIT_MINUNIT_H */ | ||||||
							
								
								
									
										77
									
								
								applications/tests/minunit_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								applications/tests/minunit_test.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | |||||||
|  | #include <stdio.h> | ||||||
|  | #include "flipper.h" | ||||||
|  | #include "log.h" | ||||||
|  | #include "minunit_vars.h" | ||||||
|  | #include "minunit.h" | ||||||
|  | 
 | ||||||
|  | bool test_furi_ac_create_kill(); | ||||||
|  | bool test_furi_ac_switch_exit(); | ||||||
|  | bool test_furi_pipe_record(); | ||||||
|  | bool test_furi_holding_data(); | ||||||
|  | bool test_furi_concurrent_access(); | ||||||
|  | bool test_furi_nonexistent_data(); | ||||||
|  | bool test_furi_mute_algorithm(); | ||||||
|  | 
 | ||||||
|  | static int foo = 0; | ||||||
|  | 
 | ||||||
|  | void test_setup(void) { | ||||||
|  |     foo = 7; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void test_teardown(void) { | ||||||
|  |     /* Nothing */ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST(test_check) { | ||||||
|  |     mu_check(foo != 6); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST(mu_test_furi_ac_create_kill) { | ||||||
|  |     mu_assert_int_eq(test_furi_ac_create_kill(), true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST(mu_test_furi_ac_switch_exit) { | ||||||
|  |     mu_assert_int_eq(test_furi_ac_switch_exit(), true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST(mu_test_furi_pipe_record) { | ||||||
|  |     mu_assert_int_eq(test_furi_pipe_record(), true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST(mu_test_furi_holding_data) { | ||||||
|  |     mu_assert_int_eq(test_furi_holding_data(), true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST(mu_test_furi_concurrent_access) { | ||||||
|  |     mu_assert_int_eq(test_furi_concurrent_access(), true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST(mu_test_furi_nonexistent_data) { | ||||||
|  |     mu_assert_int_eq(test_furi_nonexistent_data(), true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | MU_TEST(mu_test_furi_mute_algorithm) { | ||||||
|  |     mu_assert_int_eq(test_furi_mute_algorithm(test_log), true); | ||||||
|  | } | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | MU_TEST_SUITE(test_suite) { | ||||||
|  |     MU_SUITE_CONFIGURE(&test_setup, &test_teardown); | ||||||
|  | 
 | ||||||
|  |     MU_RUN_TEST(test_check); | ||||||
|  |     MU_RUN_TEST(mu_test_furi_ac_create_kill); | ||||||
|  |     MU_RUN_TEST(mu_test_furi_ac_switch_exit); | ||||||
|  |     MU_RUN_TEST(mu_test_furi_pipe_record); | ||||||
|  |     MU_RUN_TEST(mu_test_furi_holding_data); | ||||||
|  |     MU_RUN_TEST(mu_test_furi_concurrent_access); | ||||||
|  |     MU_RUN_TEST(mu_test_furi_nonexistent_data); | ||||||
|  |     // MU_RUN_TEST(mu_test_furi_mute_algorithm);
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int run_minunit() { | ||||||
|  |     MU_RUN_SUITE(test_suite); | ||||||
|  |     MU_REPORT(); | ||||||
|  | 
 | ||||||
|  |     return MU_EXIT_CODE; | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								applications/tests/minunit_vars.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								applications/tests/minunit_vars.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "minunit.h" | ||||||
|  | 
 | ||||||
|  | /*  Misc. counters */ | ||||||
|  | int minunit_run = 0; | ||||||
|  | int minunit_assert = 0; | ||||||
|  | int minunit_fail = 0; | ||||||
|  | int minunit_status = 0; | ||||||
|  | 
 | ||||||
|  | /*  Timers */ | ||||||
|  | double minunit_real_timer = 0; | ||||||
|  | double minunit_proc_timer = 0; | ||||||
|  | 
 | ||||||
|  | /*  Last message */ | ||||||
|  | char minunit_last_message[MINUNIT_MESSAGE_LEN]; | ||||||
							
								
								
									
										15
									
								
								applications/tests/minunit_vars_ex.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								applications/tests/minunit_vars_ex.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "minunit.h" | ||||||
|  | 
 | ||||||
|  | /*  Misc. counters */ | ||||||
|  | extern int minunit_run; | ||||||
|  | extern int minunit_assert; | ||||||
|  | extern int minunit_fail; | ||||||
|  | extern int minunit_status; | ||||||
|  | 
 | ||||||
|  | /*  Timers */ | ||||||
|  | extern double minunit_real_timer; | ||||||
|  | extern double minunit_proc_timer; | ||||||
|  | 
 | ||||||
|  | /*  Last message */ | ||||||
|  | extern char minunit_last_message[MINUNIT_MESSAGE_LEN]; | ||||||
| @ -4,70 +4,38 @@ | |||||||
| 
 | 
 | ||||||
| // #include "flipper-core.h" TODO: Rust build disabled
 | // #include "flipper-core.h" TODO: Rust build disabled
 | ||||||
| 
 | 
 | ||||||
| bool test_furi_ac_create_kill(FuriRecordSubscriber* log); | int run_minunit(); | ||||||
| bool test_furi_ac_switch_exit(FuriRecordSubscriber* log); |  | ||||||
| 
 |  | ||||||
| bool test_furi_pipe_record(FuriRecordSubscriber* log); |  | ||||||
| bool test_furi_holding_data(FuriRecordSubscriber* log); |  | ||||||
| bool test_furi_concurrent_access(FuriRecordSubscriber* log); |  | ||||||
| bool test_furi_nonexistent_data(FuriRecordSubscriber* log); |  | ||||||
| bool test_furi_mute_algorithm(FuriRecordSubscriber* log); |  | ||||||
| 
 | 
 | ||||||
| void flipper_test_app(void* p) { | void flipper_test_app(void* p) { | ||||||
|     FuriRecordSubscriber* log = get_default_log(); |     // create pins
 | ||||||
|  |     GpioPin red = {.pin = LED_RED_Pin, .port = LED_RED_GPIO_Port}; | ||||||
|  |     GpioPin green = {.pin = LED_GREEN_Pin, .port = LED_GREEN_GPIO_Port}; | ||||||
|  |     GpioPin blue = {.pin = LED_BLUE_Pin, .port = LED_BLUE_GPIO_Port}; | ||||||
| 
 | 
 | ||||||
|     if(test_furi_ac_create_kill(log)) { |     // configure pins
 | ||||||
|         fuprintf(log, "[TEST] test_furi_ac_create_kill PASSED\n"); |     pinMode(red, GpioModeOpenDrain); | ||||||
|  |     pinMode(green, GpioModeOpenDrain); | ||||||
|  |     pinMode(blue, GpioModeOpenDrain); | ||||||
|  | 
 | ||||||
|  |     digitalWrite(red, HIGH); | ||||||
|  |     digitalWrite(green, HIGH); | ||||||
|  |     digitalWrite(blue, LOW); | ||||||
|  | 
 | ||||||
|  |     uint32_t exitcode = run_minunit(); | ||||||
|  | 
 | ||||||
|  |     if(exitcode == 0) { | ||||||
|  |         // test passed
 | ||||||
|  |         digitalWrite(red, HIGH); | ||||||
|  |         digitalWrite(green, LOW); | ||||||
|  |         digitalWrite(blue, HIGH); | ||||||
|     } else { |     } else { | ||||||
|         fuprintf(log, "[TEST] test_furi_ac_create_kill FAILED\n"); |         // test failed
 | ||||||
|  |         digitalWrite(red, LOW); | ||||||
|  |         digitalWrite(green, HIGH); | ||||||
|  |         digitalWrite(blue, HIGH); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(test_furi_ac_switch_exit(log)) { |     set_exitcode(exitcode); | ||||||
|         fuprintf(log, "[TEST] test_furi_ac_switch_exit PASSED\n"); |  | ||||||
|     } else { |  | ||||||
|         fuprintf(log, "[TEST] test_furi_ac_switch_exit FAILED\n"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(test_furi_pipe_record(log)) { |  | ||||||
|         fuprintf(log, "[TEST] test_furi_pipe_record PASSED\n"); |  | ||||||
|     } else { |  | ||||||
|         fuprintf(log, "[TEST] test_furi_pipe_record FAILED\n"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(test_furi_holding_data(log)) { |  | ||||||
|         fuprintf(log, "[TEST] test_furi_holding_data PASSED\n"); |  | ||||||
|     } else { |  | ||||||
|         fuprintf(log, "[TEST] test_furi_holding_data FAILED\n"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(test_furi_concurrent_access(log)) { |  | ||||||
|         fuprintf(log, "[TEST] test_furi_concurrent_access PASSED\n"); |  | ||||||
|     } else { |  | ||||||
|         fuprintf(log, "[TEST] test_furi_concurrent_access FAILED\n"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(test_furi_nonexistent_data(log)) { |  | ||||||
|         fuprintf(log, "[TEST] test_furi_nonexistent_data PASSED\n"); |  | ||||||
|     } else { |  | ||||||
|         fuprintf(log, "[TEST] test_furi_nonexistent_data FAILED\n"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(test_furi_mute_algorithm(log)) { |  | ||||||
|         fuprintf(log, "[TEST] test_furi_mute_algorithm PASSED\n"); |  | ||||||
|     } else { |  | ||||||
|         fuprintf(log, "[TEST] test_furi_mute_algorithm FAILED\n"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /*
 |  | ||||||
|     TODO: Rust build disabled |  | ||||||
|     if(add(1, 2) == 3) { |  | ||||||
|         fuprintf(log, "[TEST] Rust add PASSED\n"); |  | ||||||
|     } else { |  | ||||||
|         fuprintf(log, "[TEST] Rust add FAILED\n"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     rust_uart_write(); |  | ||||||
|     */ |  | ||||||
| 
 | 
 | ||||||
|     furiac_exit(NULL); |     furiac_exit(NULL); | ||||||
| } | } | ||||||
							
								
								
									
										13
									
								
								core/app.cpp
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								core/app.cpp
									
									
									
									
									
								
							| @ -1,14 +1,20 @@ | |||||||
| #include "flipper.h" |  | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| 
 | 
 | ||||||
| extern "C" { | extern "C" { | ||||||
|  |     #include "flipper.h" | ||||||
|     #include "furi.h" |     #include "furi.h" | ||||||
|     #include "log.h" |     #include "log.h" | ||||||
|     #include "startup.h" |     #include "startup.h" | ||||||
|     #include "tty_uart.h" |     #include "tty_uart.h" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| extern "C" void app() { | // for testing purpose
 | ||||||
|  | uint32_t exitcode = 0; | ||||||
|  | extern "C" void set_exitcode(uint32_t _exitcode) { | ||||||
|  |     exitcode = _exitcode; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | extern "C" int app() { | ||||||
|     register_tty_uart(); |     register_tty_uart(); | ||||||
| 
 | 
 | ||||||
|     FuriRecordSubscriber* log = get_default_log(); |     FuriRecordSubscriber* log = get_default_log(); | ||||||
| @ -33,8 +39,9 @@ extern "C" void app() { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         delay(500); |         delay(500); | ||||||
|         // TODO add deferred event queue here
 |  | ||||||
|     } while(is_alive); |     } while(is_alive); | ||||||
| 
 | 
 | ||||||
|     fuprintf(log, "\n=== Bye from Flipper Zero! ===\n\n"); |     fuprintf(log, "\n=== Bye from Flipper Zero! ===\n\n"); | ||||||
|  | 
 | ||||||
|  |     return (int)exitcode; | ||||||
| } | } | ||||||
| @ -29,3 +29,5 @@ extern "C" { | |||||||
| #define INPUT GpioModeInput | #define INPUT GpioModeInput | ||||||
| #define LOW false | #define LOW false | ||||||
| #define HIGH true | #define HIGH true | ||||||
|  | 
 | ||||||
|  | void set_exitcode(uint32_t _exitcode); | ||||||
|  | |||||||
| @ -10,5 +10,9 @@ HAL_UART_Transmit(UART_HandleTypeDef* handle, uint8_t* bufer, uint16_t size, uin | |||||||
| 
 | 
 | ||||||
| typedef uint32_t TIM_HandleTypeDef; | typedef uint32_t TIM_HandleTypeDef; | ||||||
| 
 | 
 | ||||||
| #define LED_RED_GPIO_Port 1 |  | ||||||
| #define LED_RED_Pin 1 | #define LED_RED_Pin 1 | ||||||
|  | #define LED_RED_GPIO_Port "Red:" | ||||||
|  | #define LED_GREEN_Pin 1 | ||||||
|  | #define LED_GREEN_GPIO_Port "Green:" | ||||||
|  | #define LED_BLUE_Pin 1 | ||||||
|  | #define LED_BLUE_GPIO_Port "Blue:" | ||||||
|  | |||||||
| @ -4,10 +4,8 @@ Flipper devices inc. | |||||||
| Local fw build entry point. | Local fw build entry point. | ||||||
| */ | */ | ||||||
| 
 | 
 | ||||||
| void app(); | int app(); | ||||||
| 
 | 
 | ||||||
| int main() { | int main() { | ||||||
|     app(); |     return app(); | ||||||
| 
 |  | ||||||
|     return 0; |  | ||||||
| } | } | ||||||
							
								
								
									
										126
									
								
								wiki/Testing.md
									
									
									
									
									
								
							
							
						
						
									
										126
									
								
								wiki/Testing.md
									
									
									
									
									
								
							| @ -1,121 +1,11 @@ | |||||||
| # Bootloader testcase | # Integration testing | ||||||
| 
 | 
 | ||||||
| 1. `# Clean flash` | * **[Bootloader testcase](Bootloader-test)** | ||||||
| 2. `make -C bootloader flash` `# Load bootloader` | * **[Input testcase](Input-test)** | ||||||
| 3.  `# reboot device` | * **[General testcase](General-building-test)** | ||||||
|     * Press right |  | ||||||
|     * Press left |  | ||||||
|     * Wait 0.1 s |  | ||||||
|     * Release left |  | ||||||
|     * Release right |  | ||||||
| 4. Wait 0.5 s |  | ||||||
| 5. `# Expect no FW` |  | ||||||
|     * Expect: no uart welcome message |  | ||||||
|     * Expect: red led on |  | ||||||
|     * Expect: no USB |  | ||||||
| 6. `# reboot device and go to DFU` |  | ||||||
|     * Press left  |  | ||||||
|     * Press right |  | ||||||
|     * Wait 0.1 s |  | ||||||
|     * Release left |  | ||||||
|     * Wait 0.5 s  |  | ||||||
|     * Release right |  | ||||||
| 7. Wait 0.5 s |  | ||||||
| 8. `# Expect DFU` |  | ||||||
|     * Expect: blue led on |  | ||||||
|     * Expect: USB: DFU |  | ||||||
| 9. `target_f2/deploy-dfu.sh` `# load FW` |  | ||||||
| 10. `# reboot device` |  | ||||||
|     * Press right |  | ||||||
|     * Press left |  | ||||||
|     * Wait 0.1 s |  | ||||||
|     * Release left |  | ||||||
|     * Release right |  | ||||||
| 11. Wait 0.5 s |  | ||||||
| 12. `# Expect FW` |  | ||||||
|     * Expect: uart welcome message |  | ||||||
|     * Expect: USB Flipper CDC |  | ||||||
| 
 | 
 | ||||||
| # Input testcase | # Unit testing | ||||||
| 
 | 
 | ||||||
| 1. `docker-compose exec dev make -C target_f2 example_input_dump` | 1. We use [minunit]() as testing framework | ||||||
| 2. Flash | 2. Tests root placed in `applications/tests/minuint_test.c` | ||||||
| 3. For x in ``` | 3. There is `Run local tests` job in `CI` pipeline (`.github/workflows/ci.yml`) | ||||||
| [ |  | ||||||
|     (Up, "00"), |  | ||||||
|     (Down, "01"), |  | ||||||
|     (Right, "02"), |  | ||||||
|     (Left, "03"), |  | ||||||
|     (Ok, "04"), |  | ||||||
|     (Back, "05"), |  | ||||||
| ] |  | ||||||
| ``` |  | ||||||
|     * Press ${x[0]} |  | ||||||
|     * wait 0.05 |  | ||||||
|     * Expect: Uart: "event: ${x[1]} pressed" |  | ||||||
|     * wait 0.05 |  | ||||||
|     * Release ${x[0]} |  | ||||||
|     * wait 0.05 |  | ||||||
|     * Expect: Uart: "event: ${x[1]} released" |  | ||||||
|     * wait 0.05 |  | ||||||
| 
 |  | ||||||
| TODO: add debouncing check (multiple press and check there is no multiple events) |  | ||||||
| 
 |  | ||||||
| # General building testcase |  | ||||||
| 
 |  | ||||||
| Local target: |  | ||||||
| 
 |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=local APP_TEST=1 clean` |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=local APP_TEST=1 run` |  | ||||||
| * check tests pass/fail |  | ||||||
| 
 |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_BLINK=1 run` |  | ||||||
| * GPIO on and off |  | ||||||
| 
 |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_UART_WRITE=1 run` |  | ||||||
| * GPIO on/off and `counter: %` writes |  | ||||||
| 
 |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_IPC=1 run` |  | ||||||
| * ASCII display draw |  | ||||||
| 
 |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_INPUT_DUMP=1 run` not implemented |  | ||||||
| 
 |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_QRCODE=1 run` |  | ||||||
| * Some writes to display |  | ||||||
| 
 |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_DISPLAY=1 run` |  | ||||||
| * Some writes to display |  | ||||||
| 
 |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_FATFS=1 flash` |  | ||||||
| * TODO: FatFs emulation and test not implemented |  | ||||||
| 
 |  | ||||||
| F2 target: |  | ||||||
| 
 |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=f2 APP_TEST=1 clean` |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=f2 APP_TEST=1 flash` |  | ||||||
| * check UART for test pass/fail |  | ||||||
| 
 |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_BLINK=1 flash` |  | ||||||
| * Red LED blink (1s period) |  | ||||||
| 
 |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_UART_WRITE=1 flash` |  | ||||||
| * Red LED shortly blinking, `counter: %` writes to UART |  | ||||||
| 
 |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_IPC=1 flash` |  | ||||||
| * ASCII display draw in UART |  | ||||||
| 
 |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_INPUT_DUMP=1 flash` |  | ||||||
| * Press all buttons, `state` and `event` writes to UART |  | ||||||
| 
 |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_QRCODE=1 flash` |  | ||||||
| * QR code show on the screen |  | ||||||
| 
 |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_DISPLAY=1 flash` |  | ||||||
| * `Hello world` show on the screen |  | ||||||
| 
 |  | ||||||
| * `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_FATFS=1 flash` |  | ||||||
| * `Init sd card error` on the screen |  | ||||||
| * Insert SD-card |  | ||||||
| * Reboot |  | ||||||
| * Show file list on the screen |  | ||||||
| * Scroll by pressing up and down |  | ||||||
|  | |||||||
| @ -35,7 +35,7 @@ For more HW- and RTOS- specific checks we run real FW in [Renode](https://interr | |||||||
| 
 | 
 | ||||||
| Eventually we run real FW on remote debug/test bench (#26): flipper board + RPi + some stuff to control and check real hardware. | Eventually we run real FW on remote debug/test bench (#26): flipper board + RPi + some stuff to control and check real hardware. | ||||||
| 
 | 
 | ||||||
| # Debug/test bench (not implemented) | # Debug/test bench (in progress) | ||||||
| 
 | 
 | ||||||
| * 24×7 connected target Flipper device and accessible via Internet. Raspberry PI or some Linux single-board PC can be used as basic high-level control board. | * 24×7 connected target Flipper device and accessible via Internet. Raspberry PI or some Linux single-board PC can be used as basic high-level control board. | ||||||
| * Tool can push/click each user buttons by hardware by "control board" (low level). Usage of optocouples/reed-switch relays is fine for that. | * Tool can push/click each user buttons by hardware by "control board" (low level). Usage of optocouples/reed-switch relays is fine for that. | ||||||
| @ -61,3 +61,7 @@ Eventually we run real FW on remote debug/test bench (#26): flipper board + RPi | |||||||
| 2. Run CI tests: | 2. Run CI tests: | ||||||
| 	* For test automation we can use RobotDemo or simple expect tool/python scripts/etc. | 	* For test automation we can use RobotDemo or simple expect tool/python scripts/etc. | ||||||
| 	* Apply test cases and submit its results. | 	* Apply test cases and submit its results. | ||||||
|  | 
 | ||||||
|  | # Testing | ||||||
|  | 
 | ||||||
|  | You can read about testing in [Testing](Testing) page. | ||||||
|  | |||||||
							
								
								
									
										35
									
								
								wiki/testing/Bootloader-test.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								wiki/testing/Bootloader-test.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | 1. `# Clean flash` | ||||||
|  | 2. `make -C bootloader flash` `# Load bootloader` | ||||||
|  | 3.  `# reboot device` | ||||||
|  |     * Press right | ||||||
|  |     * Press left | ||||||
|  |     * Wait 0.1 s | ||||||
|  |     * Release left | ||||||
|  |     * Release right | ||||||
|  | 4. Wait 0.5 s | ||||||
|  | 5. `# Expect no FW` | ||||||
|  |     * Expect: no uart welcome message | ||||||
|  |     * Expect: red led on | ||||||
|  |     * Expect: no USB | ||||||
|  | 6. `# reboot device and go to DFU` | ||||||
|  |     * Press left  | ||||||
|  |     * Press right | ||||||
|  |     * Wait 0.1 s | ||||||
|  |     * Release left | ||||||
|  |     * Wait 0.5 s  | ||||||
|  |     * Release right | ||||||
|  | 7. Wait 0.5 s | ||||||
|  | 8. `# Expect DFU` | ||||||
|  |     * Expect: blue led on | ||||||
|  |     * Expect: USB: DFU | ||||||
|  | 9. `target_f2/deploy-dfu.sh` `# load FW` | ||||||
|  | 10. `# reboot device` | ||||||
|  |     * Press right | ||||||
|  |     * Press left | ||||||
|  |     * Wait 0.1 s | ||||||
|  |     * Release left | ||||||
|  |     * Release right | ||||||
|  | 11. Wait 0.5 s | ||||||
|  | 12. `# Expect FW` | ||||||
|  |     * Expect: uart welcome message | ||||||
|  |     * Expect: USB Flipper CDC | ||||||
							
								
								
									
										59
									
								
								wiki/testing/General-building-test.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								wiki/testing/General-building-test.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | |||||||
|  | Local target: | ||||||
|  | 
 | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=local APP_TEST=1 clean` | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=local APP_TEST=1 run` | ||||||
|  | * check tests pass/fail (by exitcode == 0) | ||||||
|  | 
 | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_BLINK=1 run` | ||||||
|  | * GPIO on and off | ||||||
|  | 
 | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_UART_WRITE=1 run` | ||||||
|  | * GPIO on/off and `counter: %` writes | ||||||
|  | 
 | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_IPC=1 run` | ||||||
|  | * ASCII display draw | ||||||
|  | 
 | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_INPUT_DUMP=1 run` not implemented | ||||||
|  | 
 | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_QRCODE=1 run` | ||||||
|  | * Some writes to display | ||||||
|  | 
 | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_DISPLAY=1 run` | ||||||
|  | * Some writes to display | ||||||
|  | 
 | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_FATFS=1 flash` | ||||||
|  | * TODO: FatFs emulation and test not implemented | ||||||
|  | 
 | ||||||
|  | F2 target: | ||||||
|  | 
 | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=f2 APP_TEST=1 clean` | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=f2 APP_TEST=1 flash` | ||||||
|  | * check UART for test pass/fail | ||||||
|  | * blue led when test is running | ||||||
|  | * green led if test is passed | ||||||
|  | * red led if test is failed | ||||||
|  | 
 | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_BLINK=1 flash` | ||||||
|  | * Red LED blink (1s period) | ||||||
|  | 
 | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_UART_WRITE=1 flash` | ||||||
|  | * Red LED shortly blinking, `counter: %` writes to UART | ||||||
|  | 
 | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_IPC=1 flash` | ||||||
|  | * ASCII display draw in UART | ||||||
|  | 
 | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_INPUT_DUMP=1 flash` | ||||||
|  | * Press all buttons, `state` and `event` writes to UART | ||||||
|  | 
 | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_QRCODE=1 flash` | ||||||
|  | * QR code show on the screen | ||||||
|  | 
 | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_DISPLAY=1 flash` | ||||||
|  | * `Hello world` show on the screen | ||||||
|  | 
 | ||||||
|  | * `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_FATFS=1 flash` | ||||||
|  | * `Init sd card error` on the screen | ||||||
|  | * Insert SD-card | ||||||
|  | * Reboot | ||||||
|  | * Show file list on the screen | ||||||
|  | * Scroll by pressing up and down | ||||||
							
								
								
									
										22
									
								
								wiki/testing/Input-test.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								wiki/testing/Input-test.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | 1. `docker-compose exec dev make -C target_f2 example_input_dump` | ||||||
|  | 2. Flash | ||||||
|  | 3. For x in ``` | ||||||
|  | [ | ||||||
|  |     (Up, "00"), | ||||||
|  |     (Down, "01"), | ||||||
|  |     (Right, "02"), | ||||||
|  |     (Left, "03"), | ||||||
|  |     (Ok, "04"), | ||||||
|  |     (Back, "05"), | ||||||
|  | ] | ||||||
|  | ``` | ||||||
|  |     * Press ${x[0]} | ||||||
|  |     * wait 0.05 | ||||||
|  |     * Expect: Uart: "event: ${x[1]} pressed" | ||||||
|  |     * wait 0.05 | ||||||
|  |     * Release ${x[0]} | ||||||
|  |     * wait 0.05 | ||||||
|  |     * Expect: Uart: "event: ${x[1]} released" | ||||||
|  |     * wait 0.05 | ||||||
|  | 
 | ||||||
|  | TODO: add debouncing check (multiple press and check there is no multiple events) | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 coreglitch
						coreglitch