Imported Upstream version 17.25.3
[platform/upstream/libzypp.git] / zypp / misc / LoadTestcase.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file  zypp/misc/LoadTestcase.cc
10  *
11 */
12 #include "LoadTestcase.h"
13 #include "HelixHelpers.h"
14 #include "YamlTestcaseHelpers.h"
15 #include <zypp/PathInfo.h>
16 #include <zypp/base/LogControl.h>
17
18 namespace zypp::misc::testcase {
19
20   static const std::string helixControlFile = "solver-test.xml";
21   static const std::string yamlControlFile  = "zypp-control.yaml";
22
23   struct LoadTestcase::Impl {
24     TestcaseSetup _setup;
25     std::vector<TestcaseTrial> _trials;
26
27     bool loadHelix (const Pathname &filename, std::string *err);
28
29     bool loadYaml  ( const Pathname &path, std::string *err);
30   };
31
32
33   struct TestcaseTrial::Impl
34   {
35     std::vector<Node> nodes;
36     Impl *clone() const { return new Impl(*this); }
37   };
38
39   struct TestcaseTrial::Node::Impl
40   {
41     std::string name;
42     std::string value;
43     std::map<std::string, std::string> properties;
44     std::vector<std::shared_ptr<Node>> children;
45     Impl *clone() const { return new Impl(*this); }
46   };
47
48   TestcaseTrial::TestcaseTrial() : _pimpl ( new Impl() )
49   { }
50
51   TestcaseTrial::~TestcaseTrial()
52   { }
53
54   const std::vector<TestcaseTrial::Node> &TestcaseTrial::nodes() const
55   { return _pimpl->nodes; }
56
57   std::vector<TestcaseTrial::Node> &TestcaseTrial::nodes()
58   { return _pimpl->nodes; }
59
60   TestcaseTrial::Node::Node() : _pimpl ( new Impl() )
61   { }
62
63   TestcaseTrial::Node::~Node()
64   { }
65
66   const std::string &TestcaseTrial::Node::name() const
67   { return _pimpl->name; }
68
69   std::string &TestcaseTrial::Node::name()
70   { return _pimpl->name; }
71
72   const std::string &TestcaseTrial::Node::value() const
73   { return _pimpl->value; }
74
75   std::string &TestcaseTrial::Node::value()
76   { return _pimpl->value; }
77
78   const std::string &TestcaseTrial::Node::getProp( const std::string &name, const std::string &def ) const
79   {
80     if ( _pimpl->properties.find( name) == _pimpl->properties.end() )
81       return def;
82     return _pimpl->properties.at( name );
83   }
84
85   const std::map<std::string, std::string> &TestcaseTrial::Node::properties() const
86   { return _pimpl->properties; }
87
88   std::map<std::string, std::string> &TestcaseTrial::Node::properties()
89   { return _pimpl->properties; }
90
91   const std::vector<std::shared_ptr<TestcaseTrial::Node> > &TestcaseTrial::Node::children() const
92   { return _pimpl->children; }
93
94   std::vector<std::shared_ptr<TestcaseTrial::Node> > &TestcaseTrial::Node::children()
95   { return _pimpl->children; }
96
97   bool LoadTestcase::Impl::loadHelix(const zypp::filesystem::Pathname &filename, std::string *err)
98   {
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 << "'");
102       return false;
103     }
104
105
106     auto root = helix::detail::XmlNode (xmlDocGetRootElement (xml_doc));
107
108     DBG << "Parsing file '" << filename << "'" << std::endl;
109
110     if (!root.equals("test")) {
111       if ( err ) *err = (str::Str() << "Node not 'test' in parse_xml_test():" << root.name() << "'");
112       return false;
113     }
114
115     bool setupDone = false;
116     auto node = root.children();
117     while (node) {
118       if (node->type() == XML_ELEMENT_NODE) {
119         if (node->equals( "setup" )) {
120           if ( setupDone ) {
121             if ( err ) *err = "Multiple setup tags found, this is not supported";
122             return false;
123           }
124           setupDone = true;
125           if ( !helix::detail::parseSetup( *node, _setup, err ) )
126             return false;
127
128         } else if (node->equals( "trial" )) {
129           if ( !setupDone ) {
130             if ( err ) *err = "Any trials must be preceeded by the setup!";
131             return false;
132           }
133           TestcaseTrial trial;
134           if ( !helix::detail::parseTrial( *node, trial, err ) )
135             return false;
136           _trials.push_back( trial );
137         } else {
138           ERR << "Unknown tag '" << node->name() << "' in test" << std::endl;
139         }
140       }
141       node = ( node->next() );
142     }
143     xmlFreeDoc (xml_doc);
144     return true;
145   }
146
147   bool LoadTestcase::Impl::loadYaml(const zypp::filesystem::Pathname &path, std::string *err)
148   {
149     DBG << "Parsing file '" << path << "'" << std::endl;
150
151     const auto makeError = [&]( const std::string_view &errStr ){
152       if ( err ) *err = errStr;
153       return false;
154     };
155
156     YAML::Node control;
157     try {
158       control = YAML::LoadFile( path.asString() );
159
160       if ( control.Type() != YAML::NodeType::Map )
161         return makeError("Root node must be of type Map.");
162
163       const auto &setup = control["setup"];
164       if ( !setup )
165         return makeError("The 'setup' section is required.");
166
167       if ( !yamltest::detail::parseSetup( setup, _setup, err) )
168         return false;
169
170       const auto &trials = control["trials"];
171       if ( !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.");
175
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.");
180
181         if ( !yamltest::detail::parseTrial( trial["trial"], t, err) )
182           return false;
183         _trials.push_back( t );
184       }
185     } catch ( YAML::Exception &e ) {
186       if ( err ) *err = e.what();
187       return false;
188     } catch ( ... )  {
189       if ( err ) *err = "Unknown error when parsing the control file";
190       return false;
191     }
192     return true;
193   }
194
195   LoadTestcase::LoadTestcase() : _pimpl( new Impl() )
196   { }
197
198   LoadTestcase::~LoadTestcase()
199   { }
200
201   bool LoadTestcase::loadTestcaseAt(const zypp::filesystem::Pathname &path, std::string *err)
202   {
203     const auto t = testcaseTypeAt( path );
204     if ( t == LoadTestcase::None ) {
205       if ( err ) *err = "Unsopported or no testcase in directory";
206       return false;
207     }
208
209     // reset everything
210     _pimpl.reset( new Impl() );
211     _pimpl->_setup.data().globalPath = path;
212
213     switch (t) {
214       case LoadTestcase::Helix:
215         return _pimpl->loadHelix( path / helixControlFile, err );
216       case LoadTestcase::Yaml:
217         return _pimpl->loadYaml( path / yamlControlFile, err );
218       default:
219         return false;
220     }
221   }
222
223   LoadTestcase::Type LoadTestcase::testcaseTypeAt(const zypp::filesystem::Pathname &path)
224   {
225     if ( filesystem::PathInfo( path / helixControlFile ).isFile() ) {
226       return LoadTestcase::Helix;
227     } else if ( filesystem::PathInfo( path / yamlControlFile ).isFile() ) {
228       return LoadTestcase::Yaml;
229     }
230     return LoadTestcase::None;
231   }
232
233   const TestcaseSetup &LoadTestcase::setupInfo() const
234   {
235     return _pimpl->_setup;
236   }
237
238   const std::vector<TestcaseTrial> &LoadTestcase::trialInfo() const
239   {
240     return _pimpl->_trials;
241   }
242
243 }