1 #include <boost/test/auto_unit_test.hpp>
3 #include "zypp/CpeId.h"
8 using zypp::SetCompare;
9 using zypp::SetRelation;
11 typedef CpeId::Value Value;
13 ///////////////////////////////////////////////////////////////////
14 /// Symmetric attribute compare if wildcards are involved!
15 /// The specs define any comarison with a wildcarded attribute as
16 /// target to return \c uncomparable:
18 /// wildcardfree <=> wildcarded ==> uncomparable,
19 /// wildcarded <=> wildcardfree ==> superset or disjoint
21 /// But a symmetric result is much more intuitive:
23 /// wildcardfree <=> wildcarded ==> subset or disjoint
24 /// wildcarded <=> wildcardfree ==> superset or disjoint
26 ///////////////////////////////////////////////////////////////////
27 #define WFN_STRICT_SPEC 0
29 #define defVALUE(N,S) \
30 const std::string N##Str( S ); \
33 defVALUE( wildcardfree, "STrv\\*al\\?" ); // '\?' quoted?
34 const std::string wildcardfreeUri( "STrv%2aal%3f" );
35 const std::string wildcardfreeFs( "STrv\\*al\\?" );
37 defVALUE( wildcardfree2, "stRV\\*al\\\\\\?" ); // '\\\?' backslash, quoted?
39 defVALUE( wildcarded, "strv\\*AL?" ); // '?' ?
40 const std::string wildcardedUri( "strv%2aAL%01" );
41 const std::string wildcardedFs( "strv\\*AL?" );
43 defVALUE( wildcarded2, "strv\\*AL\\\\?" ); // '\\?' backslash, ?
47 BOOST_AUTO_TEST_CASE(cpeid_value_ANY)
49 for ( const auto & c : { Value(), Value(nullptr), Value("*"), Value::ANY } )
51 BOOST_CHECK( c.isANY() );
52 BOOST_CHECK( ! c.isNA() );
53 BOOST_CHECK( c.isLogical() );
54 BOOST_CHECK( ! c.isString() );
55 BOOST_CHECK( c == Value::ANY );
56 BOOST_CHECK( c == nullptr ); // ANY
57 BOOST_CHECK( c != Value::NA );
58 BOOST_CHECK( c != wildcardfree );
59 BOOST_CHECK( c != wildcarded );
60 BOOST_CHECK( ! c.isWildcardfree() );
61 BOOST_CHECK( ! c.isWildcarded() );
62 BOOST_CHECK_EQUAL( c.asFs(), "*" );
63 BOOST_CHECK_EQUAL( c.asUri(), "" );
64 BOOST_CHECK_EQUAL( c.asWfn(), "*" );
65 BOOST_CHECK_EQUAL( c.asString(), c.asWfn() );
69 BOOST_AUTO_TEST_CASE(cpeid_value_NA)
71 for ( const auto & c : { Value(""), Value::NA } )
73 BOOST_CHECK( ! c.isANY() );
74 BOOST_CHECK( c.isNA() );
75 BOOST_CHECK( c.isLogical() );
76 BOOST_CHECK( ! c.isString() );
77 BOOST_CHECK( c != Value::ANY );
78 BOOST_CHECK( c == Value::NA );
79 BOOST_CHECK( c == std::string() ); // NA
80 BOOST_CHECK( c == "" ); // NA
81 BOOST_CHECK( c != wildcardfree );
82 BOOST_CHECK( c != wildcarded );
83 BOOST_CHECK( ! c.isWildcardfree() );
84 BOOST_CHECK( ! c.isWildcarded() );
85 BOOST_CHECK_EQUAL( c.asFs(), "-" );
86 BOOST_CHECK_EQUAL( c.asUri(), "-" );
87 BOOST_CHECK_EQUAL( c.asWfn(), "" );
88 BOOST_CHECK_EQUAL( c.asString(), c.asWfn() );
92 BOOST_AUTO_TEST_CASE(cpeid_value_string_wildcardfree)
94 for ( const auto & c : { wildcardfree } )
96 BOOST_CHECK( ! c.isANY() );
97 BOOST_CHECK( ! c.isNA() );
98 BOOST_CHECK( ! c.isLogical() );
99 BOOST_CHECK( c.isString() );
100 BOOST_CHECK( c != Value::ANY );
101 BOOST_CHECK( c != Value::NA );
102 BOOST_CHECK( c == wildcardfree );
103 BOOST_CHECK( c == wildcardfreeStr );
104 BOOST_CHECK( c == wildcardfreeStr.c_str() );
105 BOOST_CHECK( c != wildcarded );
106 BOOST_CHECK( c.isWildcardfree() );
107 BOOST_CHECK( ! c.isWildcarded() );
108 BOOST_CHECK_EQUAL( c.asFs(), wildcardfreeFs );
109 BOOST_CHECK_EQUAL( c.asUri(), wildcardfreeUri );
110 BOOST_CHECK_EQUAL( c.asWfn(), wildcardfreeStr );
111 BOOST_CHECK_EQUAL( c.asString(), c.asWfn() );
114 BOOST_CHECK( wildcardfree2 == wildcardfree2 );
115 BOOST_CHECK( wildcardfree2 != wildcardfree );
116 BOOST_CHECK( wildcardfree2 != wildcarded );
117 BOOST_CHECK( wildcardfree2.isWildcardfree() );
118 BOOST_CHECK( ! wildcardfree2.isWildcarded() );
121 BOOST_AUTO_TEST_CASE(cpeid_value_string_wildcarded)
123 for ( const auto & c : { wildcarded } )
125 BOOST_CHECK( ! c.isANY() );
126 BOOST_CHECK( ! c.isNA() );
127 BOOST_CHECK( ! c.isLogical() );
128 BOOST_CHECK( c.isString() );
129 BOOST_CHECK( c != Value::ANY );
130 BOOST_CHECK( c != Value::NA );
131 BOOST_CHECK( c != wildcardfree );
133 BOOST_CHECK( c != wildcarded ); // !!! According to the CPE Name Matching Specification Version 2.3
134 // unquoted wildcard characters yield an undefined result (not ==).
136 BOOST_CHECK( c == wildcarded );
138 BOOST_CHECK( ! c.isWildcardfree() );
139 BOOST_CHECK( c.isWildcarded() );
140 BOOST_CHECK_EQUAL( c.asFs(), wildcardedFs );
141 BOOST_CHECK_EQUAL( c.asUri(), wildcardedUri );
142 BOOST_CHECK_EQUAL( c.asWfn(), wildcardedStr );
143 BOOST_CHECK_EQUAL( c.asString(), c.asWfn() );
147 BOOST_CHECK( wildcarded2 != wildcarded2 ); // unquoted wildcard characters yield an undefined result (not ==).
149 BOOST_CHECK( wildcarded2 == wildcarded2 );
151 BOOST_CHECK( wildcarded2 != wildcardfree );
152 BOOST_CHECK( wildcarded2 != wildcarded );
153 BOOST_CHECK( ! wildcarded2.isWildcardfree() );
154 BOOST_CHECK( wildcarded2.isWildcarded() );
159 BOOST_AUTO_TEST_CASE(cpeid_value_valid)
161 static const char *const hdig = "0123456789abcdef";
163 for ( char ch = 0; ch < CHAR_MAX; ++ch )
165 // cout << "==== " << unsigned(ch) << endl;
166 char qchstr[] = { '\\', ch, '\0' };
167 std::string chstr( qchstr+1 );
168 char pchstr[] = { '%', hdig[(unsigned char)(ch)/16], hdig[(unsigned char)(ch)%16], '\0' };
172 BOOST_CHECK( Value( chstr ).isNA() );
173 BOOST_CHECK_THROW( (Value( chstr, Value::fsFormat )), std::invalid_argument );
174 BOOST_CHECK( Value( chstr, Value::uriFormat ).isANY() );
176 else if ( ch <= ' ' || '~' < ch )
178 BOOST_CHECK_THROW( (Value( chstr )), std::invalid_argument );
179 BOOST_CHECK_THROW( (Value( chstr, Value::fsFormat )), std::invalid_argument );
180 BOOST_CHECK_THROW( (Value( chstr, Value::uriFormat )), std::invalid_argument );
182 else if ( ( '0' <= ch && ch <= '9' )
183 || ( 'A' <= ch && ch <= 'Z' )
184 || ( 'a' <= ch && ch <= 'z' )
187 BOOST_CHECK( Value( chstr ).isString() );
188 BOOST_CHECK( Value( chstr, Value::fsFormat ).isString() );
189 BOOST_CHECK( Value( chstr, Value::uriFormat ).isString() );
191 BOOST_CHECK_THROW( (Value( qchstr )), std::invalid_argument );
192 BOOST_CHECK( Value( qchstr, Value::fsFormat ).isString() );
193 BOOST_CHECK( Value( qchstr, Value::uriFormat ).isString() );
195 BOOST_CHECK( Value( pchstr, Value::uriFormat ).isString() );
197 else if ( ch == '*' )
199 BOOST_CHECK( Value( chstr ).isANY() );
200 BOOST_CHECK( Value( chstr, Value::fsFormat ).isANY() );
201 BOOST_CHECK( Value( chstr, Value::uriFormat ).isString() );
203 BOOST_CHECK( Value( qchstr ).isString() );
204 BOOST_CHECK( Value( qchstr, Value::fsFormat ).isString() );
205 BOOST_CHECK( Value( qchstr, Value::uriFormat ).isString() );
207 BOOST_CHECK( Value( pchstr, Value::uriFormat ).isString() );
209 else if ( ch == '?' )
211 BOOST_CHECK( Value( chstr ).isString() );
212 BOOST_CHECK( Value( chstr, Value::fsFormat ).isString() );
213 BOOST_CHECK( Value( chstr, Value::uriFormat ).isString() );
215 BOOST_CHECK( Value( qchstr ).isString() );
216 BOOST_CHECK( Value( qchstr, Value::fsFormat ).isString() );
217 BOOST_CHECK( Value( qchstr, Value::uriFormat ).isString() );
219 BOOST_CHECK( Value( pchstr, Value::uriFormat ).isString() );
221 else if ( ch == '-' )
223 BOOST_CHECK_THROW( (Value( chstr )), std::invalid_argument );
224 BOOST_CHECK( Value( chstr, Value::fsFormat ).isNA() );
225 BOOST_CHECK( Value( chstr, Value::uriFormat ).isNA() );
227 BOOST_CHECK_THROW( (Value( qchstr )), std::invalid_argument );
228 BOOST_CHECK( Value( qchstr, Value::fsFormat ).isString() );
229 BOOST_CHECK( Value( qchstr, Value::uriFormat ).isString() );
231 BOOST_CHECK( Value( pchstr, Value::uriFormat ).isString() );
233 else if ( ch == '\\' )
235 BOOST_CHECK_THROW( (Value( chstr )), std::invalid_argument );
236 BOOST_CHECK_THROW( (Value( chstr, Value::fsFormat )), std::invalid_argument );
237 BOOST_CHECK( (Value( chstr, Value::uriFormat )).isString() );
239 BOOST_CHECK( Value( qchstr ).isString() );
240 BOOST_CHECK( Value( qchstr, Value::fsFormat ).isString() );
241 BOOST_CHECK( Value( qchstr, Value::uriFormat ).isString() );
243 BOOST_CHECK( Value( pchstr, Value::uriFormat ).isString() );
247 BOOST_CHECK_THROW( (Value( chstr )), std::invalid_argument );
248 Value f( chstr, Value::fsFormat );
249 BOOST_CHECK( f.isString() );
250 Value u( chstr, Value::uriFormat );
251 BOOST_CHECK( u.isString() );
253 BOOST_CHECK_EQUAL( f.asString(), u.asString() );
256 BOOST_CHECK_EQUAL( f.asFs(), chstr );
257 BOOST_CHECK_EQUAL( f.asUri(), chstr );
261 BOOST_CHECK_EQUAL( f.asFs(), qchstr );
262 BOOST_CHECK_EQUAL( f.asUri(), pchstr );
265 BOOST_CHECK( Value( qchstr ).isString() );
266 BOOST_CHECK( Value( qchstr, Value::fsFormat ).isString() );
267 BOOST_CHECK( Value( qchstr, Value::uriFormat ).isString() );
269 BOOST_CHECK( Value( pchstr, Value::uriFormat ).isString() );
273 BOOST_CHECK_THROW( Value( "\\!\\a\\-\\_\\.\\!" ), std::invalid_argument );
274 BOOST_CHECK_EQUAL( Value( "\\!\\a\\-\\_\\.\\!", Value::fsFormat ).asFs(), "\\!a-_.\\!" );
277 BOOST_AUTO_TEST_CASE(cpeid_type_checks)
279 for ( const auto & c : { Value::ANY, Value::NA, wildcardfree, wildcarded } )
281 BOOST_CHECK_EQUAL( c.isANY(), c.type() == Value::Type::ANY );
282 BOOST_CHECK_EQUAL( c.isNA(), c.type() == Value::Type::NA );
283 BOOST_CHECK_EQUAL( c.isWildcardfree(), c.type() == Value::Type::wildcardfree );
284 BOOST_CHECK_EQUAL( c.isWildcarded(), c.type() == Value::Type::wildcarded );
285 BOOST_CHECK_EQUAL( c.isLogical(), c.isLogical( c.type() ) );
286 BOOST_CHECK_EQUAL( c.isString(), c.isString( c.type() ) );
287 BOOST_CHECK_EQUAL( c.isLogical(), ! c.isString() );
291 BOOST_AUTO_TEST_CASE(cpeid_compare)
293 BOOST_CHECK( compare( Value::ANY, Value::ANY, SetCompare::equal ) );
294 BOOST_CHECK( compare( Value::ANY, Value::NA, SetCompare::properSuperset ) );
295 BOOST_CHECK( compare( Value::ANY, wildcardfree, SetCompare::properSuperset ) );
297 BOOST_CHECK( compare( Value::ANY, wildcarded, SetCompare::uncomparable ) );
299 BOOST_CHECK( compare( Value::ANY, wildcarded, SetCompare::properSuperset ) );
302 BOOST_CHECK( compare( Value::NA, Value::ANY, SetCompare::properSubset ) );
303 BOOST_CHECK( compare( Value::NA, Value::NA, SetCompare::equal ) );
304 BOOST_CHECK( compare( Value::NA, wildcardfree, SetCompare::disjoint ) );
306 BOOST_CHECK( compare( Value::NA, wildcarded, SetCompare::uncomparable ) );
308 BOOST_CHECK( compare( Value::NA, wildcarded, SetCompare::disjoint ) );
311 BOOST_CHECK( compare( wildcardfree, Value::ANY, SetCompare::properSubset ) );
312 BOOST_CHECK( compare( wildcardfree, Value::NA, SetCompare::disjoint ) );
313 //BOOST_CHECK( compare( wildcardfree, wildcardfree, _NeedsCloserLook, // equal or disjoint
314 BOOST_CHECK( compare( wildcardfree, wildcardfree, SetCompare::equal ) );
315 BOOST_CHECK( compare( wildcardfree, wildcardfree2, SetCompare::disjoint ) );
317 BOOST_CHECK( compare( wildcardfree, wildcarded, SetCompare::uncomparable ) );
319 //BOOST_CHECK( compare( wildcardfree, wildcarded, _NeedsCloserLook, // subset or disjoint
320 BOOST_CHECK( compare( wildcardfree, wildcarded, SetCompare::properSubset ) );
321 BOOST_CHECK( compare( wildcardfree, wildcarded2, SetCompare::disjoint ) );
324 BOOST_CHECK( compare( wildcarded, Value::ANY, SetCompare::properSubset ) );
325 BOOST_CHECK( compare( wildcarded, Value::NA, SetCompare::disjoint ) );
326 //BOOST_CHECK( compare( wildcarded, wildcardfree, _NeedsCloserLook, // superset or disjoint
327 BOOST_CHECK( compare( wildcarded, wildcardfree, SetCompare::properSuperset ) );
328 BOOST_CHECK( compare( wildcarded, wildcardfree2, SetCompare::disjoint ) );
330 BOOST_CHECK( compare( wildcarded, wildcarded, SetCompare::uncomparable ) );
332 //BOOST_CHECK( compare( wildcarded, wildcarded, _NeedsCloserLook, // equal or uncomparable
333 BOOST_CHECK( compare( wildcarded, wildcarded, SetCompare::equal ) );
334 BOOST_CHECK( compare( wildcarded, wildcarded2, SetCompare::uncomparable ) );
339 BOOST_AUTO_TEST_CASE(cpeid_value_string_wildcard)
341 for ( const auto & c : { Value( "a" ), Value( "\\*" ), Value( "\\?" ) } )
343 BOOST_CHECK( c.isWildcardfree() );
344 BOOST_CHECK( !c.isWildcarded() );
347 for ( const auto & c : { Value( "*\\*" ), Value( "\\**" ), Value( "?" ), Value( "??\\?" ), Value( "\\???" ) } )
349 BOOST_CHECK( !c.isWildcardfree() );
350 BOOST_CHECK( c.isWildcarded() );
354 ///////////////////////////////////////////////////////////////////
355 ///////////////////////////////////////////////////////////////////
357 BOOST_AUTO_TEST_CASE(cpeid_basics)
359 BOOST_CHECK_THROW( CpeId( "malformed" ), std::invalid_argument );
360 CpeId none( "malformed", CpeId::noThrow );
361 BOOST_CHECK_EQUAL( CpeId::NoThrowType::lastMalformed, "malformed" );
362 CpeId( "", CpeId::noThrow );
363 BOOST_CHECK_EQUAL( CpeId::NoThrowType::lastMalformed, "" );
365 for ( const auto & c : { CpeId(), CpeId( nullptr ), CpeId( "" ), CpeId( std::string() ), CpeId( "cpe:2.3:" ), CpeId( "cpe:/" ) } )
367 BOOST_CHECK( ! c ); // evaluate false in boolean context
368 BOOST_CHECK_EQUAL( c.asString(), c.asFs() );
369 BOOST_CHECK_EQUAL( c.asFs(), "cpe:2.3:*:*:*:*:*:*:*:*:*:*:*" );
370 BOOST_CHECK_EQUAL( c.asUri(), "cpe:/" );
371 BOOST_CHECK_EQUAL( c.asWfn(), "wfn:[]" );
372 BOOST_CHECK_EQUAL( c, none ); // matching!!
375 for ( const auto & c : { CpeId( "cpe:/o:sle" ), CpeId( "cpe:/o:*" ) } )
377 BOOST_CHECK( c ); // evaluate true in boolean context
378 BOOST_CHECK( ! c.asString().empty() );// empty string rep
379 BOOST_CHECK_EQUAL( c, c ); // matching!!
383 void testStrconv( const std::string & fs, const std::string & uri, const std::string & wfn )
386 CpeId fromURI( uri );
388 BOOST_CHECK_EQUAL( fromFS, fromURI );
390 for ( const auto & c : { fromFS, fromURI } )
392 BOOST_CHECK_EQUAL( c.asFs(), fs );
393 BOOST_CHECK_EQUAL( c.asUri(), uri );
394 BOOST_CHECK_EQUAL( c.asWfn(), wfn );
398 BOOST_AUTO_TEST_CASE(cpeid_strconv)
400 // colon embedded in product value
401 testStrconv ( "cpe:2.3:a:opensuse:lib\\:zypp:14.16.0:beta:*:*:*:*:*:-",
402 "cpe:/a:opensuse:lib%3azypp:14.16.0:beta:~~~~~-",
403 "wfn:[part=\"a\",vendor=\"opensuse\",product=\"lib\\:zypp\",version=\"14\\.16\\.0\",update=\"beta\",other=NA]" );
405 testStrconv ( "cpe:2.3:a:hp:insight_diagnostics:7.4.0.1570:-:*:*:online:win2003:x64:*",
406 "cpe:/a:hp:insight_diagnostics:7.4.0.1570:-:~~online~win2003~x64~",
407 "wfn:[part=\"a\",vendor=\"hp\",product=\"insight_diagnostics\",version=\"7\\.4\\.0\\.1570\",update=NA,sw_edition=\"online\",target_sw=\"win2003\",target_hw=\"x64\"]" );
409 testStrconv ( "cpe:2.3:a:hp:openview_network_manager:7.51:*:*:*:*:linux:*:*",
410 "cpe:/a:hp:openview_network_manager:7.51::~~~linux~~",
411 "wfn:[part=\"a\",vendor=\"hp\",product=\"openview_network_manager\",version=\"7\\.51\",target_sw=\"linux\"]" );
413 testStrconv ( "cpe:2.3:a:foo\\\\bar:big\\$money_manager_2010:*:*:*:*:special:ipod_touch:80gb:*",
414 "cpe:/a:foo%5cbar:big%24money_manager_2010:::~~special~ipod_touch~80gb~",
415 "wfn:[part=\"a\",vendor=\"foo\\\\bar\",product=\"big\\$money_manager_2010\",sw_edition=\"special\",target_sw=\"ipod_touch\",target_hw=\"80gb\"]" );
417 BOOST_CHECK_THROW( (CpeId( "cpe:/x:" )), std::invalid_argument ); // illegal part 'x'
418 BOOST_CHECK_THROW( CpeId( "cpe:/a:foo%5cbar:big%24money_2010%07:::~~special~ipod_touch~80gb~" ), std::invalid_argument ); // illegal %07
419 BOOST_CHECK_EQUAL( CpeId( "cpe:/a:foo~bar:big%7emoney_2010" ).asUri(), "cpe:/a:foo%7ebar:big%7emoney_2010" ); // unescaped ~ is ok but not preferred
422 BOOST_AUTO_TEST_CASE(cpeid_matches)
424 CpeId sle( "cpe:/o:sles" );
425 CpeId win( "cpe:/o:windows" );
427 CpeId ons( "cpe:2.3:o:??????s" );
428 CpeId oops( "cpe:2.3:o:?????s" );
430 BOOST_CHECK_EQUAL( compare( sle, win ), SetRelation::disjoint );
432 BOOST_CHECK_EQUAL( compare( sle, any ), SetRelation::subset );
433 BOOST_CHECK_EQUAL( compare( win, any ), SetRelation::subset );
435 BOOST_CHECK_EQUAL( compare( any, sle ), SetRelation::superset );
436 BOOST_CHECK_EQUAL( compare( any, win ), SetRelation::superset );
439 BOOST_CHECK_EQUAL( compare( sle, ons ), SetRelation::uncomparable );
440 BOOST_CHECK_EQUAL( compare( win, ons ), SetRelation::uncomparable );
442 BOOST_CHECK_EQUAL( compare( sle, ons ), SetRelation::subset );
443 BOOST_CHECK_EQUAL( compare( win, ons ), SetRelation::subset );
446 BOOST_CHECK_EQUAL( compare( ons, sle ), SetRelation::superset );
447 BOOST_CHECK_EQUAL( compare( ons, win ), SetRelation::superset );
449 BOOST_CHECK_EQUAL( compare( oops, sle ), SetRelation::superset );
450 BOOST_CHECK_EQUAL( compare( oops, win ), SetRelation::disjoint );