1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/misc/LoadTestcase.cc
12 #include "LoadTestcase.h"
13 #include "HelixHelpers.h"
14 #include "YamlTestcaseHelpers.h"
15 #include <zypp/PathInfo.h>
16 #include <zypp/base/LogControl.h>
18 namespace zypp::misc::testcase {
20 static const std::string helixControlFile = "solver-test.xml";
21 static const std::string yamlControlFile = "zypp-control.yaml";
23 struct LoadTestcase::Impl {
25 std::vector<TestcaseTrial> _trials;
27 bool loadHelix (const Pathname &filename, std::string *err);
29 bool loadYaml ( const Pathname &path, std::string *err);
33 struct TestcaseTrial::Impl
35 std::vector<Node> nodes;
36 Impl *clone() const { return new Impl(*this); }
39 struct TestcaseTrial::Node::Impl
43 std::map<std::string, std::string> properties;
44 std::vector<std::shared_ptr<Node>> children;
45 Impl *clone() const { return new Impl(*this); }
48 TestcaseTrial::TestcaseTrial() : _pimpl ( new Impl() )
51 TestcaseTrial::~TestcaseTrial()
54 const std::vector<TestcaseTrial::Node> &TestcaseTrial::nodes() const
55 { return _pimpl->nodes; }
57 std::vector<TestcaseTrial::Node> &TestcaseTrial::nodes()
58 { return _pimpl->nodes; }
60 TestcaseTrial::Node::Node() : _pimpl ( new Impl() )
63 TestcaseTrial::Node::~Node()
66 const std::string &TestcaseTrial::Node::name() const
67 { return _pimpl->name; }
69 std::string &TestcaseTrial::Node::name()
70 { return _pimpl->name; }
72 const std::string &TestcaseTrial::Node::value() const
73 { return _pimpl->value; }
75 std::string &TestcaseTrial::Node::value()
76 { return _pimpl->value; }
78 const std::string &TestcaseTrial::Node::getProp( const std::string &name, const std::string &def ) const
80 if ( _pimpl->properties.find( name) == _pimpl->properties.end() )
82 return _pimpl->properties.at( name );
85 const std::map<std::string, std::string> &TestcaseTrial::Node::properties() const
86 { return _pimpl->properties; }
88 std::map<std::string, std::string> &TestcaseTrial::Node::properties()
89 { return _pimpl->properties; }
91 const std::vector<std::shared_ptr<TestcaseTrial::Node> > &TestcaseTrial::Node::children() const
92 { return _pimpl->children; }
94 std::vector<std::shared_ptr<TestcaseTrial::Node> > &TestcaseTrial::Node::children()
95 { return _pimpl->children; }
97 bool LoadTestcase::Impl::loadHelix(const zypp::filesystem::Pathname &filename, std::string *err)
99 xmlDocPtr xml_doc = xmlParseFile ( filename.c_str() );
100 if (xml_doc == NULL) {
101 if ( err ) *err = (str::Str() << "Can't parse test file '" << filename << "'");
106 auto root = helix::detail::XmlNode (xmlDocGetRootElement (xml_doc));
108 DBG << "Parsing file '" << filename << "'" << std::endl;
110 if (!root.equals("test")) {
111 if ( err ) *err = (str::Str() << "Node not 'test' in parse_xml_test():" << root.name() << "'");
115 bool setupDone = false;
116 auto node = root.children();
118 if (node->type() == XML_ELEMENT_NODE) {
119 if (node->equals( "setup" )) {
121 if ( err ) *err = "Multiple setup tags found, this is not supported";
125 if ( !helix::detail::parseSetup( *node, _setup, err ) )
128 } else if (node->equals( "trial" )) {
130 if ( err ) *err = "Any trials must be preceeded by the setup!";
134 if ( !helix::detail::parseTrial( *node, trial, err ) )
136 _trials.push_back( trial );
138 ERR << "Unknown tag '" << node->name() << "' in test" << std::endl;
141 node = ( node->next() );
143 xmlFreeDoc (xml_doc);
147 bool LoadTestcase::Impl::loadYaml(const zypp::filesystem::Pathname &path, std::string *err)
149 DBG << "Parsing file '" << path << "'" << std::endl;
151 const auto makeError = [&]( const std::string_view &errStr ){
152 if ( err ) *err = errStr;
158 control = YAML::LoadFile( path.asString() );
160 if ( control.Type() != YAML::NodeType::Map )
161 return makeError("Root node must be of type Map.");
163 const auto &setup = control["setup"];
165 return makeError("The 'setup' section is required.");
167 if ( !yamltest::detail::parseSetup( setup, _setup, err) )
170 const auto &trials = control["trials"];
172 return makeError("The 'trials' section is required.");
173 if ( trials.Type() != YAML::NodeType::Sequence )
174 return makeError("The 'trials' section must be of type Sequence.");
176 for ( const auto &trial : trials ) {
177 zypp::misc::testcase::TestcaseTrial t;
178 if ( !trial["trial"] )
179 return makeError("Every element in the trials sequence needs to have the 'trial' key.");
181 if ( !yamltest::detail::parseTrial( trial["trial"], t, err) )
183 _trials.push_back( t );
185 } catch ( YAML::Exception &e ) {
186 if ( err ) *err = e.what();
189 if ( err ) *err = "Unknown error when parsing the control file";
195 LoadTestcase::LoadTestcase() : _pimpl( new Impl() )
198 LoadTestcase::~LoadTestcase()
201 bool LoadTestcase::loadTestcaseAt(const zypp::filesystem::Pathname &path, std::string *err)
203 const auto t = testcaseTypeAt( path );
204 if ( t == LoadTestcase::None ) {
205 if ( err ) *err = "Unsopported or no testcase in directory";
210 _pimpl.reset( new Impl() );
211 _pimpl->_setup.data().globalPath = path;
214 case LoadTestcase::Helix:
215 return _pimpl->loadHelix( path / helixControlFile, err );
216 case LoadTestcase::Yaml:
217 return _pimpl->loadYaml( path / yamlControlFile, err );
223 LoadTestcase::Type LoadTestcase::testcaseTypeAt(const zypp::filesystem::Pathname &path)
225 if ( filesystem::PathInfo( path / helixControlFile ).isFile() ) {
226 return LoadTestcase::Helix;
227 } else if ( filesystem::PathInfo( path / yamlControlFile ).isFile() ) {
228 return LoadTestcase::Yaml;
230 return LoadTestcase::None;
233 const TestcaseSetup &LoadTestcase::setupInfo() const
235 return _pimpl->_setup;
238 const std::vector<TestcaseTrial> &LoadTestcase::trialInfo() const
240 return _pimpl->_trials;