| File: | t/10-coverage.t |
| Coverage: | 98.3% |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | 1 1 1 | 4608 3 39 | use strict; | ||||
| 2 | 1 1 1 | 5 1 55 | use warnings; | ||||
| 3 | |||||||
| 4 | 1 1 1 | 806 1566 153 | use FindBin qw/$Bin/; | ||||
| 5 | 1 1 1 | 414 894 10 | use lib qq{$Bin/../lib}; | ||||
| 6 | 1 1 1 | 843 124504 5 | use Test::More; | ||||
| 7 | |||||||
| 8 | 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 | 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 | 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 | 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 | |||||||