| // NOTE: file is forked from tensorflow/lite/c/c_api_test.cc with tests that |
| // we don't support in the shim removed. It is as unmodified as possible such |
| // that if a user of the tflite API does what this test does they should be able |
| // to use our shim too. |
| |
| /* Copyright 2018 The TensorFlow Authors. All Rights Reserved. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ==============================================================================*/ |
| |
| #include <stdarg.h> |
| #include <stdint.h> |
| |
| #include <array> |
| #include <fstream> |
| #include <vector> |
| |
| #include "iree/testing/gtest.h" |
| |
| // NOTE: we pull in our own copy here in case the tflite API changes upstream. |
| #define TFL_COMPILE_LIBRARY 1 |
| #include "runtime/bindings/tflite/include/tensorflow/lite/c/c_api.h" |
| |
| // Test model is available both on the filesystem and here for embedding testing |
| // embedding the module directly in a binary. |
| #include "runtime/bindings/tflite/testdata/add_dynamic_c.h" |
| #define IREE_BINDINGS_TFLITE_TESTDATA_ADD_DYNAMIC_EMBEDDED_DATA \ |
| iree_tflite_testdata_add_dynamic_create()->data |
| #define IREE_BINDINGS_TFLITE_TESTDATA_ADD_DYNAMIC_EMBEDDED_SIZE \ |
| iree_tflite_testdata_add_dynamic_create()->size |
| #include "runtime/bindings/tflite/testdata/add_static_c.h" |
| #define IREE_BINDINGS_TFLITE_TESTDATA_ADD_STATIC_EMBEDDED_DATA \ |
| iree_tflite_testdata_add_static_create()->data |
| #define IREE_BINDINGS_TFLITE_TESTDATA_ADD_STATIC_EMBEDDED_SIZE \ |
| iree_tflite_testdata_add_static_create()->size |
| |
| // TODO(#3971): currently can't nicely load these due to cmake issues. |
| #define IREE_BINDINGS_TFLITE_TESTDATA_ADD_STATIC_PATH \ |
| "tensorflow/lite/testdata/add.bin" |
| |
| namespace { |
| |
| TEST(CAPI, Version) { EXPECT_STRNE("", TfLiteVersion()); } |
| |
| // The original test has been modified here because it uses dynamic shapes |
| // even though the model has static shapes defined 🤦. IREE does not support |
| // this misbehavior of compiling with static shapes and treating them as dynamic |
| // at runtime. |
| // |
| // TODO(#3975): need to remove SIP stuff and pass buffer views. |
| TEST(CApiSimple, DISABLED_DynamicSmoke) { |
| // TODO(#3971): currently can't nicely load these due to cmake issues. |
| // TfLiteModel* model = |
| // TfLiteModelCreateFromFile(IREE_BINDINGS_TFLITE_TESTDATA_ADD_DYNAMIC_PATH); |
| TfLiteModel* model = TfLiteModelCreate( |
| IREE_BINDINGS_TFLITE_TESTDATA_ADD_DYNAMIC_EMBEDDED_DATA, |
| IREE_BINDINGS_TFLITE_TESTDATA_ADD_DYNAMIC_EMBEDDED_SIZE); |
| ASSERT_NE(model, nullptr); |
| |
| TfLiteInterpreterOptions* options = TfLiteInterpreterOptionsCreate(); |
| ASSERT_NE(options, nullptr); |
| TfLiteInterpreterOptionsSetNumThreads(options, 2); |
| |
| TfLiteInterpreter* interpreter = TfLiteInterpreterCreate(model, options); |
| ASSERT_NE(interpreter, nullptr); |
| |
| // The options/model can be deleted immediately after interpreter creation. |
| TfLiteInterpreterOptionsDelete(options); |
| TfLiteModelDelete(model); |
| |
| ASSERT_EQ(TfLiteInterpreterAllocateTensors(interpreter), kTfLiteOk); |
| ASSERT_EQ(TfLiteInterpreterGetInputTensorCount(interpreter), 1); |
| ASSERT_EQ(TfLiteInterpreterGetOutputTensorCount(interpreter), 1); |
| |
| std::array<int, 1> input_dims = {2}; |
| ASSERT_EQ(TfLiteInterpreterResizeInputTensor( |
| interpreter, 0, input_dims.data(), input_dims.size()), |
| kTfLiteOk); |
| ASSERT_EQ(TfLiteInterpreterAllocateTensors(interpreter), kTfLiteOk); |
| |
| TfLiteTensor* input_tensor = TfLiteInterpreterGetInputTensor(interpreter, 0); |
| ASSERT_NE(input_tensor, nullptr); |
| EXPECT_EQ(TfLiteTensorType(input_tensor), kTfLiteFloat32); |
| EXPECT_EQ(TfLiteTensorNumDims(input_tensor), 1); |
| EXPECT_EQ(TfLiteTensorDim(input_tensor, 0), 2); |
| EXPECT_EQ(TfLiteTensorByteSize(input_tensor), sizeof(float) * 2); |
| EXPECT_NE(TfLiteTensorData(input_tensor), nullptr); |
| EXPECT_STREQ(TfLiteTensorName(input_tensor), "input"); |
| |
| TfLiteQuantizationParams input_params = |
| TfLiteTensorQuantizationParams(input_tensor); |
| EXPECT_EQ(input_params.scale, 0.f); |
| EXPECT_EQ(input_params.zero_point, 0); |
| |
| std::array<float, 2> input = {1.f, 3.f}; |
| ASSERT_EQ(TfLiteTensorCopyFromBuffer(input_tensor, input.data(), |
| input.size() * sizeof(float)), |
| kTfLiteOk); |
| |
| ASSERT_EQ(TfLiteInterpreterInvoke(interpreter), kTfLiteOk); |
| |
| const TfLiteTensor* output_tensor = |
| TfLiteInterpreterGetOutputTensor(interpreter, 0); |
| ASSERT_NE(output_tensor, nullptr); |
| EXPECT_EQ(TfLiteTensorType(output_tensor), kTfLiteFloat32); |
| EXPECT_EQ(TfLiteTensorNumDims(output_tensor), 1); |
| EXPECT_EQ(TfLiteTensorDim(output_tensor, 0), 2); |
| EXPECT_EQ(TfLiteTensorByteSize(output_tensor), sizeof(float) * 2); |
| EXPECT_NE(TfLiteTensorData(output_tensor), nullptr); |
| EXPECT_STREQ(TfLiteTensorName(output_tensor), "output"); |
| |
| TfLiteQuantizationParams output_params = |
| TfLiteTensorQuantizationParams(output_tensor); |
| EXPECT_EQ(output_params.scale, 0.f); |
| EXPECT_EQ(output_params.zero_point, 0); |
| |
| std::array<float, 2> output; |
| ASSERT_EQ(TfLiteTensorCopyToBuffer(output_tensor, output.data(), |
| output.size() * sizeof(float)), |
| kTfLiteOk); |
| EXPECT_EQ(output[0], 3.f); |
| EXPECT_EQ(output[1], 9.f); |
| |
| TfLiteInterpreterDelete(interpreter); |
| } |
| |
| // As with above but without the static->dynamic override junk. |
| TEST(CApiSimple, StaticSmoke) { |
| // TODO(#3971): currently can't nicely load these due to cmake issues. |
| // TfLiteModel* model = |
| // TfLiteModelCreateFromFile(IREE_BINDINGS_TFLITE_TESTDATA_ADD_STATIC_PATH); |
| TfLiteModel* model = |
| TfLiteModelCreate(IREE_BINDINGS_TFLITE_TESTDATA_ADD_STATIC_EMBEDDED_DATA, |
| IREE_BINDINGS_TFLITE_TESTDATA_ADD_STATIC_EMBEDDED_SIZE); |
| ASSERT_NE(model, nullptr); |
| |
| TfLiteInterpreterOptions* options = TfLiteInterpreterOptionsCreate(); |
| ASSERT_NE(options, nullptr); |
| TfLiteInterpreterOptionsSetNumThreads(options, 2); |
| |
| TfLiteInterpreter* interpreter = TfLiteInterpreterCreate(model, options); |
| ASSERT_NE(interpreter, nullptr); |
| |
| // The options/model can be deleted immediately after interpreter creation. |
| TfLiteInterpreterOptionsDelete(options); |
| TfLiteModelDelete(model); |
| |
| ASSERT_EQ(TfLiteInterpreterAllocateTensors(interpreter), kTfLiteOk); |
| ASSERT_EQ(TfLiteInterpreterGetInputTensorCount(interpreter), 1); |
| ASSERT_EQ(TfLiteInterpreterGetOutputTensorCount(interpreter), 1); |
| |
| TfLiteTensor* input_tensor = TfLiteInterpreterGetInputTensor(interpreter, 0); |
| ASSERT_NE(input_tensor, nullptr); |
| EXPECT_EQ(TfLiteTensorType(input_tensor), kTfLiteFloat32); |
| EXPECT_EQ(TfLiteTensorNumDims(input_tensor), 4); |
| EXPECT_EQ(TfLiteTensorDim(input_tensor, 0), 1); |
| EXPECT_EQ(TfLiteTensorDim(input_tensor, 1), 8); |
| EXPECT_EQ(TfLiteTensorDim(input_tensor, 2), 8); |
| EXPECT_EQ(TfLiteTensorDim(input_tensor, 3), 3); |
| EXPECT_EQ(TfLiteTensorByteSize(input_tensor), sizeof(float) * 1 * 8 * 8 * 3); |
| EXPECT_NE(TfLiteTensorData(input_tensor), nullptr); |
| EXPECT_STREQ(TfLiteTensorName(input_tensor), "input"); |
| |
| TfLiteQuantizationParams input_params = |
| TfLiteTensorQuantizationParams(input_tensor); |
| EXPECT_EQ(input_params.scale, 0.f); |
| EXPECT_EQ(input_params.zero_point, 0); |
| |
| std::array<float, 1 * 8 * 8 * 3> input = { |
| 1.f, |
| 3.f, |
| }; |
| ASSERT_EQ(TfLiteTensorCopyFromBuffer(input_tensor, input.data(), |
| input.size() * sizeof(float)), |
| kTfLiteOk); |
| |
| ASSERT_EQ(TfLiteInterpreterInvoke(interpreter), kTfLiteOk); |
| |
| const TfLiteTensor* output_tensor = |
| TfLiteInterpreterGetOutputTensor(interpreter, 0); |
| ASSERT_NE(output_tensor, nullptr); |
| EXPECT_EQ(TfLiteTensorType(output_tensor), kTfLiteFloat32); |
| EXPECT_EQ(TfLiteTensorNumDims(output_tensor), 4); |
| EXPECT_EQ(TfLiteTensorDim(output_tensor, 0), 1); |
| EXPECT_EQ(TfLiteTensorDim(output_tensor, 1), 8); |
| EXPECT_EQ(TfLiteTensorDim(output_tensor, 2), 8); |
| EXPECT_EQ(TfLiteTensorDim(output_tensor, 3), 3); |
| EXPECT_EQ(TfLiteTensorByteSize(output_tensor), sizeof(float) * 1 * 8 * 8 * 3); |
| EXPECT_NE(TfLiteTensorData(output_tensor), nullptr); |
| EXPECT_STREQ(TfLiteTensorName(output_tensor), "output"); |
| |
| TfLiteQuantizationParams output_params = |
| TfLiteTensorQuantizationParams(output_tensor); |
| EXPECT_EQ(output_params.scale, 0.f); |
| EXPECT_EQ(output_params.zero_point, 0); |
| |
| std::array<float, 1 * 8 * 8 * 3> output; |
| ASSERT_EQ(TfLiteTensorCopyToBuffer(output_tensor, output.data(), |
| output.size() * sizeof(float)), |
| kTfLiteOk); |
| EXPECT_EQ(output[0], 2.f); |
| EXPECT_EQ(output[1], 6.f); |
| |
| TfLiteInterpreterDelete(interpreter); |
| } |
| |
| // TODO(#3971): fix cmake data deps. |
| // TODO(#3972): plumb through quantization params. |
| TEST(CApiSimple, DISABLED_QuantizationParams) { |
| TfLiteModel* model = |
| TfLiteModelCreateFromFile("tensorflow/lite/testdata/add_quantized.bin"); |
| ASSERT_NE(model, nullptr); |
| |
| TfLiteInterpreter* interpreter = TfLiteInterpreterCreate(model, nullptr); |
| ASSERT_NE(interpreter, nullptr); |
| |
| TfLiteModelDelete(model); |
| |
| const std::array<int, 1> input_dims = {2}; |
| ASSERT_EQ(TfLiteInterpreterResizeInputTensor( |
| interpreter, 0, input_dims.data(), input_dims.size()), |
| kTfLiteOk); |
| ASSERT_EQ(TfLiteInterpreterAllocateTensors(interpreter), kTfLiteOk); |
| |
| TfLiteTensor* input_tensor = TfLiteInterpreterGetInputTensor(interpreter, 0); |
| ASSERT_NE(input_tensor, nullptr); |
| EXPECT_EQ(TfLiteTensorType(input_tensor), kTfLiteUInt8); |
| EXPECT_EQ(TfLiteTensorNumDims(input_tensor), 1); |
| EXPECT_EQ(TfLiteTensorDim(input_tensor, 0), 2); |
| |
| TfLiteQuantizationParams input_params = |
| TfLiteTensorQuantizationParams(input_tensor); |
| EXPECT_EQ(input_params.scale, 0.003922f); |
| EXPECT_EQ(input_params.zero_point, 0); |
| |
| const std::array<uint8_t, 2> input = {1, 3}; |
| ASSERT_EQ(TfLiteTensorCopyFromBuffer(input_tensor, input.data(), |
| input.size() * sizeof(uint8_t)), |
| kTfLiteOk); |
| |
| ASSERT_EQ(TfLiteInterpreterInvoke(interpreter), kTfLiteOk); |
| |
| const TfLiteTensor* output_tensor = |
| TfLiteInterpreterGetOutputTensor(interpreter, 0); |
| ASSERT_NE(output_tensor, nullptr); |
| |
| TfLiteQuantizationParams output_params = |
| TfLiteTensorQuantizationParams(output_tensor); |
| EXPECT_EQ(output_params.scale, 0.003922f); |
| EXPECT_EQ(output_params.zero_point, 0); |
| |
| std::array<uint8_t, 2> output; |
| ASSERT_EQ(TfLiteTensorCopyToBuffer(output_tensor, output.data(), |
| output.size() * sizeof(uint8_t)), |
| kTfLiteOk); |
| EXPECT_EQ(output[0], 3); |
| EXPECT_EQ(output[1], 9); |
| |
| const float dequantizedOutput0 = |
| output_params.scale * (output[0] - output_params.zero_point); |
| const float dequantizedOutput1 = |
| output_params.scale * (output[1] - output_params.zero_point); |
| EXPECT_EQ(dequantizedOutput0, 0.011766f); |
| EXPECT_EQ(dequantizedOutput1, 0.035298f); |
| |
| TfLiteInterpreterDelete(interpreter); |
| } |
| |
| // TODO(#3972): extract !quant.uniform and plumb through iree.reflection. |
| #if 0 |
| |
| // TODO(#3971): fix cmake data deps. |
| TEST(CApiSimple, DISABLED_ErrorReporter) { |
| TfLiteModel* model = |
| TfLiteModelCreateFromFile(IREE_BINDINGS_TFLITE_TESTDATA_ADD_PATH); |
| TfLiteInterpreterOptions* options = TfLiteInterpreterOptionsCreate(); |
| |
| // Install a custom error reporter into the interpreter by way of options. |
| tflite::TestErrorReporter reporter; |
| TfLiteInterpreterOptionsSetErrorReporter( |
| options, |
| [](void* user_data, const char* format, va_list args) { |
| reinterpret_cast<tflite::TestErrorReporter*>(user_data)->Report(format, |
| args); |
| }, |
| &reporter); |
| TfLiteInterpreter* interpreter = TfLiteInterpreterCreate(model, options); |
| |
| // The options/model can be deleted immediately after interpreter creation. |
| TfLiteInterpreterOptionsDelete(options); |
| TfLiteModelDelete(model); |
| |
| // Invoke the interpreter before tensor allocation. |
| EXPECT_EQ(TfLiteInterpreterInvoke(interpreter), kTfLiteError); |
| |
| // The error should propagate to the custom error reporter. |
| EXPECT_EQ(reporter.error_messages(), |
| "Invoke called on model that is not ready."); |
| EXPECT_EQ(reporter.num_calls(), 1); |
| |
| TfLiteInterpreterDelete(interpreter); |
| } |
| |
| #endif // 0 |
| |
| TEST(CApiSimple, ValidModel) { |
| TfLiteModel* model = |
| TfLiteModelCreate(IREE_BINDINGS_TFLITE_TESTDATA_ADD_STATIC_EMBEDDED_DATA, |
| IREE_BINDINGS_TFLITE_TESTDATA_ADD_STATIC_EMBEDDED_SIZE); |
| ASSERT_NE(model, nullptr); |
| TfLiteModelDelete(model); |
| } |
| |
| // TODO(#3971): fix cmake data deps. |
| TEST(CApiSimple, DISABLED_ValidModelFromFile) { |
| TfLiteModel* model = |
| TfLiteModelCreateFromFile(IREE_BINDINGS_TFLITE_TESTDATA_ADD_STATIC_PATH); |
| ASSERT_NE(model, nullptr); |
| TfLiteModelDelete(model); |
| } |
| |
| TEST(CApiSimple, InvalidModel) { |
| std::vector<char> invalid_model(20, 'c'); |
| TfLiteModel* model = |
| TfLiteModelCreate(invalid_model.data(), invalid_model.size()); |
| ASSERT_EQ(model, nullptr); |
| } |
| |
| // TODO(#3971): fix cmake data deps. |
| TEST(CApiSimple, DISABLED_InvalidModelFromFile) { |
| TfLiteModel* model = TfLiteModelCreateFromFile("invalid/path/foo.vmfb"); |
| ASSERT_EQ(model, nullptr); |
| } |
| |
| } // namespace |