// Copyright (C) 2017-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//

#include <gtest/gtest.h>

#include <legacy/cnn_network_impl.hpp>
#include <legacy/details/ie_cnn_network_iterator.hpp>
#include <string>
#include <sstream>
#include <fstream>
#include <memory>
#include <map>

#include <cpp/ie_cnn_network.h>
#include <legacy/ie_util_internal.hpp>
#include <ie_parameter.hpp>
#include <ie_core.hpp>
#include <legacy/net_pass.h>
#include <legacy/convert_function_to_cnn_network.hpp>
#include <legacy/transformations/convert_opset1_to_legacy/convert_opset1_to_legacy.hpp>
#include <ngraph/pass/manager.hpp>

#include <ngraph/opsets/opset3.hpp>
#include <ngraph/opsets/opset5.hpp>
#include <ngraph/function.hpp>
#include <ngraph/variant.hpp>
#include <ngraph/op/maximum.hpp>
#include <ngraph/op/constant.hpp>
#include <ngraph/op/convert.hpp>
#include <ngraph/op/parameter.hpp>
#include <ngraph/op/relu.hpp>
#include <ngraph/op/prelu.hpp>
#include <ngraph/op/result.hpp>
#include <common_test_utils/ngraph_test_utils.hpp>

#include "common_test_utils/file_utils.hpp"
#include "common_test_utils/common_utils.hpp"
#include "transformations/rt_info/primitives_priority_attribute.hpp"
#include "cnn_network_ngraph_impl.hpp"

using namespace testing;
using namespace InferenceEngine;

IE_SUPPRESS_DEPRECATED_START

TEST(CNNNGraphImplTests, TestNMS5OutputNames) {
    std::shared_ptr<ngraph::Function> f;
    {
        auto boxes = std::make_shared<ngraph::opset5::Parameter>(ngraph::element::f32, ngraph::Shape{1, 1000, 4});
        auto scores = std::make_shared<ngraph::opset5::Parameter>(ngraph::element::f32, ngraph::Shape{1, 1, 1000});
        auto max_output_boxes_per_class = ngraph::opset5::Constant::create(ngraph::element::i64, ngraph::Shape{}, {10});
        auto iou_threshold = ngraph::opset5::Constant::create(ngraph::element::f32, ngraph::Shape{}, {0.75});
        auto score_threshold = ngraph::opset5::Constant::create(ngraph::element::f32, ngraph::Shape{}, {0.7});
        auto nms = std::make_shared<ngraph::opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class,  iou_threshold, score_threshold,
                                                               ngraph::opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
        nms->set_friendly_name("nms");
        f = std::make_shared<ngraph::Function>(ngraph::OutputVector{nms->output(0), nms->output(1), nms->output(2)}, ngraph::ParameterVector{boxes, scores});
    }

    InferenceEngine::CNNNetwork cnnNet(f);
    auto outputs_info = cnnNet.getOutputsInfo();
    ASSERT_EQ(outputs_info.size(), 3);
    ASSERT_EQ(outputs_info.count("nms.0"), 1);
    ASSERT_EQ(outputs_info.count("nms.1"), 1);
    ASSERT_EQ(outputs_info.count("nms.2"), 1);
}

TEST(CNNNGraphImplTests, TestConvertWithRemoveLastLayerNetwork) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 22, 22});
        ngraph::element::Type type(ngraph::element::Type_t::i32);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        param->set_friendly_name("param");
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        relu->set_friendly_name("relu");
        auto convert = std::make_shared<ngraph::op::Convert>(relu, ngraph::element::Type_t::i64);
        convert->set_friendly_name("convert");
        auto result = std::make_shared<ngraph::op::Result>(convert);

        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};

        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::details::CNNNetworkNGraphImpl cnnNet(ngraph);
    auto convertedNet = InferenceEngine::CNNNetwork(std::make_shared<details::CNNNetworkImpl>(cnnNet));
    // Remove convert layer
    InferenceEngine::NetPass::ConvertPrecision(convertedNet, Precision::I64, Precision::I32);
    ASSERT_NO_THROW(cloneNet(convertedNet));
}

TEST(CNNNGraphImplTests, TestResultWithNotEqualName) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 22, 22});
        ngraph::element::Type type(ngraph::element::Type_t::f32);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        relu->set_friendly_name("test_layer_name");
        auto result = std::make_shared<ngraph::op::Result>(relu);
        result->set_friendly_name("test_layer_name");

        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};

        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::details::CNNNetworkNGraphImpl cnnNet(ngraph);
    ASSERT_NO_THROW(auto convertedNet = std::make_shared<details::CNNNetworkImpl>(cnnNet));
}

TEST(CNNNGraphImplTests, TestGetOutputAfterConvertNetwork) {
    const std::string testLayerName = "testReLU";
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 22, 22});
        ngraph::element::Type type(ngraph::element::Type_t::f32);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        relu->set_friendly_name(testLayerName);
        auto relu2 = std::make_shared<ngraph::op::Relu>(relu);
        relu2->set_friendly_name("relu2");
        auto result = std::make_shared<ngraph::op::Result>(relu2);

        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};

        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::CNNNetwork cnnNet(ngraph);
    // convert to old representation
    InferenceEngine::CNNNetwork convertedNetwork(std::make_shared<details::CNNNetworkImpl>(cnnNet));
    convertedNetwork.addOutput(testLayerName);

    InferenceEngine::OutputsDataMap outs = cnnNet.getOutputsInfo();
    InferenceEngine::OutputsDataMap convertedOuts = convertedNetwork.getOutputsInfo();
    ASSERT_EQ(1, outs.size());
    ASSERT_EQ(2, convertedOuts.size());
}

TEST(CNNNGraphImplTests, TestSetCurrentBatch) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 22, 22});
        ngraph::element::Type type(ngraph::element::Type_t::f32);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        auto result = std::make_shared<ngraph::op::Result>(relu);

        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};

        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::details::CNNNetworkNGraphImpl cnnNet(ngraph);
    ASSERT_EQ(1, cnnNet.getBatchSize());
    ASSERT_EQ(OK, cnnNet.setBatchSize(1, nullptr));
    ASSERT_EQ(1, cnnNet.getBatchSize());
    ASSERT_NE(nullptr, cnnNet.getFunction());
}

