LLVM OpenMP
TestOMPTraitParser.cpp
Go to the documentation of this file.
1//===- TestOMPTraitParser.cpp - Tests for OMP Trait Parser ---------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "kmp_traits.h"
10#include "gtest/gtest.h"
11
12namespace {
13
14//===----------------------------------------------------------------------===//
15// Helper to parse and auto-cleanup
16//===----------------------------------------------------------------------===//
17
18class ParserTest : public ::testing::Test {
19protected:
20 kmp_trait_context *context = nullptr;
21
22 void parse(const char *spec, const char *dbg_name = nullptr) {
23 context = kmp_trait_context::parse_from_spec(kmp_str_ref(spec), dbg_name);
24 }
25
26 void TearDown() override {
27 if (context) {
28 delete context;
29 context = nullptr;
30 }
31 }
32};
33
34template <bool expected_result>
35static void check_result_single(kmp_trait_context *context,
37 int expected_device_num) {
38 EXPECT_EQ(context->match(expected_device_num), expected_result);
39 EXPECT_EQ(result.contains(expected_device_num), expected_result);
40}
41
42template <bool expected_result, int... device_nums>
43static void check_result(kmp_trait_context *context,
44 const kmp_vector<int> &result) {
45 (check_result_single<expected_result>(context, result, device_nums), ...);
46}
47
48template <bool expected_result, int... device_nums>
49static void check_result(kmp_trait_context *context) {
50 const kmp_vector<int> &result = context->evaluate();
51 check_result<expected_result, device_nums...>(context, result);
52}
53
54//===----------------------------------------------------------------------===//
55// Literal Device Numbers
56//===----------------------------------------------------------------------===//
57
58TEST_F(ParserTest, SingleLiteral) {
59 parse("5");
60
61 ASSERT_NE(context, nullptr);
62 // Device 5 is out of range (mock has 4 devices: 0-3), so match returns false
63
64 EXPECT_EQ(context->evaluate().size(), 0u);
65 check_result<false, 5, 0, 4, 6>(context);
66}
67
68TEST_F(ParserTest, ZeroLiteral) {
69 parse("0");
70
71 ASSERT_NE(context, nullptr);
72 kmp_vector<int> result = context->evaluate();
73
74 EXPECT_EQ(result.size(), 1u);
75 check_result<true, 0>(context, result);
76 check_result<false, 1>(context, result);
77}
78
79TEST_F(ParserTest, MultipleLiterals) {
80 parse("1,2,3");
81
82 ASSERT_NE(context, nullptr);
83 kmp_vector<int> result = context->evaluate();
84
85 EXPECT_EQ(result.size(), 3u);
86 check_result<true, 1, 2, 3>(context, result);
87 check_result<false, 0, 4>(context, result);
88}
89
90TEST_F(ParserTest, LiteralsWithSpaces) {
91 parse("1, 2, 3");
92
93 ASSERT_NE(context, nullptr);
94 kmp_vector<int> result = context->evaluate();
95
96 EXPECT_EQ(result.size(), 3u);
97 check_result<true, 1, 2, 3>(context, result);
98 check_result<false, 0, 4>(context, result);
99}
100
101TEST_F(ParserTest, LiteralsWithLeadingSpaces) {
102 parse(" 1, 2, 3");
103
104 ASSERT_NE(context, nullptr);
105 kmp_vector<int> result = context->evaluate();
106
107 EXPECT_EQ(result.size(), 3u);
108 check_result<true, 1, 2, 3>(context, result);
109 check_result<false, 0, 4>(context, result);
110}
111
112TEST_F(ParserTest, LargeLiteral) {
113 parse("12345");
114
115 ASSERT_NE(context, nullptr);
116 // Device 12345 is out of range, so match returns false
117 kmp_vector<int> result = context->evaluate();
118
119 EXPECT_EQ(result.size(), 0u);
120 check_result<false, 12345, 0>(context, result);
121}
122
123//===----------------------------------------------------------------------===//
124// Wildcard
125//===----------------------------------------------------------------------===//
126
127TEST_F(ParserTest, Wildcard) {
128 parse("*");
129
130 ASSERT_NE(context, nullptr);
131 // Wildcard matches all 4 mock devices
132 kmp_vector<int> result = context->evaluate();
133
134 EXPECT_EQ(result.size(), 4u);
135 check_result<true, 0, 1, 2, 3>(context, result);
136 check_result<false, 100>(context, result);
137}
138
139TEST_F(ParserTest, WildcardWithLiterals) {
140 parse("1, *, 3");
141
142 ASSERT_NE(context, nullptr);
143 // Wildcard makes all in-range devices match
144 kmp_vector<int> result = context->evaluate();
145
146 EXPECT_EQ(result.size(), 4u);
147 check_result<true, 0, 1, 2, 3>(context, result);
148 check_result<false, 100>(context, result);
149}
150
151//===----------------------------------------------------------------------===//
152// UID Traits
153//===----------------------------------------------------------------------===//
154
155TEST_F(ParserTest, UIDTrait) {
156 parse("uid(device-0)");
157
158 ASSERT_NE(context, nullptr);
159 // Uses mock: device-0 is at index 0
160 kmp_vector<int> result = context->evaluate();
161
162 EXPECT_EQ(result.size(), 1u);
163 check_result<true, 0>(context, result);
164 check_result<false, 1>(context, result);
165}
166
167TEST_F(ParserTest, UIDTraitWithUnderscore) {
168 parse("uid(my_device_123)");
169
170 ASSERT_NE(context, nullptr);
171 // This UID doesn't match any mock device
172 kmp_vector<int> result = context->evaluate();
173
174 EXPECT_EQ(result.size(), 0u);
175 check_result<false, 0, 1>(context, result);
176}
177
178TEST_F(ParserTest, UIDTraitWithDash) {
179 parse("uid(device-2)");
180
181 ASSERT_NE(context, nullptr);
182 // Uses mock: device-2 is at index 2
183 kmp_vector<int> result = context->evaluate();
184
185 EXPECT_EQ(result.size(), 1u);
186 check_result<true, 2>(context, result);
187 check_result<false, 0, 1, 3>(context, result);
188}
189
190TEST_F(ParserTest, MultipleUIDTraits) {
191 parse("uid(device-1), uid(device-3)");
192
193 ASSERT_NE(context, nullptr);
194 kmp_vector<int> result = context->evaluate();
195
196 EXPECT_EQ(result.size(), 2u);
197 check_result<true, 1, 3>(context, result);
198 check_result<false, 0, 2>(context, result);
199}
200
201TEST_F(ParserTest, MixedLiteralsAndUIDs) {
202 parse("0, uid(device-2), 1, uid(device-3)");
203
204 ASSERT_NE(context, nullptr);
205 kmp_vector<int> result = context->evaluate();
206
207 EXPECT_EQ(result.size(), 4u);
208 check_result<true, 0, 1, 2, 3>(context, result);
209}
210
211//===----------------------------------------------------------------------===//
212// Negation
213//===----------------------------------------------------------------------===//
214
215TEST_F(ParserTest, NegatedUID) {
216 parse("!uid(device-0)");
217
218 ASSERT_NE(context, nullptr);
219 // Negated: everything except device-0 matches
220 kmp_vector<int> result = context->evaluate();
221
222 EXPECT_EQ(result.size(), 3u);
223 check_result<false, 0>(context, result);
224 check_result<true, 1, 2, 3>(context, result);
225}
226
227//===----------------------------------------------------------------------===//
228// Grouping with Parentheses
229//===----------------------------------------------------------------------===//
230
231TEST_F(ParserTest, SimpleGroup) {
232 parse("(uid(device-1))");
233
234 ASSERT_NE(context, nullptr);
235 kmp_vector<int> result = context->evaluate();
236
237 EXPECT_EQ(result.size(), 1u);
238 check_result<false, 0>(context, result);
239 check_result<true, 1>(context, result);
240}
241
242TEST_F(ParserTest, GroupWithOR) {
243 parse("(uid(device-0) || uid(device-2))");
244
245 ASSERT_NE(context, nullptr);
246 kmp_vector<int> result = context->evaluate();
247
248 EXPECT_EQ(result.size(), 2u);
249 check_result<true, 0, 2>(context, result);
250 check_result<false, 1, 3>(context, result);
251}
252
253TEST_F(ParserTest, GroupWithAND) {
254 parse("(uid(device-0) && uid(device-0))");
255
256 ASSERT_NE(context, nullptr);
257 // Both refer to same device, so AND passes for device 0
258 kmp_vector<int> result = context->evaluate();
259
260 EXPECT_EQ(result.size(), 1u);
261 check_result<true, 0>(context, result);
262 check_result<false, 1>(context, result);
263}
264
265TEST_F(ParserTest, NegatedGroup) {
266 parse("!(uid(device-0) || uid(device-1))");
267
268 ASSERT_NE(context, nullptr);
269 // Negated: matches devices NOT in {0, 1}
270 kmp_vector<int> result = context->evaluate();
271
272 EXPECT_EQ(result.size(), 2u);
273 check_result<false, 0, 1>(context, result);
274 check_result<true, 2, 3>(context, result);
275}
276
277//===----------------------------------------------------------------------===//
278// Complex Expressions
279//===----------------------------------------------------------------------===//
280
281TEST_F(ParserTest, ComplexMixed) {
282 parse("0, 1, uid(device-2), *");
283
284 ASSERT_NE(context, nullptr);
285 kmp_vector<int> result = context->evaluate();
286
287 EXPECT_EQ(result.size(), 4u);
288 check_result<true, 0, 1, 2, 3>(context, result);
289 check_result<false, 100>(context, result);
290}
291
292TEST_F(ParserTest, MultipleORGroups) {
293 parse("(uid(device-0) || uid(device-1)), (uid(device-2) || uid(device-3))");
294
295 ASSERT_NE(context, nullptr);
296 kmp_vector<int> result = context->evaluate();
297
298 EXPECT_EQ(result.size(), 4u);
299 check_result<true, 0, 1, 2, 3>(context, result);
300}
301
302//===----------------------------------------------------------------------===//
303// Complex Boolean Operators
304//===----------------------------------------------------------------------===//
305
306TEST_F(ParserTest, ThreeWayOR) {
307 // Three UIDs combined with OR
308 parse("(uid(device-0) || uid(device-1) || uid(device-2))");
309
310 ASSERT_NE(context, nullptr);
311 kmp_vector<int> result = context->evaluate();
312
313 EXPECT_EQ(result.size(), 3u);
314 check_result<true, 0, 1, 2>(context, result);
315 check_result<false, 3>(context, result);
316}
317
318TEST_F(ParserTest, FourWayOR) {
319 // All four mock devices via OR
320 parse("(uid(device-0) || uid(device-1) || uid(device-2) || uid(device-3))");
321
322 ASSERT_NE(context, nullptr);
323 kmp_vector<int> result = context->evaluate();
324
325 EXPECT_EQ(result.size(), 4u);
326 check_result<true, 0, 1, 2, 3>(context, result);
327}
328
329TEST_F(ParserTest, ThreeWayAND) {
330 // Three identical UIDs with AND - all must match same device
331 parse("(uid(device-1) && uid(device-1) && uid(device-1))");
332
333 ASSERT_NE(context, nullptr);
334 kmp_vector<int> result = context->evaluate();
335
336 EXPECT_EQ(result.size(), 1u);
337 check_result<true, 1>(context, result);
338 check_result<false, 0, 2, 3>(context, result);
339}
340
341TEST_F(ParserTest, ANDWithDifferentUIDs) {
342 // AND with different UIDs - can never match (device can't have two UIDs)
343 parse("(uid(device-0) && uid(device-1))");
344
345 ASSERT_NE(context, nullptr);
346 kmp_vector<int> result = context->evaluate();
347
348 EXPECT_EQ(result.size(), 0u);
349 check_result<false, 0, 1, 2, 3>(context, result);
350}
351
352TEST_F(ParserTest, NegatedThreeWayOR) {
353 // Negate a group of three UIDs - matches devices NOT in {0, 1, 2}
354 parse("!(uid(device-0) || uid(device-1) || uid(device-2))");
355
356 ASSERT_NE(context, nullptr);
357 kmp_vector<int> result = context->evaluate();
358
359 EXPECT_EQ(result.size(), 1u);
360 check_result<false, 0, 1, 2>(context, result);
361 check_result<true, 3>(context, result);
362}
363
364TEST_F(ParserTest, NegatedAND) {
365 // Negate an AND group - since AND never matches, negation matches all
366 parse("!(uid(device-0) && uid(device-1))");
367
368 ASSERT_NE(context, nullptr);
369 kmp_vector<int> result = context->evaluate();
370
371 EXPECT_EQ(result.size(), 4u);
372 check_result<true, 0, 1, 2, 3>(context, result);
373}
374
375TEST_F(ParserTest, NegatedANDWithSameUID) {
376 // Negate an AND that matches device-0 - matches everything except 0
377 parse("!(uid(device-0) && uid(device-0))");
378
379 ASSERT_NE(context, nullptr);
380 kmp_vector<int> result = context->evaluate();
381
382 EXPECT_EQ(result.size(), 3u);
383 check_result<false, 0>(context, result);
384 check_result<true, 1, 2, 3>(context, result);
385}
386
387TEST_F(ParserTest, NestedParensWithOR) {
388 // Nested parentheses around OR
389 parse("((uid(device-0) || uid(device-1)))");
390
391 ASSERT_NE(context, nullptr);
392 kmp_vector<int> result = context->evaluate();
393
394 EXPECT_EQ(result.size(), 2u);
395 check_result<true, 0, 1>(context, result);
396 check_result<false, 2, 3>(context, result);
397}
398
399TEST_F(ParserTest, NestedParensWithAND) {
400 // Nested parentheses around AND
401 parse("((uid(device-2) && uid(device-2)))");
402
403 ASSERT_NE(context, nullptr);
404 kmp_vector<int> result = context->evaluate();
405
406 EXPECT_EQ(result.size(), 1u);
407 check_result<true, 2>(context, result);
408 check_result<false, 0, 1, 3>(context, result);
409}
410
411TEST_F(ParserTest, DoubleNegation) {
412 // Double negation: !!uid(device-0) should match device-0
413 parse("!(!uid(device-0))");
414
415 ASSERT_NE(context, nullptr);
416 kmp_vector<int> result = context->evaluate();
417
418 EXPECT_EQ(result.size(), 1u);
419 check_result<true, 0>(context, result);
420 check_result<false, 1, 2, 3>(context, result);
421}
422
423TEST_F(ParserTest, NegatedNestedOR) {
424 // Negate nested OR group
425 parse("!((uid(device-0) || uid(device-1)))");
426
427 ASSERT_NE(context, nullptr);
428 kmp_vector<int> result = context->evaluate();
429
430 EXPECT_EQ(result.size(), 2u);
431 check_result<false, 0, 1>(context, result);
432 check_result<true, 2, 3>(context, result);
433}
434
435TEST_F(ParserTest, MultipleNegatedExprs) {
436 // Multiple negated clauses - OR semantics between clauses
437 parse("!uid(device-0), !uid(device-1)");
438
439 ASSERT_NE(context, nullptr);
440 // First clause matches 1,2,3; Second clause matches 0,2,3
441 // OR between clauses: union = all devices
442 kmp_vector<int> result = context->evaluate();
443
444 EXPECT_EQ(result.size(), 4u);
445 check_result<true, 0, 1, 2, 3>(context, result);
446}
447
448TEST_F(ParserTest, MixedNegatedAndNonNegated) {
449 // Mix negated and non-negated clauses
450 parse("uid(device-0), !uid(device-0)");
451
452 ASSERT_NE(context, nullptr);
453 // First matches 0, second matches 1,2,3 -> union = all
454 kmp_vector<int> result = context->evaluate();
455
456 EXPECT_EQ(result.size(), 4u);
457 check_result<true, 0, 1, 2, 3>(context, result);
458}
459
460TEST_F(ParserTest, ComplexORGroupsInSeparateExprs) {
461 // Complex OR groups as separate clauses
462 parse("(uid(device-0) || uid(device-1)), (uid(device-2) || uid(device-3))");
463
464 ASSERT_NE(context, nullptr);
465 // First matches 0,1; Second matches 2,3 -> union = all
466 kmp_vector<int> result = context->evaluate();
467
468 EXPECT_EQ(result.size(), 4u);
469 check_result<true, 0, 1, 2, 3>(context, result);
470}
471
472TEST_F(ParserTest, NegatedORGroupWithLiteral) {
473 // Negated OR group combined with literal in separate clauses
474 parse("!(uid(device-0) || uid(device-1)), 0");
475
476 ASSERT_NE(context, nullptr);
477 // First matches 2,3; Second matches 0 -> union = 0,2,3
478 kmp_vector<int> result = context->evaluate();
479
480 EXPECT_EQ(result.size(), 3u);
481 check_result<true, 0, 2, 3>(context, result);
482 check_result<false, 1>(context, result);
483}
484
485TEST_F(ParserTest, DeeplyNestedWithOperators) {
486 // Deeply nested with operators
487 parse("(((uid(device-0) || uid(device-1))))");
488
489 ASSERT_NE(context, nullptr);
490 kmp_vector<int> result = context->evaluate();
491
492 EXPECT_EQ(result.size(), 2u);
493 check_result<true, 0, 1>(context, result);
494 check_result<false, 2, 3>(context, result);
495}
496
497TEST_F(ParserTest, ORWithSpacesAroundOperators) {
498 // OR with lots of whitespace
499 parse("( uid(device-0) || uid(device-2) || uid(device-3) )");
500
501 ASSERT_NE(context, nullptr);
502 kmp_vector<int> result = context->evaluate();
503
504 EXPECT_EQ(result.size(), 3u);
505 check_result<true, 0, 2, 3>(context, result);
506 check_result<false, 1>(context, result);
507}
508
509TEST_F(ParserTest, ANDWithSpacesAroundOperators) {
510 // AND with lots of whitespace
511 parse("( uid(device-1) && uid(device-1) )");
512
513 ASSERT_NE(context, nullptr);
514 kmp_vector<int> result = context->evaluate();
515
516 EXPECT_EQ(result.size(), 1u);
517 check_result<true, 1>(context, result);
518 check_result<false, 0, 2, 3>(context, result);
519}
520
521//===----------------------------------------------------------------------===//
522// Mixed && and || (in separate clauses/groups)
523//===----------------------------------------------------------------------===//
524
525TEST_F(ParserTest, ORExprAndANDExpr) {
526 // OR group in first clause, AND group in second clause
527 parse("(uid(device-0) || uid(device-1)), (uid(device-2) && uid(device-2))");
528
529 ASSERT_NE(context, nullptr);
530 // First clause matches 0,1; Second clause matches 2 -> union = 0,1,2
531 kmp_vector<int> result = context->evaluate();
532
533 EXPECT_EQ(result.size(), 3u);
534 check_result<true, 0, 1, 2>(context, result);
535 check_result<false, 3>(context, result);
536}
537
538TEST_F(ParserTest, ANDExprAndORExpr) {
539 // AND group first, OR group second
540 parse("(uid(device-0) && uid(device-0)), (uid(device-2) || uid(device-3))");
541
542 ASSERT_NE(context, nullptr);
543 // First clause matches 0; Second clause matches 2,3 -> union = 0,2,3
544 kmp_vector<int> result = context->evaluate();
545
546 EXPECT_EQ(result.size(), 3u);
547 check_result<true, 0, 2, 3>(context, result);
548 check_result<false, 1>(context, result);
549}
550
551TEST_F(ParserTest, MultipleANDAndORExprs) {
552 // Multiple clauses alternating between AND and OR
553 parse("(uid(device-0) && uid(device-0)), (uid(device-1) || uid(device-2)), "
554 "(uid(device-3) && uid(device-3))");
555
556 ASSERT_NE(context, nullptr);
557 // Expr 1 matches 0; Expr 2 matches 1,2; Expr 3 matches 3 -> all
558 kmp_vector<int> result = context->evaluate();
559
560 EXPECT_EQ(result.size(), 4u);
561 check_result<true, 0, 1, 2, 3>(context, result);
562}
563
564TEST_F(ParserTest, NegatedORWithAND) {
565 // Negated OR clause combined with AND clause
566 parse("!(uid(device-0) || uid(device-1)), (uid(device-0) && uid(device-0))");
567
568 ASSERT_NE(context, nullptr);
569 // First clause matches 2,3; Second clause matches 0 -> union = 0,2,3
570 kmp_vector<int> result = context->evaluate();
571
572 EXPECT_EQ(result.size(), 3u);
573 check_result<true, 0, 2, 3>(context, result);
574 check_result<false, 1>(context, result);
575}
576
577TEST_F(ParserTest, NegatedANDWithOR) {
578 // Negated AND clause combined with OR clause
579 parse("!(uid(device-0) && uid(device-0)), (uid(device-0) || uid(device-1))");
580
581 ASSERT_NE(context, nullptr);
582 // First clause matches 1,2,3; Second clause matches 0,1 -> all
583 kmp_vector<int> result = context->evaluate();
584
585 EXPECT_EQ(result.size(), 4u);
586 check_result<true, 0, 1, 2, 3>(context, result);
587}
588
589TEST_F(ParserTest, ComplexMixedOperators) {
590 // Complex mix: OR, AND, negated OR, literal
591 parse("(uid(device-0) || uid(device-1)), (uid(device-2) && uid(device-2)), "
592 "!(uid(device-0) || uid(device-1) || uid(device-2)), 0");
593
594 ASSERT_NE(context, nullptr);
595 // Expr 1: 0,1; Expr 2: 2; Expr 3: NOT(0,1,2) = 3; Expr 4: 0
596 // Union = all
597 kmp_vector<int> result = context->evaluate();
598
599 EXPECT_EQ(result.size(), 4u);
600 check_result<true, 0, 1, 2, 3>(context, result);
601}
602
603TEST_F(ParserTest, ANDNeverMatchesWithOR) {
604 // AND that never matches combined with OR that does
605 parse("(uid(device-0) && uid(device-1)), (uid(device-2) || uid(device-3))");
606
607 ASSERT_NE(context, nullptr);
608 // First clause: never matches (different UIDs); Second: 2,3
609 kmp_vector<int> result = context->evaluate();
610
611 EXPECT_EQ(result.size(), 2u);
612 check_result<false, 0, 1>(context, result);
613 check_result<true, 2, 3>(context, result);
614}
615
616TEST_F(ParserTest, ORNeverMatchesWithAND) {
617 // OR with non-existent UIDs combined with AND that matches
618 parse("(uid(nonexistent-a) || uid(nonexistent-b)), (uid(device-0) && "
619 "uid(device-0))");
620
621 ASSERT_NE(context, nullptr);
622 // First clause: no match; Second: 0
623 kmp_vector<int> result = context->evaluate();
624
625 EXPECT_EQ(result.size(), 1u);
626 check_result<true, 0>(context, result);
627 check_result<false, 1, 2, 3>(context, result);
628}
629
630TEST_F(ParserTest, ThreeWayORAndThreeWayAND) {
631 // Three-way OR and three-way AND in separate clauses
632 parse("(uid(device-0) || uid(device-1) || uid(device-2)), (uid(device-3) && "
633 "uid(device-3) && uid(device-3))");
634
635 ASSERT_NE(context, nullptr);
636 // First: 0,1,2; Second: 3 -> all
637 kmp_vector<int> result = context->evaluate();
638
639 EXPECT_EQ(result.size(), 4u);
640 check_result<true, 0, 1, 2, 3>(context, result);
641}
642
643TEST_F(ParserTest, NegatedMixedExprs) {
644 // Both clauses negated with different operators
645 parse("!(uid(device-0) || uid(device-1)), !(uid(device-2) && uid(device-2))");
646
647 ASSERT_NE(context, nullptr);
648 // First: NOT(0,1) = 2,3; Second: NOT(2) = 0,1,3
649 // Union = all
650 kmp_vector<int> result = context->evaluate();
651
652 EXPECT_EQ(result.size(), 4u);
653 check_result<true, 0, 1, 2, 3>(context, result);
654}
655
656TEST_F(ParserTest, LiteralsWithMixedOperatorExprs) {
657 // Literals combined with both OR and AND clauses
658 parse("0, (uid(device-1) || uid(device-2)), 3, (uid(device-0) && "
659 "uid(device-0))");
660
661 ASSERT_NE(context, nullptr);
662 // Literals: 0,3; OR clause: 1,2; AND clause: 0
663 // Union = all
664 kmp_vector<int> result = context->evaluate();
665
666 EXPECT_EQ(result.size(), 4u);
667 check_result<true, 0, 1, 2, 3>(context, result);
668}
669
670//===----------------------------------------------------------------------===//
671// Nested Mixed Operators (|| and && at different nesting levels)
672//===----------------------------------------------------------------------===//
673
674TEST_F(ParserTest, ORContainingANDGroup) {
675 // Outer OR with inner AND group: (A || (B && C))
676 // For (B && C) to match, both B and C must match same device
677 parse("(uid(device-0) || (uid(device-1) && uid(device-1)))");
678
679 ASSERT_NE(context, nullptr);
680 // device-0 matches via first operand
681 // device-1 matches via (device-1 && device-1)
682 kmp_vector<int> result = context->evaluate();
683
684 EXPECT_EQ(result.size(), 2u);
685 check_result<true, 0, 1>(context, result);
686 check_result<false, 2, 3>(context, result);
687}
688
689TEST_F(ParserTest, ANDContainingORGroup) {
690 // Outer AND with inner OR group: (A && (B || C))
691 // Both the trait A and the group (B || C) must match
692 // Since A is uid(device-0), only device-0 can satisfy A
693 // (B || C) must also match device-0 for AND to succeed
694 parse("(uid(device-0) && (uid(device-0) || uid(device-1)))");
695
696 ASSERT_NE(context, nullptr);
697 // device-0: uid(device-0) matches AND (uid(device-0) || uid(device-1))
698 // matches -> true device-1: uid(device-0) doesn't match -> false
699 kmp_vector<int> result = context->evaluate();
700
701 EXPECT_EQ(result.size(), 1u);
702 check_result<true, 0>(context, result);
703 check_result<false, 1, 2, 3>(context, result);
704}
705
706TEST_F(ParserTest, ORWithTwoANDGroups) {
707 // ((A && B) || (C && D)) - OR of two AND groups
708 parse(
709 "((uid(device-0) && uid(device-0)) || (uid(device-2) && uid(device-2)))");
710
711 ASSERT_NE(context, nullptr);
712 // First AND matches device-0; Second AND matches device-2
713 kmp_vector<int> result = context->evaluate();
714
715 EXPECT_EQ(result.size(), 2u);
716 check_result<true, 0, 2>(context, result);
717 check_result<false, 1, 3>(context, result);
718}
719
720TEST_F(ParserTest, ANDWithTwoORGroups) {
721 // ((A || B) && (C || D)) - AND of two OR groups
722 // For a device to match: must match (A || B) AND must match (C || D)
723 parse(
724 "((uid(device-0) || uid(device-1)) && (uid(device-0) || uid(device-2)))");
725
726 ASSERT_NE(context, nullptr);
727 // device-0: matches (0||1) AND matches (0||2) -> true
728 // device-1: matches (0||1) but NOT (0||2) -> false
729 // device-2: NOT (0||1) -> false
730 kmp_vector<int> result = context->evaluate();
731
732 EXPECT_EQ(result.size(), 1u);
733 check_result<true, 0>(context, result);
734 check_result<false, 1, 2, 3>(context, result);
735}
736
737TEST_F(ParserTest, ORWithNestedANDContainingOR) {
738 // (A || (B && (C || D))) - three levels of nesting
739 parse(
740 "(uid(device-3) || (uid(device-0) && (uid(device-0) || uid(device-1))))");
741
742 ASSERT_NE(context, nullptr);
743 // device-0: inner (0||1) matches, uid(device-0) matches -> AND matches; OR
744 // satisfied device-1: inner (0||1) matches, but uid(device-0) doesn't -> AND
745 // fails; outer uid(device-3) fails device-3: outer uid(device-3) matches
746 // directly
747 kmp_vector<int> result = context->evaluate();
748
749 EXPECT_EQ(result.size(), 2u);
750 check_result<true, 0, 3>(context, result);
751 check_result<false, 1, 2>(context, result);
752}
753
754TEST_F(ParserTest, ANDWithNestedORContainingAND) {
755 // (A && (B || (C && D))) - three levels of nesting
756 parse(
757 "(uid(device-0) && (uid(device-0) || (uid(device-1) && uid(device-1))))");
758
759 ASSERT_NE(context, nullptr);
760 // device-0: uid(device-0) matches; inner (uid(device-0) || ...) matches ->
761 // AND satisfied device-1: uid(device-0) doesn't match -> AND fails
762 kmp_vector<int> result = context->evaluate();
763
764 EXPECT_EQ(result.size(), 1u);
765 check_result<true, 0>(context, result);
766 check_result<false, 1, 2, 3>(context, result);
767}
768
769TEST_F(ParserTest, NegatedORContainingAND) {
770 // !(A || (B && C)) - negated complex expression
771 parse("!(uid(device-0) || (uid(device-1) && uid(device-1)))");
772
773 ASSERT_NE(context, nullptr);
774 // Without negation: matches 0, 1
775 // With negation: matches 2, 3
776 kmp_vector<int> result = context->evaluate();
777
778 EXPECT_EQ(result.size(), 2u);
779 check_result<false, 0, 1>(context, result);
780 check_result<true, 2, 3>(context, result);
781}
782
783TEST_F(ParserTest, NegatedANDContainingOR) {
784 // !(A && (B || C)) - negated complex expression
785 parse("!(uid(device-0) && (uid(device-0) || uid(device-1)))");
786
787 ASSERT_NE(context, nullptr);
788 // Without negation: matches only 0
789 // With negation: matches 1, 2, 3
790 kmp_vector<int> result = context->evaluate();
791
792 EXPECT_EQ(result.size(), 3u);
793 check_result<false, 0>(context, result);
794 check_result<true, 1, 2, 3>(context, result);
795}
796
797TEST_F(ParserTest, ComplexNestedWithAllDevices) {
798 // ((A || B) && (C || D)) where union covers all but AND restricts
799 parse(
800 "((uid(device-0) || uid(device-1)) && (uid(device-1) || uid(device-2)))");
801
802 ASSERT_NE(context, nullptr);
803 // device-0: (0||1)=true, (1||2)=false -> AND=false
804 // device-1: (0||1)=true, (1||2)=true -> AND=true
805 // device-2: (0||1)=false -> AND=false
806 kmp_vector<int> result = context->evaluate();
807
808 EXPECT_EQ(result.size(), 1u);
809 check_result<true, 1>(context, result);
810 check_result<false, 0, 2, 3>(context, result);
811}
812
813TEST_F(ParserTest, TripleNestedMixedOperators) {
814 // (((A || B) && C) || D) - deeply nested with alternating operators
815 parse(
816 "(((uid(device-0) || uid(device-1)) && uid(device-0)) || uid(device-3))");
817
818 ASSERT_NE(context, nullptr);
819 // Inner (0||1): matches 0, 1
820 // Middle ((0||1) && 0): matches only 0
821 // Outer (... || 3): matches 0, 3
822 kmp_vector<int> result = context->evaluate();
823
824 EXPECT_EQ(result.size(), 2u);
825 check_result<true, 0, 3>(context, result);
826 check_result<false, 1, 2>(context, result);
827}
828
829TEST_F(ParserTest, ANDChainWithNestedOR) {
830 // (A && (B || C) && D) - wait, this mixes operators at same level
831 // Actually: ((A && (B || C)) is valid - let's do that
832 // Let's do: (uid(device-0) && (uid(device-0) || uid(device-1)) &&
833 // uid(device-0)) This is three-way AND where middle operand is an OR group
834 parse("(uid(device-0) && (uid(device-0) || uid(device-1)) && uid(device-0))");
835
836 ASSERT_NE(context, nullptr);
837 // All three must match: uid(device-0), (0||1), uid(device-0)
838 // Only device-0 satisfies all
839 kmp_vector<int> result = context->evaluate();
840
841 EXPECT_EQ(result.size(), 1u);
842 check_result<true, 0>(context, result);
843 check_result<false, 1, 2, 3>(context, result);
844}
845
846TEST_F(ParserTest, ORChainWithNestedAND) {
847 // (A || (B && C) || D) - three-way OR where middle is AND group
848 parse("(uid(device-0) || (uid(device-1) && uid(device-1)) || uid(device-3))");
849
850 ASSERT_NE(context, nullptr);
851 // Any of: device-0, (device-1 && device-1), device-3
852 // Matches: 0, 1, 3
853 kmp_vector<int> result = context->evaluate();
854
855 EXPECT_EQ(result.size(), 3u);
856 check_result<true, 0, 1, 3>(context, result);
857 check_result<false, 2>(context, result);
858}
859
860TEST_F(ParserTest, NestedMixedWithSpaces) {
861 // Nested mixed operators with lots of whitespace
862 parse("( uid(device-0) || ( uid(device-1) && uid(device-1) ) || "
863 "uid(device-2) )");
864
865 ASSERT_NE(context, nullptr);
866 kmp_vector<int> result = context->evaluate();
867
868 EXPECT_EQ(result.size(), 3u);
869 check_result<true, 0, 1, 2>(context, result);
870 check_result<false, 3>(context, result);
871}
872
873TEST_F(ParserTest, DeepNestingWithinLimit) {
874 // A moderate nesting depth (well under the recursion limit) parses fine and
875 // is transparent to matching.
876 constexpr int depth = 20;
877 const char *inner = "uid(device-0)";
878 char spec[64];
879 char *p = spec;
880 for (int i = 0; i < depth; ++i)
881 *p++ = '(';
882 for (const char *s = inner; *s; ++s)
883 *p++ = *s;
884 for (int i = 0; i < depth; ++i)
885 *p++ = ')';
886 *p = '\0';
887 parse(spec, "deep_nesting_within_limit");
888
889 ASSERT_NE(context, nullptr);
890 kmp_vector<int> result = context->evaluate();
891
892 EXPECT_EQ(result.size(), 1u);
893 check_result<true, 0>(context, result);
894 check_result<false, 1, 2, 3>(context, result);
895}
896
897//===----------------------------------------------------------------------===//
898// Empty Input
899//===----------------------------------------------------------------------===//
900
901TEST_F(ParserTest, EmptyString) {
902 parse("");
903
904 ASSERT_NE(context, nullptr);
905 // Empty context matches nothing
906 kmp_vector<int> result = context->evaluate();
907
908 EXPECT_EQ(result.size(), 0u);
909 check_result<false, 0, 1>(context, result);
910}
911
912TEST_F(ParserTest, OnlyWhitespace) {
913 parse(" ");
914
915 ASSERT_NE(context, nullptr);
916 kmp_vector<int> result = context->evaluate();
917
918 EXPECT_EQ(result.size(), 0u);
919 check_result<false, 0>(context, result);
920}
921
922//===----------------------------------------------------------------------===//
923// Error Cases
924//===----------------------------------------------------------------------===//
925
926TEST_F(ParserTest, OnlyComma) {
927 ASSERT_DEATH(
928 parse(",", "test_only_comma"),
929 "OMP: Error #[0-9]+: trait parser while parsing test_only_comma: "
930 "failed to parse trait specification \\(,\\)");
931}
932
933TEST_F(ParserTest, OnlyCommaNullDbgName) {
934 ASSERT_DEATH(parse(","),
935 "OMP: Error #[0-9]+: trait parser while parsing \\(null\\): "
936 "failed to parse trait specification \\(,\\)");
937}
938
939TEST_F(ParserTest, MixedAndOrSameLevel) {
940 // OpenMP 6.0 explicitly excludes "&&" and "||" from appearing in the same
941 // grouping level.
942 ASSERT_DEATH(
943 parse("uid(a) && uid(b) || uid(c)", "mixed_and_or_same_level"),
944 "OMP: Error #[0-9]+: trait parser while parsing mixed_and_or_same_level: "
945 "failed to parse trait specification "
946 "\\(\\|\\| uid\\(c\\)\\)");
947}
948
949TEST_F(ParserTest, MixedOrAndSameLevel) {
950 ASSERT_DEATH(
951 parse("uid(a) || uid(b) && uid(c)", "mixed_or_and_same_level"),
952 "OMP: Error #[0-9]+: trait parser while parsing mixed_or_and_same_level: "
953 "failed to parse trait specification "
954 "\\(&& uid\\(c\\)\\)");
955}
956
957TEST_F(ParserTest, InvalidUID) {
958 // Empty UID is not allowed; the offending token (the closing ')') is reported
959 // as the invalid trait value.
960 ASSERT_DEATH(parse("uid()", "invalid_uid"),
961 "OMP: Error #[0-9]+: trait parser while parsing invalid_uid: "
962 "invalid value for trait 'uid' \\(\\)\\)");
963}
964
965TEST_F(ParserTest, MissingOpenParenAfterTrait) {
966 // A trait name must be followed by "(".
967 ASSERT_DEATH(parse("uid", "missing_open_paren"),
968 "OMP: Error #[0-9]+: trait parser while parsing "
969 "missing_open_paren: error: expected '\\(' after trait name");
970}
971
972TEST_F(ParserTest, MissingCloseParenAfterTraitValue) {
973 // A trait value must be followed by ")".
974 ASSERT_DEATH(parse("uid(a", "missing_close_paren"),
975 "OMP: Error #[0-9]+: trait parser while parsing "
976 "missing_close_paren: error: expected '\\)' after trait value");
977}
978
979TEST_F(ParserTest, UnclosedParenthesis) {
980 // Once "(" is consumed we are committed to a group, so a missing ")" is a
981 // hard error rather than a recoverable parse failure.
982 ASSERT_DEATH(
983 parse("(uid(a)", "unclosed_parenthesis"),
984 "OMP: Error #[0-9]+: trait parser while parsing unclosed_parenthesis: "
985 "error: expected '\\)' after trait expression group");
986}
987
988TEST_F(ParserTest, UnmatchedClosingParenthesis) {
989 ASSERT_DEATH(parse("uid(a))", "unmatched_closing_parenthesis"),
990 "OMP: Error #[0-9]+: trait parser while parsing "
991 "unmatched_closing_parenthesis: "
992 "failed to parse trait specification \\(\\)\\)");
993}
994
995TEST_F(ParserTest, EmptyParentheses) {
996 // "(" commits to a group, so an empty group is a hard error.
997 ASSERT_DEATH(
998 parse("()", "empty_parentheses"),
999 "OMP: Error #[0-9]+: trait parser while parsing empty_parentheses: "
1000 "error: expected trait expression after '\\('");
1001}
1002
1003TEST_F(ParserTest, TrailingOperator) {
1004 // A consumed "&&"/"||" commits to another operand, so its absence is a hard
1005 // error.
1006 ASSERT_DEATH(
1007 parse("uid(a) &&", "trailing_operator"),
1008 "OMP: Error #[0-9]+: trait parser while parsing trailing_operator: "
1009 "error: expected trait expression after operator");
1010}
1011
1012TEST_F(ParserTest, NegationWithoutTrait) {
1013 // A consumed "!" commits to a trait expression, so its absence is a hard
1014 // error rather than a recoverable parse failure.
1015 ASSERT_DEATH(
1016 parse("!", "negation_without_trait"),
1017 "OMP: Error #[0-9]+: trait parser while parsing negation_without_trait: "
1018 "error: expected trait expression after '!'");
1019}
1020
1021TEST_F(ParserTest, DeviceNumberOutOfRange) {
1022 // A word shaped like a device number is treated as a device number, so one
1023 // that does not fit an int is a hard error.
1024 ASSERT_DEATH(parse("99999999999", "device_number_out_of_range"),
1025 "OMP: Error #[0-9]+: trait parser while parsing "
1026 "device_number_out_of_range: error: device number out of range");
1027}
1028
1029TEST_F(ParserTest, NegativeDeviceNumber) {
1030 // "-5" is lexed as a single WORD; the parser recognizes its numeric shape and
1031 // rejects it as an out-of-range device number rather than treating it as a
1032 // name/uid_value.
1033 ASSERT_DEATH(parse("-5", "negative_device_number"),
1034 "OMP: Error #[0-9]+: trait parser while parsing "
1035 "negative_device_number: error: device number out of range");
1036}
1037
1038TEST_F(ParserTest, LeadingOperator) {
1039 ASSERT_DEATH(
1040 parse("&& uid(a)", "leading_operator"),
1041 "OMP: Error #[0-9]+: trait parser while parsing leading_operator: "
1042 "failed to parse trait specification \\(&& uid\\(a\\)\\)");
1043}
1044
1045TEST_F(ParserTest, DoubleComma) {
1046 ASSERT_DEATH(parse("uid(a),,uid(b)", "double_comma"),
1047 "OMP: Error #[0-9]+: trait parser while parsing double_comma: "
1048 "failed to parse trait specification \\(,,uid\\(b\\)\\)");
1049}
1050
1051TEST_F(ParserTest, UnrecognizedTrait) {
1052 // "uid" is currently the only trait name; any other word is not a trait, so
1053 // the clause cannot be parsed.
1054 ASSERT_DEATH(
1055 parse("foo", "unrecognized_trait"),
1056 "OMP: Error #[0-9]+: trait parser while parsing unrecognized_trait: "
1057 "failed to parse trait specification \\(foo\\)");
1058}
1059
1060TEST_F(ParserTest, MaxRecursionExceeded) {
1061 // Nesting parentheses deeper than the recursion limit is a hard error. The
1062 // guard trips before the input is exhausted, so unbalanced "(" are enough.
1063 char spec[101];
1064 for (int i = 0; i < 100; ++i)
1065 spec[i] = '(';
1066 spec[100] = '\0';
1067 ASSERT_DEATH(parse(spec, "max_recursion"),
1068 "OMP: Error #[0-9]+: trait parser while parsing max_recursion: "
1069 "max recursion depth \\(64\\) exceeded");
1070}
1071
1072} // namespace
TEST_F(OmptSequencedAsserterTest, DefaultState)
int result[2]
bool match(int device)
Definition kmp_traits.h:467
const kmp_vector< int > & evaluate()
Definition kmp_traits.h:455
static kmp_trait_context * parse_from_spec(kmp_str_ref spec, const char *dbg_name=nullptr)
kmp_vector is a vector class for managing small vectors.
Definition kmp_adt.h:148
size_t size() const
Definition kmp_adt.h:318
void const char const char int ITT_FORMAT __itt_group_sync s
void const char const char int ITT_FORMAT __itt_group_sync p
#define i
Definition kmp_stub.cpp:87