ts: added findDataFile() utility function and SkipTestException
authorAlexander Alekhin <alexander.alekhin@intel.com>
Tue, 29 Nov 2016 17:28:59 +0000 (20:28 +0300)
committerAlexander Alekhin <alexander.alekhin@intel.com>
Tue, 29 Nov 2016 20:17:39 +0000 (23:17 +0300)
CMakeLists.txt
cmake/OpenCVUtils.cmake
modules/ts/CMakeLists.txt
modules/ts/include/opencv2/ts.hpp
modules/ts/include/opencv2/ts/ts_ext.hpp
modules/ts/src/ts.cpp
modules/ts/src/ts_perf.cpp

index 8337ec9..5ac27a2 100644 (file)
@@ -362,14 +362,12 @@ if (OPENCV_TEST_DATA_PATH)
   get_filename_component(OPENCV_TEST_DATA_PATH ${OPENCV_TEST_DATA_PATH} ABSOLUTE)
 endif()
 
-if(OPENCV_TEST_DATA_PATH AND NOT OPENCV_TEST_DATA_INSTALL_PATH)
-  if(ANDROID)
-    ocv_update(OPENCV_TEST_DATA_INSTALL_PATH "sdk/etc/testdata")
-  elseif(WIN32)
-    ocv_update(OPENCV_TEST_DATA_INSTALL_PATH "testdata")
-  else()
-    ocv_update(OPENCV_TEST_DATA_INSTALL_PATH "share/OpenCV/testdata")
-  endif()
+if(ANDROID)
+  ocv_update(OPENCV_TEST_DATA_INSTALL_PATH "sdk/etc/testdata")
+elseif(WIN32)
+  ocv_update(OPENCV_TEST_DATA_INSTALL_PATH "testdata")
+else()
+  ocv_update(OPENCV_TEST_DATA_INSTALL_PATH "share/OpenCV/testdata")
 endif()
 
 if(ANDROID)
index 34b4565..8969a50 100644 (file)
@@ -1035,3 +1035,21 @@ function(ocv_add_test_from_target test_name test_kind the_target)
     endif()
   endif()
 endfunction()