TEST(CNNNGraphImplTests, TestSetBatch) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 22, 22});
        ngraph::element::Type type(ngraph::element::Type_t::f32);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        auto result = std::make_shared<ngraph::op::Result>(relu);

        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};

        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::details::CNNNetworkNGraphImpl cnnNet(ngraph);
    ASSERT_EQ(1, cnnNet.getBatchSize());
    ASSERT_EQ(OK, cnnNet.setBatchSize(2, nullptr));  // must not trigger conversion
    ASSERT_EQ(2, cnnNet.getBatchSize());
    ASSERT_NE(nullptr, cnnNet.getFunction());
}

TEST(CNNNGraphImplTests, TestGetBatchScalar) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::Shape shape({});
        ngraph::element::Type type(ngraph::element::Type_t::f32);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        auto result = std::make_shared<ngraph::op::Result>(relu);

        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};

        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::details::CNNNetworkNGraphImpl cnnNet(ngraph);
    ASSERT_EQ(1, cnnNet.getBatchSize());
}

TEST(CNNNGraphImplTests, TestSetBatchScalar) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::Shape shape({});
        ngraph::element::Type type(ngraph::element::Type_t::f32);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        auto result = std::make_shared<ngraph::op::Result>(relu);

        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};

        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::details::CNNNetworkNGraphImpl cnnNet(ngraph);
    ASSERT_EQ(1, cnnNet.getBatchSize());
    ASSERT_EQ(PARAMETER_MISMATCH, cnnNet.setBatchSize(2, nullptr));  // must not trigger conversion
}

TEST(CNNNGraphImplTests, TestGetBatchDynamic) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        auto param = std::make_shared<ngraph::op::Parameter>(ngraph::element::Type_t::f32, ngraph::PartialShape{5, ngraph::Dimension::dynamic()});
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        auto result = std::make_shared<ngraph::op::Result>(relu);
        ngraph = std::make_shared<ngraph::Function>(ngraph::ResultVector{result}, ngraph::ParameterVector{param});
    }

    InferenceEngine::details::CNNNetworkNGraphImpl cnnNet(ngraph);
    ASSERT_TRUE(cnnNet.getFunction()->get_parameters()[0]->get_partial_shape().is_dynamic());
    ASSERT_EQ(5, cnnNet.getBatchSize());
}

TEST(CNNNGraphImplTests, TestSetBatchDynamic) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        auto param = std::make_shared<ngraph::op::Parameter>(ngraph::element::Type_t::f32, ngraph::PartialShape::dynamic());
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        auto result = std::make_shared<ngraph::op::Result>(relu);
        ngraph = std::make_shared<ngraph::Function>(ngraph::ResultVector{result}, ngraph::ParameterVector{param});
    }

    InferenceEngine::details::CNNNetworkNGraphImpl cnnNet(ngraph);
    ASSERT_EQ(1, cnnNet.getBatchSize());
    ASSERT_EQ(PARAMETER_MISMATCH, cnnNet.setBatchSize(2, nullptr));  // must not trigger conversion
}

TEST(CNNNGraphImplTests, TestSaveAffinity) {
    const std::string testAffinity = "testAffinity";
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 22, 22});
        ngraph::element::Type type(ngraph::element::Type_t::f32);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        auto& rtInfo = relu->get_rt_info();
        rtInfo["affinity"] = std::make_shared<ngraph::VariantWrapper<std::string>> (testAffinity);
        relu->set_friendly_name("testReLU");
        auto result = std::make_shared<ngraph::op::Result>(relu);

        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};

        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::CNNNetwork cnnNet(ngraph);
    auto convertedNetwork = std::make_shared<InferenceEngine::details::CNNNetworkImpl>(cnnNet);
    auto cnnLayer = CommonTestUtils::getLayerByName(InferenceEngine::CNNNetwork(convertedNetwork), "testReLU");
    ASSERT_NE(nullptr, cnnLayer);
    ASSERT_EQ(cnnLayer->affinity, testAffinity);
}

TEST(CNNNGraphImplTests, TestAddOutput) {
    const std::string testLayerName = "testReLU";
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 22, 22});
        ngraph::element::Type type(ngraph::element::Type_t::f32);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        relu->set_friendly_name(testLayerName);
        auto relu2 = std::make_shared<ngraph::op::Relu>(relu);
        relu2->set_friendly_name("relu2");
        auto result = std::make_shared<ngraph::op::Result>(relu2);

        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};

        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::CNNNetwork cnnNet(ngraph);
    ASSERT_NE(nullptr, cnnNet.getFunction());
    ASSERT_EQ(4, cnnNet.layerCount());

    cnnNet.addOutput(testLayerName);
    ASSERT_NE(nullptr, cnnNet.getFunction());
    ASSERT_EQ(5, cnnNet.layerCount());
    auto outputs = cnnNet.getOutputsInfo();
    ASSERT_EQ(2, outputs.size());
    ASSERT_TRUE(outputs.find("relu2") != outputs.end());
    ASSERT_TRUE(outputs.find(testLayerName) != outputs.end());
}

TEST(CNNNGraphImplTests, TestAddOutputTwoTimes) {
    const std::string testLayerName = "testReLU";
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 22, 22});
        ngraph::element::Type type(ngraph::element::Type_t::f32);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        relu->set_friendly_name(testLayerName);
        auto relu2 = std::make_shared<ngraph::op::Relu>(relu);
        relu2->set_friendly_name("relu2");
        auto result = std::make_shared<ngraph::op::Result>(relu2);

        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};

        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::CNNNetwork cnnNet(ngraph);
    ASSERT_NE(nullptr, cnnNet.getFunction());
    ASSERT_EQ(4, cnnNet.layerCount());

    cnnNet.addOutput(testLayerName);
    ASSERT_NE(nullptr, cnnNet.getFunction());
    ASSERT_EQ(5, cnnNet.layerCount());
    cnnNet.addOutput(testLayerName);
    ASSERT_NE(nullptr, cnnNet.getFunction());
    ASSERT_EQ(5, cnnNet.layerCount());
    auto outputs = cnnNet.getOutputsInfo();
    ASSERT_EQ(2, outputs.size());
    ASSERT_TRUE(outputs.find("relu2") != outputs.end());
    ASSERT_TRUE(outputs.find(testLayerName) != outputs.end());
}

