Add support for simpler test case list syntax
authorPyry Haulos <phaulos@google.com>
Fri, 31 Oct 2014 00:45:41 +0000 (17:45 -0700)
committerPyry Haulos <phaulos@google.com>
Fri, 31 Oct 2014 16:57:08 +0000 (09:57 -0700)
This change adds support for mustpass-style (full test case path per
line) test case format. All existing test case list command line options
(--deqp-caselist, --deqp-stdin-caselist, --deqp-caselist-file) now
accept both trie- and list-style inputs.

Format is detected by looking at the first character; trie always starts
with '{', list-style format is assumed otherwise.

de::CommandLine default value behavior has been changed. Options that
take values (i.e. not --flags) don't have value set, unless default
value is provided when registering options. Calling getOption() on
command line option whose value is not provided is now an error.
hasOption() should be used first to check if a value was provided at
all.

Test case list parsing is now covered by tests in the internal module.

Bug: 17958588
Change-Id: If6063165ff59cbd8e538d6f829c583541e104fd1

executor/tools/xeCommandLineExecutor.cpp
framework/common/tcuCommandLine.cpp
framework/common/tcuTestCase.cpp
framework/common/tcuTestCase.hpp
framework/delibs/decpp/deCommandLine.cpp
framework/delibs/decpp/deCommandLine.hpp
framework/egl/egluGLContextFactory.cpp
modules/egl/teglTestPackage.cpp
modules/internal/ditFrameworkTests.cpp

index 28caa565100b9ab00bdc3fc29cac45bb69dd5506..de8f08280b6786977494aa1e9eed011865dd3415 100644 (file)
@@ -81,19 +81,19 @@ void registerOptions (de::cmdline::Parser& parser)
                { "no",         false   }
        };
 
-       parser << Option<StartServer>   ("s",           "start-server", "Start local execserver")
-                  << Option<Host>                      ("c",           "connect",              "Connect to host",                                                      "127.0.0.1")
-                  << Option<Port>                      ("p",           "port",                 "Select TCP port to use",                                       "50016")
-                  << Option<CaseListDir>       ("cd",          "caselistdir",  "Path to test case XML files",                          ".")
-                  << Option<TestSet>           ("t",           "testset",              "Test set",                                                                     parseCommaSeparatedList)
-                  << Option<ExcludeSet>        ("e",           "exclude",              "Comma-separated list of exclude filters",      parseCommaSeparatedList)
+       parser << Option<StartServer>   ("s",           "start-server", "Start local execserver",                                                               "")
+                  << Option<Host>                      ("c",           "connect",              "Connect to host",                                                                              "127.0.0.1")
+                  << Option<Port>                      ("p",           "port",                 "Select TCP port to use",                                                               "50016")
+                  << Option<CaseListDir>       ("cd",          "caselistdir",  "Path to test case XML files",                                                  ".")
+                  << Option<TestSet>           ("t",           "testset",              "Test set",                                                                                             parseCommaSeparatedList,        "")
+                  << Option<ExcludeSet>        ("e",           "exclude",              "Comma-separated list of exclude filters",                              parseCommaSeparatedList,        "")
                   << Option<ContinueFile>      (DE_NULL,       "continue",             "Continue execution by initializing results from existing test log")
-                  << Option<TestLogFile>       ("o",           "out",                  "Output test log filename")
-                  << Option<InfoLogFile>       ("i",           "info",                 "Output info log filename")
-                  << Option<Summary>           (DE_NULL,       "summary",              "Print summary at the end",                                     s_yesNo,        "yes")
-                  << Option<BinaryName>        ("b",           "binaryname",   "Test binary path, relative to working directory")
-                  << Option<WorkingDir>        ("wd",          "workdir",              "Working directory for test execution")
-                  << Option<CmdLineArgs>       (DE_NULL,       "cmdline",              "Additional command line arguments for test binary");
+                  << Option<TestLogFile>       ("o",           "out",                  "Output test log filename",                                                             "")
+                  << Option<InfoLogFile>       ("i",           "info",                 "Output info log filename",                                                             "")
+                  << Option<Summary>           (DE_NULL,       "summary",              "Print summary at the end",                                                             s_yesNo,        "yes")
+                  << Option<BinaryName>        ("b",           "binaryname",   "Test binary path, relative to working directory",              "")
+                  << Option<WorkingDir>        ("wd",          "workdir",              "Working directory for test execution",                                 "")
+                  << Option<CmdLineArgs>       (DE_NULL,       "cmdline",              "Additional command line arguments for test binary",    "");
 }
 
 } // opt
index c598e79187947312c8c31d9bc7ad9e25821d4491..3bcc06a19e2ade6d76239c10da8876b10a1c5fe9 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "tcuCommandLine.hpp"
 #include "tcuPlatform.hpp"
+#include "tcuTestCase.hpp"
 #include "deFilePath.hpp"
 #include "deStringUtil.hpp"
 #include "deString.h"
@@ -139,22 +140,22 @@ void registerOptions (de::cmdline::Parser& parser)
                << Option<StdinCaseList>                (DE_NULL,       "deqp-stdin-caselist",                  "Read case list (in trie format) from stdin")
                << Option<LogFilename>                  (DE_NULL,       "deqp-log-filename",                    "Write test results to given file",                                     "TestResults.qpa")
                << Option<RunMode>                              (DE_NULL,       "deqp-runmode",                                 "Execute tests, or write list of test cases into a file",
-                                                                                                                                                                       s_runModes, "execute")
+                                                                                                                                                                                                                                                                               s_runModes,                     "execute")
                << Option<WatchDog>                             (DE_NULL,       "deqp-watchdog",                                "Enable test watchdog",                                                         s_enableNames,          "disable")
                << Option<CrashHandler>                 (DE_NULL,       "deqp-crashhandler",                    "Enable crash handling",                                                        s_enableNames,          "disable")
-               << Option<BaseSeed>                             (DE_NULL,       "deqp-base-seed",                               "Base seed for test cases that use randomization")
-               << Option<TestIterationCount>   (DE_NULL,       "deqp-test-iteration-count",    "Iteration count for cases that support variable number of iterations")
+               << Option<BaseSeed>                             (DE_NULL,       "deqp-base-seed",                               "Base seed for test cases that use randomization",                                              "0")
+               << Option<TestIterationCount>   (DE_NULL,       "deqp-test-iteration-count",    "Iteration count for cases that support variable number of iterations", "0")
                << Option<Visibility>                   (DE_NULL,       "deqp-visibility",                              "Default test window visibility",                                       s_visibilites,          "windowed")
-               << Option<SurfaceWidth>                 (DE_NULL,       "deqp-surface-width",                   "Use given surface width if possible",  "-1")
-               << Option<SurfaceHeight>                (DE_NULL,       "deqp-surface-height",                  "Use given surface height if possible", "-1")
+               << Option<SurfaceWidth>                 (DE_NULL,       "deqp-surface-width",                   "Use given surface width if possible",                                                                  "-1")
+               << Option<SurfaceHeight>                (DE_NULL,       "deqp-surface-height",                  "Use given surface height if possible",                                                                 "-1")
                << Option<SurfaceType>                  (DE_NULL,       "deqp-surface-type",                    "Use given surface type",                                                       s_surfaceTypes,         "window")
                << Option<ScreenRotation>               (DE_NULL,       "deqp-screen-rotation",                 "Screen rotation for platforms that support it",        s_screenRotations,      "0")
                << Option<GLContextType>                (DE_NULL,       "deqp-gl-context-type",                 "OpenGL context type for platforms that support multiple")
-               << Option<GLConfigID>                   (DE_NULL,       "deqp-gl-config-id",                    "OpenGL (ES) render config ID (EGL config id on EGL platforms)",        "-1")
+               << Option<GLConfigID>                   (DE_NULL,       "deqp-gl-config-id",                    "OpenGL (ES) render config ID (EGL config id on EGL platforms)",                "-1")
                << Option<GLConfigName>                 (DE_NULL,       "deqp-gl-config-name",                  "Symbolic OpenGL (ES) render config name")
                << Option<GLContextFlags>               (DE_NULL,       "deqp-gl-context-flags",                "OpenGL context flags (comma-separated, supports debug and robust)")
