File Coverage: t/10-coverage.t

# File Coverage

File: | t/10-coverage.t
Coverage: | 98.3%

line | stmt | bran | cond | sub | pod | time | code
--- | --- | --- | --- | --- | --- | --- | ---
1 | 1 1 1 |  |  | 1 |  | 4608 3 39 | use strict;
2 | 1 1 1 |  |  | 1 |  | 5 1 55 | use warnings;
3 |
4 | 1 1 1 |  |  | 1 |  | 806 1566 153 | use FindBin qw/$Bin/;
5 | 1 1 1 |  |  | 1 |  | 414 894 10 | use lib qq{$Bin/../lib};
6 | 1 1 1 |  |  | 1 |  | 843 124504 5 | use Test::More;
7 |
8 | 1 1 1 |  |  | 1 |  | 516 2 2 | use OpenMP::Environment;
9 | 1 |  |  |  |  | 57 | use OpenMP::Environment::Validation qw/
10 |  |  |  |  |  |  | validate_value validate_assignment assert_variable assert_environment
11 |  |  |  |  |  |  | analyze_environment
12 | 1 1 |  |  | 1 |  | 4 1 | /;
13 |
14 |  |  |  |  |  |  | # Exercise each explicit-import arm independently. This complements the
15 |  |  |  |  |  |  | # public DSL tests without changing the regression files from master.
16 |  |  |  |  |  |  | {
17 | 0 |  |  |  |  | 0 | package OpenMPEnvironmentExplicitAssert;
18 | 1 1 1 |  |  | 1 |  | 18 1 2 | use OpenMP::Environment qw/assert/;
19 |  |  |  |  |  |  | }
20 | 1 |  |  |  |  | 64187 | package main;
21 | 1 |  |  |  |  | 8 | ok( OpenMPEnvironmentExplicitAssert->can(q{assert}), q{explicit assert import is covered} );
22 | 1 |  |  |  |  | 294 | ok( !OpenMPEnvironmentExplicitAssert->can(q{unset}), q{explicit assert import does not imply unset} );
23 |
24 |  |  |  |  |  |  | {
25 | 0 |  |  |  |  | 0 | package OpenMPEnvironmentExplicitConstant;
26 | 1 1 1 |  |  | 1 |  | 4 1 2 | use OpenMP::Environment qw/omp_num_threads/;
27 |  |  |  |  |  |  | }
28 | 1 |  |  |  |  | 198 | package main;
29 | 1 |  |  |  |  | 2 | is OpenMPEnvironmentExplicitConstant::omp_num_threads(), q{OMP_NUM_THREADS}, q{explicit constant-only import is covered};
30 | 1 |  |  |  |  | 214 | ok( !OpenMPEnvironmentExplicitConstant->can(q{assert}), q{constant-only import does not imply assert} );
31 | 1 |  |  |  |  | 197 | ok( !OpenMPEnvironmentExplicitConstant->can(q{unset}), q{constant-only import does not imply unset} );
32 |
33 |  |  |  |  |  |  | # Retained private compatibility helper: cover both sides of each legacy
34 |  |  |  |  |  |  | # predicate independently.
35 | 1 |  |  |  |  | 187 | is OpenMP::Environment::_is_ge_if_set( 1, q{x} ),
36 |  |  |  |  |  |  | q{Value must be an integer great than or equal to 1},
37 |  |  |  |  |  |  | q{legacy integer helper rejects non-integers};
38 | 1 |  |  |  |  | 204 | is OpenMP::Environment::_is_ge_if_set( 1, 0 ),
39 |  |  |  |  |  |  | q{Value must be an integer great than or equal to 1},
40 |  |  |  |  |  |  | q{legacy integer helper rejects values below the minimum};
41 | 1 |  |  |  |  | 201 | is OpenMP::Environment::_is_ge_if_set( 1, 1 ), undef,
42 |  |  |  |  |  |  | q{legacy integer helper accepts the minimum};
43 |
44 |  |  |  |  |  |  | # OpenMP 5.2 generally permits leading/trailing whitespace in environment
45 |  |  |  |  |  |  | # values. The public boolean compatibility behavior should still unset false
46 |  |  |  |  |  |  | # values after that standard whitespace is ignored.
47 | 1 |  |  |  |  | 250 | my $whitespace_env = OpenMP::Environment->new;
48 | 1 |  |  |  |  | 2 | $whitespace_env->omp_dynamic(q{true});
49 | 1 |  |  |  |  | 2 | $whitespace_env->omp_dynamic(q{ false });
50 |  |  |  |  |  |  | ok !exists $ENV{OMP_DYNAMIC},
51 | 1 |  |  |  |  | 2 | q{OMP_DYNAMIC whitespace-wrapped FALSE retains historical unset behavior};
52 | 1 |  |  |  |  | 197 | $whitespace_env->omp_nested(q{true});
53 | 1 |  |  |  |  | 2 | $whitespace_env->omp_nested(q{ FALSE });
54 |  |  |  |  |  |  | ok !exists $ENV{OMP_NESTED},
55 | 1 |  |  |  |  | 2 | q{OMP_NESTED whitespace-wrapped FALSE retains historical unset behavior};
56 |
57 |  |  |  |  |  |  | # Assignment validation: exercise the strict legacy-validated failure path.
58 | 1 |  |  |  |  | 221 | local $@;
59 | 1 1 |  |  |  |  | 2 2 | eval { validate_assignment( q{OMP_CANCELLATION}, q{maybe} ) };
60 | 1 |  |  |  |  | 4 | like $@, qr/OMP_CANCELLATION/, q{strict assignment path can fail for historically validated variables};
61 |
62 |  |  |  |  |  |  | # assert_variable/assert_environment both distinguish absent values from keys
63 |  |  |  |  |  |  | # that exist but are undef.
64 | 1 |  |  |  |  | 201 | my %undefined_one = ( OMP_NUM_THREADS => undef );
65 | 1 |  |  |  |  | 2 | ok assert_variable( \%undefined_one, q{OMP_NUM_THREADS} ),
66 |  |  |  |  |  |  | q{assert_variable accepts an explicitly undefined supported value};
67 |
68 | 1 |  |  |  |  | 191 | my %undefined_environment = (
69 |  |  |  |  |  |  | OMP_NUM_THREADS => undef,
70 |  |  |  |  |  |  | OMP_SCHEDULE => q{static},
71 |  |  |  |  |  |  | );
72 | 1 |  |  |  |  | 2 | ok assert_environment( \%undefined_environment ),
73 |  |  |  |  |  |  | q{assert_environment skips explicitly undefined supported values};
74 |
75 |  |  |  |  |  |  | # Cover each stage of the portable OMP_NESTED / OMP_MAX_ACTIVE_LEVELS
76 |  |  |  |  |  |  | # relationship without any system probing.
77 | 1 |  |  |  |  | 205 | my $a = analyze_environment( { OMP_NESTED => q{FALSE} } );
78 | 1 |  |  |  |  | 3 | ok $a->{valid}, q{FALSE OMP_NESTED alone is valid};
79 |
80 | 1 |  |  |  |  | 190 | $a = analyze_environment({
81 |  |  |  |  |  |  | OMP_NESTED => q{FALSE},
82 |  |  |  |  |  |  | OMP_MAX_ACTIVE_LEVELS => q{bogus},
83 |  |  |  |  |  |  | });
84 | 1 |  |  |  |  | 3 | ok !$a->{valid}, q{nonnumeric max-active-levels is a value error rather than a cross-variable conflict};
85 | 1 1 |  |  |  |  | 211 3 | is scalar( @{ $a->{conflicts} } ), 0,
86 |  |  |  |  |  |  | q{nonnumeric max-active-levels does not enter numeric conflict logic};
87 |
88 | 1 |  |  |  |  | 206 | $a = analyze_environment({
89 |  |  |  |  |  |  | OMP_NESTED => q{FALSE},
90 |  |  |  |  |  |  | OMP_MAX_ACTIVE_LEVELS => 1,
91 |  |  |  |  |  |  | });
92 | 1 |  |  |  |  | 3 | ok $a->{valid}, q{FALSE OMP_NESTED with one active level is not the implementation-defined conflict};
93 | 1 1 |  |  |  |  | 188 3 | is scalar( @{ $a->{conflicts} } ), 0,
94 |  |  |  |  |  |  | q{one active level leaves the conflict list empty};
95 |
96 | 1 |  |  |  |  | 203 | $a = analyze_environment({
97 |  |  |  |  |  |  | OMP_NESTED => q{FALSE},
98 |  |  |  |  |  |  | OMP_MAX_ACTIVE_LEVELS => 2,
99 |  |  |  |  |  |  | });
100 | 1 |  |  |  |  | 3 | ok !$a->{valid}, q{FALSE OMP_NESTED with more than one active level is flagged};
101 | 1 1 |  |  |  |  | 213 3 | is scalar( @{ $a->{conflicts} } ), 1,
102 |  |  |  |  |  |  | q{the implementation-defined combination produces one conflict};
103 |
104 |  |  |  |  |  |  | # Precedence/nesting notes: cover the partial and alternate paths separately.
105 | 1 |  |  |  |  | 202 | $a = analyze_environment({ GOMP_CPU_AFFINITY => q{0-3} });
106 | 1 |  |  |  |  | 3 | ok $a->{valid}, q{GOMP_CPU_AFFINITY alone is valid};
107 | 1 1 |  |  |  |  | 192 3 | is scalar( @{ $a->{notes} } ), 0,
108 |  |  |  |  |  |  | q{GNU affinity precedence note requires OMP_PROC_BIND too};
109 |
110 | 1 |  |  |  |  | 204 | $a = analyze_environment({ OMP_NESTED => q{TRUE} });
111 | 1 |  |  |  |  | 3 | ok $a->{valid}, q{OMP_NESTED alone is valid};
112 | 1 1 |  |  |  |  | 188 3 | is scalar( @{ $a->{notes} } ), 0,
113 |  |  |  |  |  |  | q{OMP_NESTED relationship note requires OMP_MAX_ACTIVE_LEVELS too};
114 |
115 | 1 |  |  |  |  | 202 | $a = analyze_environment({
116 |  |  |  |  |  |  | OMP_NUM_THREADS => 8,
117 |  |  |  |  |  |  | OMP_PROC_BIND => q{SPREAD,CLOSE},
118 |  |  |  |  |  |  | });
119 | 1 |  |  |  |  | 3 | ok $a->{valid}, q{PROC_BIND list with scalar NUM_THREADS is valid};
120 | 1 1 |  |  |  |  | 188 5 | ok grep( /max-active-levels-var/, @{ $a->{notes} } ),
121 |  |  |  |  |  |  | q{PROC_BIND list independently triggers the nested-level initialization note};
122 |
123 | 1 |  |  |  |  | 184 | $a = analyze_environment({
124 |  |  |  |  |  |  | OMP_NUM_THREADS => 8,
125 |  |  |  |  |  |  | OMP_PROC_BIND => q{CLOSE},
126 |  |  |  |  |  |  | });
127 | 1 |  |  |  |  | 3 | ok $a->{valid}, q{single-item thread and binding settings are valid};
128 | 1 1 |  |  |  |  | 185 2 | is scalar( @{ $a->{notes} } ), 0,
129 |  |  |  |  |  |  | q{single-item values do not trigger the nested-list note};
130 |
131 |  |  |  |  |  |  | # OMP_PLACES parser edges exercise parenthesis state, top-level separator
132 |  |  |  |  |  |  | # behavior, explicit place suffixes, and resource strides.
133 | 1 |  |  |  |  | 216 | for my $bad (
134 |  |  |  |  |  |  | q{vendor(1,2)},
135 |  |  |  |  |  |  | q{vendor(1,2},
136 |  |  |  |  |  |  | q{vendor(1,2))},
137 |  |  |  |  |  |  | ) {
138 | 3 |  |  |  |  | 376 | local $@;
139 | 3 3 |  |  |  |  | 3 5 | eval { validate_value( q{OMP_PLACES}, $bad ) };
140 | 3 |  |  |  |  | 10 | like $@, qr/OMP_PLACES|Expected an explicit/, qq{OMP_PLACES rejects malformed parenthesized form $bad};
141 |  |  |  |  |  |  | }
142 |
143 | 1 |  |  |  |  | 186 | is validate_value( q{OMP_PLACES}, q{0:4:-1} ), q{0:4:-1},
144 |  |  |  |  |  |  | q{OMP_PLACES accepts a signed explicit-place stride};
145 | 1 |  |  |  |  | 203 | is validate_value( q{OMP_PLACES}, q{{0:4:-1}} ), q{{0:4:-1}},
146 |  |  |  |  |  |  | q{OMP_PLACES accepts a signed resource stride};
147 |
148 |
149 |  |  |  |  |  |  | # Public OMP_PLACES parsing rejects unbalanced input before the lower-level
150 |  |  |  |  |  |  | # place helper is reached; call the helper directly to cover its defensive
151 |  |  |  |  |  |  | # unbalanced-brace return and the matching-brace end-of-input path.
152 | 1 |  |  |  |  | 203 | is OpenMP::Environment::Validation::_validate_place_interval(q[{0]),
153 |  |  |  |  |  |  | q{Unbalanced braces in OMP_PLACES},
154 |  |  |  |  |  |  | q{place-interval helper reports an unbalanced opening brace};
155 | 1 |  |  |  |  | 202 | is OpenMP::Environment::Validation::_matching_brace(q[{0]), -1,
156 |  |  |  |  |  |  | q{matching-brace helper reports end-of-input without a close};
157 |
158 |  |  |  |  |  |  | # A colon-bearing unknown memory-space name reaches the second half of the
159 |  |  |  |  |  |  | # allocator name/space decision rather than the no-colon form.
160 | 1 |  |  |  |  | 200 | local $@;
161 | 1 1 |  |  |  |  | 2 2 | eval { validate_value( q{OMP_ALLOCATOR}, q{not_a_space:alignment=16} ) };
162 | 1 |  |  |  |  | 4 | like $@, qr/predefined OpenMP allocator or memory space/,
163 |  |  |  |  |  |  | q{OMP_ALLOCATOR rejects an unknown colon-qualified memory space};
164 |
165 |  |  |  |  |  |  | # Cover width syntax on the long affinity field form.
166 | 1 |  |  |  |  | 195 | is validate_value( q{OMP_AFFINITY_FORMAT}, q{thread %.3{thread_num}} ),
167 |  |  |  |  |  |  | q{thread %.3{thread_num}},
168 |  |  |  |  |  |  | q{OMP_AFFINITY_FORMAT accepts width syntax on a long field};
169 |
170 |  |  |  |  |  |  | # Direct helper coverage for the defined/no-comma/comma cases used by
171 |  |  |  |  |  |  | # analyze_environment. These are internal policy helpers, not system probes.
172 | 1 |  |  |  |  | 203 | is OpenMP::Environment::Validation::_has_multiple_items(undef), 0,
173 |  |  |  |  |  |  | q{multiple-item helper handles undef};
174 | 1 |  |  |  |  | 199 | is OpenMP::Environment::Validation::_has_multiple_items(q{8}), 0,
175 |  |  |  |  |  |  | q{multiple-item helper handles a scalar value};
176 | 1 |  |  |  |  | 201 | is OpenMP::Environment::Validation::_has_multiple_items(q{8,4}), 1,
177 |  |  |  |  |  |  | q{multiple-item helper recognizes a list};
178 | 1 |  |  |  |  | 198 | is OpenMP::Environment::Validation::_has_proc_bind_list(undef), 0,
179 |  |  |  |  |  |  | q{proc-bind list helper handles undef};
180 | 1 |  |  |  |  | 198 | is OpenMP::Environment::Validation::_has_proc_bind_list(q{CLOSE}), 0,
181 |  |  |  |  |  |  | q{proc-bind list helper handles one policy};
182 | 1 |  |  |  |  | 199 | is OpenMP::Environment::Validation::_has_proc_bind_list(q{CLOSE,SPREAD}), 1,
183 |  |  |  |  |  |  | q{proc-bind list helper recognizes multiple policies};
184 |
185 |  |  |  |  |  |  | # The enum helper has both successful and rejected paths and is used by many
186 |  |  |  |  |  |  | # public validators.
187 | 1 |  |  |  |  | 196 | is OpenMP::Environment::Validation::_enum( q{TRUE}, qw/TRUE FALSE/ ), undef,
188 |  |  |  |  |  |  | q{enum helper recognizes an allowed value};
189 | 1 |  |  |  |  | 243 | like OpenMP::Environment::Validation::_enum( q{MAYBE}, qw/TRUE FALSE/ ),
190 |  |  |  |  |  |  | qr/Expected one of/,
191 |  |  |  |  |  |  | q{enum helper reports a rejected value};
192 |
193 | 1 |  |  |  |  | 189 | done_testing;
194 |