TEST(CNNNGraphImplTests, TestAddOutputFromConvertedNetwork) {
    const std::string testLayerName = "testReLU";
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 22, 22});
        ngraph::element::Type type(ngraph::element::Type_t::f32);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        relu->set_friendly_name(testLayerName);
        auto relu2 = std::make_shared<ngraph::op::Relu>(relu);
        relu2->set_friendly_name("relu2");
        auto result = std::make_shared<ngraph::op::Result>(relu2);

        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};

        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::CNNNetwork cnnNet(ngraph);
    ASSERT_NE(nullptr, cnnNet.getFunction());
    ASSERT_EQ(4, cnnNet.layerCount());

    cnnNet.addOutput(testLayerName);
    ASSERT_NE(nullptr, cnnNet.getFunction());
    ASSERT_EQ(5, cnnNet.layerCount());
    // convert to old representation
    InferenceEngine::CNNNetwork convertedNetwork(std::make_shared<InferenceEngine::details::CNNNetworkImpl>(cnnNet));
    auto outputs = convertedNetwork.getOutputsInfo();
    ASSERT_EQ(2, outputs.size());
    ASSERT_TRUE(outputs.find("relu2") != outputs.end());
    ASSERT_TRUE(outputs.find(testLayerName) != outputs.end());
}

TEST(CNNNGraphImplTests, ConstantAsInternalAndExternalLayer) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 22, 22});
        ngraph::element::Type type(ngraph::element::Type_t::f32);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        auto constant = ngraph::op::Constant::create(ngraph::element::Type_t::f32, {1}, {2});
        auto prelu = std::make_shared<ngraph::op::PRelu>(param, constant);
        auto add = std::make_shared<ngraph::op::v1::Maximum>(prelu, constant);
        auto result = std::make_shared<ngraph::op::Result>(add);

        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};

        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::CNNNetwork cnnNet(ngraph);
    auto convertedNetwork = std::make_shared<InferenceEngine::details::CNNNetworkImpl>(cnnNet);
    ASSERT_EQ(4, convertedNetwork->layerCount());
}

TEST(CNNNGraphImplTests, SaveInputInfoAfterConversion) {
    std::string name = "param";
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 22, 22});
        ngraph::element::Type type(ngraph::element::Type_t::f32);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        param->set_friendly_name(name);
        auto constant = ngraph::op::Constant::create(ngraph::element::Type_t::f32, {1}, {2});
        auto prelu = std::make_shared<ngraph::op::PRelu>(param, constant);
        auto add = std::make_shared<ngraph::op::v1::Maximum>(prelu, constant);
        auto result = std::make_shared<ngraph::op::Result>(add);

        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};

        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::details::CNNNetworkNGraphImpl cnnNet(ngraph);
    auto inputInfo = cnnNet.getInput(name);
    ASSERT_EQ(inputInfo->getPreProcess().getResizeAlgorithm(), ResizeAlgorithm::NO_RESIZE);
    inputInfo->getPreProcess().setResizeAlgorithm(ResizeAlgorithm::RESIZE_AREA);
    ASSERT_EQ(inputInfo->getPreProcess().getResizeAlgorithm(), ResizeAlgorithm::RESIZE_AREA);

    auto cnnNetImpl = std::make_shared<details::CNNNetworkImpl>(cnnNet);
    inputInfo = cnnNetImpl->getInput(name);
    ASSERT_EQ(inputInfo->getPreProcess().getResizeAlgorithm(), ResizeAlgorithm::RESIZE_AREA);
}

TEST(CNNNGraphImplTests, SavePrimitivesPriority) {
    std::string model = R"V0G0N(
<net name="Activation" version="10">
    <layers>
        <layer name="in1" type="Parameter" id="0" version="opset1">
            <data shape="1,3,22,22" element_type="f32" PrimitivesPriority="cpu:avx2"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>3</dim>
                    <dim>22</dim>
                    <dim>22</dim>
                </port>
            </output>
        </layer>
        <layer name="activation" id="1" type="ReLU" version="opset1">
            <input>
                <port id="1" precision="FP32">
                    <dim>1</dim>
                    <dim>3</dim>
                    <dim>22</dim>
                    <dim>22</dim>
                </port>
            </input>
            <output>
                <port id="2" precision="FP32">
                    <dim>1</dim>
                    <dim>3</dim>
                    <dim>22</dim>
                    <dim>22</dim>
                </port>
            </output>
        </layer>
        <layer name="output" type="Result" id="2" version="opset1">
            <input>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>3</dim>
                    <dim>22</dim>
                    <dim>22</dim>
                </port>
            </input>
        </layer>
    </layers>
    <edges>
        <edge from-layer="0" from-port="0" to-layer="1" to-port="1"/>
        <edge from-layer="1" from-port="2" to-layer="2" to-port="0"/>
    </edges>
</net>
)V0G0N";
    const Core ie;
    Blob::Ptr weights;

    auto network = ie.ReadNetwork(model, weights);
    auto inputInfo = network.getInputsInfo();
    auto convertedNetwork = std::make_shared<InferenceEngine::details::CNNNetworkImpl>(network);
    auto cnnLayer = getCreatorLayer(inputInfo.begin()->second->getInputData()).lock();
    ASSERT_NE(nullptr, cnnLayer);
    ASSERT_NE(cnnLayer->params.find("PrimitivesPriority"), cnnLayer->params.end());
    ASSERT_EQ("cpu:avx2", cnnLayer->params["PrimitivesPriority"]);
}

TEST(CNNNGraphImplTests, ReadFromCNNNetReader) {
    std::string model = R"V0G0N(
<net name="Activation" version="10">
    <layers>
        <layer name="in1" type="Parameter" id="0" version="opset1">
            <data shape="1,3,22,22" element_type="f32"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>3</dim>
                    <dim>22</dim>
                    <dim>22</dim>
                </port>
            </output>
        </layer>
        <layer name="activation" id="1" type="ReLU" version="opset1">
            <input>
                <port id="1" precision="FP32">
                    <dim>1</dim>
                    <dim>3</dim>
                    <dim>22</dim>
                    <dim>22</dim>
                </port>
            </input>
            <output>
                <port id="2" precision="FP32">
                    <dim>1</dim>
                    <dim>3</dim>
                    <dim>22</dim>
                    <dim>22</dim>
                </port>
            </output>
        </layer>
        <layer name="output" type="Result" id="2" version="opset1">
            <input>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>3</dim>
                    <dim>22</dim>
                    <dim>22</dim>
                </port>
            </input>
        </layer>
    </layers>
    <edges>
        <edge from-layer="0" from-port="0" to-layer="1" to-port="1"/>
        <edge from-layer="1" from-port="2" to-layer="2" to-port="0"/>
    </edges>
</net>
)V0G0N";
    InferenceEngine::Core core;
    CNNNetwork network = core.ReadNetwork(model, InferenceEngine::Blob::CPtr());
    ASSERT_EQ(3, network.layerCount());
}

