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/c/common.h"
58 #include "tensorflow/lite/micro/micro_error_reporter.h"
59 #include "tensorflow/lite/micro/system_setup.h"
60 
61 namespace micro_test {
62 extern int tests_passed;
63 extern int tests_failed;
64 extern bool is_test_complete;
65 extern bool did_test_fail;
66 }  // namespace micro_test
67 
68 namespace tflite {
69 
70 // This additional helper function is used (instead of directly calling
71 // tflite::InitializeTarget from the TF_LITE_MICRO_TESTS_BEGIN macro) to avoid
72 // adding a dependency from every bazel test target to micro:system_setp (which
73 // is the target that implements InitializeTarget().
74 //
75 // The underlying issue here is that the use of the macros results in
76 // dependencies that can be containted within the micro/testing:micro_test
77 // target bleeding on to all the tests.
InitializeTest()78 inline void InitializeTest() { InitializeTarget(); }
79 }  // namespace tflite
80 
81 #define TF_LITE_MICRO_TESTS_BEGIN   \
82   namespace micro_test {            \
83   int tests_passed;                 \
84   int tests_failed;                 \
85   bool is_test_complete;            \
86   bool did_test_fail;               \
87   }                                 \
88                                     \
89   int main(int argc, char** argv) { \
90     micro_test::tests_passed = 0;   \
91     micro_test::tests_failed = 0;   \
92     tflite::InitializeTest();
93 
94 #define TF_LITE_MICRO_TESTS_END                                       \
95   MicroPrintf("%d/%d tests passed", micro_test::tests_passed,         \
96               (micro_test::tests_failed + micro_test::tests_passed)); \
97   if (micro_test::tests_failed == 0) {                                \
98     MicroPrintf("~~~ALL TESTS PASSED~~~\n");                          \
99     return kTfLiteOk;                                                 \
100   } else {                                                            \
101     MicroPrintf("~~~SOME TESTS FAILED~~~\n");                         \
102     return kTfLiteError;                                              \
103   }                                                                   \
104   }
105 
106 // TODO(petewarden): I'm going to hell for what I'm doing to this poor for loop.
107 #define TF_LITE_MICRO_TEST(name)                                           \
108   MicroPrintf("Testing " #name);                                           \
109   for (micro_test::is_test_complete = false,                               \
110       micro_test::did_test_fail = false;                                   \
111        !micro_test::is_test_complete; micro_test::is_test_complete = true, \
112       micro_test::tests_passed += (micro_test::did_test_fail) ? 0 : 1,     \
113       micro_test::tests_failed += (micro_test::did_test_fail) ? 1 : 0)
114 
115 #define TF_LITE_MICRO_EXPECT(x)                               \
116   do {                                                        \
117     if (!(x)) {                                               \
118       MicroPrintf(#x " failed at %s:%d", __FILE__, __LINE__); \
119       micro_test::did_test_fail = true;                       \
120     }                                                         \
121   } while (false)
122 
123 // TODO(b/139142772): this macro is used with types other than ints even though
124 // the printf specifier is %d.
125 #define TF_LITE_MICRO_EXPECT_EQ(x, y)                                    \
126   do {                                                                   \
127     auto vx = x;                                                         \
128     auto vy = y;                                                         \
129     if ((vx) != (vy)) {                                                  \
130       MicroPrintf(#x " == " #y " failed at %s:%d (%d vs %d)", __FILE__,  \
131                   __LINE__, static_cast<int>(vx), static_cast<int>(vy)); \
132       micro_test::did_test_fail = true;                                  \
133     }                                                                    \
134   } while (false)
135 
136 #define TF_LITE_MICRO_EXPECT_NE(x, y)                                   \
137   do {                                                                  \
138     if ((x) == (y)) {                                                   \
139       MicroPrintf(#x " != " #y " failed at %s:%d", __FILE__, __LINE__); \
140       micro_test::did_test_fail = true;                                 \
141     }                                                                   \
142   } while (false)
143 
144 // TODO(wangtz): Making it more generic once needed.
145 #define TF_LITE_MICRO_ARRAY_ELEMENT_EXPECT_NEAR(arr1, idx1, arr2, idx2,       \
146                                                 epsilon)                      \
147   do {                                                                        \
148     auto delta = ((arr1)[(idx1)] > (arr2)[(idx2)])                            \
149                      ? ((arr1)[(idx1)] - (arr2)[(idx2)])                      \
150                      : ((arr2)[(idx2)] - (arr1)[(idx1)]);                     \
151     if (delta > epsilon) {                                                    \
152       MicroPrintf(#arr1 "[%d] (%f) near " #arr2 "[%d] (%f) failed at %s:%d",  \
153                   static_cast<int>(idx1), static_cast<float>((arr1)[(idx1)]), \
154                   static_cast<int>(idx2), static_cast<float>((arr2)[(idx2)]), \
155                   __FILE__, __LINE__);                                        \
156       micro_test::did_test_fail = true;                                       \
157     }                                                                         \
158   } while (false)
159 
160 // The check vx != vy is needed to properly handle the case where both
161 // x and y evaluate to infinity. See #46960 for more details.
162 #define TF_LITE_MICRO_EXPECT_NEAR(x, y, epsilon)                              \
163   do {                                                                        \
164     auto vx = (x);                                                            \
165     auto vy = (y);                                                            \
166     auto delta = ((vx) > (vy)) ? ((vx) - (vy)) : ((vy) - (vx));               \
167     if (vx != vy && delta > epsilon) {                                        \
168       MicroPrintf(#x " (%f) near " #y " (%f) failed at %s:%d",                \
169                   static_cast<double>(vx), static_cast<double>(vy), __FILE__, \
170                   __LINE__);                                                  \
171       micro_test::did_test_fail = true;                                       \
172     }                                                                         \
173   } while (false)
174 
175 #define TF_LITE_MICRO_EXPECT_GT(x, y)                                  \
176   do {                                                                 \
177     if ((x) <= (y)) {                                                  \
178       MicroPrintf(#x " > " #y " failed at %s:%d", __FILE__, __LINE__); \
179       micro_test::did_test_fail = true;                                \
180     }                                                                  \
181   } while (false)
182 
183 #define TF_LITE_MICRO_EXPECT_LT(x, y)                                  \
184   do {                                                                 \
185     if ((x) >= (y)) {                                                  \
186       MicroPrintf(#x " < " #y " failed at %s:%d", __FILE__, __LINE__); \
187       micro_test::did_test_fail = true;                                \
188     }                                                                  \
189   } while (false)
190 
191 #define TF_LITE_MICRO_EXPECT_GE(x, y)                                   \
192   do {                                                                  \
193     if ((x) < (y)) {                                                    \
194       MicroPrintf(#x " >= " #y " failed at %s:%d", __FILE__, __LINE__); \
195       micro_test::did_test_fail = true;                                 \
196     }                                                                   \
197   } while (false)
198 
199 #define TF_LITE_MICRO_EXPECT_LE(x, y)                                   \
200   do {                                                                  \
201     if ((x) > (y)) {                                                    \
202       MicroPrintf(#x " <= " #y " failed at %s:%d", __FILE__, __LINE__); \
203       micro_test::did_test_fail = true;                                 \
204     }                                                                   \
205   } while (false)
206 
207 #define TF_LITE_MICRO_EXPECT_TRUE(x)                                       \
208   do {                                                                     \
209     if (!(x)) {                                                            \
210       MicroPrintf(#x " was not true failed at %s:%d", __FILE__, __LINE__); \
211       micro_test::did_test_fail = true;                                    \
212     }                                                                      \
213   } while (false)
214 
215 #define TF_LITE_MICRO_EXPECT_FALSE(x)                                       \
216   do {                                                                      \
217     if (x) {                                                                \
218       MicroPrintf(#x " was not false failed at %s:%d", __FILE__, __LINE__); \
219       micro_test::did_test_fail = true;                                     \
220     }                                                                       \
221   } while (false)
222 
223 #define TF_LITE_MICRO_FAIL(msg)                       \
224   do {                                                \
225     MicroPrintf("FAIL: %s", msg, __FILE__, __LINE__); \
226     micro_test::did_test_fail = true;                 \
227   } while (false)
228 
229 #define TF_LITE_MICRO_EXPECT_STRING_EQ(string1, string2)                     \
230   do {                                                                       \
231     for (int i = 0; string1[i] != '\0' && string2[i] != '\0'; i++) {         \
232       if (string1[i] != string2[i]) {                                        \
233         MicroPrintf("FAIL: %s did not match %s", string1, string2, __FILE__, \
234                     __LINE__);                                               \
235         micro_test::did_test_fail = true;                                    \
236       }                                                                      \
237     }                                                                        \
238   } while (false)
239 
240 #endif  // TENSORFLOW_LITE_MICRO_TESTING_MICRO_TEST_H_
241