-               << Option<CLPlatformID>                 (DE_NULL,       "deqp-cl-platform-id",                  "Execute tests on given OpenCL platform (IDs start from 1)",            "1")
-               << Option<CLDeviceIDs>                  (DE_NULL,       "deqp-cl-device-ids",                   "Execute tests on given CL devices (comma-separated, IDs start from 1)",        parseIntList)
+               << Option<CLPlatformID>                 (DE_NULL,       "deqp-cl-platform-id",                  "Execute tests on given OpenCL platform (IDs start from 1)",                    "1")
+               << Option<CLDeviceIDs>                  (DE_NULL,       "deqp-cl-device-ids",                   "Execute tests on given CL devices (comma-separated, IDs start from 1)",        parseIntList,   "")
                << Option<CLBuildOptions>               (DE_NULL,       "deqp-cl-build-options",                "Extra build options for OpenCL compiler")
                << Option<EGLDisplayType>               (DE_NULL,       "deqp-egl-display-type",                "EGL native display type")
                << Option<EGLWindowType>                (DE_NULL,       "deqp-egl-window-type",                 "EGL native window type")
@@ -238,15 +239,24 @@ public:
                                                                                CaseTreeNode            (const std::string& name) : m_name(name) {}
                                                                                ~CaseTreeNode           (void);
 
-       void                                                            addChild                        (CaseTreeNode* child) { m_children.push_back(child); }
+       const std::string&                                      getName                         (void) const { return m_name;                           }
+       bool                                                            hasChildren                     (void) const { return !m_children.empty();      }
+
+       bool                                                            hasChild                        (const std::string& name) const;
+       const CaseTreeNode*                                     getChild                        (const std::string& name) const;
+       CaseTreeNode*                                           getChild                        (const std::string& name);
 
-       const std::string&                                      getName                         (void) const { return m_name;           }
-       const std::vector<CaseTreeNode*>&       getChildren                     (void) const { return m_children;       }
+       void                                                            addChild                        (CaseTreeNode* child) { m_children.push_back(child); }
 
 private:
                                                                                CaseTreeNode            (const CaseTreeNode&);
        CaseTreeNode&                                           operator=                       (const CaseTreeNode&);
 
+       enum { NOT_FOUND = -1 };
+
+       // \todo [2014-10-30 pyry] Speed up with hash / sorting
+       int                                                                     findChildNdx            (const std::string& name) const;
+
        std::string                                                     m_name;
        std::vector<CaseTreeNode*>                      m_children;
 };
@@ -257,64 +267,242 @@ CaseTreeNode::~CaseTreeNode (void)
                delete *i;
 }
 
-static CaseTreeNode* parseCaseTree (std::istream& in)
+int CaseTreeNode::findChildNdx (const std::string& name) const
+{
+       for (int ndx = 0; ndx < (int)m_children.size(); ++ndx)
+       {
+               if (m_children[ndx]->getName() == name)
+                       return ndx;
+       }
+       return NOT_FOUND;
+}
+
+inline bool CaseTreeNode::hasChild (const std::string& name) const
+{
+       return findChildNdx(name) != NOT_FOUND;
+}
+
+inline const CaseTreeNode* CaseTreeNode::getChild (const std::string& name) const
+{
+       const int ndx = findChildNdx(name);
+       return ndx == NOT_FOUND ? DE_NULL : m_children[ndx];
+}
+
+inline CaseTreeNode* CaseTreeNode::getChild (const std::string& name)
+{
+       const int ndx = findChildNdx(name);
+       return ndx == NOT_FOUND ? DE_NULL : m_children[ndx];
+}
+
+static int getCurrentComponentLen (const char* path)
+{
+       int ndx = 0;
+       for (; path[ndx] != 0 && path[ndx] != '.'; ++ndx);
+       return ndx;
+}
+
+static const CaseTreeNode* findNode (const CaseTreeNode* root, const char* path)
+{
+       const CaseTreeNode*     curNode         = root;
+       const char*                     curPath         = path;
+       int                                     curLen          = getCurrentComponentLen(curPath);
+
+       for (;;)
+       {
+               curNode = curNode->getChild(std::string(curPath, curPath+curLen));
+
+               if (!curNode)
+                       break;
+
+               curPath += curLen;
+
+               if (curPath[0] == 0)
+                       break;
+               else
+               {
+                       DE_ASSERT(curPath[0] == '.');
+                       curPath         += 1;
+                       curLen           = getCurrentComponentLen(curPath);
+               }
+       }
+
+       return curNode;
+}
+
+static void parseCaseTrie (CaseTreeNode* root, std::istream& in)
 {
        vector<CaseTreeNode*>   nodeStack;
        string                                  curName;
+       bool                                    expectNode              = true;
 
        if (in.get() != '{')
-               throw std::invalid_argument("Malformed case tree");
+               throw std::invalid_argument("Malformed case trie");
 
-       nodeStack.reserve(1);
-       nodeStack.push_back(new CaseTreeNode(""));
+       nodeStack.push_back(root);
 
-       try
+       while (!nodeStack.empty())
        {
-               for (;;)
+               const int       curChr  = in.get();
+
+               if (curChr == std::char_traits<char>::eof() || curChr == 0)
+                       throw std::invalid_argument("Unterminated case tree");
+
+               if (curChr == '{' || curChr == ',' || curChr == '}')
                {
-                       const int       curChr  = in.get();
+                       if (!curName.empty() && expectNode)
+                       {
+                               CaseTreeNode* const newChild = new CaseTreeNode(curName);
 
-                       if (curChr == std::char_traits<char>::eof() || curChr == 0)
-                               break;
+                               try
+                               {
+                                       nodeStack.back()->addChild(newChild);
+                               }
+                               catch (...)
+                               {
+                                       delete newChild;
+                                       throw;
+                               }
 
-                       if (nodeStack.empty())
-                               throw std::invalid_argument("Trailing characters at end of case tree");
+                               if (curChr == '{')
+                                       nodeStack.push_back(newChild);
 
-                       if (!curName.empty() && (curChr == '{' || curChr == ',' || curChr == '}'))
+                               curName.clear();
+                       }
+                       else if (curName.empty() == expectNode)
+                               throw std::invalid_argument(expectNode ? "Empty node name" : "Missing node separator");
+
+                       if (curChr == '}')
                        {
-                               // Create child and push to stack.
-                               nodeStack.reserve(nodeStack.size()+1);
-                               nodeStack.push_back(new CaseTreeNode(curName));
+                               expectNode = false;
+                               nodeStack.pop_back();
+                       }
+                       else
+                               expectNode = true;
+               }
+               else if (isValidTestCaseNameChar((char)curChr))
+                       curName += (char)curChr;
+               else
+                       throw std::invalid_argument("Illegal character in node name");
+       }
+}
 