TEST(CNNNGraphImplTests, ReadMeanImageFromCNNNetReader) {
    std::string model = R"V0G0N(
<net name="Activation" version="10">
    <pre-process mean-precision="FP32" reference-layer-name="data">
        <channel id="0">
            <mean offset="0" size="1936"/>
        </channel>
        <channel id="1">
            <mean offset="1936" size="1936"/>
        </channel>
        <channel id="2">
            <mean offset="3872" size="1936"/>
        </channel>
    </pre-process>
    <layers>
        <layer name="data" type="Parameter" id="0" version="opset1">
            <data shape="1,3,22,22" element_type="f32"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>3</dim>
                    <dim>22</dim>
                    <dim>22</dim>
                </port>
            </output>
        </layer>
        <layer name="activation" id="1" type="ReLU" version="opset1">
            <input>
                <port id="1" precision="FP32">
                    <dim>1</dim>
                    <dim>3</dim>
                    <dim>22</dim>
                    <dim>22</dim>
                </port>
            </input>
            <output>
                <port id="2" precision="FP32">
                    <dim>1</dim>
                    <dim>3</dim>
                    <dim>22</dim>
                    <dim>22</dim>
                </port>
            </output>
        </layer>
        <layer name="output" type="Result" id="2" version="opset1">
            <input>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>3</dim>
                    <dim>22</dim>
                    <dim>22</dim>
                </port>
            </input>
        </layer>
    </layers>
    <edges>
        <edge from-layer="0" from-port="0" to-layer="1" to-port="1"/>
        <edge from-layer="1" from-port="2" to-layer="2" to-port="0"/>
    </edges>
</net>
)V0G0N";
    InferenceEngine::Core core;
    size_t hwSize = 22*22;
    size_t dataSize = hwSize*3;
    Blob::Ptr data = make_shared_blob<float>(TensorDesc(Precision::FP32, {dataSize}, Layout::C));
    data->allocate();
    {
        auto lockData = data->buffer();
        float *dataPtr = lockData.as<float*>();

        for (size_t i = 0; i < dataSize; ++i) {
            dataPtr[i] = i;
        }
    }
    CNNNetwork network = core.ReadNetwork(model, data);
    ASSERT_EQ(3, network.layerCount());
    auto inputInfo = network.getInputsInfo().begin()->second;
    ASSERT_NE(inputInfo, nullptr);
    auto preProc = inputInfo->getPreProcess();
    ASSERT_EQ(3, preProc.getNumberOfChannels());
    ASSERT_EQ(preProc.getMeanVariant(), MeanVariant::MEAN_IMAGE);

    for (size_t i = 0; i < preProc.getNumberOfChannels(); i++) {
        auto chMeanImg = preProc[i];
        ASSERT_NE(chMeanImg, nullptr);
        ASSERT_NE(chMeanImg->meanData, nullptr);
        auto lockData = chMeanImg->meanData->cbuffer();
        auto *dataPtr = lockData.as<const float*>();
        for (size_t j = 0; j < hwSize; j++) {
            ASSERT_EQ(dataPtr[j], hwSize*i + j);
        }
    }
}

TEST(CNNNGraphImplTests, CanChangeInputPrecision) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 16, 16});
        ngraph::element::Type type(ngraph::element::Type_t::f16);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        param->set_friendly_name("input");
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        relu->set_friendly_name("output");
        auto result = std::make_shared<ngraph::op::Result>(relu);
        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};
        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::CNNNetwork cnnNet(ngraph);
    {
        SCOPED_TRACE("After ctor");

        const auto inputsInfo = cnnNet.getInputsInfo();

        ASSERT_EQ(inputsInfo.at("input")->getPrecision(), Precision::FP32)
                << "FP32 is default presision";
    }
    {
        SCOPED_TRACE("Manually set input precision");

        const auto inputsInfo = cnnNet.getInputsInfo();

        inputsInfo.at("input")->setPrecision(Precision::FP16);
    }
    InferenceEngine::CNNNetwork convertedNetwork;
    {
        SCOPED_TRACE("Convert to old format");

        // convert to old representation
        convertedNetwork = InferenceEngine::CNNNetwork(
            std::make_shared<InferenceEngine::details::CNNNetworkImpl>(cnnNet));
    }
    {
        SCOPED_TRACE("After conversion");

        const auto inputsInfo = convertedNetwork.getInputsInfo();

        ASSERT_EQ(inputsInfo.at("input")->getPrecision(), Precision::FP16)
                << "Manually set presision should be left unchanged";
    }
}

TEST(CNNNGraphImplTests, CanChangeInputLayout) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 16, 16});
        ngraph::element::Type type(ngraph::element::Type_t::f16);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        param->set_friendly_name("input");
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        relu->set_friendly_name("output");
        auto result = std::make_shared<ngraph::op::Result>(relu);
        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};
        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::CNNNetwork cnnNet(ngraph);
    {
        SCOPED_TRACE("After ctor");

        const auto inputsInfo = cnnNet.getInputsInfo();

        ASSERT_EQ(inputsInfo.at("input")->getLayout(), Layout::NCHW)
                << "NCHW is default layout";
    }
    {
        SCOPED_TRACE("Manually set input layout");

        const auto inputsInfo = cnnNet.getInputsInfo();

        inputsInfo.at("input")->setLayout(Layout::NHWC);
    }
    InferenceEngine::CNNNetwork convertedNetwork;
    {
        SCOPED_TRACE("Convert to old format");

        // convert to old representation
        convertedNetwork = InferenceEngine::CNNNetwork(
            std::make_shared<InferenceEngine::details::CNNNetworkImpl>(cnnNet));
    }
    {
        SCOPED_TRACE("After conversion");

        const auto inputsInfo = convertedNetwork.getInputsInfo();

        ASSERT_EQ(inputsInfo.at("input")->getLayout(), Layout::NHWC)
                << "Manually set layout should be left unchanged";
    }
}

