1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/misc/YamlTestcaseHelpers.h
12 #ifndef ZYPP_MISC_YAMLTESTCASEHELPERS_H
13 #define ZYPP_MISC_YAMLTESTCASEHELPERS_H
15 #include <zypp/base/LogControl.h>
16 #include "LoadTestcase.h"
17 #include "TestcaseSetupImpl.h"
19 #include <yaml-cpp/yaml.h>
21 #include <type_traits>
23 namespace yamltest::detail {
25 bool parseSetup ( const YAML::Node &setup, zypp::misc::testcase::TestcaseSetup &t, std::string *err ) {
27 auto &target = t.data();
28 MIL << "Parsing setup node " << std::endl;
29 for ( YAML::const_iterator it = setup.begin(); it != setup.end(); it++ ) {
31 const std::string &key = it->first.as<std::string>();
32 const auto &data = it->second;
34 MIL << "Found key " << key << std::endl;
36 // reads a sequence either from a file or inline, depending on the type of "data"
37 auto readListInlineOrFromFile = [&]( const auto &cb , std::string *err ) -> bool {
38 if ( data.Type() == YAML::NodeType::Sequence ) {
40 for ( const auto &node: data ) {
41 if ( !cb( node, err ) ) return false;
44 MIL << "Loaded " << cnt << " Elements inline" << std::endl;
46 const std::string &fName = data.as<std::string>();
47 MIL << "Trying to load list from file " << fName << std::endl;
49 auto doc = YAML::LoadFile( fName );
50 if ( doc.Type() != YAML::NodeType::Sequence ) {
51 if ( err ) *err = "Expected the top node to be a sequence in external file for key: ";
56 for ( const auto &node : doc ) {
57 if ( !cb( node, err ) ) return false;
60 MIL << "Loaded " << cnt << " Elements from file" << std::endl;
61 } catch ( YAML::Exception &e ) {
62 if ( err ) *err = e.what();
65 if ( err ) *err = zypp::str::Str() << "Unknown error when parsing the file for " << key;
72 if ( key == "resolverFlags" ) {
73 #define if_SolverFlag( N ) if ( data[#N] ) { target.N = data[#N].as<bool>(); }
74 if_SolverFlag( ignorealreadyrecommended ) if ( data["ignorealready"] ) { target.ignorealreadyrecommended = data["ignorealready"].as<bool>(); }
75 if_SolverFlag( onlyRequires ) if ( data["ignorerecommended"] ) { target.onlyRequires = data["ignorerecommended"].as<bool>(); }
76 if_SolverFlag( forceResolve )
78 if_SolverFlag( cleandepsOnRemove )
80 if_SolverFlag( allowDowngrade )
81 if_SolverFlag( allowNameChange )
82 if_SolverFlag( allowArchChange )
83 if_SolverFlag( allowVendorChange )
85 if_SolverFlag( dupAllowDowngrade )
86 if_SolverFlag( dupAllowNameChange )
87 if_SolverFlag( dupAllowArchChange )
88 if_SolverFlag( dupAllowVendorChange )
90 if ( data["focus"] ) {
91 target.resolverFocus = zypp::resolverFocusFromString( data["focus"].as<std::string>() );
93 } else if ( key == ("system") ) {
94 target.systemRepo = zypp::misc::testcase::RepoDataImpl {
95 zypp::misc::testcase::TestcaseRepoType::Testtags,
98 data["file"].as<std::string>()
101 else if ( key == ("hardwareInfo") ) {
102 target.hardwareInfoFile = data.as<std::string>();
104 else if ( key == ("modalias") ) {
105 bool success = readListInlineOrFromFile( [&target]( const YAML::Node &dataNode, auto ){
106 target.modaliasList.push_back( dataNode.as<std::string>() );
109 if ( !success ) return false;
111 else if ( key == ("multiversion") ) {
112 bool success = readListInlineOrFromFile( [&target]( const YAML::Node &dataNode, auto ){
113 target.multiversionSpec.insert( dataNode.as<std::string>() );
116 if ( !success ) return false;
118 else if (key == ("channels")) {
119 bool success = readListInlineOrFromFile( [&target]( const YAML::Node &dataNode, auto ){
120 std::string name = dataNode["alias"].as<std::string>();
121 std::string file = dataNode["file"].as<std::string>();
122 std::string type = dataNode["type"].as<std::string>();
125 if ( dataNode["priority"] )
126 prio = dataNode["priority"].as<unsigned>();
128 target.repos.push_back( zypp::misc::testcase::RepoDataImpl{
129 zypp::misc::testcase::TestcaseRepoType::Testtags,
136 if ( !success ) return false;
138 else if ( key == ("sources") )
140 bool success = readListInlineOrFromFile( [&target]( const YAML::Node &dataNode, auto ){
141 std::string url = dataNode["url"].as<std::string>();
142 std::string alias = dataNode["name"].as<std::string>();
143 target.repos.push_back( zypp::misc::testcase::RepoDataImpl{
144 zypp::misc::testcase::TestcaseRepoType::Url,
151 if ( !success ) return false;
153 else if ( key == ("force-install") )
155 bool success = readListInlineOrFromFile( [&target]( const YAML::Node &dataNode, auto ){
156 target.forceInstallTasks.push_back( zypp::misc::testcase::ForceInstallImpl{
157 dataNode["channel"].as<std::string>(),
158 dataNode["package"].as<std::string>(),
159 dataNode["kind"].as<std::string>()
163 if ( !success ) return false;
165 else if ( key == ("mediaid") )
167 target.show_mediaid = data.as<bool>();
169 else if ( key == ("arch") ) {
170 std::string architecture = data.as<std::string>();
171 if ( architecture.empty() ) {
172 if (err) *err = zypp::str::Str() << "Property 'arch' in setup can not be empty." << std::endl;
176 MIL << "Setting architecture to '" << architecture << "'" << std::endl;
177 target.architecture = zypp::Arch( architecture );
180 else if ( key == ("locales") )
182 bool success = readListInlineOrFromFile( [&target]( const YAML::Node &dataNode, std::string *err ){
183 zypp::Locale loc( dataNode["name"].as<std::string>() );
184 std::string fate = dataNode["fate"].as<std::string>();
186 if (err) *err = zypp::str::Str() << "Bad or missing name in locale..." << std::endl;
189 else if ( fate == "added" ) {
190 target.localesTracker.added().insert( loc );
192 else if ( fate == "removed" ) {
193 target.localesTracker.removed().insert( loc );
196 target.localesTracker.current().insert( loc );
200 if ( !success ) return false;
202 else if ( key == ("vendors") )
204 bool success = readListInlineOrFromFile( [&target]( const YAML::Node & dataNode, std::string * err ) {
205 std::vector<std::string> vlist;
206 for ( const auto & node : dataNode )
207 vlist.push_back( node.as<std::string>() );
208 if ( ! vlist.empty() )
209 target.vendorLists.push_back( std::move(vlist) );
212 if ( !success ) return false;
214 else if ( key == ("autoinst") ) {
215 bool success = readListInlineOrFromFile( [&]( const YAML::Node &dataNode, auto ){
216 target.autoinstalled.push( zypp::IdString( dataNode.as<std::string>() ).id() );
219 if ( !success ) return false;
221 else if ( key == ("systemCheck") ) {
222 target.systemCheck = data.as<std::string>();
224 else if ( key == ("setlicencebit") ) {
225 target.set_licence = data.as<bool>();
228 ERR << "Ignoring unrecognized tag '" << key << "' in setup" << std::endl;
234 template <typename T>
235 bool parseJobs ( const YAML::Node &trial, std::vector<T> &target, std::string *err );
237 template <typename T>
238 bool parseSingleJob ( const YAML::Node &jobNode, std::vector<T> &target, std::string *err ) {
240 constexpr bool isSubNode = std::is_same_v<T, std::shared_ptr<zypp::misc::testcase::TestcaseTrial::Node>>;
241 if ( jobNode["include"] ) {
243 const auto &fName = jobNode["include"].as<std::string>();
244 MIL << "Including file " << fName << std::endl;
246 auto doc = YAML::LoadFile( fName );
247 if ( !parseJobs( doc, target, err ) )
249 MIL << "Including file " << fName << "was successfull" << std::endl;
250 } catch ( YAML::Exception &e ) {
251 if ( err ) *err = e.what();
254 if ( err ) *err = zypp::str::Str() << "Unknown error when parsing the file: " << fName;
260 zypp::misc::testcase::TestcaseTrial::Node n;
261 if ( !jobNode["job"] ) {
263 auto errStr = zypp::str::Str();
264 const auto &mark = jobNode.Mark();
265 errStr << "'job' key missing from trial node.";
266 if ( !mark.is_null() ) {
267 errStr << " Line: " << mark.line << " Col: " << mark.column << " pos: " << mark.pos;
274 for ( const auto &elem : jobNode ) {
275 const std::string &key = elem.first.as<std::string>();
276 const auto &data = elem.second;
277 if ( key == "job" ) {
278 n.name() = data.as<std::string>();
279 } else if ( key == "__content") {
280 n.value() = data.as<std::string>();
282 if( data.IsScalar() ) {
283 n.properties().insert( { key, data.as<std::string>() } );
284 } if ( data.IsSequence() ) {
285 // if the type of a data field is a sequence, we treat all the elements in there
286 // as sub elements. Just like in XML you can have sub nodes its the same here
287 // the key name is ignored in those cases and can be chosen freely
288 if ( !parseJobs( data, n.children(), err ) )
290 } else if ( data.IsMap() ) {
291 // if the type of a data field is a map, we build a child node from it.
292 // Just like with sequence but a single field.
293 // The key name is ignored in those cases and can be chosen freely
294 if ( !parseSingleJob( data, n.children(), err) )
297 ERR << "Ignoring field " << key << " with unsupported type." << std::endl;
301 if constexpr ( isSubNode ) {
302 target.push_back( std::make_shared<zypp::misc::testcase::TestcaseTrial::Node>( std::move(n) ) );
304 target.push_back( std::move(n) );
309 template <typename T>
310 bool parseJobs ( const YAML::Node &trial, std::vector<T> &target, std::string *err ) {
311 for ( const auto &jobNode : trial ) {
312 if ( !parseSingleJob( jobNode, target, err ) )
318 bool parseTrial ( const YAML::Node &trial, zypp::misc::testcase::TestcaseTrial &target, std::string *err ) {
319 MIL << "Parsing trials." << std::endl;
320 return parseJobs( trial, target.nodes(), err );
324 #endif // ZYPP_MISC_YAMLTESTCASEHELPERS_H