-                               curName.clear();
+static void parseCaseList (CaseTreeNode* root, std::istream& in)
+{
+       // \note Algorithm assumes that cases are sorted by groups, but will
+       //               function fine, albeit more slowly, if that is not the case.
+       vector<CaseTreeNode*>   nodeStack;
+       int                                             stackPos        = 0;
+       string                                  curName;
+
+       nodeStack.resize(8, DE_NULL);
+
+       nodeStack[0] = root;
+
+       for (;;)
+       {
+               const int       curChr  = in.get();
+
+               if (curChr == std::char_traits<char>::eof() || curChr == 0 || curChr == '\n' || curChr == '\r')
+               {
+                       if (curName.empty())
+                               throw std::invalid_argument("Empty test case name");
+
+                       if (nodeStack[stackPos]->hasChild(curName))
+                               throw std::invalid_argument("Duplicate test case");
+
+                       CaseTreeNode* const newChild = new CaseTreeNode(curName);
+
+                       try
+                       {
+                               nodeStack[stackPos]->addChild(newChild);
                        }
+                       catch (...)
+                       {
+                               delete newChild;
+                               throw;
+                       }
+
+                       curName.clear();
+                       stackPos = 0;
+
+                       if (curChr == '\r' && in.peek() == '\n')
+                               in.get();
 
-                       if (curChr == ',' || curChr == '}')
                        {
-                               // Attach to parent
-                               if (nodeStack.size() < 2)
-                                       throw std::invalid_argument("Malformed case tree");
+                               const int nextChr = in.peek();
 
-                               (*(nodeStack.end()-2))->addChild(nodeStack.back());
-                               nodeStack.pop_back();
+                               if (nextChr == std::char_traits<char>::eof() || nextChr == 0)
+                                       break;
                        }
-                       else if (curChr != '{')
-                               curName += (char)curChr;
                }
+               else if (curChr == '.')
+               {
+                       if (curName.empty())
+                               throw std::invalid_argument("Empty test group name");
 
-               if (nodeStack.size() != 1 || nodeStack[0]->getName() != "")
-                       throw std::invalid_argument("Unterminated case tree");
+                       if ((int)nodeStack.size() <= stackPos+1)
+                               nodeStack.resize(nodeStack.size()*2, DE_NULL);
+
+                       if (!nodeStack[stackPos+1] || nodeStack[stackPos+1]->getName() != curName)
+                       {
+                               CaseTreeNode* curGroup = nodeStack[stackPos]->getChild(curName);
+
+                               if (!curGroup)
+                               {
+                                       curGroup = new CaseTreeNode(curName);
+
+                                       try
+                                       {
+                                               nodeStack[stackPos]->addChild(curGroup);
+                                       }
+                                       catch (...)
+                                       {
+                                               delete curGroup;
+                                               throw;
+                                       }
+                               }
+
+                               nodeStack[stackPos+1] = curGroup;
+
+                               if ((int)nodeStack.size() > stackPos+2)
+                                       nodeStack[stackPos+2] = DE_NULL; // Invalidate rest of entries
+                       }
+
+                       DE_ASSERT(nodeStack[stackPos+1]->getName() == curName);
+
+                       curName.clear();
+                       stackPos += 1;
+               }
+               else if (isValidTestCaseNameChar((char)curChr))
+                       curName += (char)curChr;
+               else
+                       throw std::invalid_argument("Illegal character in test case name");
        }
-       catch (...)
+}
+
+static CaseTreeNode* parseCaseList (std::istream& in)
+{
+       CaseTreeNode* const root = new CaseTreeNode("");
+       try
        {
-               // Nodes in stack are not attached to any parents and must be deleted individually.
-               for (vector<CaseTreeNode*>::const_iterator i = nodeStack.begin(); i != nodeStack.end(); ++i)
-                       delete *i;
+               if (in.peek() == '{')
+                       parseCaseTrie(root, in);
+               else
+                       parseCaseList(root, in);
+
+               {
+                       const int curChr = in.get();
+                       if (curChr != std::char_traits<char>::eof() && curChr != 0)
+                               throw std::invalid_argument("Trailing characters at end of case list");
+               }
 
+               return root;
+       }
+       catch (...)
+       {
+               delete root;
                throw;
        }
-
-       return nodeStack[0];
 }
 
 class CasePaths
@@ -507,9 +695,9 @@ bool CommandLine::parse (int argc, const char* const* argv)
        if (!m_cmdLine.getOption<opt::LogImages>())
                m_logFlags |= QP_TEST_LOG_EXCLUDE_IMAGES;
 
