--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. 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 "TFLExporterImpl.h"
+
+#include "schema_generated.h"
+
+#include <loco/IR/PermutingCodec.h>
+#include <stdex/Memory.h>
+
+#include <gtest/gtest.h>
+
+namespace
+{
+
+class TFLExporterImplTests : public ::testing::Test
+{
+public:
+ TFLExporterImplTests() { _graph = loco::make_graph(); }
+
+public:
+ virtual ~TFLExporterImplTests() = default;
+
+protected:
+ loco::Graph *graph(void) { return _graph.get(); }
+
+ template <typename NodeT> NodeT *make_node(void);
+
+private:
+ std::unique_ptr<loco::Graph> _graph;
+};
+
+template <typename NodeT> NodeT *TFLExporterImplTests::make_node(void)
+{
+ return graph()->nodes()->create<NodeT>();
+}
+
+template <> loco::FeatureEncode *TFLExporterImplTests::make_node(void)
+{
+ loco::FeatureEncode *encode_layer = graph()->nodes()->create<loco::FeatureEncode>();
+
+ auto encoder = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
+ (*encoder->perm())[loco::FeatureAxis::Count] = 0;
+ (*encoder->perm())[loco::FeatureAxis::Depth] = 1;
+ (*encoder->perm())[loco::FeatureAxis::Height] = 2;
+ (*encoder->perm())[loco::FeatureAxis::Width] = 3;
+ encode_layer->encoder(std::move(encoder));
+
+ return encode_layer;
+}
+
+template <> loco::FeatureDecode *TFLExporterImplTests::make_node(void)
+{
+ loco::FeatureDecode *decode_layer = graph()->nodes()->create<loco::FeatureDecode>();
+
+ auto decoder = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
+ (*decoder->perm())[loco::FeatureAxis::Count] = 0;
+ (*decoder->perm())[loco::FeatureAxis::Depth] = 1;
+ (*decoder->perm())[loco::FeatureAxis::Height] = 2;
+ (*decoder->perm())[loco::FeatureAxis::Width] = 3;
+ decode_layer->decoder(std::move(decoder));
+
+ return decode_layer;
+}
+
+} // namespace
+
+/**
+ * What happens when there is a mismatch between generation and execution order!?
+ */
+TEST_F(TFLExporterImplTests, Regression_0000)
+{
+ // Execution Order: MaxPool2D -> ReLU
+ // Generation Order: ReLU -> MaxPool2D
+ auto pull = make_node<loco::Pull>();
+ {
+ pull->dtype(loco::DataType::FLOAT32);
+ pull->shape({1, 8, 8, 3});
+ }
+ auto relu = make_node<loco::ReLU>();
+ auto encode = make_node<loco::FeatureEncode>();
+ auto maxpool = make_node<loco::MaxPool2D>();
+ auto decode = make_node<loco::FeatureDecode>();
+ auto push = make_node<loco::Push>();
+
+ ASSERT_EQ(maxpool->window()->vertical(), 1);
+ ASSERT_EQ(maxpool->window()->horizontal(), 1);
+
+ encode->input(pull);
+ maxpool->ifm(encode);
+ relu->input(maxpool);
+ decode->input(relu);
+ push->from(decode);
+
+ auto input = graph()->inputs()->create();
+ {
+ input->name("input");
+ input->node(pull);
+ }
+ auto output = graph()->outputs()->create();
+ {
+ output->name("output");
+ output->node(push);
+ }
+
+ exo::TFLExporter::Impl exporter{graph()};
+ {
+ int64_t maxpool_execution_index = -1;
+ int64_t relu_exeuction_index = -1;
+
+ auto model = tflite::GetModel(exporter.getBufferPointer());
+ auto operators = model->subgraphs()->Get(0)->operators();
+
+ for (uint32_t n = 0; n < operators->Length(); ++n)
+ {
+ auto opcode_index = operators->Get(n)->opcode_index();
+
+ switch (model->operator_codes()->Get(opcode_index)->builtin_code())
+ {
+ case tflite::BuiltinOperator_RELU:
+ ASSERT_EQ(relu_exeuction_index, -1);
+ relu_exeuction_index = static_cast<int64_t>(n);
+ break;
+ case tflite::BuiltinOperator_MAX_POOL_2D:
+ ASSERT_EQ(maxpool_execution_index, -1);
+ maxpool_execution_index = static_cast<int64_t>(n);
+ break;
+ default:
+ break;
+ }
+ }
+
+ ASSERT_NE(maxpool_execution_index, -1);
+ ASSERT_NE(relu_exeuction_index, -1);
+ // maxpool SHOULD precede ReLU
+ ASSERT_LT(maxpool_execution_index, relu_exeuction_index);
+ }
+}