TEST(CNNNGraphImplTests, CanChangeOutputPrecision) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 16, 16});
        ngraph::element::Type type(ngraph::element::Type_t::f16);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        param->set_friendly_name("input");
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        relu->set_friendly_name("output");
        auto result = std::make_shared<ngraph::op::Result>(relu);
        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};
        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::CNNNetwork cnnNet(ngraph);
    {
        SCOPED_TRACE("After ctor");

        const auto outputsInfo = cnnNet.getOutputsInfo();

        ASSERT_EQ(outputsInfo.at("output")->getPrecision(), Precision::FP32)
                << "FP32 is default presision";
    }
    {
        SCOPED_TRACE("Manually set output precision");

        const auto outputsInfo = cnnNet.getOutputsInfo();

        outputsInfo.at("output")->setPrecision(Precision::FP16);
    }
    InferenceEngine::CNNNetwork convertedNetwork;
    {
        SCOPED_TRACE("Convert to old format");

        // convert to old representation
        convertedNetwork = InferenceEngine::CNNNetwork(
            std::make_shared<InferenceEngine::details::CNNNetworkImpl>(cnnNet));
    }
    {
        SCOPED_TRACE("After conversion");

        const auto outputsInfo = convertedNetwork.getOutputsInfo();

        ASSERT_EQ(outputsInfo.at("output")->getPrecision(), Precision::FP16)
                << "Manually set presision should be left unchanged";
    }
}

TEST(CNNNGraphImplTests, CanChangeOutputLayout) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 16, 16});
        ngraph::element::Type type(ngraph::element::Type_t::f16);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        param->set_friendly_name("input");
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        relu->set_friendly_name("output");
        auto result = std::make_shared<ngraph::op::Result>(relu);
        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};
        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::CNNNetwork cnnNet(ngraph);
    {
        SCOPED_TRACE("After ctor");

        const auto outputsInfo = cnnNet.getOutputsInfo();

        ASSERT_EQ(outputsInfo.at("output")->getLayout(), Layout::NCHW)
                << "NCHW is default layout";
    }
    {
        SCOPED_TRACE("Manually set output layout");

        const auto outputsInfo = cnnNet.getOutputsInfo();

        outputsInfo.at("output")->setLayout(Layout::NHWC);
    }
    InferenceEngine::CNNNetwork convertedNetwork;
    {
        SCOPED_TRACE("Convert to old format");

        // convert to old representation
        convertedNetwork = InferenceEngine::CNNNetwork(
            std::make_shared<InferenceEngine::details::CNNNetworkImpl>(cnnNet));
    }
    {
        SCOPED_TRACE("After conversion");

        const auto outputsInfo = convertedNetwork.getOutputsInfo();

        ASSERT_EQ(outputsInfo.at("output")->getLayout(), Layout::NHWC)
                << "Manually set layout should be left unchanged";
    }
}

TEST(CNNNGraphImplTests, TestCheckStats) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 22, 22});
        ngraph::element::Type type(ngraph::element::Type_t::f32);
        auto param = std::make_shared<ngraph::op::Parameter>(type, shape);
        auto relu = std::make_shared<ngraph::op::Relu>(param);
        auto result = std::make_shared<ngraph::op::Result>(relu);

        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};

        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    InferenceEngine::details::CNNNetworkNGraphImpl cnnNet(ngraph);
}

TEST(CNNNGraphImplTests, CanSetBatchReadValue) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        auto input = std::make_shared<ngraph::opset3::Parameter>(ngraph::element::f32, ngraph::Shape{1, 2});
        auto constant = std::make_shared<ngraph::opset3::Constant>(ngraph::element::f32, ngraph::Shape{1, 2},
                std::vector<float>{1, 2});

        auto read_value = std::make_shared<ngraph::opset3::ReadValue>(constant, "variable_id");
        auto assign = std::make_shared<ngraph::opset3::Assign>(read_value, "variable_id");
        assign->add_control_dependency(read_value);
        auto add = std::make_shared<ngraph::opset3::Add>(input, read_value);
        auto result = std::make_shared<ngraph::op::Result>(add);

        ngraph::ParameterVector params = {input};
        ngraph::ResultVector results = {result};
        ngraph::SinkVector sinks = {assign};

        ngraph = std::make_shared<ngraph::Function>(results, sinks, params);
    }

    InferenceEngine::details::CNNNetworkNGraphImpl cnnNet(ngraph);
    auto convertedNet = std::make_shared<details::CNNNetworkImpl>(cnnNet);
    auto status = convertedNet->setBatchSize(4, nullptr);
    EXPECT_EQ(status, StatusCode::OK);
}

TEST(CNNNGraphImplTests, addSameOutput) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 22, 22});
        ngraph::element::Type type(ngraph::element::Type_t::f32);
        auto param = std::make_shared<ngraph::opset3::Parameter>(type, shape);
        auto relu = std::make_shared<ngraph::opset3::Relu>(param);
        auto shapeof = std::make_shared<ngraph::opset3::ShapeOf>(param);
        auto reshape = std::make_shared<ngraph::opset3::Reshape>(relu, shapeof, true);
        reshape->set_friendly_name("reshape");
        auto result = std::make_shared<ngraph::op::Result>(reshape);

        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};

        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    CNNNetwork cnnNetwork(ngraph);
    cnnNetwork.addOutput("reshape");
    auto outputs = cnnNetwork.getOutputsInfo();

    ASSERT_EQ(outputs.size(), 1);
    ASSERT_EQ(outputs.count("reshape"), 1);
    ASSERT_EQ(outputs["reshape"]->getLayout(), InferenceEngine::Layout::NCHW);
}

TEST(CNNNGraphImplTests, addOutput) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 22, 22});
        ngraph::element::Type type(ngraph::element::Type_t::f32);
        auto param = std::make_shared<ngraph::opset3::Parameter>(type, shape);
        auto relu = std::make_shared<ngraph::opset3::Relu>(param);
        auto shapeof = std::make_shared<ngraph::opset3::ShapeOf>(param);
        auto reshape = std::make_shared<ngraph::opset3::Reshape>(relu, shapeof, true);
        reshape->set_friendly_name("reshape");
        auto relu2 = std::make_shared<ngraph::opset3::Relu>(reshape);
        auto result = std::make_shared<ngraph::op::Result>(relu2);

        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};

        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    CNNNetwork cnnNetwork(ngraph);
    cnnNetwork.addOutput("reshape");
    auto outputs = cnnNetwork.getOutputsInfo();

    ASSERT_EQ(outputs.size(), 2);
    ASSERT_EQ(outputs.count("reshape"), 1);
    ASSERT_EQ(outputs["reshape"]->getLayout(), InferenceEngine::Layout::NCHW);
}

