IVGCVSW-4968 Fix exception handling in TfLiteParser.

* The function TfLiteParser::CreateNetworkFromModel was continuing
  to parse the input file even after a fatal exception was encountered.
  restructure catch exceptions outside the for loop.
* Add simple unit tests to test some exception handling.

Signed-off-by: Colm Donelan <Colm.Donelan@arm.com>
Change-Id: I202ca6819d40a47159b4ac8f2847958f945666c2
diff --git a/src/armnnTfLiteParser/TfLiteParser.cpp b/src/armnnTfLiteParser/TfLiteParser.cpp
index c695caa..1737da8 100644
--- a/src/armnnTfLiteParser/TfLiteParser.cpp
+++ b/src/armnnTfLiteParser/TfLiteParser.cpp
@@ -619,9 +619,6 @@
     m_Network = INetwork::Create();
     ARMNN_ASSERT(m_Model.get() != nullptr);
 
-    bool failedToCreate = false;
-    std::stringstream errors;
-
     if (m_Model->subgraphs.size() != 1)
     {
         throw ParseException(
@@ -632,65 +629,52 @@
     }
 
     size_t subgraphIndex = 0;
-    for (SubgraphPtr const & subgraph : m_Model->subgraphs)
+    size_t operatorIndex = 0;
+    try
     {
-        m_SubgraphConnections.emplace_back(subgraph->tensors.size());
-
-        size_t operatorIndex = 0;
-        for (OperatorPtr const & op : subgraph->operators)
+        for (SubgraphPtr const& subgraph : m_Model->subgraphs)
         {
-            try
+            m_SubgraphConnections.emplace_back(subgraph->tensors.size());
+            for (OperatorPtr const& op : subgraph->operators)
             {
-                auto const & opCodePtr = m_Model->operator_codes[op->opcode_index];
+                auto const& opCodePtr = m_Model->operator_codes[op->opcode_index];
                 auto builtinCode = opCodePtr->builtin_code;
 
                 if (builtinCode > tflite::BuiltinOperator_MAX)
                 {
-                    throw ParseException(
-                            boost::str(
-                                    boost::format("Operator code %1% is out of range 0-%2%. "
-                                                  "subgraph:%3% operator idx:%4%. %5%") %
-                                                  builtinCode %
-                                                  tflite::BuiltinOperator_MAX %
-                                                  subgraphIndex %
-                                                  operatorIndex %
-                                                  CHECK_LOCATION().AsString()));
+                    throw ParseException(boost::str(boost::format("Operator code %1% is out of range 0-%2%. "
+                                                                  "subgraph:%3% operator idx:%4%. %5%") %
+                                                    builtinCode % tflite::BuiltinOperator_MAX % subgraphIndex %
+                                                    operatorIndex % CHECK_LOCATION().AsString()));
                 }
 
                 // lookup and call the parser function
-                auto & parserFunction = m_ParserFunctions[builtinCode];
+                auto& parserFunction = m_ParserFunctions[builtinCode];
                 (this->*parserFunction)(subgraphIndex, operatorIndex);
+                ++operatorIndex;
             }
-            catch (const ParseException& e)
-            {
-                failedToCreate = true;
-                std::stringstream errorString;
 
-                errorString << "Failed to parse operator #" << operatorIndex
-                            << " within subgraph #" << subgraphIndex
-                            << " error: " << e.what();
-                ARMNN_LOG(error) << errorString.str();
+            SetupInputLayers(subgraphIndex);
+            SetupOutputLayers(subgraphIndex);
+            SetupConstantLayers(subgraphIndex);
 
-                errors << errorString.str() << "\n";
-            }
-            ++operatorIndex;
+            ++subgraphIndex;
+            operatorIndex = 0;
         }
-
-        SetupInputLayers(subgraphIndex);
-        SetupOutputLayers(subgraphIndex);
-        SetupConstantLayers(subgraphIndex);
-
-        ++subgraphIndex;
     }
-
-    if (failedToCreate)
+    catch (const ParseException& e)
     {
-        // we can skip everything and let the outer exception handler deal with the error
+        std::stringstream errorString;
+        errorString << "Failed to parse operator #" << operatorIndex << " within subgraph #"
+                    << subgraphIndex << " error: " << e.what();
+        ARMNN_LOG(error) << errorString.str();
+        std::stringstream errors;
+        errors << errorString.str() << "\n";
         throw ParseException(errors.str());
     }
 
     // establish the connections from the layer outputs to the inputs of the subsequent layers
-    for (size_t subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
+    for (subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
     {
         for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
         {
diff --git a/src/armnnTfLiteParser/test/TfLiteParser.cpp b/src/armnnTfLiteParser/test/TfLiteParser.cpp
new file mode 100644
index 0000000..36827c0
--- /dev/null
+++ b/src/armnnTfLiteParser/test/TfLiteParser.cpp
@@ -0,0 +1,41 @@
+//
+// Copyright © 2020 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include <boost/test/unit_test.hpp>
+#include "ParserFlatbuffersFixture.hpp"
+#include "../TfLiteParser.hpp"
+
+BOOST_AUTO_TEST_SUITE(TensorflowLiteParser)
+
+BOOST_AUTO_TEST_CASE(ParseEmptyBinaryData)
+{
+    ITfLiteParser::TfLiteParserOptions options;
+    ITfLiteParserPtr m_Parser(ITfLiteParser::Create(armnn::Optional<ITfLiteParser::TfLiteParserOptions>(options)));
+    // Should throw armnn::ParseException: Buffer doesn't conform to the expected Tensorflow Lite flatbuffers format.
+    BOOST_CHECK_THROW(m_Parser->CreateNetworkFromBinary({0}), armnn::ParseException);
+}
+
+struct NoInputBindingsFixture : public ParserFlatbuffersFixture
+{
+    explicit NoInputBindingsFixture()
+    {
+        m_JsonString = R"(
+            {
+                "version": 3,
+                "operator_codes": [ { "builtin_code": "CONV_2D" } ],
+                "subgraphs": [ { } ]
+            }
+        )";
+        SetupSingleInputSingleOutput("inputTensor", "outputTensor");
+    }
+};
+
+BOOST_FIXTURE_TEST_CASE( ParseBadInputBindings, NoInputBindingsFixture )
+{
+    // Should throw armnn::ParseException: No input binding found for subgraph:0 and name:inputTensor.
+    BOOST_CHECK_THROW( (RunTest<4, armnn::DataType::QAsymmU8>(0, { }, { 0 })), armnn::ParseException);
+}
+
+BOOST_AUTO_TEST_SUITE_END()