• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 // An ultra-lightweight testing framework designed for use with microcontroller
17 // applications. Its only dependency is on TensorFlow Lite's ErrorReporter
18 // interface, where log messages are output. This is designed to be usable even
19 // when no standard C or C++ libraries are available, and without any dynamic
20 // memory allocation or reliance on global constructors.
21 //
22 // To build a test, you use syntax similar to gunit, but with some extra
23 // decoration to create a hidden 'main' function containing each of the tests to
24 // be run. Your code should look something like:
25 // ----------------------------------------------------------------------------
26 // #include "path/to/this/header"
27 //
28 // TF_LITE_MICRO_TESTS_BEGIN
29 //
30 // TF_LITE_MICRO_TEST(SomeTest) {
31 //   TF_LITE_LOG_EXPECT_EQ(true, true);
32 // }
33 //
34 // TF_LITE_MICRO_TESTS_END
35 // ----------------------------------------------------------------------------
36 // If you compile this for your platform, you'll get a normal binary that you
37 // should be able to run. Executing it will output logging information like this
38 // to stderr (or whatever equivalent is available and written to by
39 // ErrorReporter):
40 // ----------------------------------------------------------------------------
41 // Testing SomeTest
42 // 1/1 tests passed
43 // ~~~ALL TESTS PASSED~~~
44 // ----------------------------------------------------------------------------
45 // This is designed to be human-readable, so you can just run tests manually,
46 // but the string "~~~ALL TESTS PASSED~~~" should only appear if all of the
47 // tests do pass. This makes it possible to integrate with automated test
48 // systems by scanning the output logs and looking for that magic value.
49 //
50 // This framework is intended to be a rudimentary alternative to no testing at
51 // all on systems that struggle to run more conventional approaches, so use with
52 // caution!
53 
54 #ifndef TENSORFLOW_LITE_MICRO_TESTING_MICRO_TEST_H_
55 #define TENSORFLOW_LITE_MICRO_TESTING_MICRO_TEST_H_
56 
57 #include "tensorflow/lite/micro/micro_error_reporter.h"
58 
59 namespace micro_test {
60 extern int tests_passed;
61 extern int tests_failed;
62 extern bool is_test_complete;
63 extern bool did_test_fail;
64 extern tflite::ErrorReporter* reporter;
65 }  // namespace micro_test
66 
67 #define TF_LITE_MICRO_TESTS_BEGIN              \
68   namespace micro_test {                       \
69   int tests_passed;                            \
70   int tests_failed;                            \
71   bool is_test_complete;                       \
72   bool did_test_fail;                          \
73   tflite::ErrorReporter* reporter;             \
74   }                                            \
75                                                \
76   int main(int argc, char** argv) {            \
77     micro_test::tests_passed = 0;              \
78     micro_test::tests_failed = 0;              \
79     tflite::MicroErrorReporter error_reporter; \
80     micro_test::reporter = &error_reporter;
81 
82 #define TF_LITE_MICRO_TESTS_END                                \
83   micro_test::reporter->Report(                                \
84       "%d/%d tests passed", micro_test::tests_passed,          \
85       (micro_test::tests_failed + micro_test::tests_passed));  \
86   if (micro_test::tests_failed == 0) {                         \
87     micro_test::reporter->Report("~~~ALL TESTS PASSED~~~\n");  \
88   } else {                                                     \
89     micro_test::reporter->Report("~~~SOME TESTS FAILED~~~\n"); \
90   }                                                            \
91   }
92 
93 // TODO(petewarden): I'm going to hell for what I'm doing to this poor for loop.
94 #define TF_LITE_MICRO_TEST(name)                                           \
95   micro_test::reporter->Report("Testing %s", #name);                       \
96   for (micro_test::is_test_complete = false,                               \
97       micro_test::did_test_fail = false;                                   \
98        !micro_test::is_test_complete; micro_test::is_test_complete = true, \
99       micro_test::tests_passed += (micro_test::did_test_fail) ? 0 : 1,     \
100       micro_test::tests_failed += (micro_test::did_test_fail) ? 1 : 0)
101 
102 #define TF_LITE_MICRO_EXPECT(x)                                                \
103   do {                                                                         \
104     if (!(x)) {                                                                \
105       micro_test::reporter->Report(#x " failed at %s:%d", __FILE__, __LINE__); \
106       micro_test::did_test_fail = true;                                        \
107     }                                                                          \
108   } while (false)
109 
110 #define TF_LITE_MICRO_EXPECT_EQ(x, y)                                          \
111   do {                                                                         \
112     if ((x) != (y)) {                                                          \
113       micro_test::reporter->Report(#x " == " #y " failed at %s:%d (%d vs %d)", \
114                                    __FILE__, __LINE__, (x), (y));              \
115       micro_test::did_test_fail = true;                                        \
116     }                                                                          \
117   } while (false)
118 
119 #define TF_LITE_MICRO_EXPECT_NE(x, y)                                         \
120   do {                                                                        \
121     if ((x) == (y)) {                                                         \
122       micro_test::reporter->Report(#x " != " #y " failed at %s:%d", __FILE__, \
123                                    __LINE__);                                 \
124       micro_test::did_test_fail = true;                                       \
125     }                                                                         \
126   } while (false)
127 
128 // TODO(wangtz): Making it more generic once needed.
129 #define TF_LITE_MICRO_ARRAY_ELEMENT_EXPECT_NEAR(arr1, idx1, arr2, idx2, \
130                                                 epsilon)                \
131   do {                                                                  \
132     auto delta = ((arr1)[(idx1)] > (arr2)[(idx2)])                      \
133                      ? ((arr1)[(idx1)] - (arr2)[(idx2)])                \
134                      : ((arr2)[(idx2)] - (arr1)[(idx1)]);               \
135     if (delta > epsilon) {                                              \
136       micro_test::reporter->Report(                                     \
137           #arr1 "[%d] (%f) near " #arr2 "[%d] (%f) failed at %s:%d",    \
138           static_cast<int>(idx1), static_cast<float>((arr1)[(idx1)]),   \
139           static_cast<int>(idx2), static_cast<float>((arr2)[(idx2)]),   \
140           __FILE__, __LINE__);                                          \
141       micro_test::did_test_fail = true;                                 \
142     }                                                                   \
143   } while (false)
144 
145 #define TF_LITE_MICRO_EXPECT_NEAR(x, y, epsilon)                              \
146   do {                                                                        \
147     auto delta = ((x) > (y)) ? ((x) - (y)) : ((y) - (x));                     \
148     if (delta > epsilon) {                                                    \
149       micro_test::reporter->Report(                                           \
150           #x " (%f) near " #y " (%f) failed at %s:%d", static_cast<float>(x), \
151           static_cast<float>(y), __FILE__, __LINE__);                         \
152       micro_test::did_test_fail = true;                                       \
153     }                                                                         \
154   } while (false)
155 
156 #define TF_LITE_MICRO_EXPECT_GT(x, y)                                        \
157   do {                                                                       \
158     if ((x) <= (y)) {                                                        \
159       micro_test::reporter->Report(#x " > " #y " failed at %s:%d", __FILE__, \
160                                    __LINE__);                                \
161       micro_test::did_test_fail = true;                                      \
162     }                                                                        \
163   } while (false)
164 
165 #define TF_LITE_MICRO_EXPECT_LT(x, y)                                        \
166   do {                                                                       \
167     if ((x) >= (y)) {                                                        \
168       micro_test::reporter->Report(#x " < " #y " failed at %s:%d", __FILE__, \
169                                    __LINE__);                                \
170       micro_test::did_test_fail = true;                                      \
171     }                                                                        \
172   } while (false)
173 
174 #define TF_LITE_MICRO_EXPECT_GE(x, y)                                         \
175   do {                                                                        \
176     if ((x) < (y)) {                                                          \
177       micro_test::reporter->Report(#x " >= " #y " failed at %s:%d", __FILE__, \
178                                    __LINE__);                                 \
179       micro_test::did_test_fail = true;                                       \
180     }                                                                         \
181   } while (false)
182 
183 #define TF_LITE_MICRO_EXPECT_LE(x, y)                                         \
184   do {                                                                        \
185     if ((x) > (y)) {                                                          \
186       micro_test::reporter->Report(#x " <= " #y " failed at %s:%d", __FILE__, \
187                                    __LINE__);                                 \
188       micro_test::did_test_fail = true;                                       \
189     }                                                                         \
190   } while (false)
191 
192 #define TF_LITE_MICRO_EXPECT_TRUE(x)                                   \
193   do {                                                                 \
194     if (!(x)) {                                                        \
195       micro_test::reporter->Report(#x " was not true failed at %s:%d", \
196                                    __FILE__, __LINE__);                \
197       micro_test::did_test_fail = true;                                \
198     }                                                                  \
199   } while (false)
200 
201 #define TF_LITE_MICRO_EXPECT_FALSE(x)                                   \
202   do {                                                                  \
203     if (x) {                                                            \
204       micro_test::reporter->Report(#x " was not false failed at %s:%d", \
205                                    __FILE__, __LINE__);                 \
206       micro_test::did_test_fail = true;                                 \
207     }                                                                   \
208   } while (false)
209 
210 #define TF_LITE_MICRO_FAIL(msg)                                        \
211   do {                                                                 \
212     micro_test::reporter->Report("FAIL: %s", msg, __FILE__, __LINE__); \
213     micro_test::did_test_fail = true;                                  \
214   } while (false)
215 
216 #endif  // TENSORFLOW_LITE_MICRO_TESTING_MICRO_TEST_H_
217