Publishing 2019 R1 content
[platform/upstream/dldt.git] / inference-engine / tests / unit / cnn_network / parser_tests_base.hpp
1 // Copyright (C) 2018-2019 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 //
4
5 #pragma once
6
7 #include <fstream>
8 #include <gtest/gtest.h>
9 #include "xml_father.hpp"
10 #include "cnn_network_impl.hpp"
11 #include  <tests_common.hpp>
12 #include "ie_format_parser.h"
13 #include <string>
14 #include "pugixml.hpp"
15 #include "xml_parse_utils.h"
16 #include "mean_image.h"
17 #include "ie_blob_proxy.hpp"
18
19 class FormatParserTest : public TestsCommon {
20  public:
21     FormatParserTest() {
22     }
23
24  protected:
25     const char kPathSeparator =
26 #if defined _WIN32 || defined __CYGWIN__
27         '\\';
28 #else
29         '/';
30 #endif
31     const std::string parentDir = std::string("..") + std::to_string(FormatParserTest::kPathSeparator);
32
33     std::string getParentDir(std::string currentFile) const {
34         return parentDir + currentFile;
35     }
36
37  protected:
38     InferenceEngine::details::CNNNetworkImplPtr net;
39     ModelsPath _path_to_models;
40
41     InferenceEngine::InputInfo::Ptr getFirstInput() const {
42         return ::getFirstInput(net.get());
43     }
44     template <class LayerType>
45     std::shared_ptr<LayerType> getLayer(const std::string & name) const {
46         InferenceEngine::CNNLayerPtr ptr;
47         net->getLayerByName(name.c_str(), ptr, nullptr);
48         return std::dynamic_pointer_cast<LayerType>(ptr);
49     }
50
51
52     virtual void SetUp() {
53         _path_to_models += kPathSeparator;
54     }
55
56     void assertParseFail(const std::string& fileContent) {
57         try {
58             parse(fileContent);
59             FAIL() << "Parser didn't throw";
60         } catch (const std::exception& ex) {
61             SUCCEED() << ex.what();
62         }
63     }
64
65     void assertParseSucceed(const std::string& fileContent) {
66         ASSERT_NO_THROW(parse(fileContent));
67     }
68
69     void assertSetWeightsFail(const InferenceEngine::TBlob<uint8_t>::Ptr& binBlob) {
70         try {
71             parser->SetWeights(binBlob);
72             FAIL() << "Parser didn't throw";
73         } catch (const std::exception& ex) {
74             SUCCEED() << ex.what();
75         }
76     }
77
78     void assertSetWeightsSucceed(const InferenceEngine::TBlob<uint8_t>::Ptr& binBlob) {
79         ASSERT_NO_THROW(parser->SetWeights(binBlob));
80     }
81
82     void parse(const std::string& fileContent) {
83         // check which version it is...
84         pugi::xml_document xmlDoc;
85         auto res = xmlDoc.load_string(fileContent.c_str());
86
87         EXPECT_EQ(pugi::status_ok, res.status) << res.description() << " at offset " << res.offset;
88
89
90         pugi::xml_node root = xmlDoc.document_element();
91
92         int version = XMLParseUtils::GetIntAttr(root, "version", 2);
93         if (version < 2) THROW_IE_EXCEPTION << "Deprecated IR's versions: " << version;
94         if (version > 3) THROW_IE_EXCEPTION << "cannot parse future versions: " << version;
95         parser.reset(new InferenceEngine::details::FormatParser(version));
96
97         net = parser->Parse(root);
98     }
99
100 #define initlayerIn(name, id, portid) \
101     node("layer").attr("type", "Power").attr("name", name).attr("id", id)\
102         .node("power_data").attr("power", 1).attr("scale", 1).attr("shift", 0).close()\
103         .node("input")\
104             .node("port").attr("id", portid)\
105               .node("dim", MT_CHANNELS)\
106               .node("dim", MT_HEIGHT)\
107               .node("dim", MT_WIDTH)\
108             .close()\
109         .close()\
110     .close()
111
112 #define initlayerInV2(name, id, portid) \
113     node("layer").attr("type", "Power").attr("name", name).attr("id", id)\
114         .node("power_data").attr("power", 1).attr("scale", 1).attr("shift", 0).close()\
115         .node("input")\
116             .node("port").attr("id", portid)\
117               .node("dim", MT_BATCH)\
118               .node("dim", MT_CHANNELS)\
119               .node("dim", MT_HEIGHT)\
120               .node("dim", MT_WIDTH)\
121             .close()\
122         .close()\
123     .close()
124
125 #define initInputlayer(name, id, portid) \
126     node("layer").attr("type", "Input").attr("name", name).attr("id", id)\
127         .node("output")\
128             .node("port").attr("id", portid)\
129               .node("dim", MT_BATCH)\
130               .node("dim", MT_CHANNELS)\
131               .node("dim", MT_HEIGHT)\
132               .node("dim", MT_WIDTH)\
133             .close()\
134         .close()\
135     .close()
136
137 #define initInputlayer5D(name, id, portid) \
138     node("layer").attr("type", "Input").attr("name", name).attr("id", id)\
139         .node("output")\
140             .node("port").attr("id", portid)\
141               .node("dim", MT_BATCH)\
142               .node("dim", MT_CHANNELS)\
143               .node("dim", MT_DEPTH)\
144               .node("dim", MT_HEIGHT)\
145               .node("dim", MT_WIDTH)\
146             .close()\
147         .close()\
148     .close()
149
150 #define initPowerlayerInOutV2(name, id, portid, outputid) \
151     node("layer").attr("type", "Power").attr("name", name).attr("id", id)\
152         .node("power_data").attr("power", 1).attr("scale", 1).attr("shift", 0).close()\
153         .node("input")\
154             .node("port").attr("id", portid)\
155               .node("dim", MT_BATCH)\
156               .node("dim", MT_CHANNELS)\
157               .node("dim", MT_HEIGHT)\
158               .node("dim", MT_WIDTH)\
159             .close()\
160         .close()\
161         .node("output")\
162             .node("port").attr("id", outputid)\
163               .node("dim", MT_BATCH)\
164               .node("dim", MT_CHANNELS)\
165               .node("dim", MT_HEIGHT)\
166               .node("dim", MT_WIDTH)\
167             .close()\
168         .close()\
169     .close()
170
171
172 #define initPowerlayerInOut(name, id, portid, outputid) \
173     node("layer").attr("type", "Power").attr("name", name).attr("id", id)\
174         .node("power_data").attr("power", 1).attr("scale", 1).attr("shift", 0).close()\
175         .node("input")\
176             .node("port").attr("id", portid)\
177               .node("dim", MT_CHANNELS)\
178               .node("dim", MT_HEIGHT)\
179               .node("dim", MT_WIDTH)\
180             .close()\
181         .close()\
182         .node("output")\
183             .node("port").attr("id", outputid)\
184               .node("dim", MT_CHANNELS)\
185               .node("dim", MT_HEIGHT)\
186               .node("dim", MT_WIDTH)\
187             .close()\
188         .close()\
189     .close()
190
191 #define initlayerInOut(name, type, id, portid, outputid) \
192     node("layer").attr("type", type).attr("name", name).attr("id", id)\
193         .node("input")\
194             .node("port").attr("id", portid)\
195               .node("dim", MT_BATCH)\
196               .node("dim", MT_CHANNELS)\
197               .node("dim", MT_HEIGHT)\
198               .node("dim", MT_WIDTH)\
199             .close()\
200         .close()\
201         .node("output")\
202             .node("port").attr("id", outputid)\
203               .node("dim", MT_BATCH)\
204               .node("dim", MT_CHANNELS)\
205               .node("dim", MT_HEIGHT)\
206               .node("dim", MT_WIDTH)\
207             .close()\
208         .close()\
209
210
211 #define initConv5DlayerInOut(name, id, group, output, kernel, pads_begin, pads_end, strides, dilations, inputid, outputid) \
212     node("layer").attr("type", "Convolution").attr("name", name).attr("id", id)\
213         .node("data").attr("group", group).attr("output", output).attr("kernel", kernel).attr("pads_begin", pads_begin).attr("pads_end", pads_end).attr("strides", strides).attr("dilations", dilations).close()\
214         .node("input")\
215             .node("port").attr("id", inputid)\
216               .node("dim", MT_BATCH)\
217               .node("dim", MT_CHANNELS)\
218               .node("dim", MT_DEPTH)\
219               .node("dim", MT_HEIGHT)\
220               .node("dim", MT_WIDTH)\
221             .close()\
222         .close()\
223         .node("output")\
224             .node("port").attr("id", outputid)\
225               .node("dim", MT_BATCH)\
226               .node("dim", MT_CHANNELS)\
227               .node("dim", MT_DEPTH)\
228               .node("dim", MT_HEIGHT)\
229               .node("dim", MT_WIDTH)\
230             .close()\
231         .close()\
232
233
234 #define initedge(fl, fp, tl, tp)\
235     node("edge").attr("from-layer", fl).attr("from-port", fp).attr("to-layer", tl).attr("to-port", tp).close()
236
237 /*
238     auto net(const string & name1) const -> decltype(XMLFather().node("net").attr("name", name1).initlayers().initedges()) {
239         return XMLFather().node("net").attr("name", name1).initlayers().initedges();
240     }*/
241
242 #define MAKE_ALEXNET_FOR_MEAN_TESTS()\
243 xml().node("net").attr("name", "AlexNet")\
244 .node("input").attr("name", "data")\
245     .node("dim", MT_CHANNELS)\
246     .node("dim", MT_HEIGHT)\
247     .node("dim", MT_WIDTH)\
248     .newnode("layers")\
249         .initPowerlayerInOut("power", 0, 0, 1)\
250         .initlayerIn("power", 1, 0)\
251     .newnode("edges")\
252         .initedge(0,1,1,0)\
253     .newnode("pre-process")
254
255 #define MAKE_ALEXNET_FOR_MEAN_TESTS_V2()\
256 xml().node("net").attr("name", "AlexNet").attr("version", 2)\
257     .node("layers")\
258         .initInputlayer("data", 0, 0)\
259         .initPowerlayerInOutV2("power1", 1, 1, 2)\
260         .initlayerInV2("power2", 2, 3)\
261     .newnode("edges")\
262         .initedge(0,0,1,1)\
263         .initedge(1,2,2,3)\
264     .newnode("pre-process")
265
266
267 #define BEGIN_NET()\
268 _BEGIN_NET(2)
269
270 #define BEGIN_NET_V3()\
271 _BEGIN_NET(3)
272
273 #define BEGIN_NET_V2()\
274 _BEGIN_NET(2)
275
276 #define _BEGIN_NET(x)\
277 xml().node("net").attr("name", "AlexNet").attr("version", x)\
278     .node("layers")\
279         .initInputlayer("data", 0, 0)\
280
281
282 #define END_NET()\
283     .newnode("edges")\
284         .initedge(0,0,1,1)\
285             .close()
286
287
288     template <class T>
289     InferenceEngine::TBlob<uint8_t>::Ptr makeBinBlobForMeanTest() {
290         typename InferenceEngine::TBlob<T>::Ptr binBlobFloat(new InferenceEngine::TBlob<T>(InferenceEngine::Precision::FP32, InferenceEngine::CHW, { MT_HEIGHT, MT_WIDTH, MT_CHANNELS }));
291         binBlobFloat->allocate();
292         binBlobFloat->set(MeanImage<T>::getValue());
293         InferenceEngine::SizeVector dims_dst = { MT_HEIGHT, MT_WIDTH * sizeof(T), MT_CHANNELS };
294         typename InferenceEngine::TBlobProxy<uint8_t>::Ptr binBlob(new InferenceEngine::TBlobProxy<uint8_t>(InferenceEngine::Precision::FP32, InferenceEngine::CHW, binBlobFloat, 0, dims_dst));
295         return binBlob;
296     }
297
298     template<class T>
299     void assertMeanImagePerChannelCorrect() {
300         std::vector<T> meanImage = MeanImage<T>::getValue();
301         auto & pp = getFirstInput()->getPreProcess();
302         ASSERT_EQ(MT_CHANNELS, pp.getNumberOfChannels());
303         for (unsigned channel = 0, globalPixel = 0; channel < MT_CHANNELS; channel++) {
304             auto actualMeanChannel = std::dynamic_pointer_cast<InferenceEngine::TBlob<T> >(pp[channel]->meanData);
305             ASSERT_EQ(MT_HEIGHT * MT_WIDTH, actualMeanChannel->size());
306             for (unsigned pixel = 0; pixel < actualMeanChannel->size(); pixel++, globalPixel++) {
307                 ASSERT_FLOAT_EQ(meanImage[globalPixel], actualMeanChannel->readOnly()[pixel]);
308             }
309         }
310     }
311
312     template<class T>
313     void assertMeanImageCorrect() {
314         std::vector<T> meanImage = MeanImage<T>::getValue();
315
316         auto & pp = getFirstInput()->getPreProcess();
317         ASSERT_EQ(MT_CHANNELS, pp.getNumberOfChannels());
318         for (size_t c = 0; c < pp.getNumberOfChannels(); c++) {
319             auto actualMeanTBlob = std::dynamic_pointer_cast<InferenceEngine::TBlob<T> >(pp[c]->meanData);
320             ASSERT_EQ(MT_WIDTH, actualMeanTBlob->dims()[0]);
321             ASSERT_EQ(MT_HEIGHT, actualMeanTBlob->dims()[1]);
322             ASSERT_EQ(MT_WIDTH*MT_HEIGHT, actualMeanTBlob->size());
323             for (unsigned index = 0; index < actualMeanTBlob->size(); index++) {
324                 ASSERT_FLOAT_EQ(meanImage[index+c*MT_WIDTH*MT_HEIGHT], actualMeanTBlob->readOnly()[index]);
325             }
326         }
327     }
328
329     testing::XMLFather xml() {
330         return testing::XMLFather();
331     }
332
333     std::shared_ptr<InferenceEngine::details::FormatParser> parser;
334
335  public:
336
337     int getXmlVersion(pugi::xml_node& root) {
338         if (!root.child("InputData").empty()) return 2;
339         return 1;
340     }
341
342
343
344     std::string getXmlPath(const std::string& filePath) {
345         std::string xmlPath = filePath;
346         const auto openFlags = std::ios_base::ate | std::ios_base::binary;
347         std::ifstream fp(xmlPath, openFlags);
348         //TODO: Dueto multi directory build systems, and single directory build system
349         //, it is usualy a problem to deal with relative paths.
350         if (!fp.is_open()) {
351             fp.open(getParentDir(xmlPath), openFlags);
352             EXPECT_TRUE(fp.is_open())
353                             << "cannot open file " << xmlPath << " or " << getParentDir(xmlPath);
354             fp.close();
355             xmlPath = getParentDir(xmlPath);
356         }
357         return xmlPath;
358     }
359
360     std::string readFileContent(const std::string& filePath) {
361
362         const auto openFlags = std::ios_base::ate | std::ios_base::binary;
363         std::ifstream fp(getXmlPath(filePath), openFlags);
364         EXPECT_TRUE(fp.is_open()) << "Cannot open file: " << filePath;
365         if (!fp.is_open())
366             return std::string();
367
368         std::streamsize size = fp.tellg();
369         EXPECT_GE(size, 1) << "file is empty: " << filePath;
370         if (size == 0)
371             return std::string();
372
373         std::string str;
374
375         str.reserve((size_t)size);
376         fp.seekg(0, std::ios::beg);
377
378         str.assign((std::istreambuf_iterator<char>(fp)),
379                    std::istreambuf_iterator<char>());
380         return str;
381     }
382 };