TEST(CNNNGraphImplTests, addOutputForParameter) {
    std::shared_ptr<ngraph::Function> ngraph;
    {
        ngraph::PartialShape shape({1, 3, 22, 22});
        ngraph::element::Type type(ngraph::element::Type_t::f32);
        auto param = std::make_shared<ngraph::opset3::Parameter>(type, shape);
        param->set_friendly_name("param");
        auto relu = std::make_shared<ngraph::opset3::Relu>(param);
        auto result = std::make_shared<ngraph::op::Result>(relu);

        ngraph::ParameterVector params = {param};
        ngraph::ResultVector results = {result};

        ngraph = std::make_shared<ngraph::Function>(results, params);
    }

    CNNNetwork cnnNetwork(ngraph);
    cnnNetwork.addOutput("param");
    {
        auto output_info = cnnNetwork.getOutputsInfo();
        ASSERT_EQ(output_info.count("param"), 1);
        ASSERT_EQ(output_info["param"]->getTensorDesc().getDims(), SizeVector({1, 3, 22, 22}));
    }

    cnnNetwork.reshape({{"param", SizeVector({1, 3, 32, 64})}});
    cnnNetwork.addOutput("param");
    {
        auto output_info = cnnNetwork.getOutputsInfo();
        ASSERT_EQ(output_info.count("param"), 1);
        ASSERT_EQ(output_info["param"]->getTensorDesc().getDims(), SizeVector({1, 3, 32, 64}));
    }
}

TEST(CNNNGraphImplTests, AddOutputToExperimentalOpOpset6) {
    std::string model = R"V0G0N(
<net name="Activation" version="10">
    <layers>
        <layer id="0" name="in0" type="Parameter" version="opset1">
            <data element_type="f32" shape="1000,4"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1000</dim>
                    <dim>4</dim>
                </port>
            </output>
        </layer>
        <layer id="1" name="in1" type="Parameter" version="opset1">
            <data element_type="f32" shape="1,256,200,336"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>200</dim>
                    <dim>336</dim>
                </port>
            </output>
        </layer>
        <layer id="2" name="in2" type="Parameter" version="opset1">
            <data element_type="f32" shape="1,256,100,168"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>100</dim>
                    <dim>168</dim>
                </port>
            </output>
        </layer>
        <layer id="3" name="in3" type="Parameter" version="opset1">
            <data element_type="f32" shape="1,256,50,84"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>50</dim>
                    <dim>84</dim>
                </port>
            </output>
        </layer>
        <layer id="4" name="in4" type="Parameter" version="opset1">
            <data element_type="f32" shape="1,256,25,42"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>25</dim>
                    <dim>42</dim>
                </port>
            </output>
        </layer>
        <layer id="5" name="exp" type="ExperimentalDetectronROIFeatureExtractor" version="opset6">
            <data aligned="0" output_size="7" pyramid_scales="4,8,16,32" sampling_ratio="2"/>
            <input>
                <port id="0">
                    <dim>1000</dim>
                    <dim>4</dim>
                </port>
                <port id="1">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>200</dim>
                    <dim>336</dim>
                </port>
                <port id="2">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>100</dim>
                    <dim>168</dim>
                </port>
                <port id="3">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>50</dim>
                    <dim>84</dim>
                </port>
                <port id="4">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>25</dim>
                    <dim>42</dim>
                </port>
            </input>
            <output>
                <port id="5" precision="FP32">
                    <dim>1000</dim>
                    <dim>256</dim>
                    <dim>7</dim>
                    <dim>7</dim>
                </port>
            </output>
        </layer>
        <layer name="activation" id="6" type="ReLU" version="opset1">
            <input>
                <port id="0" precision="FP32">
                    <dim>1000</dim>
                    <dim>256</dim>
                    <dim>7</dim>
                    <dim>7</dim>
                </port>
            </input>
            <output>
                <port id="1" precision="FP32">
                    <dim>1000</dim>
                    <dim>256</dim>
                    <dim>7</dim>
                    <dim>7</dim>
                </port>
            </output>
        </layer>
        <layer name="output" type="Result" id="7" version="opset1">
            <input>
                <port id="0" precision="FP32">
                    <dim>1000</dim>
                    <dim>256</dim>
                    <dim>7</dim>
                    <dim>7</dim>
                </port>
            </input>
        </layer>
    </layers>
    <edges>
        <edge from-layer="0" from-port="0" to-layer="5" to-port="0"/>
        <edge from-layer="1" from-port="0" to-layer="5" to-port="1"/>
        <edge from-layer="2" from-port="0" to-layer="5" to-port="2"/>
        <edge from-layer="3" from-port="0" to-layer="5" to-port="3"/>
        <edge from-layer="4" from-port="0" to-layer="5" to-port="4"/>
        <edge from-layer="5" from-port="5" to-layer="6" to-port="0"/>
        <edge from-layer="6" from-port="1" to-layer="7" to-port="0"/>
    </edges>
</net>
)V0G0N";
    InferenceEngine::Core core;
    CNNNetwork network = core.ReadNetwork(model, InferenceEngine::Blob::CPtr());
    network.addOutput("exp");
    auto outputs = network.getOutputsInfo();
    ASSERT_NE(outputs.find("exp.0"), outputs.end());
}

