CpeId: Basic functionality incl. matching
[platform/upstream/libzypp.git] / tests / zypp / CpeId_test.cc
1 #include <boost/test/auto_unit_test.hpp>
2 #include "zypp/CpeId.h"
3
4 using std::cout;
5 using std::endl;
6
7 using zypp::SetCompare;
8 using zypp::SetRelation;
9 using zypp::CpeId;
10 typedef CpeId::Value Value;
11
12 #define defVALUE(N,S)                   \
13   const std::string N##Str( S );        \
14   Value N( N##Str );
15
16 defVALUE( wildcardfree,                 "STrv\\*al\\?" );       // '\?'         quoted?
17 const std::string wildcardfreeUri(      "STrv%2aal%3f" );
18 const std::string wildcardfreeFs(       "STrv\\*al\\?" );
19
20 defVALUE( wildcardfree2,                "stRV\\*al\\\\\\?" );   // '\\\?'       backslash, quoted?
21
22 defVALUE( wildcarded,                   "strv\\*AL?" );         // '?'          ?
23 const std::string wildcardedUri(        "strv%2aAL%01" );
24 const std::string wildcardedFs(         "strv\\*AL?" );
25
26 defVALUE( wildcarded2,                  "strv\\*AL\\\\?" );     // '\\?'        backslash, ?
27
28
29
30 BOOST_AUTO_TEST_CASE(cpeid_value_ANY)
31 {
32   for ( const auto & c : { Value(), Value(nullptr), Value("*"), Value::ANY } )
33   {
34     BOOST_CHECK( c.isANY() );
35     BOOST_CHECK( ! c.isNA() );
36     BOOST_CHECK( c.isLogical() );
37     BOOST_CHECK( ! c.isString() );
38     BOOST_CHECK( c == Value::ANY );
39     BOOST_CHECK( c == nullptr );        // ANY
40     BOOST_CHECK( c != Value::NA );
41     BOOST_CHECK( c != wildcardfree );
42     BOOST_CHECK( c != wildcarded );
43     BOOST_CHECK( ! c.isWildcardfree() );
44     BOOST_CHECK( ! c.isWildcarded() );
45     BOOST_CHECK_EQUAL( c.asFs(), "*" );
46     BOOST_CHECK_EQUAL( c.asUri(), "" );
47     BOOST_CHECK_EQUAL( c.asWfn(), "*" );
48     BOOST_CHECK_EQUAL( c.asString(), c.asWfn() );
49   }
50 }
51
52 BOOST_AUTO_TEST_CASE(cpeid_value_NA)
53 {
54   for ( const auto & c : { Value(""), Value::NA } )
55   {
56     BOOST_CHECK( ! c.isANY() );
57     BOOST_CHECK( c.isNA() );
58     BOOST_CHECK( c.isLogical() );
59     BOOST_CHECK( ! c.isString() );
60     BOOST_CHECK( c != Value::ANY );
61     BOOST_CHECK( c == Value::NA );
62     BOOST_CHECK( c == std::string() );  // NA
63     BOOST_CHECK( c == "" );             // NA
64     BOOST_CHECK( c != wildcardfree );
65     BOOST_CHECK( c != wildcarded );
66     BOOST_CHECK( ! c.isWildcardfree() );
67     BOOST_CHECK( ! c.isWildcarded() );
68     BOOST_CHECK_EQUAL( c.asFs(), "-" );
69     BOOST_CHECK_EQUAL( c.asUri(), "-" );
70     BOOST_CHECK_EQUAL( c.asWfn(), "" );
71     BOOST_CHECK_EQUAL( c.asString(), c.asWfn() );
72   }
73 }
74
75 BOOST_AUTO_TEST_CASE(cpeid_value_string_wildcardfree)
76 {
77   for ( const auto & c : { wildcardfree } )
78   {
79     BOOST_CHECK( ! c.isANY() );
80     BOOST_CHECK( ! c.isNA() );
81     BOOST_CHECK( ! c.isLogical() );
82     BOOST_CHECK( c.isString() );
83     BOOST_CHECK( c != Value::ANY );
84     BOOST_CHECK( c != Value::NA );
85     BOOST_CHECK( c == wildcardfree );
86     BOOST_CHECK( c == wildcardfreeStr );
87     BOOST_CHECK( c == wildcardfreeStr.c_str() );
88     BOOST_CHECK( c != wildcarded );
89     BOOST_CHECK( c.isWildcardfree() );
90     BOOST_CHECK( ! c.isWildcarded() );
91     BOOST_CHECK_EQUAL( c.asFs(), wildcardfreeFs );
92     BOOST_CHECK_EQUAL( c.asUri(), wildcardfreeUri );
93     BOOST_CHECK_EQUAL( c.asWfn(), wildcardfreeStr );
94     BOOST_CHECK_EQUAL( c.asString(), c.asWfn() );
95   }
96
97   BOOST_CHECK( wildcardfree2 == wildcardfree2 );
98   BOOST_CHECK( wildcardfree2 != wildcardfree );
99   BOOST_CHECK( wildcardfree2 != wildcarded );
100   BOOST_CHECK( wildcardfree2.isWildcardfree() );
101   BOOST_CHECK( ! wildcardfree2.isWildcarded() );
102 }
103
104 BOOST_AUTO_TEST_CASE(cpeid_value_string_wildcarded)
105 {
106   for ( const auto & c : { wildcarded } )
107   {
108     BOOST_CHECK( ! c.isANY() );
109     BOOST_CHECK( ! c.isNA() );
110     BOOST_CHECK( ! c.isLogical() );
111     BOOST_CHECK( c.isString() );
112     BOOST_CHECK( c != Value::ANY );
113     BOOST_CHECK( c != Value::NA );
114     BOOST_CHECK( c != wildcardfree );
115     BOOST_CHECK( c != wildcarded );     // !!! According to the CPE Name Matching Specification Version 2.3
116                                         // unquoted wildcard characters yield an undefined result (not ==).
117     BOOST_CHECK( ! c.isWildcardfree() );
118     BOOST_CHECK( c.isWildcarded() );
119     BOOST_CHECK_EQUAL( c.asFs(), wildcardedFs );
120     BOOST_CHECK_EQUAL( c.asUri(), wildcardedUri );
121     BOOST_CHECK_EQUAL( c.asWfn(), wildcardedStr );
122     BOOST_CHECK_EQUAL( c.asString(), c.asWfn() );
123   }
124
125   BOOST_CHECK( wildcarded2 != wildcarded2 );    // unquoted wildcard characters yield an undefined result (not ==).
126   BOOST_CHECK( wildcarded2 != wildcardfree );
127   BOOST_CHECK( wildcarded2 != wildcarded );
128   BOOST_CHECK( ! wildcarded2.isWildcardfree() );
129   BOOST_CHECK( wildcarded2.isWildcarded() );
130
131
132 }
133
134 BOOST_AUTO_TEST_CASE(cpeid_value_valid)
135 {
136   static const char *const hdig = "0123456789abcdef";
137
138   for ( char ch = 0x00; ch >= 0; ++ch )
139   {
140     // cout << "==== " << unsigned(ch) << endl;
141     char qchstr[] = { '\\', ch, '\0' };
142     std::string chstr( qchstr+1 );
143     char pchstr[] = { '%', hdig[(unsigned char)(ch)/16], hdig[(unsigned char)(ch)%16], '\0' };
144
145     if ( ch == '\0' )
146     {
147       BOOST_CHECK( Value( chstr ).isNA() );
148       BOOST_CHECK_THROW( (Value( chstr, Value::fsFormat )), std::invalid_argument );
149       BOOST_CHECK( Value( chstr, Value::uriFormat ).isANY() );
150     }
151     else if ( ch <= ' ' || '~' < ch )
152     {
153       BOOST_CHECK_THROW( (Value( chstr )), std::invalid_argument );
154       BOOST_CHECK_THROW( (Value( chstr, Value::fsFormat )), std::invalid_argument );
155       BOOST_CHECK_THROW( (Value( chstr, Value::uriFormat )), std::invalid_argument );
156     }
157     else if ( ( '0' <= ch && ch <= '9' )
158            || ( 'A' <= ch && ch <= 'Z' )
159            || ( 'a' <= ch && ch <= 'z' )
160            || ch == '_' )
161     {
162       BOOST_CHECK( Value( chstr ).isString() );
163       BOOST_CHECK( Value( chstr, Value::fsFormat ).isString() );
164       BOOST_CHECK( Value( chstr, Value::uriFormat ).isString() );
165
166       BOOST_CHECK_THROW( (Value( qchstr )), std::invalid_argument );
167       BOOST_CHECK( Value( qchstr, Value::fsFormat ).isString() );
168       BOOST_CHECK( Value( qchstr, Value::uriFormat ).isString() );
169
170       BOOST_CHECK( Value( pchstr, Value::uriFormat ).isString() );
171     }
172     else if ( ch == '*' )
173     {
174       BOOST_CHECK( Value( chstr ).isANY() );
175       BOOST_CHECK( Value( chstr, Value::fsFormat ).isANY() );
176       BOOST_CHECK( Value( chstr, Value::uriFormat ).isString() );
177
178       BOOST_CHECK( Value( qchstr ).isString() );
179       BOOST_CHECK( Value( qchstr, Value::fsFormat ).isString() );
180       BOOST_CHECK( Value( qchstr, Value::uriFormat ).isString() );
181
182       BOOST_CHECK( Value( pchstr, Value::uriFormat ).isString() );
183     }
184     else if ( ch == '?' )
185     {
186       BOOST_CHECK( Value( chstr ).isString() );
187       BOOST_CHECK( Value( chstr, Value::fsFormat ).isString() );
188       BOOST_CHECK( Value( chstr, Value::uriFormat ).isString() );
189
190       BOOST_CHECK( Value( qchstr ).isString() );
191       BOOST_CHECK( Value( qchstr, Value::fsFormat ).isString() );
192       BOOST_CHECK( Value( qchstr, Value::uriFormat ).isString() );
193
194       BOOST_CHECK( Value( pchstr, Value::uriFormat ).isString() );
195     }
196     else if ( ch == '-' )
197     {
198       BOOST_CHECK_THROW( (Value( chstr )), std::invalid_argument );
199       BOOST_CHECK( Value( chstr, Value::fsFormat ).isNA() );
200       BOOST_CHECK( Value( chstr, Value::uriFormat ).isNA() );
201
202       BOOST_CHECK_THROW( (Value( qchstr )), std::invalid_argument );
203       BOOST_CHECK( Value( qchstr, Value::fsFormat ).isString() );
204       BOOST_CHECK( Value( qchstr, Value::uriFormat ).isString() );
205
206       BOOST_CHECK( Value( pchstr, Value::uriFormat ).isString() );
207     }
208     else if ( ch == '\\' )
209     {
210       BOOST_CHECK_THROW( (Value( chstr )), std::invalid_argument );
211       BOOST_CHECK_THROW( (Value( chstr, Value::fsFormat )), std::invalid_argument );
212       BOOST_CHECK( (Value( chstr, Value::uriFormat )).isString() );
213
214       BOOST_CHECK( Value( qchstr ).isString() );
215       BOOST_CHECK( Value( qchstr, Value::fsFormat ).isString() );
216       BOOST_CHECK( Value( qchstr, Value::uriFormat ).isString() );
217
218       BOOST_CHECK( Value( pchstr, Value::uriFormat ).isString() );
219     }
220     else
221     {
222       BOOST_CHECK_THROW( (Value( chstr )), std::invalid_argument );
223       Value f( chstr, Value::fsFormat );
224       BOOST_CHECK( f.isString() );
225       Value u( chstr, Value::uriFormat );
226       BOOST_CHECK( u.isString() );
227
228       BOOST_CHECK_EQUAL( f.asString(), u.asString() );
229       if ( ch == '.' )
230       {
231         BOOST_CHECK_EQUAL( f.asFs(), chstr );
232         BOOST_CHECK_EQUAL( f.asUri(), chstr );
233       }
234       else
235       {
236         BOOST_CHECK_EQUAL( f.asFs(), qchstr );
237         BOOST_CHECK_EQUAL( f.asUri(), pchstr );
238       }
239
240       BOOST_CHECK( Value( qchstr ).isString() );
241       BOOST_CHECK( Value( qchstr, Value::fsFormat ).isString() );
242       BOOST_CHECK( Value( qchstr, Value::uriFormat ).isString() );
243
244       BOOST_CHECK( Value( pchstr, Value::uriFormat ).isString() );
245     }
246   }
247
248   BOOST_CHECK_THROW( Value( "\\!\\a\\-\\_\\.\\!" ), std::invalid_argument );
249   BOOST_CHECK_EQUAL( Value( "\\!\\a\\-\\_\\.\\!", Value::fsFormat ).asFs(), "\\!a-_.\\!" );
250 }
251
252 BOOST_AUTO_TEST_CASE(cpeid_type_checks)
253 {
254   for ( const auto & c : { Value::ANY, Value::NA, wildcardfree, wildcarded } )
255   {
256     BOOST_CHECK_EQUAL( c.isANY(),               c.type() == Value::Type::ANY );
257     BOOST_CHECK_EQUAL( c.isNA(),                c.type() == Value::Type::NA );
258     BOOST_CHECK_EQUAL( c.isWildcardfree(),      c.type() == Value::Type::wildcardfree );
259     BOOST_CHECK_EQUAL( c.isWildcarded(),        c.type() == Value::Type::wildcarded );
260     BOOST_CHECK_EQUAL( c.isLogical(),           c.isLogical( c.type() ) );
261     BOOST_CHECK_EQUAL( c.isString(),            c.isString( c.type() ) );
262     BOOST_CHECK_EQUAL( c.isLogical(),           ! c.isString() );
263   }
264 }
265
266 BOOST_AUTO_TEST_CASE(cpeid_compare)
267 {
268   BOOST_CHECK( compare( Value::ANY,     Value::ANY,     SetCompare::equal               ) );
269   BOOST_CHECK( compare( Value::ANY,     Value::NA,      SetCompare::properSuperset      ) );
270   BOOST_CHECK( compare( Value::ANY,     wildcardfree,   SetCompare::properSuperset      ) );
271   BOOST_CHECK( compare( Value::ANY,     wildcarded,     SetCompare::uncomparable        ) );
272
273   BOOST_CHECK( compare( Value::NA,      Value::ANY,     SetCompare::properSubset        ) );
274   BOOST_CHECK( compare( Value::NA,      Value::NA,      SetCompare::equal               ) );
275   BOOST_CHECK( compare( Value::NA,      wildcardfree,   SetCompare::disjoint            ) );
276   BOOST_CHECK( compare( Value::NA,      wildcarded,     SetCompare::uncomparable        ) );
277
278   BOOST_CHECK( compare( wildcardfree,   Value::ANY,     SetCompare::properSubset        ) );
279   BOOST_CHECK( compare( wildcardfree,   Value::NA,      SetCompare::disjoint            ) );
280   //BOOST_CHECK( compare( wildcardfree, wildcardfree,   _NeedsCloserLook,       // equal or disjoint
281   BOOST_CHECK( compare( wildcardfree,   wildcardfree,   SetCompare::equal               ) );
282   BOOST_CHECK( compare( wildcardfree,   wildcardfree2,  SetCompare::disjoint            ) );
283   BOOST_CHECK( compare( wildcardfree,   wildcarded,     SetCompare::uncomparable        ) );
284
285   BOOST_CHECK( compare( wildcarded,     Value::ANY,     SetCompare::properSubset        ) );
286   BOOST_CHECK( compare( wildcarded,     Value::NA,      SetCompare::disjoint            ) );
287   //BOOST_CHECK( compare( wildcarded,   wildcardfree,   _NeedsCloserLook,       // superset or disjoint
288   BOOST_CHECK( compare( wildcarded,     wildcardfree,   SetCompare::properSuperset      ) );
289   BOOST_CHECK( compare( wildcarded,     wildcardfree2,  SetCompare::disjoint            ) );
290   BOOST_CHECK( compare( wildcarded,     wildcarded,     SetCompare::uncomparable        ) );
291 }
292
293
294 BOOST_AUTO_TEST_CASE(cpeid_value_string_wildcard)
295 {
296   for ( const auto & c : { Value( "a" ), Value( "\\*" ), Value( "\\?" ) } )
297   {
298     BOOST_CHECK( c.isWildcardfree() );
299     BOOST_CHECK( !c.isWildcarded() );
300   }
301
302   for ( const auto & c : { Value( "*\\*" ), Value( "\\**" ), Value( "?" ), Value( "??\\?" ), Value( "\\???" ) } )
303   {
304     BOOST_CHECK( !c.isWildcardfree() );
305     BOOST_CHECK( c.isWildcarded() );
306   }
307 }
308
309 ///////////////////////////////////////////////////////////////////
310 ///////////////////////////////////////////////////////////////////
311
312 BOOST_AUTO_TEST_CASE(cpeid_basics)
313 {
314   BOOST_CHECK_THROW( CpeId( "malformed" ), std::invalid_argument );
315   CpeId none( "malformed", CpeId::noThrow );
316
317   for ( const auto & c : { CpeId(), CpeId( nullptr ), CpeId( "" ), CpeId( std::string() ) } )
318   {
319     BOOST_CHECK( ! c );                 // evaluate false in boolean context
320     BOOST_CHECK_EQUAL( c.asString(), c.asFs() );
321     BOOST_CHECK_EQUAL( c.asFs(),  "cpe:2.3:*:*:*:*:*:*:*:*:*:*:*" );
322     BOOST_CHECK_EQUAL( c.asUri(), "cpe:/" );
323     BOOST_CHECK_EQUAL( c.asWfn(), "wfn:[]" );
324     BOOST_CHECK_EQUAL( c, none );       // matching!!
325   }
326
327   for ( const auto & c : { CpeId( "cpe:/o:sle" ), CpeId( "cpe:/o:*" ) } )
328   {
329     BOOST_CHECK( c );                   // evaluate true in boolean context
330     BOOST_CHECK( ! c.asString().empty() );// empty string rep
331     BOOST_CHECK_EQUAL( c, c );          // matching!!
332   }
333 }
334
335 void testStrconv( const std::string & fs, const std::string & uri, const std::string & wfn )
336 {
337   CpeId fromFS( fs ) ;
338   CpeId fromURI( uri );
339
340   BOOST_CHECK_EQUAL( fromFS, fromURI );
341
342   for ( const auto & c : { fromFS, fromURI } )
343   {
344     BOOST_CHECK_EQUAL( c.asFs(), fs );
345     BOOST_CHECK_EQUAL( c.asUri(), uri );
346     BOOST_CHECK_EQUAL( c.asWfn(), wfn );
347   }
348 }
349
350 BOOST_AUTO_TEST_CASE(cpeid_strconv)
351 {
352   // colon embedded in product value
353   testStrconv ( "cpe:2.3:a:opensuse:lib\\:zypp:14.16.0:beta:*:*:*:*:*:-",
354                 "cpe:/a:opensuse:lib%3azypp:14.16.0:beta:~~~~~-",
355                 "wfn:[part=\"a\",vendor=\"opensuse\",product=\"lib\\:zypp\",version=\"14\\.16\\.0\",update=\"beta\",other=NA]" );
356
357   testStrconv ( "cpe:2.3:a:hp:insight_diagnostics:7.4.0.1570:-:*:*:online:win2003:x64:*",
358                 "cpe:/a:hp:insight_diagnostics:7.4.0.1570:-:~~online~win2003~x64~",
359                 "wfn:[part=\"a\",vendor=\"hp\",product=\"insight_diagnostics\",version=\"7\\.4\\.0\\.1570\",update=NA,sw_edition=\"online\",target_sw=\"win2003\",target_hw=\"x64\"]" );
360
361   testStrconv ( "cpe:2.3:a:hp:openview_network_manager:7.51:*:*:*:*:linux:*:*",
362                 "cpe:/a:hp:openview_network_manager:7.51::~~~linux~~",
363                 "wfn:[part=\"a\",vendor=\"hp\",product=\"openview_network_manager\",version=\"7\\.51\",target_sw=\"linux\"]" );
364
365   testStrconv ( "cpe:2.3:a:foo\\\\bar:big\\$money_manager_2010:*:*:*:*:special:ipod_touch:80gb:*",
366                 "cpe:/a:foo%5cbar:big%24money_manager_2010:::~~special~ipod_touch~80gb~",
367                 "wfn:[part=\"a\",vendor=\"foo\\\\bar\",product=\"big\\$money_manager_2010\",sw_edition=\"special\",target_sw=\"ipod_touch\",target_hw=\"80gb\"]" );
368
369   BOOST_CHECK_THROW( (CpeId( "cpe:/x:" )), std::invalid_argument );     // illegal part 'x'
370   BOOST_CHECK_THROW( CpeId( "cpe:/a:foo%5cbar:big%24money_2010%07:::~~special~ipod_touch~80gb~" ), std::invalid_argument );     // illegal %07
371   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
372 }
373
374 BOOST_AUTO_TEST_CASE(cpeid_matches)
375 {
376   CpeId sle( "cpe:/o:sles" );
377   CpeId win( "cpe:/o:windows" );
378   CpeId any;
379   CpeId ons( "cpe:2.3:o:??????s" );
380
381   BOOST_CHECK_EQUAL( compare( sle, win ), SetRelation::disjoint );
382
383   BOOST_CHECK_EQUAL( compare( sle, any ), SetRelation::subset );
384   BOOST_CHECK_EQUAL( compare( win, any ), SetRelation::subset );
385
386   BOOST_CHECK_EQUAL( compare( any, sle ), SetRelation::superset );
387   BOOST_CHECK_EQUAL( compare( any, win ), SetRelation::superset );
388
389   BOOST_CHECK_EQUAL( compare( sle, ons ), SetRelation::uncomparable );
390   BOOST_CHECK_EQUAL( compare( win, ons ), SetRelation::uncomparable );
391
392   BOOST_CHECK_EQUAL( compare( ons, sle ), SetRelation::superset );
393   BOOST_CHECK_EQUAL( compare( ons, win ), SetRelation::superset );
394 }