IVGCVSW-5280 Switch tests/InferenceTest and derived tests over to cxxopts
authorJames Ward <james.ward@arm.com>
Mon, 12 Oct 2020 13:17:36 +0000 (14:17 +0100)
committerJames Ward <james.ward@arm.com>
Wed, 14 Oct 2020 12:41:58 +0000 (12:41 +0000)
* refactor AddCommandLineOptions() functions to allow checking of required options
* add CxxoptsUtils.hpp file for convenience functions
!referencetests:268500

Signed-off-by: James Ward <james.ward@arm.com>
Change-Id: Ica954b210b2981b7cd10995f0d75fcb2a2f7b443

tests/CxxoptsUtils.hpp [new file with mode: 0644]
tests/DeepSpeechV1InferenceTest.hpp
tests/InferenceModel.hpp
tests/InferenceTest.cpp
tests/InferenceTest.hpp
tests/InferenceTest.inl
tests/MobileNetSsdInferenceTest.hpp
tests/YoloInferenceTest.hpp

diff --git a/tests/CxxoptsUtils.hpp b/tests/CxxoptsUtils.hpp
new file mode 100644 (file)
index 0000000..518fc1b
--- /dev/null
@@ -0,0 +1,27 @@
+//
+// Copyright © 2020 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <cxxopts/cxxopts.hpp>
+
+/**
+ * Ensure all mandatory command-line parameters have been passed to cxxopts.
+ * @param result returned from the cxxopts parse(argc, argv) call
+ * @param required vector of strings listing the mandatory parameters to be input from the command-line
+ * @return boolean value - true if all required parameters satisfied, false otherwise
+ * */
+inline bool CheckRequiredOptions(const cxxopts::ParseResult& result, const std::vector<std::string>& required)
+{
+    for(const std::string& str : required)
+    {
+        if(result.count(str) == 0)
+        {
+            std::cerr << "--" << str << " parameter is mandatory" << std::endl;
+            return false;
+        }
+    }
+    return true;
+}
index d859ba7..ac799cb 100644 (file)
@@ -99,31 +99,28 @@ public:
         : m_ConstructModel(constructModel)
     {}
 
-    virtual void AddCommandLineOptions(boost::program_options::options_description& options) override
+    virtual void AddCommandLineOptions(cxxopts::Options& options, std::vector<std::string>& required) override
     {
-        namespace po = boost::program_options;
-
-        options.add_options()
-                ("input-seq-dir,s", po::value<std::string>(&m_InputSeqDir)->required(),
-                 "Path to directory containing test data for m_InputSeq");
-        options.add_options()
-                ("prev-state-h-dir,h", po::value<std::string>(&m_PrevStateHDir)->required(),
-                 "Path to directory containing test data for m_PrevStateH");
-        options.add_options()
-                ("prev-state-c-dir,c", po::value<std::string>(&m_PrevStateCDir)->required(),
-                 "Path to directory containing test data for m_PrevStateC");
-        options.add_options()
-                ("logits-dir,l", po::value<std::string>(&m_LogitsDir)->required(),
-                 "Path to directory containing test data for m_Logits");
-        options.add_options()
-                ("new-state-h-dir,H", po::value<std::string>(&m_NewStateHDir)->required(),
-                 "Path to directory containing test data for m_NewStateH");
-        options.add_options()
-                ("new-state-c-dir,C", po::value<std::string>(&m_NewStateCDir)->required(),
-                 "Path to directory containing test data for m_NewStateC");
-
-
-        Model::AddCommandLineOptions(options, m_ModelCommandLineOptions);
+        options
+            .allow_unrecognised_options()
+            .add_options()
+                ("s,input-seq-dir", "Path to directory containing test data for m_InputSeq",
+                 cxxopts::value<std::string>(m_InputSeqDir))
+                ("h,prev-state-h-dir", "Path to directory containing test data for m_PrevStateH",
+                 cxxopts::value<std::string>(m_PrevStateHDir))
+                ("c,prev-state-c-dir", "Path to directory containing test data for m_PrevStateC",
+                 cxxopts::value<std::string>(m_PrevStateCDir))
+                ("l,logits-dir", "Path to directory containing test data for m_Logits",
+                 cxxopts::value<std::string>(m_LogitsDir))
+                ("H,new-state-h-dir", "Path to directory containing test data for m_NewStateH",
+                 cxxopts::value<std::string>(m_NewStateHDir))
+                ("C,new-state-c-dir", "Path to directory containing test data for m_NewStateC",
+                 cxxopts::value<std::string>(m_NewStateCDir));
+
+        required.insert(required.end(), {"input-seq-dir", "prev-state-h-dir", "prev-state-c-dir", "logits-dir",
+                                         "new-state-h-dir", "new-state-c-dir"});
+
+        Model::AddCommandLineOptions(options, m_ModelCommandLineOptions, required);
     }
 
     virtual bool ProcessCommandLineOptions(const InferenceTestOptions &commonOptions) override
