}
}
+namespace
+{
+ std::string q2str( const PoolQuery & q_r )
+ {
+ str::Str s;
+ q_r.serialize( s.stream() );
+ return s;
+ }
+
+ template <class OutputIterator>
+ void str2q( const std::string & s_r, OutputIterator out_r )
+ {
+ std::istringstream s( s_r );
+ do {
+ PoolQuery q;
+ if ( q.recover( s ) )
+ *out_r++ = std::move(q);
+ else
+ break;
+ } while ( true );
+ }
+
+ typedef std::set<PoolQuery> Pqs;
+
+ PoolQuery str2q( const std::string & s_r )
+ {
+ Pqs ret;
+ str2q( s_r, std::insert_iterator<Pqs>( ret, ret.end() ) );
+ return *ret.begin();
+ }
+
+
+ std::string serialized( const std::string & arg_r )
+ { return "\n" + arg_r + "\n\n"; }
+
+ template <typename... Args>
+ std::string serialized( const std::string & arg_r, Args... args_r )
+ { return "\n" + arg_r + serialized( args_r... ); }
+
+
+ void testSerializeAndBack( const PoolQuery & q_r, const PoolQuery & expect_r, bool equal_r = true )
+ {
+ static unsigned i = 0;
+
+ std::string s { q2str( q_r ) };
+ PoolQuery q { str2q( s ) };
+ BOOST_CHECK_EQUAL( (q == expect_r), equal_r );
+
+ if ( ++i && (q == expect_r) != equal_r )
+ {
+ cout << "+++" << endl;
+ cout << q << endl;
+ cout << "=== " << i << " ^v SerializeAndBack == " << equal_r << endl;
+ cout << expect_r << endl;
+ cout << "---" << endl;
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(zypperLocksSerialize)
+{
+ // Fix/cleanup zypper locks (old style, new stule, complex) (bsc#1112911)
+ // As you may notice: locks (by now) ignore any arch component
+ cout << "****zypperLocksSerialize****" << endl;
+ std::string n { "n*" };
+ Rel o { Rel::EQ };
+ Edition e { "v", "r", 1 };
+ Arch a { "a" };
+
+ {
+ // old style
+ // solvable_name: n*
+ PoolQuery oldq;
+ oldq.addAttribute( sat::SolvAttr::name, n );
+ testSerializeAndBack( oldq, oldq );
+
+ { // new style
+ PoolQuery q;
+ q.addDependency( sat::SolvAttr::name, n, Rel::ANY, Edition(), Arch_empty );
+ testSerializeAndBack( q, oldq );
+ }
+ { // new style + arch rule however stays complex
+ PoolQuery q;
+ q.addDependency( sat::SolvAttr::name, n, Rel::ANY, Edition(), a );
+ testSerializeAndBack( q, oldq, false );
+ testSerializeAndBack( q, q );
+ }
+ }
+
+ {
+ // old style
+ // solvable_name: n*
+ // version: == 1:v-r
+ PoolQuery oldq;
+ oldq.addAttribute( sat::SolvAttr::name, n );
+ oldq.setEdition( e, o );
+ testSerializeAndBack( oldq, oldq );
+
+ { // new style
+ PoolQuery q;
+ q.addDependency( sat::SolvAttr::name, n, o, e, Arch_empty );
+ testSerializeAndBack( q, oldq );
+ }
+
+ { // new style + arch rule however stays complex
+ PoolQuery q;
+ q.addDependency( sat::SolvAttr::name, n, o, e, a );
+ testSerializeAndBack( q, oldq, false );
+ testSerializeAndBack( q, q );
+ }
+ }
+}
} while ( true );
+ // OLD STYLE VERSIONED LOCKS:
+ // solvable_name: kernel
+ // version: > 1
+ //
+ // NEW STYLE VERSIONED LOCKS:
+ // complex: AttrMatchData solvable:name kernel C SolvableRange\ >\ 1\ \"\"
+ // or
+ // solvable_name: kernel > 1
+ //
+ // Semantically equivalent as locks, but due to the different syntax
+ // the complex lock is wrongly handled by zypper.
+ //
+ // bsc#1112911: Unfortunately all styles are found in real-life locks-files.
+ // libzypp will try to make sure, when parsing the locks-file, that complex
+ // locks are rewritten into to OLD STYLE queries zypper can handle.
+ if ( !_pimpl->_attrs.count(SolvAttr::name) && _pimpl->_uncompiledPredicated.size() == 1 )
+ {
+ // No OLD STYLE lock for SolvAttr::name and exactly one complex lock...
+ const AttrMatchData & attrmatch { *_pimpl->_uncompiledPredicated.begin() };
+ if ( attrmatch.attr == SolvAttr::name && attrmatch.strMatcher.flags().mode() == Match::OTHER )
+ {
+ // ...for SolvAttr::name and following the global search flags.
+ // A candidate for a rewrite?
+
+ std::vector<std::string> words;
+ str::splitEscaped( attrmatch.predicateStr, std::back_inserter(words) );
+ if ( words.size() < 4 || words[3].empty() )
+ {
+ // We have _NO_ arch rule in the complex predicate, so we can simplify it.
+ //
+ // NOTE: AFAIK it's not possible to create (or have created) a complex lock
+ // with arch rule with zypper means. Nevertheless, in case such a rule made it
+ // into a locks file, it's better to have a strange looking 'zypper locks' list
+ // than to lock the wrong packages.
+ // (and remember that you can't use "addAttribute( SolvAttr::arch, ... )" because
+ // attributes are `OR`ed)
+
+ // kind
+ if ( attrmatch.kindPredicate )
+ {
+ _pimpl->_kinds.clear(); // an explicit kind overwrites any global one
+ addKind( attrmatch.kindPredicate );
+ }
+
+ // name
+ addAttribute( SolvAttr::name, attrmatch.strMatcher.searchstring() );
+
+ // edition
+ std::vector<std::string> words;
+ str::splitEscaped( attrmatch.predicateStr, std::back_inserter(words) );
+ if ( ! words.empty() )
+ {
+ if ( words[0] == "EditionRange" || words[0] == "SolvableRange" )
+ {
+ setEdition( Edition(words[2]), Rel(words[1]) );
+ }
+ }
+
+ // finally remove the complex lock
+ _pimpl->_uncompiledPredicated.clear();
+ }
+ }
+ }
+
return finded_something;
}