+
+macro(ocv_add_testdata basedir dest_subdir)
+  if(BUILD_TESTS)
+    cmake_parse_arguments(__TESTDATA "" "COMPONENT" "" ${ARGN})
+    if(NOT CMAKE_CROSSCOMPILING AND NOT INSTALL_TESTS)
+      file(COPY ${basedir}/
+           DESTINATION ${CMAKE_BINARY_DIR}/${OPENCV_TEST_DATA_INSTALL_PATH}/${dest_subdir}
+           ${__TESTDATA_UNPARSED_ARGUMENTS}
+      )
+    endif()
+    if(INSTALL_TESTS)
+      install(DIRECTORY ${basedir}/
+              DESTINATION ${OPENCV_TEST_DATA_INSTALL_PATH}/contrib/text
+              ${ARGN}
+      )
+    endif()
+  endif()
+endmacro()
index 8d625f8..f95bed0 100644 (file)
@@ -21,3 +21,23 @@ ocv_add_module(ts INTERNAL opencv_core opencv_imgproc opencv_imgcodecs opencv_vi
 ocv_glob_module_sources()
 ocv_module_include_directories()
 ocv_create_module()
+
+# generate config file
+set(OPENCV_TESTS_CONFIG_FILE "${CMAKE_BINARY_DIR}/opencv_tests_config.hpp")
+set(OPENCV_TESTS_CONFIG_STR "")
+if(CMAKE_INSTALL_PREFIX)
+  set(OPENCV_TESTS_CONFIG_STR "${OPENCV_TESTS_CONFIG_STR}
+#define OPENCV_INSTALL_PREFIX \"${CMAKE_INSTALL_PREFIX}\"
+")
+endif()
+if(OPENCV_TEST_DATA_INSTALL_PATH)
+  set(OPENCV_TESTS_CONFIG_STR "${OPENCV_TESTS_CONFIG_STR}
+#define OPENCV_TEST_DATA_INSTALL_PATH \"${OPENCV_TEST_DATA_INSTALL_PATH}\"
+")
+endif()
+if(EXISTS "${OPENCV_TESTS_CONFIG_FILE}")
+  file(READ "${OPENCV_TESTS_CONFIG_FILE}" __content)
+endif()
+if(NOT OPENCV_TESTS_CONFIG_STR STREQUAL "${__content}")
+  file(WRITE "${OPENCV_TESTS_CONFIG_FILE}" "${OPENCV_TESTS_CONFIG_STR}")
+endif()
index 5f5dec6..28a071a 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef OPENCV_GTESTCV_HPP
-#define OPENCV_GTESTCV_HPP
+#ifndef OPENCV_TS_HPP
+#define OPENCV_TS_HPP
 
 #include "opencv2/core/cvdef.h"
 #include <stdarg.h> // for va_list
@@ -55,6 +55,14 @@ using cv::Rect;
 using cv::InputArray;
 using cv::noArray;
 
+class SkipTestException: public cv::Exception
+{
+public:
+    int dummy; // workaround for MacOSX Xcode 7.3 bug (don't make class "empty")
+    SkipTestException() : dummy(0) {}
+    SkipTestException(const cv::String& message) : dummy(0) { this->msg = message; }
+};
+
 class CV_EXPORTS TS;
 
 CV_EXPORTS int64 readSeed(const char* str);
@@ -420,6 +428,8 @@ public:
     // returns textual description of failure code
     static string str_from_code( const TS::FailureCode code );
 
+    std::vector<std::string> data_search_path;
+    std::vector<std::string> data_search_subdir;
 protected:
 
     // these are allocated within a test to try keep them valid in case of stack corruption
@@ -539,17 +549,37 @@ struct CV_EXPORTS DefaultRngAuto
     DefaultRngAuto& operator=(const DefaultRngAuto&);
 };
 
-}
-
-namespace cvtest
-{
 
 // test images generation functions
 CV_EXPORTS void fillGradient(Mat& img, int delta = 5);
 CV_EXPORTS void smoothBorder(Mat& img, const Scalar& color, int delta = 3);
 
 CV_EXPORTS void printVersionInfo(bool useStdOut = true);
-} //namespace cvtest
+
+
+// Utility functions
+
+CV_EXPORTS void addDataSearchPath(const std::string& path);
+CV_EXPORTS void addDataSearchSubDirectory(const std::string& subdir);
+
+/*! @brief Try to find requested data file
+
+  Search directories:
+
+  0. TS::data_search_path (search sub-directories are not used)
+  1. OPENCV_TEST_DATA_PATH environment variable
+  2. One of these:
+     a. OpenCV testdata based on build location: "./" + "share/OpenCV/testdata"
+     b. OpenCV testdata at install location: CMAKE_INSTALL_PREFIX + "share/OpenCV/testdata"
+
+  Search sub-directories:
+
+  - addDataSearchSubDirectory()
+  - modulename from TS::init()
+
+ */
+CV_EXPORTS std::string findDataFile(const std::string& relative_path, bool required = true);
+
 
 #ifndef __CV_TEST_EXEC_ARGS
 #if defined(_MSC_VER) && (_MSC_VER <= 1400)
@@ -562,9 +592,9 @@ CV_EXPORTS void printVersionInfo(bool useStdOut = true);
 #endif
 
 #ifdef HAVE_OPENCL
-namespace cvtest { namespace ocl {
+namespace ocl {
 void dumpOpenCLDevice();
-} }
+}
 #define TEST_DUMP_OCL_INFO cvtest::ocl::dumpOpenCLDevice();
 #else
 #define TEST_DUMP_OCL_INFO
@@ -575,11 +605,13 @@ void parseCustomOptions(int argc, char **argv);
 #define CV_TEST_MAIN(resourcesubdir, ...) \
 int main(int argc, char **argv) \
 { \
-    __CV_TEST_EXEC_ARGS(__VA_ARGS__) \
-    cvtest::TS::ptr()->init(resourcesubdir); \
+    using namespace cvtest; \
+    TS* ts = TS::ptr(); \
+    ts->init(resourcesubdir); \
     ::testing::InitGoogleTest(&argc, argv); \
     cvtest::printVersionInfo(); \
     TEST_DUMP_OCL_INFO \
+    __CV_TEST_EXEC_ARGS(__VA_ARGS__) \
     parseCustomOptions(argc, argv); \
     return RUN_ALL_TESTS(); \
 }
@@ -591,7 +623,9 @@ int main(int argc, char **argv) \
     FAIL() << "No equivalent implementation."; \
 } while (0)
 
-#endif
+} //namespace cvtest
+
+#endif // OPENCV_TS_HPP
 
 #include "opencv2/ts/ts_perf.hpp"
 
index ed6009c..781b61e 100644 (file)
@@ -8,7 +8,25 @@
 #ifndef OPENCV_TS_EXT_HPP
 #define OPENCV_TS_EXT_HPP
 