index fd8dede..dbc0419 100644 (file)
@@ -29,7 +29,8 @@
 #include "armnn/utility/StringUtils.hpp"
 #include <boost/exception/exception.hpp>
 #include <boost/exception/diagnostic_information.hpp>
-#include <boost/program_options.hpp>
+#include <cxxopts/cxxopts.hpp>
+#include "CxxoptsUtils.hpp"
 #include <fmt/format.h>
 #include <mapbox/variant.hpp>
 
@@ -347,37 +348,42 @@ public:
         }
     };
 
-    static void AddCommandLineOptions(boost::program_options::options_description& desc, CommandLineOptions& options)
+    static void AddCommandLineOptions(cxxopts::Options& options,
+                                      CommandLineOptions& cLineOptions, std::vector<std::string>& required)
     {
-        namespace po = boost::program_options;
-
         const std::vector<std::string> defaultComputes = { "CpuAcc", "CpuRef" };
 
         const std::string backendsMessage = "Which device to run layers on by default. Possible choices: "
                                           + armnn::BackendRegistryInstance().GetBackendIdsAsString();
 
-        desc.add_options()
-            ("model-dir,m", po::value<std::string>(&options.m_ModelDir)->required(),
-                "Path to directory containing model files (.caffemodel/.prototxt/.tflite)")
-            ("compute,c", po::value<std::vector<std::string>>(&options.m_ComputeDevices)->
-                default_value(defaultComputes, armnn::stringUtils::StringConcat(defaultComputes, ", "))->
-                multitoken(), backendsMessage.c_str())
-            ("dynamic-backends-path,b", po::value(&options.m_DynamicBackendsPath),
-                "Path where to load any available dynamic backend from. "
-                "If left empty (the default), dynamic backends will not be used.")
-            ("labels,l", po::value<std::string>(&options.m_Labels),
-                "Text file containing one image filename - correct label pair per line, "
-                "used to test the accuracy of the network.")
-            ("visualize-optimized-model,v",
-                po::value<bool>(&options.m_VisualizePostOptimizationModel)->default_value(false),
-             "Produce a dot file useful for visualizing the graph post optimization."
-                "The file will have the same name as the model with the .dot extention.")
-            ("fp16-turbo-mode", po::value<bool>(&options.m_EnableFp16TurboMode)->default_value(false),
-                "If this option is enabled FP32 layers, weights and biases will be converted "
-                "to FP16 where the backend supports it.")
-            ("bf16-turbo-mode", po::value<bool>(&options.m_EnableBf16TurboMode)->default_value(false),
-                "If this option is enabled FP32 layers, weights and biases will be converted "
-                "to BF16 where the backend supports it.");
+        options
+            .allow_unrecognised_options()
+            .add_options()
+                ("m,model-dir", "Path to directory containing model files (.caffemodel/.prototxt/.tflite)",
+                 cxxopts::value<std::string>(cLineOptions.m_ModelDir))
+                ("c,compute", backendsMessage.c_str(),
+                 cxxopts::value<std::vector<std::string>>(cLineOptions.m_ComputeDevices)->default_value("CpuRef"))
+                ("b,dynamic-backends-path",
+                 "Path where to load any available dynamic backend from. "
+                 "If left empty (the default), dynamic backends will not be used.",
+                 cxxopts::value(cLineOptions.m_DynamicBackendsPath))
+                ("l,labels",
+                 "Text file containing one image filename - correct label pair per line, "
+                 "used to test the accuracy of the network.", cxxopts::value<std::string>(cLineOptions.m_Labels))
+                ("v,visualize-optimized-model",
+                 "Produce a dot file useful for visualizing the graph post optimization."
+                 "The file will have the same name as the model with the .dot extention.",
+                 cxxopts::value<bool>(cLineOptions.m_VisualizePostOptimizationModel)->default_value("false"))
+                ("fp16-turbo-mode",
+                 "If this option is enabled FP32 layers, weights and biases will be converted "
+                 "to FP16 where the backend supports it.",
+                 cxxopts::value<bool>(cLineOptions.m_EnableFp16TurboMode)->default_value("false"))
+                ("bf16-turbo-mode",
+                 "If this option is enabled FP32 layers, weights and biases will be converted "
+                 "to BF16 where the backend supports it.",
+                 cxxopts::value<bool>(cLineOptions.m_EnableBf16TurboMode)->default_value("false"));
+
+        required.emplace_back("model-dir");
     }
 
     InferenceModel(const Params& params,
index b3b38d1..3392f6e 100644 (file)
@@ -8,7 +8,7 @@
 #include <Filesystem.hpp>
 
 #include "../src/armnn/Profiling.hpp"
-#include <boost/program_options.hpp>
+#include <cxxopts/cxxopts.hpp>
 
 #include <fstream>
 #include <iostream>
@@ -28,53 +28,51 @@ namespace test
 bool ParseCommandLine(int argc, char** argv, IInferenceTestCaseProvider& testCaseProvider,
     InferenceTestOptions& outParams)
 {
-    namespace po = boost::program_options;
-
-    po::options_description desc("Options");
+    cxxopts::Options options("InferenceTest", "Inference iteration parameters");
 
     try
     {
         // Adds generic options needed for all inference tests.
-        desc.add_options()
-            ("help", "Display help messages")
-            ("iterations,i", po::value<unsigned int>(&outParams.m_IterationCount)->default_value(0),
-                "Sets the number number of inferences to perform. If unset, a default number will be ran.")
-            ("inference-times-file", po::value<std::string>(&outParams.m_InferenceTimesFile)->default_value(""),
-                "If non-empty, each individual inference time will be recorded and output to this file")
-            ("event-based-profiling,e", po::value<bool>(&outParams.m_EnableProfiling)->default_value(0),
-                "Enables built in profiler. If unset, defaults to off.");
+        options
+            .allow_unrecognised_options()
+            .add_options()
+                ("h,help", "Display help messages")
+                ("i,iterations", "Sets the number of inferences to perform. If unset, will only be run once.",
+                 cxxopts::value<unsigned int>(outParams.m_IterationCount)->default_value("0"))
+                ("inference-times-file",
+                 "If non-empty, each individual inference time will be recorded and output to this file",
+                 cxxopts::value<std::string>(outParams.m_InferenceTimesFile)->default_value(""))
+                ("e,event-based-profiling", "Enables built in profiler. If unset, defaults to off.",
+                 cxxopts::value<bool>(outParams.m_EnableProfiling)->default_value("0"));
+
+        std::vector<std::string> required; //to be passed as reference to derived inference tests
 
         // Adds options specific to the ITestCaseProvider.
-        testCaseProvider.AddCommandLineOptions(desc);
-    }
-    catch (const std::exception& e)
-    {
-        // Coverity points out that default_value(...) can throw a bad_lexical_cast,
-        // and that desc.add_options() can throw boost::io::too_few_args.
-        // They really won't in any of these cases.
-        ARMNN_ASSERT_MSG(false, "Caught unexpected exception");
-        std::cerr << "Fatal internal error: " << e.what() << std::endl;
-        return false;
-    }
-
-    po::variables_map vm;
+        testCaseProvider.AddCommandLineOptions(options, required);
 
-    try
-    {
-        po::store(po::parse_command_line(argc, argv, desc), vm);
+        auto result = options.parse(argc, argv);
 
-        if (vm.count("help"))
+        if (result.count("help"))
         {
-            std::cout << desc << std::endl;
+            std::cout << options.help() << std::endl;
             return false;
         }
 
-        po::notify(vm);
+        CheckRequiredOptions(result, required);
+
+    }
+    catch (const cxxopts::OptionException& e)
+    {
+        std::cerr << e.what() << std::endl << options.help() << std::endl;
+        return false;
     }
-    catch (po::error& e)
+    catch (const std::exception& e)
     {
-        std::cerr << e.what() << std::endl << std::endl;
-        std::cerr << desc << std::endl;
+        // Coverity points out that default_value(...) can throw a bad_lexical_cast,
+        // and that desc.add_options() can throw boost::io::too_few_args.
+        // They really won't in any of these cases.
+        ARMNN_ASSERT_MSG(false, "Caught unexpected exception");
+        std::cerr << "Fatal internal error: " << e.what() << std::endl;
         return false;
     }
 
index 1dafd01..5ec744c 100644 (file)
@@ -11,7 +11,8 @@
 #include <armnn/TypesUtils.hpp>
 #include <armnn/utility/IgnoreUnused.hpp>
 
-#include <boost/program_options.hpp>
+#include <cxxopts/cxxopts.hpp>
+#include <fmt/format.h>
 
 
 namespace armnn
@@ -25,7 +26,7 @@ inline std::istream& operator>>(std::istream& in, armnn::Compute& compute)
     if (compute == armnn::Compute::Undefined)
     {
         in.setstate(std::ios_base::failbit);
-        throw boost::program_options::validation_error(boost::program_options::validation_error::invalid_option_value);
+        throw cxxopts::OptionException(fmt::format("Unrecognised compute device: {}", token));
     }
     return in;
 }
@@ -38,7 +39,7 @@ inline std::istream& operator>>(std::istream& in, armnn::BackendId& backend)
     if (compute == armnn::Compute::Undefined)
     {
         in.setstate(std::ios_base::failbit);
-        throw boost::program_options::validation_error(boost::program_options::validation_error::invalid_option_value);
+        throw cxxopts::OptionException(fmt::format("Unrecognised compute device: {}", token));
     }
     backend = compute;
     return in;
@@ -92,9 +93,9 @@ class IInferenceTestCaseProvider
 public:
     virtual ~IInferenceTestCaseProvider() {}
 
-    virtual void AddCommandLineOptions(boost::program_options::options_description& options)
+    virtual void AddCommandLineOptions(cxxopts::Options& options, std::vector<std::string>& required)
     {
-        IgnoreUnused(options);
+        IgnoreUnused(options, required);
     };
     virtual bool ProcessCommandLineOptions(const InferenceTestOptions &commonOptions)
     {
@@ -180,7 +181,7 @@ public:
     template <typename TConstructDatabaseCallable, typename TConstructModelCallable>
     ClassifierTestCaseProvider(TConstructDatabaseCallable constructDatabase, TConstructModelCallable constructModel);
 
-    virtual void AddCommandLineOptions(boost::program_options::options_description& options) override;
+    virtual void AddCommandLineOptions(cxxopts::Options& options, std::vector<std::string>& required) override;
     virtual bool ProcessCommandLineOptions(const InferenceTestOptions &commonOptions) override;
     virtual std::unique_ptr<IInferenceTestCase> GetTestCase(unsigned int testCaseId) override;
     virtual bool OnInferenceTestFinished() override;
index e8401f6..3d6dae3 100644 (file)
@@ -6,8 +6,9 @@
 
 #include <armnn/utility/Assert.hpp>
 #include <armnn/utility/NumericCast.hpp>
+#include "CxxoptsUtils.hpp"
 
-#include <boost/program_options.hpp>
+#include <cxxopts/cxxopts.hpp>
 #include <fmt/format.h>
 
 #include <fstream>
@@ -181,19 +182,21 @@ ClassifierTestCaseProvider<TDatabase, InferenceModel>::ClassifierTestCaseProvide
 
 template <typename TDatabase, typename InferenceModel>
 void ClassifierTestCaseProvider<TDatabase, InferenceModel>::AddCommandLineOptions(
-    boost::program_options::options_description& options)
+    cxxopts::Options& options, std::vector<std::string>& required)
 {
-    namespace po = boost::program_options;
-
-    options.add_options()
-        ("validation-file-in", po::value<std::string>(&m_ValidationFileIn)->default_value(""),
-            "Reads expected predictions from the given file and confirms they match the actual predictions.")
-        ("validation-file-out", po::value<std::string>(&m_ValidationFileOut)->default_value(""),
-            "Predictions are saved to the given file for later use via --validation-file-in.")
-        ("data-dir,d", po::value<std::string>(&m_DataDir)->required(),
-            "Path to directory containing test data");
-
-    InferenceModel::AddCommandLineOptions(options, m_ModelCommandLineOptions);
+    options
+        .allow_unrecognised_options()
+        .add_options()
+            ("validation-file-in",
+             "Reads expected predictions from the given file and confirms they match the actual predictions.",
+             cxxopts::value<std::string>(m_ValidationFileIn)->default_value(""))
+            ("validation-file-out", "Predictions are saved to the given file for later use via --validation-file-in.",
+             cxxopts::value<std::string>(m_ValidationFileOut)->default_value(""))
+            ("d,data-dir", "Path to directory containing test data", cxxopts::value<std::string>(m_DataDir));
+
+    required.emplace_back("data-dir"); //add to required arguments to check
+
+    InferenceModel::AddCommandLineOptions(options, m_ModelCommandLineOptions, required);
 }
 
 template <typename TDatabase, typename InferenceModel>
index 2a5d47d..7377640 100644 (file)
@@ -152,18 +152,19 @@ public:
         : m_ConstructModel(constructModel)
     {}
 
-    virtual void AddCommandLineOptions(boost::program_options::options_description& options) override
+    virtual void AddCommandLineOptions(cxxopts::Options& options, std::vector<std::string>& required) override
     {
-        namespace po = boost::program_options;
+        options
+            .allow_unrecognised_options()
+            .add_options()
+                ("d,data-dir", "Path to directory containing test data", cxxopts::value<std::string>(m_DataDir));
 
-        options.add_options()
-            ("data-dir,d", po::value<std::string>(&m_DataDir)->required(),
-             "Path to directory containing test data");
+        required.emplace_back("data-dir");
 
-        Model::AddCommandLineOptions(options, m_ModelCommandLineOptions);
+        Model::AddCommandLineOptions(options, m_ModelCommandLineOptions, required);
     }
 
-    virtual bool ProcessCommandLineOptions(const InferenceTestOptions &commonOptions) override
+    virtual bool ProcessCommandLineOptions(const InferenceTestOptionscommonOptions) override
     {
         if (!ValidateDirectory(m_DataDir))
         {
index 81ba0f5..4f391a8 100644 (file)
 #include <armnn/utility/Assert.hpp>
 #include <armnn/utility/IgnoreUnused.hpp>
 
+#include <boost/multi_array.hpp>
+#include <boost/test/tools/floating_point_comparison.hpp>
+
 #include <algorithm>
 #include <array>
 #include <utility>
 
-#include <boost/multi_array.hpp>
-#include <boost/test/tools/floating_point_comparison.hpp>
-
 constexpr size_t YoloOutputSize = 1470;
 
 template <typename Model>
@@ -187,18 +187,17 @@ public:
     {
     }
 
-    virtual void AddCommandLineOptions(boost::program_options::options_description& options) override
+    virtual void AddCommandLineOptions(cxxopts::Options& options, std::vector<std::string>& required) override
     {
-        namespace po = boost::program_options;
-
-        options.add_options()
-            ("data-dir,d", po::value<std::string>(&m_DataDir)->required(),
-                "Path to directory containing test data");
+        options
+            .allow_unrecognised_options()
+            .add_options()
+                ("d,data-dir", "Path to directory containing test data", cxxopts::value<std::string>(m_DataDir));
 
-        Model::AddCommandLineOptions(options, m_ModelCommandLineOptions);
+        Model::AddCommandLineOptions(options, m_ModelCommandLineOptions, required);
     }
 
-    virtual bool ProcessCommandLineOptions(const InferenceTestOptions &commonOptions) override
+    virtual bool ProcessCommandLineOptions(const InferenceTestOptionscommonOptions) override
     {
         if (!ValidateDirectory(m_DataDir))
         {