-       if ((m_cmdLine.getOption<opt::CasePath>().empty()?0:1) +
-               (m_cmdLine.getOption<opt::CaseList>().empty()?0:1) +
-               (m_cmdLine.getOption<opt::CaseListFile>().empty()?0:1) +
+       if ((m_cmdLine.hasOption<opt::CasePath>()?1:0) +
+               (m_cmdLine.hasOption<opt::CaseList>()?1:0) +
+               (m_cmdLine.hasOption<opt::CaseListFile>()?1:0) +
                (m_cmdLine.getOption<opt::StdinCaseList>()?1:0) > 1)
        {
                debugOut << "ERROR: multiple test case list options given!\n" << std::endl;
@@ -519,26 +707,26 @@ bool CommandLine::parse (int argc, const char* const* argv)
 
        try
        {
-               if (!m_cmdLine.getOption<opt::CaseList>().empty())
+               if (m_cmdLine.hasOption<opt::CaseList>())
                {
                        std::istringstream str(m_cmdLine.getOption<opt::CaseList>());
 
-                       m_caseTree = parseCaseTree(str);
+                       m_caseTree = parseCaseList(str);
                }
-               else if (!m_cmdLine.getOption<opt::CaseListFile>().empty())
+               else if (m_cmdLine.hasOption<opt::CaseListFile>())
                {
                        std::ifstream in(m_cmdLine.getOption<opt::CaseListFile>().c_str(), std::ios_base::binary);
 
                        if (!in.is_open() || !in.good())
                                throw Exception("Failed to open case list file '" + m_cmdLine.getOption<opt::CaseListFile>() + "'");
 
-                       m_caseTree = parseCaseTree(in);
+                       m_caseTree = parseCaseList(in);
                }
                else if (m_cmdLine.getOption<opt::StdinCaseList>())
                {
-                       m_caseTree = parseCaseTree(std::cin);
+                       m_caseTree = parseCaseList(std::cin);
                }
-               else if (!m_cmdLine.getOption<opt::CasePath>().empty())
+               else if (m_cmdLine.hasOption<opt::CasePath>())
                        m_casePaths = de::MovePtr<const CasePaths>(new CasePaths(m_cmdLine.getOption<opt::CasePath>()));
        }
        catch (const std::exception& e)
@@ -592,21 +780,18 @@ ScreenRotation                    CommandLine::getScreenRotation                  (void) const    { return m_cmdLin
 int                                            CommandLine::getGLConfigId                              (void) const    { return m_cmdLine.getOption<opt::GLConfigID>();                                }
 int                                            CommandLine::getCLPlatformId                    (void) const    { return m_cmdLine.getOption<opt::CLPlatformID>();                              }
 const std::vector<int>&        CommandLine::getCLDeviceIds                             (void) const    { return m_cmdLine.getOption<opt::CLDeviceIDs>();                               }
-const char*                            CommandLine::getEGLDisplayType                  (void) const    { return m_cmdLine.getOption<opt::EGLDisplayType>().c_str();    }
-const char*                            CommandLine::getEGLWindowType                   (void) const    { return m_cmdLine.getOption<opt::EGLWindowType>().c_str();             }
-const char*                            CommandLine::getEGLPixmapType                   (void) const    { return m_cmdLine.getOption<opt::EGLPixmapType>().c_str();             }
 bool                                   CommandLine::isOutOfMemoryTestEnabled   (void) const    { return m_cmdLine.getOption<opt::TestOOM>();                                   }
 
 const char* CommandLine::getGLContextType (void) const
 {
-       if (!m_cmdLine.getOption<opt::GLContextType>().empty())
+       if (m_cmdLine.hasOption<opt::GLContextType>())
                return m_cmdLine.getOption<opt::GLContextType>().c_str();
        else
                return DE_NULL;
 }
 const char* CommandLine::getGLConfigName (void) const
 {
-       if (!m_cmdLine.getOption<opt::GLConfigName>().empty())
+       if (m_cmdLine.hasOption<opt::GLConfigName>())
                return m_cmdLine.getOption<opt::GLConfigName>().c_str();
        else
                return DE_NULL;
@@ -614,7 +799,7 @@ const char* CommandLine::getGLConfigName (void) const
 
 const char* CommandLine::getGLContextFlags (void) const
 {
-       if (!m_cmdLine.getOption<opt::GLContextFlags>().empty())
+       if (m_cmdLine.hasOption<opt::GLContextFlags>())
                return m_cmdLine.getOption<opt::GLContextFlags>().c_str();
        else
                return DE_NULL;
@@ -622,50 +807,46 @@ const char* CommandLine::getGLContextFlags (void) const
 
 const char* CommandLine::getCLBuildOptions (void) const
 {
-       if (!m_cmdLine.getOption<opt::CLBuildOptions>().empty())
+       if (m_cmdLine.hasOption<opt::CLBuildOptions>())
                return m_cmdLine.getOption<opt::CLBuildOptions>().c_str();
        else
                return DE_NULL;
 }
 
-static bool checkTestGroupName (const CaseTreeNode* node, const char* groupName)
+const char* CommandLine::getEGLDisplayType (void) const
 {
-       for (vector<CaseTreeNode*>::const_iterator childIter = node->getChildren().begin(); childIter != node->getChildren().end(); ++childIter)
-       {
-               const CaseTreeNode* const child = *childIter;
-
-               if (deStringBeginsWith(groupName, child->getName().c_str()))
-               {
-                       const int prefixLen = (int)child->getName().length();
-
-                       if (groupName[prefixLen] == 0)
-                               return true;
-                       else if (groupName[prefixLen] == '.')
-                               return checkTestGroupName(child, groupName + prefixLen + 1);
-               }
-       }
-
-       return false;
+       if (m_cmdLine.hasOption<opt::EGLDisplayType>())
+               return m_cmdLine.getOption<opt::EGLDisplayType>().c_str();
+       else
+               return DE_NULL;
 }
 
-static bool checkTestCaseName (const CaseTreeNode* node, const char* caseName)
+const char* CommandLine::getEGLWindowType (void) const
 {
-       for (vector<CaseTreeNode*>::const_iterator childIter = node->getChildren().begin(); childIter != node->getChildren().end(); ++childIter)
-       {
-               const CaseTreeNode* const child = *childIter;
+       if (m_cmdLine.hasOption<opt::EGLWindowType>())
+               return m_cmdLine.getOption<opt::EGLWindowType>().c_str();
+       else
+               return DE_NULL;
+}
 
-               if (deStringBeginsWith(caseName, child->getName().c_str()))
-               {
-                       const int prefixLen = (int)child->getName().length();
+const char* CommandLine::getEGLPixmapType (void) const
+{
+       if (m_cmdLine.hasOption<opt::EGLPixmapType>())
+               return m_cmdLine.getOption<opt::EGLPixmapType>().c_str();
+       else
+               return DE_NULL;
+}
 
-                       if (caseName[prefixLen] == 0 && child->getChildren().empty())
-                               return true;
-                       else if (caseName[prefixLen] == '.')
-                               return checkTestCaseName(child, caseName + prefixLen + 1);
-               }
-       }
+static bool checkTestGroupName (const CaseTreeNode* root, const char* groupPath)
+{
+       const CaseTreeNode* node = findNode(root, groupPath);
+       return node && node->hasChildren();
+}
 
-       return false;
+static bool checkTestCaseName (const CaseTreeNode* root, const char* casePath)
+{
+       const CaseTreeNode* node = findNode(root, casePath);
+       return node && !node->hasChildren();
 }
 
 bool CommandLine::checkTestGroupName (const char* groupName) const
index 2138e26d2a9088320c6a39d64272ec13a8f957cc..a8aeeb70782f68f4b36f572d7f69747306930b39 100644 (file)
@@ -33,19 +33,11 @@ using namespace std;
 
 // TestNode.
 
-inline bool isValidCaseNameChar (char c)
-{
-       return de::inRange(c, 'a', 'z') ||
-                  de::inRange(c, 'A', 'Z') ||
-                  de::inRange(c, '0', '9') ||
-                  c == '_' || c == '-';
-}
-
 inline bool isValidCaseName (const char* name)
 {
        for (const char* p = name; *p != '\0'; p++)
        {
-               if (!isValidCaseNameChar(*p))
+               if (!isValidTestCaseNameChar(*p))
                        return false;
        }
        return true;
index f333f2237aaacdd42899218ef9786def67b17bc4..431f7d184e6ed33be48974e04dc0a8de6d36254c 100644 (file)
@@ -51,6 +51,14 @@ inline bool isTestNodeTypeExecutable (TestNodeType type)
                   type == NODETYPE_ACCURACY;
 }
 
+inline bool isValidTestCaseNameChar (char c)
+{
+       return de::inRange(c, 'a', 'z') ||
+                  de::inRange(c, 'A', 'Z') ||
+                  de::inRange(c, '0', '9') ||
+                  c == '_' || c == '-';
+}
+
 /*--------------------------------------------------------------------*//*!
  * \brief Test case hierarchy node
  *
index dd2b4e2bf37b6835402e589caeea5b5bba45a349..d6017805ac1be4d77111da88912fe985a19c706a 100644 (file)
@@ -54,12 +54,18 @@ TypedFieldMap::TypedFieldMap (void)
 }
 
 TypedFieldMap::~TypedFieldMap (void)
+{
+       clear();
+}
+
+void TypedFieldMap::clear (void)
 {
        for (Map::const_iterator iter = m_fields.begin(); iter != m_fields.end(); ++iter)
        {
                if (iter->second.value)
                        iter->second.destructor(iter->second.value);
        }
+       m_fields.clear();
 }
 
 bool TypedFieldMap::contains (const std::type_info* key) const
@@ -138,8 +144,8 @@ bool Parser::parse (int numArgs, const char* const* args, CommandLine* dst, std:
                // Set default values.
                if (opt.defaultValue)
                        opt.dispatchParse(&opt, opt.defaultValue, &dst->m_options);
-               else
-                       opt.getDefault(&dst->m_options);
+               else if (opt.setDefault)
+                       opt.setDefault(&dst->m_options);
        }
 
        DE_ASSERT(!dst->m_options.get<Help>());
@@ -332,10 +338,11 @@ void parseType<int> (const char* src, int* dst)
 
 // Tests
 
-DE_DECLARE_COMMAND_LINE_OPT(TestStringOpt,     std::string);
-DE_DECLARE_COMMAND_LINE_OPT(TestIntOpt,                int);
-DE_DECLARE_COMMAND_LINE_OPT(TestBoolOpt,       bool);
-DE_DECLARE_COMMAND_LINE_OPT(TestNamedOpt,      deUint64);
+DE_DECLARE_COMMAND_LINE_OPT(TestStringOpt,             std::string);
+DE_DECLARE_COMMAND_LINE_OPT(TestStringDefOpt,  std::string);
+DE_DECLARE_COMMAND_LINE_OPT(TestIntOpt,                        int);
+DE_DECLARE_COMMAND_LINE_OPT(TestBoolOpt,               bool);
+DE_DECLARE_COMMAND_LINE_OPT(TestNamedOpt,              deUint64);
 
 void selfTest (void)
 {
@@ -396,10 +403,11 @@ void selfTest (void)
                        { "huge",       ~0ull   }
                };
 
-               parser << Option<TestStringOpt> ("s",   "string",       "String option")
-                          << Option<TestIntOpt>        ("i",   "int",          "Int option")
-                          << Option<TestBoolOpt>       ("b",   "bool",         "Test boolean flag")
-                          << Option<TestNamedOpt>      ("n",   "named",        "Test named opt",       DE_ARRAY_BEGIN(s_namedValues),  DE_ARRAY_END(s_namedValues),    "one");
+               parser << Option<TestStringOpt>         ("s",   "string",       "String option")
+                          << Option<TestStringDefOpt>  ("x",   "xyz",          "String option w/ default value",       "foo")
+                          << Option<TestIntOpt>                ("i",   "int",          "Int option")
+                          << Option<TestBoolOpt>               ("b",   "bool",         "Test boolean flag")
+                          << Option<TestNamedOpt>              ("n",   "named",        "Test named opt",       DE_ARRAY_BEGIN(s_namedValues),  DE_ARRAY_END(s_namedValues),    "one");
 
                {
                        std::ostringstream err;
@@ -417,10 +425,11 @@ void selfTest (void)
                        DE_TEST_ASSERT(parseOk);
                        DE_TEST_ASSERT(err.str().empty());
 
-                       DE_TEST_ASSERT(cmdLine.getOption<TestStringOpt>() == "");
-                       DE_TEST_ASSERT(cmdLine.getOption<TestIntOpt>() == 0);
-                       DE_TEST_ASSERT(cmdLine.getOption<TestBoolOpt>() == false);
+                       DE_TEST_ASSERT(!cmdLine.hasOption<TestStringOpt>());
+                       DE_TEST_ASSERT(!cmdLine.hasOption<TestIntOpt>());
                        DE_TEST_ASSERT(cmdLine.getOption<TestNamedOpt>() == 1);
+                       DE_TEST_ASSERT(cmdLine.getOption<TestBoolOpt>() == false);
+                       DE_TEST_ASSERT(cmdLine.getOption<TestStringDefOpt>() == "foo");
                }
 
                // Basic parsing
@@ -437,6 +446,7 @@ void selfTest (void)
                        DE_TEST_ASSERT(cmdLine.getOption<TestIntOpt>() == 9);
                        DE_TEST_ASSERT(cmdLine.getOption<TestBoolOpt>());
                        DE_TEST_ASSERT(cmdLine.getOption<TestNamedOpt>() == ~0ull);
+                       DE_TEST_ASSERT(cmdLine.getOption<TestStringDefOpt>() == "foo");
                }
 
                // End of argument list (--)
@@ -451,7 +461,7 @@ void selfTest (void)
 
                        DE_TEST_ASSERT(cmdLine.getOption<TestStringOpt>() == "foo");
                        DE_TEST_ASSERT(cmdLine.getOption<TestBoolOpt>());
-                       DE_TEST_ASSERT(cmdLine.getOption<TestIntOpt>() == 0); // Not specified
+                       DE_TEST_ASSERT(!cmdLine.hasOption<TestIntOpt>());
 
                        DE_TEST_ASSERT(cmdLine.getArgs().size() == 2);
                        DE_TEST_ASSERT(cmdLine.getArgs()[0] == "--int=2");
@@ -470,7 +480,7 @@ void selfTest (void)
 
                        DE_TEST_ASSERT(cmdLine.getOption<TestStringOpt>() == "--");
                        DE_TEST_ASSERT(cmdLine.getOption<TestBoolOpt>());
-                       DE_TEST_ASSERT(cmdLine.getOption<TestIntOpt>() == 0); // Not specified
+                       DE_TEST_ASSERT(!cmdLine.hasOption<TestIntOpt>());
 
                        DE_TEST_ASSERT(cmdLine.getArgs().size() == 1);
                        DE_TEST_ASSERT(cmdLine.getArgs()[0] == "foo");
@@ -555,7 +565,7 @@ void selfTest (void)
 
                // Empty value --arg=
                {
-                       const char*                     args[]  = { "--string=", "-b" };
+                       const char*                     args[]  = { "--string=", "-b", "-x", "" };
                        CommandLine                     cmdLine;
                        std::ostringstream      err;
                        bool                            parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
@@ -563,6 +573,7 @@ void selfTest (void)
                        DE_TEST_ASSERT(parseOk);
                        DE_TEST_ASSERT(err.str().empty());
                        DE_TEST_ASSERT(cmdLine.getOption<TestStringOpt>() == "");
+                       DE_TEST_ASSERT(cmdLine.getOption<TestStringDefOpt>() == "");
                        DE_TEST_ASSERT(cmdLine.getOption<TestBoolOpt>());
                }
        }
index 4f19aac8b3b26e3f6a542144c4034104f42bd645..50bfca549012ac39f395a84e90c837c2d63cdd90 100644 (file)
@@ -58,7 +58,7 @@ struct Option
        const char*                                             shortName;
        const char*                                             longName;
        const char*                                             description;
-       const char*                                             defaultValue;           //!< Default value (parsed from string), or null if default should be T()
+       const char*                                             defaultValue;           //!< Default value (parsed from string), or null if should not be set
 
        // \note Either parse or namedValues must be null.
        ParseFunc                                               parse;                          //!< Custom parsing function or null.
@@ -165,7 +165,7 @@ public:
                                                ~TypedFieldMap                  (void);
 
        bool                            empty                                   (void) const    { return m_fields.empty();      }
-       void                            clear                                   (void)                  { m_fields.clear();                     }
+       void                            clear                                   (void);
 
        template<typename Name>
        void                            set                                             (typename TypedFieldTraits<Name>::ValueType* value);
@@ -262,7 +262,7 @@ private:
        struct OptInfo;
 
        typedef void            (*DispatchParseFunc)            (const OptInfo* info, const char* src, TypedFieldMap* dst);
-       typedef void            (*GetDefaultFunc)                       (TypedFieldMap* dst);
+       typedef void            (*SetDefaultFunc)                       (TypedFieldMap* dst);
 
        struct OptInfo
        {
@@ -279,7 +279,7 @@ private:
                size_t                                  namedValueStride;
 
                DispatchParseFunc               dispatchParse;
-               GetDefaultFunc                  getDefault;
+               SetDefaultFunc                  setDefault;
 
                OptInfo (void)
                        : shortName                     (DE_NULL)
@@ -292,7 +292,7 @@ private:
                        , namedValuesEnd        (DE_NULL)
                        , namedValueStride      (0)
                        , dispatchParse         (DE_NULL)
-                       , getDefault            (DE_NULL)
+                       , setDefault            (DE_NULL)
                {}
        };
 
@@ -340,7 +340,7 @@ void Parser::dispatchParse (const OptInfo* info, const char* src, TypedFieldMap*
 }
 
 template<typename OptType>
-void dispatchGetDefault (TypedFieldMap* dst)
+void dispatchSetDefault (TypedFieldMap* dst)
 {
        typename OptTraits<OptType>::ValueType* value = new typename OptTraits<OptType>::ValueType();
        try
@@ -384,7 +384,9 @@ void Parser::addOption (const Option<OptType>& option)
        opt.namedValuesEnd              = (const void*)option.namedValuesEnd;
        opt.namedValueStride    = sizeof(*option.namedValues);
        opt.dispatchParse               = dispatchParse<OptType>;
-       opt.getDefault                  = dispatchGetDefault<OptType>;
+
+       if (opt.isFlag)
+               opt.setDefault          = dispatchSetDefault<OptType>;
 
        addOption(opt);
 }
@@ -400,9 +402,12 @@ public:
        const TypedFieldMap&            getOptions              (void) const    { return m_options;     }
        const vector<string>&           getArgs                 (void) const    { return m_args;        }
 
+       template<typename Option>
+       bool                                            hasOption               (void) const    { return m_options.contains<Option>();  }
+
        template<typename Option>
        const typename TypedFieldTraits<Option>::ValueType&
-                                                               getOption               (void) const    { return m_options.get<Option>(); }
+                                                               getOption               (void) const    { return m_options.get<Option>();               }
 
 private:
        TypedFieldMap                           m_options;
index 6a23acc9d581770b72db6bba70db617b53df5d7d..bf9c1c73c52d5d6a23fe773d811e7acbc3906e85 100644 (file)
@@ -742,7 +742,7 @@ namespace
 template<typename Factory>
 const Factory* selectFactory (const tcu::FactoryRegistry<Factory>& registry, const char* objectTypeName, const char* cmdLineArg)
 {
-       if (cmdLineArg[0] != 0)
+       if (cmdLineArg)
        {
                const Factory* factory = registry.getFactoryByName(cmdLineArg);
 
index f1ff130882ce379e80a2b8c662f3b99a54d605b0..06ee088a54c803511531b38dda6fc4cba51d276d 100644 (file)
@@ -194,9 +194,9 @@ PackageContext::PackageContext (tcu::TestContext& testCtx)
        , m_caseWrapper (DE_NULL)
 {
        const eglu::NativeDisplayFactoryRegistry&       dpyFactoryRegistry      = testCtx.getPlatform().getEGLPlatform().getNativeDisplayFactoryRegistry();
-       const std::string                                                       displayFactoryName      = testCtx.getCommandLine().getEGLDisplayType();
-       const std::string                                                       windowFactoryName       = testCtx.getCommandLine().getEGLWindowType();
-       const std::string                                                       pixmapFactoryName       = testCtx.getCommandLine().getEGLPixmapType();
+       const char* const                                                       displayFactoryName      = testCtx.getCommandLine().getEGLDisplayType();
+       const char* const                                                       windowFactoryName       = testCtx.getCommandLine().getEGLWindowType();
+       const char* const                                                       pixmapFactoryName       = testCtx.getCommandLine().getEGLPixmapType();
 
        const eglu::NativeDisplayFactory*                       displayFactory          = DE_NULL;
        const eglu::NativeWindowFactory*                        windowFactory           = DE_NULL;
@@ -208,15 +208,15 @@ PackageContext::PackageContext (tcu::TestContext& testCtx)
                throw tcu::NotSupportedError("Platform doesn't have EGL any native display factories", DE_NULL, __FILE__, __LINE__);
        }
 
-       if (displayFactoryName.empty())
+       if (!displayFactoryName)
                displayFactory = dpyFactoryRegistry.getDefaultFactory();
        else
        {
-               displayFactory = dpyFactoryRegistry.getFactoryByName(displayFactoryName.c_str());
+               displayFactory = dpyFactoryRegistry.getFactoryByName(displayFactoryName);
 
                if (!displayFactory)
                {
-                       tcu::print("ERROR: Unknown/unsupported EGL native display type '%s'\n", displayFactoryName.c_str());
+                       tcu::print("ERROR: Unknown/unsupported EGL native display type '%s'\n", displayFactoryName);
                        tcu::print("Supported EGL native display types:\n");
 
                        for (int factoryNdx = 0; factoryNdx < (int)dpyFactoryRegistry.getFactoryCount(); factoryNdx++)
@@ -227,7 +227,7 @@ PackageContext::PackageContext (tcu::TestContext& testCtx)
                                tcu::print("  %s: %s\n", name, desc);
                        }
 
-                       throw tcu::NotSupportedError(("Unknown EGL native display type '" + displayFactoryName + "'.").c_str(), DE_NULL, __FILE__, __LINE__);
+                       throw tcu::NotSupportedError(("Unknown EGL native display type '" + std::string(displayFactoryName) + "'.").c_str(), DE_NULL, __FILE__, __LINE__);
                }
        }
 
@@ -235,13 +235,13 @@ PackageContext::PackageContext (tcu::TestContext& testCtx)
 
        if (!displayFactory->getNativeWindowRegistry().empty())
        {
-               windowFactory = windowFactoryName.empty() ? displayFactory->getNativeWindowRegistry().getDefaultFactory()
-                                                                                                 : displayFactory->getNativeWindowRegistry().getFactoryByName(windowFactoryName.c_str());
+               windowFactory = windowFactoryName ? displayFactory->getNativeWindowRegistry().getFactoryByName(windowFactoryName)
+                                                                                 : displayFactory->getNativeWindowRegistry().getDefaultFactory();
 
                if (!windowFactory)
                {
-                       DE_ASSERT(!windowFactoryName.empty());
-                       tcu::print("ERROR: Unknown/unsupported EGL native window type '%s'\n", windowFactoryName.c_str());
+                       DE_ASSERT(windowFactoryName);
+                       tcu::print("ERROR: Unknown/unsupported EGL native window type '%s'\n", windowFactoryName);
                        tcu::print("Supported EGL native window types for native display '%s':\n", displayFactory->getName());
 
                        for (int factoryNdx = 0; factoryNdx < (int)displayFactory->getNativeWindowRegistry().getFactoryCount(); factoryNdx++)
@@ -252,7 +252,7 @@ PackageContext::PackageContext (tcu::TestContext& testCtx)
                                tcu::print("  %s: %s\n", name, desc);
                        }
 
-                       throw tcu::NotSupportedError(("Unknown EGL native window type '" + windowFactoryName + "'").c_str(), DE_NULL, __FILE__, __LINE__);
+                       throw tcu::NotSupportedError(("Unknown EGL native window type '" + std::string(windowFactoryName) + "'").c_str(), DE_NULL, __FILE__, __LINE__);
                }
        }
        else
@@ -260,13 +260,13 @@ PackageContext::PackageContext (tcu::TestContext& testCtx)
 
        if (!displayFactory->getNativePixmapRegistry().empty())
        {
-               pixmapFactory = pixmapFactoryName.empty() ? displayFactory->getNativePixmapRegistry().getDefaultFactory()
-                                                                                                 : displayFactory->getNativePixmapRegistry().getFactoryByName(pixmapFactoryName.c_str());
+               pixmapFactory = pixmapFactoryName ? displayFactory->getNativePixmapRegistry().getFactoryByName(pixmapFactoryName)
+                                                                                 : displayFactory->getNativePixmapRegistry().getDefaultFactory();
 
                if (!pixmapFactory)
                {
-                       DE_ASSERT(!pixmapFactoryName.empty());
-                       tcu::print("ERROR: Unknown/unsupported EGL native pixmap type '%s'\n", pixmapFactoryName.c_str());
+                       DE_ASSERT(pixmapFactoryName);
+                       tcu::print("ERROR: Unknown/unsupported EGL native pixmap type '%s'\n", pixmapFactoryName);
                        tcu::print("Supported EGL native pixmap types for native display '%s':\n", displayFactory->getName());
 
                        for (int factoryNdx = 0; factoryNdx < (int)displayFactory->getNativePixmapRegistry().getFactoryCount(); factoryNdx++)
@@ -277,7 +277,7 @@ PackageContext::PackageContext (tcu::TestContext& testCtx)
                                tcu::print("  %s: %s\n", name, desc);
                        }
 
-                       throw tcu::NotSupportedError(("Unknown EGL native pixmap type '" + pixmapFactoryName + "'").c_str(), DE_NULL, __FILE__, __LINE__);
+                       throw tcu::NotSupportedError(("Unknown EGL native pixmap type '" + std::string(pixmapFactoryName) + "'").c_str(), DE_NULL, __FILE__, __LINE__);
                }
        }
        else
index 3882c45e843a5c50e0ba0716c4532ce9e18225dc..5776cdc99d0091c0f3f46251c21aea55b5bb6a22 100644 (file)
  *//*--------------------------------------------------------------------*/
 
 #include "ditFrameworkTests.hpp"
-
 #include "tcuFloatFormat.hpp"
+#include "tcuTestLog.hpp"
+#include "tcuCommandLine.hpp"
 
 namespace dit
 {
 
+namespace
+{
+
+using std::string;
+using std::vector;
+using tcu::TestLog;
+
+struct MatchCase
+{
+       enum Expected { NO_MATCH, MATCH_GROUP, MATCH_CASE, EXPECTED_LAST };
+
+       const char*     path;
+       Expected        expected;
+};
+
+const char* getMatchCaseExpectedDesc (MatchCase::Expected expected)
+{
+       static const char* descs[] =
+       {
+               "no match",
+               "group to match",
+               "case to match"
+       };
+       return de::getSizedArrayElement<MatchCase::EXPECTED_LAST>(descs, expected);
+}
+
+class CaseListParserCase : public tcu::TestCase
+{
+public:
+       CaseListParserCase (tcu::TestContext& testCtx, const char* name, const char* caseList, const MatchCase* subCases, int numSubCases)
+               : tcu::TestCase (testCtx, name, "")
+               , m_caseList    (caseList)
+               , m_subCases    (subCases)
+               , m_numSubCases (numSubCases)
+       {
+       }
+
+       IterateResult iterate (void)
+       {
+               TestLog&                        log             = m_testCtx.getLog();
+               tcu::CommandLine        cmdLine;
+               int                                     numPass = 0;
+
+               log << TestLog::Message << "Input:\n\"" << m_caseList << "\"" << TestLog::EndMessage;
+
+               {
+                       const char* argv[] =
+                       {
+                               "deqp",
+                               "--deqp-caselist",
+                               m_caseList
+                       };
+
+                       if (!cmdLine.parse(DE_LENGTH_OF_ARRAY(argv), argv))
+                               TCU_FAIL("Failed to parse case list");
+               }
+
+               for (int subCaseNdx = 0; subCaseNdx < m_numSubCases; subCaseNdx++)
+               {
+                       const MatchCase&        curCase         = m_subCases[subCaseNdx];
+                       bool                            matchGroup;
+                       bool                            matchCase;
+
+                       log << TestLog::Message << "Checking \"" << curCase.path << "\""
+                                                                       << ", expecting " << getMatchCaseExpectedDesc(curCase.expected)
+                               << TestLog::EndMessage;
+
+                       matchGroup      = cmdLine.checkTestGroupName(curCase.path);
+                       matchCase       = cmdLine.checkTestCaseName(curCase.path);
+
+                       if ((matchGroup == (curCase.expected == MatchCase::MATCH_GROUP)) &&
+                               (matchCase      == (curCase.expected == MatchCase::MATCH_CASE)))
+                       {
+                               log << TestLog::Message << "   pass" << TestLog::EndMessage;
+                               numPass += 1;
+                       }
+                       else
+                               log << TestLog::Message << "   FAIL!" << TestLog::EndMessage;
+               }
+
+               m_testCtx.setTestResult((numPass == m_numSubCases) ? QP_TEST_RESULT_PASS        : QP_TEST_RESULT_FAIL,
+                                                               (numPass == m_numSubCases) ? "All passed"                       : "Unexpected match result");
+
+               return STOP;
+       }
+
+private:
+       const char* const                       m_caseList;
+       const MatchCase* const          m_subCases;
+       const int                                       m_numSubCases;
+};
+
+class NegativeCaseListCase : public tcu::TestCase
+{
+public:
+       NegativeCaseListCase (tcu::TestContext& testCtx, const char* name, const char* caseList)
+               : tcu::TestCase (testCtx, name, "")
+               , m_caseList    (caseList)
+       {
+       }
+
+       IterateResult iterate (void)
+       {
+               TestLog&                        log             = m_testCtx.getLog();
+               tcu::CommandLine        cmdLine;
+
+               log << TestLog::Message << "Input:\n\"" << m_caseList << "\"" << TestLog::EndMessage;
+
+               {
+                       const char* argv[] =
+                       {
+                               "deqp",
+                               "--deqp-caselist",
+                               m_caseList
+                       };
+
+                       if (cmdLine.parse(DE_LENGTH_OF_ARRAY(argv), argv))
+                               m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Parsing passed, should have failed");
+                       else
+                               m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Parsing failed as expected");
+               }
+
+               return STOP;
+       }
+
+private:
+       const char* const       m_caseList;
+};
+
+class TrieParserTests : public tcu::TestCaseGroup
+{
+public:
+       TrieParserTests (tcu::TestContext& testCtx)
+               : tcu::TestCaseGroup(testCtx, "trie", "Test case trie parser tests")
+       {
+       }
+
+       void init (void)
+       {
+               {
+                       static const char* const        caseList        = "{test}";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "test",               MatchCase::MATCH_CASE   },
+                               { "test.cd",    MatchCase::NO_MATCH             },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "single_case", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        = "{a{b}}";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",          MatchCase::MATCH_GROUP  },
+                               { "b",          MatchCase::NO_MATCH             },
+                               { "a.b",        MatchCase::MATCH_CASE   },
+                               { "a.a",        MatchCase::NO_MATCH             },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "simple_group_1", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        = "{a{b,c}}";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",          MatchCase::MATCH_GROUP  },
+                               { "b",          MatchCase::NO_MATCH             },
+                               { "a.b",        MatchCase::MATCH_CASE   },
+                               { "a.a",        MatchCase::NO_MATCH             },
+                               { "a.c",        MatchCase::MATCH_CASE   },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "simple_group_2", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        = "{a{b},c{d,e}}";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",          MatchCase::MATCH_GROUP  },
+                               { "b",          MatchCase::NO_MATCH             },
+                               { "a.b",        MatchCase::MATCH_CASE   },
+                               { "a.c",        MatchCase::NO_MATCH             },
+                               { "a.d",        MatchCase::NO_MATCH             },
+                               { "a.e",        MatchCase::NO_MATCH             },
+                               { "c",          MatchCase::MATCH_GROUP  },
+                               { "c.b",        MatchCase::NO_MATCH             },
+                               { "c.d",        MatchCase::MATCH_CASE   },
+                               { "c.e",        MatchCase::MATCH_CASE   },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "two_groups", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        = "{a,c{d,e}}";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",          MatchCase::MATCH_CASE   },
+                               { "b",          MatchCase::NO_MATCH             },
+                               { "a.b",        MatchCase::NO_MATCH             },
+                               { "a.c",        MatchCase::NO_MATCH             },
+                               { "a.d",        MatchCase::NO_MATCH             },
+                               { "a.e",        MatchCase::NO_MATCH             },
+                               { "c",          MatchCase::MATCH_GROUP  },
+                               { "c.b",        MatchCase::NO_MATCH             },
+                               { "c.d",        MatchCase::MATCH_CASE   },
+                               { "c.e",        MatchCase::MATCH_CASE   },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "case_group", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        = "{c{d,e},a}";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",          MatchCase::MATCH_CASE   },
+                               { "b",          MatchCase::NO_MATCH             },
+                               { "a.b",        MatchCase::NO_MATCH             },
+                               { "a.c",        MatchCase::NO_MATCH             },
+                               { "a.d",        MatchCase::NO_MATCH             },
+                               { "a.e",        MatchCase::NO_MATCH             },
+                               { "c",          MatchCase::MATCH_GROUP  },
+                               { "c.b",        MatchCase::NO_MATCH             },
+                               { "c.d",        MatchCase::MATCH_CASE   },
+                               { "c.e",        MatchCase::MATCH_CASE   },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "group_case", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+
+               // Negative tests
+               addChild(new NegativeCaseListCase(m_testCtx, "empty_string",                    ""));
+               addChild(new NegativeCaseListCase(m_testCtx, "empty_root",                              "{}"));
+               addChild(new NegativeCaseListCase(m_testCtx, "empty_group",                             "{test{}}"));
+               addChild(new NegativeCaseListCase(m_testCtx, "empty_group_name_1",              "{{}}"));
+               addChild(new NegativeCaseListCase(m_testCtx, "empty_group_name_2",              "{{test}}"));
+               addChild(new NegativeCaseListCase(m_testCtx, "unterminated_root_1",             "{"));
+               addChild(new NegativeCaseListCase(m_testCtx, "unterminated_root_2",             "{test"));
+               addChild(new NegativeCaseListCase(m_testCtx, "unterminated_root_3",             "{test,"));
+               addChild(new NegativeCaseListCase(m_testCtx, "unterminated_root_4",             "{test{a}"));
+               addChild(new NegativeCaseListCase(m_testCtx, "unterminated_root_5",             "{a,b"));
+               addChild(new NegativeCaseListCase(m_testCtx, "unterminated_group_1",    "{test{"));
+               addChild(new NegativeCaseListCase(m_testCtx, "unterminated_group_2",    "{test{a"));
+               addChild(new NegativeCaseListCase(m_testCtx, "unterminated_group_3",    "{test{a,"));
+               addChild(new NegativeCaseListCase(m_testCtx, "unterminated_group_4",    "{test{a,b"));
+               addChild(new NegativeCaseListCase(m_testCtx, "empty_case_name_1",               "{a,,b}"));
+               addChild(new NegativeCaseListCase(m_testCtx, "empty_case_name_2",               "{,b}"));
+               addChild(new NegativeCaseListCase(m_testCtx, "empty_case_name_3",               "{a,}"));
+               addChild(new NegativeCaseListCase(m_testCtx, "no_separator",                    "{a{b}c}"));
+               addChild(new NegativeCaseListCase(m_testCtx, "invalid_char_1",                  "{a.b}"));
+               addChild(new NegativeCaseListCase(m_testCtx, "invalid_char_2",                  "{a[]}"));
+               addChild(new NegativeCaseListCase(m_testCtx, "trailing_char_1",                 "{a}}"));
+               addChild(new NegativeCaseListCase(m_testCtx, "trailing_char_2",                 "{a}x"));
+       }
+};
+
+class ListParserTests : public tcu::TestCaseGroup
+{
+public:
+       ListParserTests (tcu::TestContext& testCtx)
+               : tcu::TestCaseGroup(testCtx, "list", "Test case list parser tests")
+       {
+       }
+
+       void init (void)
+       {
+               {
+                       static const char* const        caseList        = "test";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "test",               MatchCase::MATCH_CASE   },
+                               { "test.cd",    MatchCase::NO_MATCH             },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "single_case", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        = "a.b";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",          MatchCase::MATCH_GROUP  },
+                               { "b",          MatchCase::NO_MATCH             },
+                               { "a.b",        MatchCase::MATCH_CASE   },
+                               { "a.a",        MatchCase::NO_MATCH             },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "simple_group_1", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        = "a.b\na.c";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",          MatchCase::MATCH_GROUP  },
+                               { "b",          MatchCase::NO_MATCH             },
+                               { "a.b",        MatchCase::MATCH_CASE   },
+                               { "a.a",        MatchCase::NO_MATCH             },
+                               { "a.c",        MatchCase::MATCH_CASE   },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "simple_group_2", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        = "a.b\na.c";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",          MatchCase::MATCH_GROUP  },
+                               { "b",          MatchCase::NO_MATCH             },
+                               { "a.b",        MatchCase::MATCH_CASE   },
+                               { "a.a",        MatchCase::NO_MATCH             },
+                               { "a.c",        MatchCase::MATCH_CASE   },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "separator_ln", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        = "a.b\ra.c";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",          MatchCase::MATCH_GROUP  },
+                               { "b",          MatchCase::NO_MATCH             },
+                               { "a.b",        MatchCase::MATCH_CASE   },
+                               { "a.a",        MatchCase::NO_MATCH             },
+                               { "a.c",        MatchCase::MATCH_CASE   },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "separator_cr", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        = "a.b\r\na.c";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",          MatchCase::MATCH_GROUP  },
+                               { "b",          MatchCase::NO_MATCH             },
+                               { "a.b",        MatchCase::MATCH_CASE   },
+                               { "a.a",        MatchCase::NO_MATCH             },
+                               { "a.c",        MatchCase::MATCH_CASE   },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "separator_crlf", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        = "a.b\na.c\n";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",          MatchCase::MATCH_GROUP  },
+                               { "b",          MatchCase::NO_MATCH             },
+                               { "a.b",        MatchCase::MATCH_CASE   },
+                               { "a.a",        MatchCase::NO_MATCH             },
+                               { "a.c",        MatchCase::MATCH_CASE   },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "end_ln", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        = "a.b\na.c\r";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",          MatchCase::MATCH_GROUP  },
+                               { "b",          MatchCase::NO_MATCH             },
+                               { "a.b",        MatchCase::MATCH_CASE   },
+                               { "a.a",        MatchCase::NO_MATCH             },
+                               { "a.c",        MatchCase::MATCH_CASE   },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "end_cr", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        = "a.b\na.c\r\n";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",          MatchCase::MATCH_GROUP  },
+                               { "b",          MatchCase::NO_MATCH             },
+                               { "a.b",        MatchCase::MATCH_CASE   },
+                               { "a.a",        MatchCase::NO_MATCH             },
+                               { "a.c",        MatchCase::MATCH_CASE   },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "end_crlf", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        = "a.b\nc.d\nc.e";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",          MatchCase::MATCH_GROUP  },
+                               { "b",          MatchCase::NO_MATCH             },
+                               { "a.b",        MatchCase::MATCH_CASE   },
+                               { "a.c",        MatchCase::NO_MATCH             },
+                               { "a.d",        MatchCase::NO_MATCH             },
+                               { "a.e",        MatchCase::NO_MATCH             },
+                               { "c",          MatchCase::MATCH_GROUP  },
+                               { "c.b",        MatchCase::NO_MATCH             },
+                               { "c.d",        MatchCase::MATCH_CASE   },
+                               { "c.e",        MatchCase::MATCH_CASE   },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "two_groups", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        = "a\nc.d\nc.e";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",          MatchCase::MATCH_CASE   },
+                               { "b",          MatchCase::NO_MATCH             },
+                               { "a.b",        MatchCase::NO_MATCH             },
+                               { "a.c",        MatchCase::NO_MATCH             },
+                               { "a.d",        MatchCase::NO_MATCH             },
+                               { "a.e",        MatchCase::NO_MATCH             },
+                               { "c",          MatchCase::MATCH_GROUP  },
+                               { "c.b",        MatchCase::NO_MATCH             },
+                               { "c.d",        MatchCase::MATCH_CASE   },
+                               { "c.e",        MatchCase::MATCH_CASE   },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "case_group", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        = "c.d\nc.e\na";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",          MatchCase::MATCH_CASE   },
+                               { "b",          MatchCase::NO_MATCH             },
+                               { "a.b",        MatchCase::NO_MATCH             },
+                               { "a.c",        MatchCase::NO_MATCH             },
+                               { "a.d",        MatchCase::NO_MATCH             },
+                               { "a.e",        MatchCase::NO_MATCH             },
+                               { "c",          MatchCase::MATCH_GROUP  },
+                               { "c.b",        MatchCase::NO_MATCH             },
+                               { "c.d",        MatchCase::MATCH_CASE   },
+                               { "c.e",        MatchCase::MATCH_CASE   },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "group_case", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        = "a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.x";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",                                                                                          MatchCase::MATCH_GROUP  },
+                               { "b",                                                                                          MatchCase::NO_MATCH             },
+                               { "a.b",                                                                                        MatchCase::MATCH_GROUP  },
+                               { "a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.x",      MatchCase::MATCH_CASE   },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "long_name", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        =
+                               "a.b.c.d.e\n"
+                               "a.b.c.f\n"
+                               "x.y.z\n"
+                               "a.b.c.d.g\n"
+                               "a.b.c.x\n";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",                          MatchCase::MATCH_GROUP  },
+                               { "a.b",                        MatchCase::MATCH_GROUP  },
+                               { "a.b.c.d.e",          MatchCase::MATCH_CASE   },
+                               { "a.b.c.d.g",          MatchCase::MATCH_CASE   },
+                               { "x.y",                        MatchCase::MATCH_GROUP  },
+                               { "x.y.z",                      MatchCase::MATCH_CASE   },
+                               { "a.b.c.f",            MatchCase::MATCH_CASE   },
+                               { "a.b.c.x",            MatchCase::MATCH_CASE   },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "partial_prefix", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+               {
+                       static const char* const        caseList        =
+                               "a.a.c.d\n"
+                               "a.b.c.d\n";
+                       static const MatchCase          subCases[]      =
+                       {
+                               { "a",                          MatchCase::MATCH_GROUP  },
+                               { "a.a",                        MatchCase::MATCH_GROUP  },
+                               { "a.b.c.d",            MatchCase::MATCH_CASE   },
+                               { "a.b.c.d",            MatchCase::MATCH_CASE   },
+                       };
+                       addChild(new CaseListParserCase(m_testCtx, "reparenting", caseList, subCases, DE_LENGTH_OF_ARRAY(subCases)));
+               }
+
+               // Negative tests
+               addChild(new NegativeCaseListCase(m_testCtx, "empty_string",                    ""));
+               addChild(new NegativeCaseListCase(m_testCtx, "empty_line",                              "\n"));
+               addChild(new NegativeCaseListCase(m_testCtx, "empty_group_name",                ".test"));
+               addChild(new NegativeCaseListCase(m_testCtx, "empty_case_name",                 "test."));
+       }
+};
+
+class CaseListParserTests : public tcu::TestCaseGroup
+{
+public:
+       CaseListParserTests (tcu::TestContext& testCtx)
+               : tcu::TestCaseGroup(testCtx, "case_list_parser", "Test case list parser tests")
+       {
+       }
+
+       void init (void)
+       {
+               addChild(new TrieParserTests(m_testCtx));
+               addChild(new ListParserTests(m_testCtx));
+       }
+};
+
 class CommonFrameworkTests : public tcu::TestCaseGroup
 {
 public:
@@ -40,9 +523,12 @@ public:
        {
                addChild(new SelfCheckCase(m_testCtx, "float_format","tcu::FloatFormat_selfTest()",
                                                                   tcu::FloatFormat_selfTest));
+               addChild(new CaseListParserTests(m_testCtx));
        }
 };
 
+} // anonymous
+
 FrameworkTests::FrameworkTests (tcu::TestContext& testCtx)
        : tcu::TestCaseGroup(testCtx, "framework", "Miscellaneous framework tests")
 {