+namespace cvtest {
 void checkIppStatus();
+}
+
+#define CV_TEST_INIT cv::ipp::setIppStatus(0);
+#define CV_TEST_CLEANUP ::cvtest::checkIppStatus();
+#define CV_TEST_BODY_IMPL \
+    { \
+       try { \
+          CV_TEST_INIT \
+          Body(); \
+          CV_TEST_CLEANUP \
+       } \
+       catch (cvtest::SkipTestException& e) \
+       { \
+          printf("[     SKIP ] %s\n", e.what()); \
+       } \
+    } \
+
 
 #undef TEST
 #define TEST(test_case_name, test_name) \
@@ -33,7 +51,7 @@ void checkIppStatus();
             ::testing::Test::TearDownTestCase, \
             new ::testing::internal::TestFactoryImpl<\
                 GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\
-    void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() { cv::ipp::setIppStatus(0); Body(); checkIppStatus(); } \
+    void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() CV_TEST_BODY_IMPL \
     void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::Body()
 
 #undef TEST_F
@@ -59,7 +77,7 @@ void checkIppStatus();
             test_fixture::TearDownTestCase, \
             new ::testing::internal::TestFactoryImpl<\
                 GTEST_TEST_CLASS_NAME_(test_fixture, test_name)>);\
-    void GTEST_TEST_CLASS_NAME_(test_fixture, test_name)::TestBody() { cv::ipp::setIppStatus(0); Body(); checkIppStatus(); } \
+    void GTEST_TEST_CLASS_NAME_(test_fixture, test_name)::TestBody() CV_TEST_BODY_IMPL \
     void GTEST_TEST_CLASS_NAME_(test_fixture, test_name)::Body()
 
 #undef TEST_P
@@ -91,7 +109,7 @@ void checkIppStatus();
   int GTEST_TEST_CLASS_NAME_(test_case_name, \
                              test_name)::gtest_registering_dummy_ = \
       GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \
-    void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() { cv::ipp::setIppStatus(0); Body(); checkIppStatus(); } \
+    void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() CV_TEST_BODY_IMPL \
     void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::Body()
 
 #endif  // OPENCV_TS_EXT_HPP
index c02822e..c60e5d6 100644 (file)
 #include <setjmp.h>
 #endif
 
+// isDirectory
+#if defined WIN32 || defined _WIN32 || defined WINCE
+# include <windows.h>
+#else
+# include <dirent.h>
+# include <sys/stat.h>
+#endif
+
+
+#include "opencv_tests_config.hpp"
+
 namespace cvtest
 {
 
+static std::string path_join(const std::string& prefix, const std::string& subpath)
+{
+    CV_Assert(subpath.empty() || subpath[0] != '/');
+    if (prefix.empty())
+        return subpath;
+    bool skipSlash = prefix.size() > 0 ? (prefix[prefix.size()-1] == '/' || prefix[prefix.size()-1] == '\\') : false;
+    std::string path = prefix + (skipSlash ? "" : "/") + subpath;
+    return path;
+}
+
+
+
 /*****************************************************************************************\
 *                                Exception and memory handlers                            *
 \*****************************************************************************************/
@@ -449,6 +472,7 @@ static int tsErrorCallback( int status, const char* func_name, const char* err_m
 
 void TS::init( const string& modulename )
 {
+    data_search_subdir.push_back(modulename);
 #ifndef WINRT
     char* datapath_dir = getenv("OPENCV_TEST_DATA_PATH");
 #else
@@ -457,11 +481,7 @@ void TS::init( const string& modulename )
 
     if( datapath_dir )
     {
-        char buf[1024];
-        size_t l = strlen(datapath_dir);
-        bool haveSlash = l > 0 && (datapath_dir[l-1] == '/' || datapath_dir[l-1] == '\\');
-        sprintf( buf, "%s%s%s/", datapath_dir, haveSlash ? "" : "/", modulename.c_str() );
-        data_path = string(buf);
+        data_path = path_join(path_join(datapath_dir, modulename), "");
     }
 
     cv::redirectError((cv::ErrorCallback)tsErrorCallback, this);
@@ -583,7 +603,7 @@ void TS::printf( int streams, const char* fmt, ... )
 }
 
 
-TS ts;
+static TS ts;
 TS* TS::ptr() { return &ts; }
 
 void fillGradient(Mat& img, int delta)
@@ -659,7 +679,6 @@ void smoothBorder(Mat& img, const Scalar& color, int delta)
     }
 }
 
-} //namespace cvtest
 
 bool test_ipp_check = false;
 
@@ -694,4 +713,115 @@ void parseCustomOptions(int argc, char **argv)
 #endif
 }
 