TEST(CNNNGraphImplTests, AddOutputToExperimentalOp) {
    std::string model = R"V0G0N(
<net name="Activation" version="10">
    <layers>
        <layer id="0" name="in0" type="Parameter" version="opset1">
            <data element_type="f32" shape="1000,4"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1000</dim>
                    <dim>4</dim>
                </port>
            </output>
        </layer>
        <layer id="1" name="in1" type="Parameter" version="opset1">
            <data element_type="f32" shape="1,256,200,336"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>200</dim>
                    <dim>336</dim>
                </port>
            </output>
        </layer>
        <layer id="2" name="in2" type="Parameter" version="opset1">
            <data element_type="f32" shape="1,256,100,168"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>100</dim>
                    <dim>168</dim>
                </port>
            </output>
        </layer>
        <layer id="3" name="in3" type="Parameter" version="opset1">
            <data element_type="f32" shape="1,256,50,84"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>50</dim>
                    <dim>84</dim>
                </port>
            </output>
        </layer>
        <layer id="4" name="in4" type="Parameter" version="opset1">
            <data element_type="f32" shape="1,256,25,42"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>25</dim>
                    <dim>42</dim>
                </port>
            </output>
        </layer>
        <layer id="5" name="exp" type="ExperimentalDetectronROIFeatureExtractor" version="experimental">
            <data aligned="0" output_size="7" pyramid_scales="4,8,16,32" sampling_ratio="2"/>
            <input>
                <port id="0">
                    <dim>1000</dim>
                    <dim>4</dim>
                </port>
                <port id="1">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>200</dim>
                    <dim>336</dim>
                </port>
                <port id="2">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>100</dim>
                    <dim>168</dim>
                </port>
                <port id="3">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>50</dim>
                    <dim>84</dim>
                </port>
                <port id="4">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>25</dim>
                    <dim>42</dim>
                </port>
            </input>
            <output>
                <port id="5" precision="FP32">
                    <dim>1000</dim>
                    <dim>256</dim>
                    <dim>7</dim>
                    <dim>7</dim>
                </port>
            </output>
        </layer>
        <layer name="activation" id="6" type="ReLU" version="opset1">
            <input>
                <port id="0" precision="FP32">
                    <dim>1000</dim>
                    <dim>256</dim>
                    <dim>7</dim>
                    <dim>7</dim>
                </port>
            </input>
            <output>
                <port id="1" precision="FP32">
                    <dim>1000</dim>
                    <dim>256</dim>
                    <dim>7</dim>
                    <dim>7</dim>
                </port>
            </output>
        </layer>
        <layer name="output" type="Result" id="7" version="opset1">
            <input>
                <port id="0" precision="FP32">
                    <dim>1000</dim>
                    <dim>256</dim>
                    <dim>7</dim>
                    <dim>7</dim>
                </port>
            </input>
        </layer>
    </layers>
    <edges>
        <edge from-layer="0" from-port="0" to-layer="5" to-port="0"/>
        <edge from-layer="1" from-port="0" to-layer="5" to-port="1"/>
        <edge from-layer="2" from-port="0" to-layer="5" to-port="2"/>
        <edge from-layer="3" from-port="0" to-layer="5" to-port="3"/>
        <edge from-layer="4" from-port="0" to-layer="5" to-port="4"/>
        <edge from-layer="5" from-port="5" to-layer="6" to-port="0"/>
        <edge from-layer="6" from-port="1" to-layer="7" to-port="0"/>
    </edges>
</net>
)V0G0N";
    InferenceEngine::Core core;
    CNNNetwork network = core.ReadNetwork(model, InferenceEngine::Blob::CPtr());
    network.addOutput("exp");
    auto outputs = network.getOutputsInfo();
    ASSERT_NE(outputs.find("exp.0"), outputs.end());
}

TEST(CNNNGraphImplTests, SaveOriginalResultNameForMultiOutputOp) {
    std::string  model = R"V0G0N(
<net name="Activation" version="10">
    <layers>
        <layer id="0" name="in0" type="Parameter" version="opset1">
            <data element_type="f32" shape="1000,4"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1000</dim>
                    <dim>4</dim>
                </port>
            </output>
        </layer>
        <layer id="1" name="in1" type="Parameter" version="opset1">
            <data element_type="f32" shape="1,256,200,336"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>200</dim>
                    <dim>336</dim>
                </port>
            </output>
        </layer>
        <layer id="2" name="in2" type="Parameter" version="opset1">
            <data element_type="f32" shape="1,256,100,168"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>100</dim>
                    <dim>168</dim>
                </port>
            </output>
        </layer>
        <layer id="3" name="in3" type="Parameter" version="opset1">
            <data element_type="f32" shape="1,256,50,84"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>50</dim>
                    <dim>84</dim>
                </port>
            </output>
        </layer>
        <layer id="4" name="in4" type="Parameter" version="opset1">
            <data element_type="f32" shape="1,256,25,42"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>25</dim>
                    <dim>42</dim>
                </port>
            </output>
        </layer>
        <layer id="5" name="exp" type="ExperimentalDetectronROIFeatureExtractor" version="experimental">
            <data aligned="0" output_size="7" pyramid_scales="4,8,16,32" sampling_ratio="2"/>
            <input>
                <port id="0">
                    <dim>1000</dim>
                    <dim>4</dim>
                </port>
                <port id="1">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>200</dim>
                    <dim>336</dim>
                </port>
                <port id="2">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>100</dim>
                    <dim>168</dim>
                </port>
                <port id="3">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>50</dim>
                    <dim>84</dim>
                </port>
                <port id="4">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>25</dim>
                    <dim>42</dim>
                </port>
            </input>
            <output>
                <port id="5" precision="FP32">
                    <dim>1000</dim>
                    <dim>256</dim>
                    <dim>7</dim>
                    <dim>7</dim>
                </port>
            </output>
        </layer>
        <layer id="6" name="fake_const" type="Const" version="opset1">
            <data offset="0" size="4" shape="1,1,1,1" element_type="f32"/>
            <output>
                <port id="1" precision="FP32">
                    <dim>1</dim>
                    <dim>1</dim>
                    <dim>1</dim>
                    <dim>1</dim>
                </port>
            </output>
        </layer>
        <layer id="7" name="text_features" type="Add" version="opset1">
            <input>
                <port id="0">
                    <dim>1000</dim>
                    <dim>256</dim>
                    <dim>7</dim>
                    <dim>7</dim>
                </port>
                <port id="1">
                    <dim>1</dim>
                    <dim>1</dim>
                    <dim>1</dim>
                    <dim>1</dim>
                </port>
            </input>
            <output>
                <port id="2" precision="FP32">
                    <dim>1000</dim>
                    <dim>256</dim>
                    <dim>7</dim>
                    <dim>7</dim>
                </port>
            </output>
        </layer>
        <layer name="output" type="Result" id="8" version="opset1">
            <input>
                <port id="0" precision="FP32">
                    <dim>1000</dim>
                    <dim>256</dim>
                    <dim>7</dim>
                    <dim>7</dim>
                </port>
            </input>
        </layer>
    </layers>
    <edges>
        <edge from-layer="0" from-port="0" to-layer="5" to-port="0"/>
        <edge from-layer="1" from-port="0" to-layer="5" to-port="1"/>
        <edge from-layer="2" from-port="0" to-layer="5" to-port="2"/>
        <edge from-layer="3" from-port="0" to-layer="5" to-port="3"/>
        <edge from-layer="4" from-port="0" to-layer="5" to-port="4"/>
        <edge from-layer="5" from-port="5" to-layer="7" to-port="0"/>
        <edge from-layer="6" from-port="1" to-layer="7" to-port="1"/>
        <edge from-layer="7" from-port="2" to-layer="8" to-port="0"/>
    </edges>
</net>
)V0G0N";

    InferenceEngine::Core core;
    Blob::Ptr data = make_shared_blob<float>(TensorDesc(Precision::FP32, {4}, Layout::C));
    data->allocate();
    {
        auto lockData = data->buffer();
        float *dataPtr = lockData.as<float*>();

        for (size_t i = 0; i < 4; ++i) {
            dataPtr[i] = 0;
        }
    }
    CNNNetwork network = core.ReadNetwork(model, data);
    {
        auto outputs = network.getOutputsInfo();
        ASSERT_NE(outputs.find("text_features"), outputs.end());
    }

    auto nGraphFunc = network.getFunction();

    ngraph::pass::Manager manager;

    manager.register_pass<ngraph::pass::ConvertOpSet1ToLegacy>();

    manager.run_passes(nGraphFunc);

    auto clonedNetwork = InferenceEngine::details::convertFunctionToICNNNetwork(nGraphFunc, network);
    {
        OutputsDataMap outputs;
        clonedNetwork->getOutputsInfo(outputs);
        ASSERT_NE(outputs.find("text_features"), outputs.end());
    }
}

