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: | ||||
|           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 | ||||
|         uses: ./.github/actions/docker | ||||
|         with: | ||||
|  | ||||
| @ -15,6 +15,7 @@ CFLAGS		+= -DAPP_TEST | ||||
| C_SOURCES	+= $(APP_DIR)/tests/furiac_test.c | ||||
| C_SOURCES	+= $(APP_DIR)/tests/furi_record_test.c | ||||
| C_SOURCES	+= $(APP_DIR)/tests/test_index.c | ||||
| C_SOURCES	+= $(APP_DIR)/tests/minunit_test.c | ||||
| endif | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| 
 | ||||
| bool test_furi_pipe_record(FuriRecordSubscriber* log) { | ||||
| bool test_furi_pipe_record() { | ||||
|     // 1. create pipe record
 | ||||
|     if(!furi_create("test/pipe", NULL, 0)) { | ||||
|         fuprintf(log, "cannot create record\n"); | ||||
|         printf("cannot create record\n"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| @ -33,27 +33,27 @@ bool test_furi_pipe_record(FuriRecordSubscriber* log) { | ||||
|     FuriRecordSubscriber* pipe_record = | ||||
|         furi_open("test/pipe", false, false, pipe_record_cb, NULL, NULL); | ||||
|     if(pipe_record == NULL) { | ||||
|         fuprintf(log, "cannot open record\n"); | ||||
|         printf("cannot open record\n"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     const uint8_t WRITE_VALUE = 1; | ||||
|     // 3. write data
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     // 4. check that subscriber get data
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     // 5. try to read, get error
 | ||||
|     uint8_t read_value = 0; | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
| @ -62,7 +62,7 @@ bool test_furi_pipe_record(FuriRecordSubscriber* log) { | ||||
| 
 | ||||
|     // 7. try to write, get error
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
| @ -87,11 +87,11 @@ void holding_record_cb(const void* value, size_t size, void* ctx) { | ||||
|     holding_record_value = *((uint8_t*)value); | ||||
| } | ||||
| 
 | ||||
| bool test_furi_holding_data(FuriRecordSubscriber* log) { | ||||
| bool test_furi_holding_data() { | ||||
|     // 1. Create holding record
 | ||||
|     uint8_t holder = 0; | ||||
|     if(!furi_create("test/holding", (void*)&holder, sizeof(holder))) { | ||||
|         fuprintf(log, "cannot create record\n"); | ||||
|         printf("cannot create record\n"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| @ -99,43 +99,43 @@ bool test_furi_holding_data(FuriRecordSubscriber* log) { | ||||
|     FuriRecordSubscriber* holding_record = | ||||
|         furi_open("test/holding", false, false, holding_record_cb, NULL, NULL); | ||||
|     if(holding_record == NULL) { | ||||
|         fuprintf(log, "cannot open record\n"); | ||||
|         printf("cannot open record\n"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     const uint8_t WRITE_VALUE = 1; | ||||
|     // 3. write data
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     // 4. check that subscriber get data
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     // 5. Read and check data
 | ||||
|     uint8_t read_value = 0; | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     // 6. Try to write/read wrong size of data
 | ||||
|     if(furi_write(holding_record, &WRITE_VALUE, 100)) { | ||||
|         fuprintf(log, "overflowed write not allowed\n"); | ||||
|         printf("overflowed write not allowed\n"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if(furi_read(holding_record, &read_value, 100)) { | ||||
|         fuprintf(log, "overflowed read not allowed\n"); | ||||
|         printf("overflowed read not allowed\n"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| @ -159,12 +159,10 @@ typedef struct { | ||||
| } ConcurrentValue; | ||||
| 
 | ||||
| void furi_concurent_app(void* p) { | ||||
|     FuriRecordSubscriber* log = (FuriRecordSubscriber*)p; | ||||
| 
 | ||||
|     FuriRecordSubscriber* holding_record = | ||||
|         furi_open("test/concurrent", false, false, NULL, NULL, NULL); | ||||
|     if(holding_record == NULL) { | ||||
|         fuprintf(log, "cannot open record\n"); | ||||
|         printf("cannot open record\n"); | ||||
|         furiac_exit(NULL); | ||||
|     } | ||||
| 
 | ||||
| @ -172,7 +170,7 @@ void furi_concurent_app(void* p) { | ||||
|         ConcurrentValue* value = (ConcurrentValue*)furi_take(holding_record); | ||||
| 
 | ||||
|         if(value == NULL) { | ||||
|             fuprintf(log, "cannot take record\n"); | ||||
|             printf("cannot take record\n"); | ||||
|             furi_give(holding_record); | ||||
|             furiac_exit(NULL); | ||||
|         } | ||||
| @ -190,11 +188,11 @@ void furi_concurent_app(void* p) { | ||||
|     furiac_exit(NULL); | ||||
| } | ||||
| 
 | ||||
| bool test_furi_concurrent_access(FuriRecordSubscriber* log) { | ||||
| bool test_furi_concurrent_access() { | ||||
|     // 1. Create holding record
 | ||||
|     ConcurrentValue holder = {.a = 0, .b = 0}; | ||||
|     if(!furi_create("test/concurrent", (void*)&holder, sizeof(ConcurrentValue))) { | ||||
|         fuprintf(log, "cannot create record\n"); | ||||
|         printf("cannot create record\n"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| @ -202,19 +200,19 @@ bool test_furi_concurrent_access(FuriRecordSubscriber* log) { | ||||
|     FuriRecordSubscriber* holding_record = | ||||
|         furi_open("test/concurrent", false, false, NULL, NULL, NULL); | ||||
|     if(holding_record == NULL) { | ||||
|         fuprintf(log, "cannot open record\n"); | ||||
|         printf("cannot open record\n"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     // 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
 | ||||
|     for(size_t i = 0; i < 4; i++) { | ||||
|         ConcurrentValue* value = (ConcurrentValue*)furi_take(holding_record); | ||||
| 
 | ||||
|         if(value == NULL) { | ||||
|             fuprintf(log, "cannot take record\n"); | ||||
|             printf("cannot take record\n"); | ||||
|             furi_give(holding_record); | ||||
|             return false; | ||||
|         } | ||||
| @ -232,12 +230,12 @@ bool test_furi_concurrent_access(FuriRecordSubscriber* log) { | ||||
|     delay(50); | ||||
| 
 | ||||
|     if(second_app->handler != NULL) { | ||||
|         fuprintf(log, "second app still alive\n"); | ||||
|         printf("second app still alive\n"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
| @ -252,7 +250,7 @@ TEST: non-existent data | ||||
| 
 | ||||
| TODO: implement this test | ||||
| */ | ||||
| bool test_furi_nonexistent_data(FuriRecordSubscriber* log) { | ||||
| bool test_furi_nonexistent_data() { | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| @ -310,11 +308,9 @@ void mute_record_state_cb(FlipperRecordState state, void* ctx) { | ||||
| } | ||||
| 
 | ||||
| void furi_mute_parent_app(void* p) { | ||||
|     FuriRecordSubscriber* log = (FuriRecordSubscriber*)p; | ||||
| 
 | ||||
|     // 1. Create pipe record
 | ||||
|     if(!furi_create("test/mute", NULL, 0)) { | ||||
|         fuprintf(log, "cannot create record\n"); | ||||
|         printf("cannot create record\n"); | ||||
|         furiac_exit(NULL); | ||||
|     } | ||||
| 
 | ||||
| @ -322,7 +318,7 @@ void furi_mute_parent_app(void* p) { | ||||
|     FuriRecordSubscriber* watch_handler = | ||||
|         furi_open("test/mute", false, false, mute_record_cb, NULL, NULL); | ||||
|     if(watch_handler == NULL) { | ||||
|         fuprintf(log, "cannot open watch handler\n"); | ||||
|         printf("cannot open watch handler\n"); | ||||
|         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:
 | ||||
|     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
 | ||||
| 
 | ||||
| @ -342,7 +338,7 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { | ||||
|     FuriRecordSubscriber* handler_a = | ||||
|         furi_open("test/mute", false, false, NULL, mute_record_state_cb, NULL); | ||||
|     if(handler_a == NULL) { | ||||
|         fuprintf(log, "cannot open handler A\n"); | ||||
|         printf("cannot open handler A\n"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| @ -350,25 +346,25 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { | ||||
| 
 | ||||
|     // Try to write data to A and check subscriber
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     // 3. Open handler B: solo=true, no_mute=true, NULL subscriber.
 | ||||
|     FuriRecordSubscriber* handler_b = furi_open("test/mute", true, true, NULL, NULL, NULL); | ||||
|     if(handler_b == NULL) { | ||||
|         fuprintf(log, "cannot open handler B\n"); | ||||
|         printf("cannot open handler B\n"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     // Check A state cb get 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; | ||||
|     } | ||||
| 
 | ||||
| @ -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)
 | ||||
|     if(furi_write(handler_a, &test_counter, sizeof(uint8_t))) { | ||||
|         fuprintf(log, "A not muted\n"); | ||||
|         printf("A not muted\n"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if(mute_last_value == test_counter) { | ||||
|         fuprintf(log, "value A must be muted\n"); | ||||
|         printf("value A must be muted\n"); | ||||
|         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.
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     // 4. Open hadler C: solo=true, no_mute=false, NULL subscriber.
 | ||||
|     FuriRecordSubscriber* handler_c = furi_open("test/mute", true, false, NULL, NULL, NULL); | ||||
|     if(handler_c == NULL) { | ||||
|         fuprintf(log, "cannot open handler C\n"); | ||||
|         printf("cannot open handler C\n"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| @ -412,7 +408,7 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { | ||||
|     // 5. Open handler D: solo=false, no_mute=false, NULL subscriber.
 | ||||
|     FuriRecordSubscriber* handler_d = furi_open("test/mute", false, false, NULL, NULL, NULL); | ||||
|     if(handler_d == NULL) { | ||||
|         fuprintf(log, "cannot open handler D\n"); | ||||
|         printf("cannot open handler D\n"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| @ -428,7 +424,7 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { | ||||
| 
 | ||||
|     // 7. Exit "parent application"
 | ||||
|     if(!furiac_kill(parent_app)) { | ||||
|         fuprintf(log, "kill parent_app fail\n"); | ||||
|         printf("kill parent_app fail\n"); | ||||
|         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 value_a = counter; | ||||
| 
 | ||||
|     FuriApp* widget = furiac_start(create_kill_app, "create_kill_app", (void*)&counter); | ||||
|     if(widget == NULL) { | ||||
|         fuprintf(log, "create widget fail\n"); | ||||
|         printf("create widget fail\n"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     delay(10); | ||||
| 
 | ||||
|     if(!furiac_kill(widget)) { | ||||
|         fuprintf(log, "kill widget fail\n"); | ||||
|         printf("kill widget fail\n"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if(value_a == counter) { | ||||
|         fuprintf(log, "counter unchanged\n"); | ||||
|         printf("counter unchanged\n"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| @ -51,7 +51,7 @@ bool test_furi_ac_create_kill(FuriRecordSubscriber* log) { | ||||
|     delay(10); | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
| @ -111,7 +111,7 @@ void task_b(void* p) { | ||||
|     furiac_exit(p); | ||||
| } | ||||
| 
 | ||||
| bool test_furi_ac_switch_exit(FuriRecordSubscriber* log) { | ||||
| bool test_furi_ac_switch_exit() { | ||||
|     // init sequence
 | ||||
|     TestSwitchSequence seq; | ||||
|     seq.count = 0; | ||||
| @ -124,7 +124,7 @@ bool test_furi_ac_switch_exit(FuriRecordSubscriber* log) { | ||||
|     seq.sequence[seq.count] = '\0'; | ||||
| 
 | ||||
|     if(strcmp(seq.sequence, "ABA/") != 0) { | ||||
|         fuprintf(log, "wrong sequence: %s\n", seq.sequence); | ||||
|         printf("wrong sequence: %s\n", seq.sequence); | ||||
|         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
 | ||||
| 
 | ||||
| bool test_furi_ac_create_kill(FuriRecordSubscriber* log); | ||||
| 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); | ||||
| int run_minunit(); | ||||
| 
 | ||||
| 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)) { | ||||
|         fuprintf(log, "[TEST] test_furi_ac_create_kill PASSED\n"); | ||||
|     // configure pins
 | ||||
|     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 { | ||||
|         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)) { | ||||
|         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(); | ||||
|     */ | ||||
|     set_exitcode(exitcode); | ||||
| 
 | ||||
|     furiac_exit(NULL); | ||||
| } | ||||
							
								
								
									
										21
									
								
								core/app.cpp
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								core/app.cpp
									
									
									
									
									
								
							| @ -1,14 +1,20 @@ | ||||
| #include "flipper.h" | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| extern "C" { | ||||
| #include "furi.h" | ||||
| #include "log.h" | ||||
| #include "startup.h" | ||||
| #include "tty_uart.h" | ||||
|     #include "flipper.h" | ||||
|     #include "furi.h" | ||||
|     #include "log.h" | ||||
|     #include "startup.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(); | ||||
| 
 | ||||
|     FuriRecordSubscriber* log = get_default_log(); | ||||
| @ -33,8 +39,9 @@ extern "C" void app() { | ||||
|             } | ||||
|         } | ||||
|         delay(500); | ||||
|         // TODO add deferred event queue here
 | ||||
|     } while(is_alive); | ||||
| 
 | ||||
|     fuprintf(log, "\n=== Bye from Flipper Zero! ===\n\n"); | ||||
| 
 | ||||
|     return (int)exitcode; | ||||
| } | ||||
| @ -29,3 +29,5 @@ extern "C" { | ||||
| #define INPUT GpioModeInput | ||||
| #define LOW false | ||||
| #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; | ||||
| 
 | ||||
| #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. | ||||
| */ | ||||
| 
 | ||||
| void app(); | ||||
| int app(); | ||||
| 
 | ||||
| int main() { | ||||
|     app(); | ||||
| 
 | ||||
|     return 0; | ||||
|     return app(); | ||||
| } | ||||
							
								
								
									
										126
									
								
								wiki/Testing.md
									
									
									
									
									
								
							
							
						
						
									
										126
									
								
								wiki/Testing.md
									
									
									
									
									
								
							| @ -1,121 +1,11 @@ | ||||
| # Bootloader testcase | ||||
| # Integration testing | ||||
| 
 | ||||
| 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 | ||||
| * **[Bootloader testcase](Bootloader-test)** | ||||
| * **[Input testcase](Input-test)** | ||||
| * **[General testcase](General-building-test)** | ||||
| 
 | ||||
| # Input testcase | ||||
| # Unit testing | ||||
| 
 | ||||
| 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) | ||||
| 
 | ||||
| # 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 | ||||
| 1. We use [minunit]() as testing framework | ||||
| 2. Tests root placed in `applications/tests/minuint_test.c` | ||||
| 3. There is `Run local tests` job in `CI` pipeline (`.github/workflows/ci.yml`) | ||||
|  | ||||
| @ -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. | ||||
| 
 | ||||
| # 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. | ||||
| * 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: | ||||
| 	* For test automation we can use RobotDemo or simple expect tool/python scripts/etc. | ||||
| 	* 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