+
+static bool isDirectory(const std::string& path)
+{
+#if defined WIN32 || defined _WIN32 || defined WINCE
+    WIN32_FILE_ATTRIBUTE_DATA all_attrs;
+#ifdef WINRT
+    wchar_t wpath[MAX_PATH];
+    size_t copied = mbstowcs(wpath, path.c_str(), MAX_PATH);
+    CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
+    BOOL status = ::GetFileAttributesExW(wpath, GetFileExInfoStandard, &all_attrs);
+#else
+    BOOL status = ::GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &all_attrs);
+#endif
+    DWORD attributes = all_attrs.dwFileAttributes;
+    return status && ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
+#else
+    struct stat s;
+    if (0 != stat(path.c_str(), &s))
+        return false;
+    return S_ISDIR(s.st_mode);
+#endif
+}
+
+CV_EXPORTS void addDataSearchPath(const std::string& path)
+{
+    if (isDirectory(path))
+        TS::ptr()->data_search_path.push_back(path);
+}
+CV_EXPORTS void addDataSearchSubDirectory(const std::string& subdir)
+{
+    TS::ptr()->data_search_subdir.push_back(subdir);
+}
+
+std::string findDataFile(const std::string& relative_path, bool required)
+{
+#define TEST_TRY_FILE_WITH_PREFIX(prefix) \
+{ \
+    std::string path = path_join(prefix, relative_path); \
+    /*printf("Trying %s\n", path.c_str());*/ \
+    FILE* f = fopen(path.c_str(), "rb"); \
+    if(f) { \
+       fclose(f); \
+       return path; \
+    } \
+}
+
+    const std::vector<std::string>& search_path = TS::ptr()->data_search_path;
+    for(size_t i = search_path.size(); i > 0; i--)
+    {
+        const std::string& prefix = search_path[i - 1];
+        TEST_TRY_FILE_WITH_PREFIX(prefix);
+    }
+
+    const std::vector<std::string>& search_subdir = TS::ptr()->data_search_subdir;
+
+#ifndef WINRT
+    char* datapath_dir = getenv("OPENCV_TEST_DATA_PATH");
+#else
+    char* datapath_dir = OPENCV_TEST_DATA_PATH;
+#endif
+
+    std::string datapath;
+    if (datapath_dir)
+    {
+        datapath = datapath_dir;
+        //CV_Assert(isDirectory(datapath) && "OPENCV_TEST_DATA_PATH is specified but it doesn't exist");
+        if (isDirectory(datapath))
+        {
+            for(size_t i = search_subdir.size(); i > 0; i--)
+            {
+                const std::string& subdir = search_subdir[i - 1];
+                std::string prefix = path_join(datapath, subdir);
+                TEST_TRY_FILE_WITH_PREFIX(prefix);
+            }
+        }
+    }
+#ifdef OPENCV_TEST_DATA_INSTALL_PATH
+    datapath = path_join("./", OPENCV_TEST_DATA_INSTALL_PATH);
+    if (isDirectory(datapath))
+    {
+        for(size_t i = search_subdir.size(); i > 0; i--)
+        {
+            const std::string& subdir = search_subdir[i - 1];
+            std::string prefix = path_join(datapath, subdir);
+            TEST_TRY_FILE_WITH_PREFIX(prefix);
+        }
+    }
+#ifdef OPENCV_INSTALL_PREFIX
+    else
+    {
+        datapath = path_join(OPENCV_INSTALL_PREFIX, OPENCV_TEST_DATA_INSTALL_PATH);
+        if (isDirectory(datapath))
+        {
+            for(size_t i = search_subdir.size(); i > 0; i--)
+            {
+                const std::string& subdir = search_subdir[i - 1];
+                std::string prefix = path_join(datapath, subdir);
+                TEST_TRY_FILE_WITH_PREFIX(prefix);
+            }
+        }
+    }
+#endif
+#endif
+    if (required)
+        CV_ErrorNoReturn(cv::Error::StsError, cv::format("OpenCV tests: Can't find required data file: %s", relative_path.c_str()));
+    throw SkipTestException(cv::format("OpenCV tests: Can't find data file: %s", relative_path.c_str()));
+}
+
+
+} //namespace cvtest
+
 /* End of file. */
index 1c40fb1..f496774 100644 (file)
@@ -19,6 +19,7 @@
 # include <sys/time.h>
 #endif
 
+using namespace cvtest;
 using namespace perf;
 
 int64 TestBase::timeLimitDefault = 0;
@@ -48,7 +49,10 @@ static bool         param_collect_impl;
 #ifdef ENABLE_INSTRUMENTATION
 static int          param_instrument;
 #endif
+
+namespace cvtest {
 extern bool         test_ipp_check;
+}
 
 #ifdef HAVE_CUDA
 static int          param_cuda_device;