TEST(CNNNGraphImplTests, SaveOriginalResultNameForMultiOutputOpOpset6) {
    std::string  model = R"V0G0N(
<net name="Activation" version="10">
    <layers>
        <layer id="0" name="in0" type="Parameter" version="opset1">
            <data element_type="f32" shape="1000,4"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1000</dim>
                    <dim>4</dim>
                </port>
            </output>
        </layer>
        <layer id="1" name="in1" type="Parameter" version="opset1">
            <data element_type="f32" shape="1,256,200,336"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>200</dim>
                    <dim>336</dim>
                </port>
            </output>
        </layer>
        <layer id="2" name="in2" type="Parameter" version="opset1">
            <data element_type="f32" shape="1,256,100,168"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>100</dim>
                    <dim>168</dim>
                </port>
            </output>
        </layer>
        <layer id="3" name="in3" type="Parameter" version="opset1">
            <data element_type="f32" shape="1,256,50,84"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>50</dim>
                    <dim>84</dim>
                </port>
            </output>
        </layer>
        <layer id="4" name="in4" type="Parameter" version="opset1">
            <data element_type="f32" shape="1,256,25,42"/>
            <output>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>25</dim>
                    <dim>42</dim>
                </port>
            </output>
        </layer>
        <layer id="5" name="exp" type="ExperimentalDetectronROIFeatureExtractor" version="opset6">
            <data aligned="0" output_size="7" pyramid_scales="4,8,16,32" sampling_ratio="2"/>
            <input>
                <port id="0">
                    <dim>1000</dim>
                    <dim>4</dim>
                </port>
                <port id="1">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>200</dim>
                    <dim>336</dim>
                </port>
                <port id="2">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>100</dim>
                    <dim>168</dim>
                </port>
                <port id="3">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>50</dim>
                    <dim>84</dim>
                </port>
                <port id="4">
                    <dim>1</dim>
                    <dim>256</dim>
                    <dim>25</dim>
                    <dim>42</dim>
                </port>
            </input>
            <output>
                <port id="5" precision="FP32">
                    <dim>1000</dim>
                    <dim>256</dim>
                    <dim>7</dim>
                    <dim>7</dim>
                </port>
            </output>
        </layer>
        <layer id="6" name="fake_const" type="Const" version="opset1">
            <data offset="0" size="4" shape="1,1,1,1" element_type="f32"/>
            <output>
                <port id="1" precision="FP32">
                    <dim>1</dim>
                    <dim>1</dim>
                    <dim>1</dim>
                    <dim>1</dim>
                </port>
            </output>
        </layer>
        <layer id="7" name="text_features" type="Add" version="opset1">
            <input>
                <port id="0">
                    <dim>1000</dim>
                    <dim>256</dim>
                    <dim>7</dim>
                    <dim>7</dim>
                </port>
                <port id="1">
                    <dim>1</dim>
                    <dim>1</dim>
                    <dim>1</dim>
                    <dim>1</dim>
                </port>
            </input>
            <output>
                <port id="2" precision="FP32">
                    <dim>1000</dim>
                    <dim>256</dim>
                    <dim>7</dim>
                    <dim>7</dim>
                </port>
            </output>
        </layer>
        <layer name="output" type="Result" id="8" version="opset1">
            <input>
                <port id="0" precision="FP32">
                    <dim>1000</dim>
                    <dim>256</dim>
                    <dim>7</dim>
                    <dim>7</dim>
                </port>
            </input>
        </layer>
    </layers>
    <edges>
        <edge from-layer="0" from-port="0" to-layer="5" to-port="0"/>
        <edge from-layer="1" from-port="0" to-layer="5" to-port="1"/>
        <edge from-layer="2" from-port="0" to-layer="5" to-port="2"/>
        <edge from-layer="3" from-port="0" to-layer="5" to-port="3"/>
        <edge from-layer="4" from-port="0" to-layer="5" to-port="4"/>
        <edge from-layer="5" from-port="5" to-layer="7" to-port="0"/>
        <edge from-layer="6" from-port="1" to-layer="7" to-port="1"/>
        <edge from-layer="7" from-port="2" to-layer="8" to-port="0"/>
    </edges>
</net>
)V0G0N";

    InferenceEngine::Core core;
    Blob::Ptr data = make_shared_blob<float>(TensorDesc(Precision::FP32, {4}, Layout::C));
    data->allocate();
    {
        auto lockData = data->buffer();
        float *dataPtr = lockData.as<float*>();

        for (size_t i = 0; i < 4; ++i) {
            dataPtr[i] = 0;
        }
    }
    CNNNetwork network = core.ReadNetwork(model, data);
    {
        auto outputs = network.getOutputsInfo();
        ASSERT_NE(outputs.find("text_features"), outputs.end());
    }

    auto nGraphFunc = network.getFunction();

    ngraph::pass::Manager manager;

    manager.register_pass<ngraph::pass::ConvertOpSet1ToLegacy>();

    manager.run_passes(nGraphFunc);

    auto clonedNetwork = InferenceEngine::details::convertFunctionToICNNNetwork(nGraphFunc, network);
    {
        OutputsDataMap outputs;
        clonedNetwork->getOutputsInfo(outputs);
        ASSERT_NE(outputs.find("text_features"), outputs.end());
    }
}
IE_SUPPRESS_DEPRECATED_END
