Skip to content

Commit c2b222c

Browse files
authored
support loading labels from environment and build improvements (fixes #51, via #50)
1 parent 80da4fb commit c2b222c

22 files changed

+438
-43
lines changed

.github/workflows/build.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ jobs:
1313
tests:
1414
name: PHP ${{ matrix.php-version }} on ${{ matrix.os }} (${{ matrix.composer-options }})
1515
runs-on: ${{ matrix.os }}
16-
continue-on-error: ${{ matrix.php-version == '8.1' }}
16+
continue-on-error: ${{ matrix.php-version == '8.2' }}
1717
strategy:
1818
fail-fast: false
1919
matrix:
2020
php-version:
2121
- "8.0"
2222
- "8.1"
23-
os:
23+
- "8.2"
24+
os:
2425
- ubuntu-latest
2526
- windows-latest
2627
- macOS-latest
@@ -49,10 +50,10 @@ jobs:
4950
${{ matrix.composer-options }}
5051

5152
- name: Run tests
52-
if: ${{ matrix.php-version != '8.1' }}
53+
if: ${{ matrix.php-version != '8.2' }}
5354
run: composer test
5455

5556
- name: Run tests (experimental)
56-
if: ${{ matrix.php-version == '8.1' }}
57+
if: ${{ matrix.php-version == '8.2' }}
5758
continue-on-error: true
5859
run: composer test

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,16 @@
2929
"require": {
3030
"php": "^8",
3131
"ext-json": "*",
32-
"doctrine/annotations": "^1.12",
32+
"doctrine/annotations": "^1.12 || ^2",
3333
"psr/log": "^1 || ^2 || ^3",
3434
"ramsey/uuid": "^3 || ^4"
3535
},
3636
"require-dev": {
3737
"jetbrains/phpstorm-attributes": "^1",
3838
"phpunit/phpunit": "^9.5.10",
3939
"psalm/plugin-phpunit": "^0.18.4",
40-
"squizlabs/php_codesniffer": "^3.6.2",
41-
"vimeo/psalm": "^4.15"
40+
"squizlabs/php_codesniffer": "^3.7.1",
41+
"vimeo/psalm": "^5.4"
4242
},
4343
"autoload": {
4444
"psr-4": {

src/Attribute/AttributeReader.php

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,19 @@
1515
use function array_values;
1616
use function class_exists;
1717
use function is_a;
18+
use function str_starts_with;
19+
use function strlen;
20+
use function substr;
1821

1922
final class AttributeReader implements AttributeReaderInterface
2023
{
24+
private const ENV_LABEL_PREFIX = 'ALLURE_LABEL_';
25+
26+
public function __construct(
27+
private string $envLabelPrefix = self::ENV_LABEL_PREFIX,
28+
) {
29+
}
30+
2131
/**
2232
* @param ReflectionClass $class
2333
* @param class-string|null $name
@@ -64,7 +74,7 @@ public function getFunctionAnnotations(ReflectionFunction $function, ?string $na
6474
*/
6575
private function getAttributeInstances(ReflectionAttribute ...$attributes): array
6676
{
67-
/** @var array<ReflectionAttribute<AttributeInterface>> $filteredAttributes */
77+
/** @psalm-var array<ReflectionAttribute<AttributeInterface>> $filteredAttributes */
6878
$filteredAttributes = array_filter(
6979
$attributes,
7080
fn (ReflectionAttribute $attribute): bool =>
@@ -77,4 +87,25 @@ class_exists($attribute->getName()) &&
7787
array_values($filteredAttributes),
7888
);
7989
}
90+
91+
/**
92+
* @param array $variables
93+
* @return list<AttributeInterface>
94+
*/
95+
public function getEnvironmentAnnotations(array $variables): array
96+
{
97+
$labels = [];
98+
/** @psalm-var mixed $value */
99+
foreach ($variables as $variableName => $value) {
100+
if (str_starts_with((string) $variableName, $this->envLabelPrefix)) {
101+
$labelName = substr((string) $variableName, strlen($this->envLabelPrefix));
102+
if ('' == $labelName) {
103+
continue;
104+
}
105+
$labels[] = new Label($labelName, isset($value) ? (string) $value : null);
106+
}
107+
}
108+
109+
return $labels;
110+
}
80111
}

src/Attribute/AttributeReaderInterface.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,10 @@ public function getPropertyAnnotations(ReflectionProperty $property, ?string $na
3838
* @return list<AttributeInterface>
3939
*/
4040
public function getFunctionAnnotations(ReflectionFunction $function, ?string $name = null): array;
41+
42+
/**
43+
* @param array $variables
44+
* @return list<AttributeInterface>
45+
*/
46+
public function getEnvironmentAnnotations(array $variables): array;
4147
}

src/Attribute/LegacyAttributeReader.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,13 @@ private function convertLegacyAnnotations(?string $name, object ...$annotations)
109109

110110
return array_values($filteredResult);
111111
}
112+
113+
/**
114+
* @param array $variables
115+
* @return list<AttributeInterface>
116+
*/
117+
public function getEnvironmentAnnotations(array $variables): array
118+
{
119+
return [];
120+
}
112121
}

test/AllureLifecycleTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public function testGetCurrentTest_Constructed_ReturnsCurrentTestFromContext(?st
8080
/**
8181
* @return iterable<string, array{string|null}>
8282
*/
83-
public function providerGetCurrentTest(): iterable
83+
public static function providerGetCurrentTest(): iterable
8484
{
8585
return [
8686
'No current test' => [null],
@@ -111,7 +111,7 @@ public function testGetCurrentStep_Constructed_ReturnsCurrentStepFromContext(?st
111111
/**
112112
* @return iterable<string, array{string|null}>
113113
*/
114-
public function providerGetCurrentStep(): iterable
114+
public static function providerGetCurrentStep(): iterable
115115
{
116116
return [
117117
'No current step' => [null],
@@ -142,7 +142,7 @@ public function testGetCurrentTestOrStep_Constructed_ReturnsCurrentTestOrStepFro
142142
/**
143143
* @return iterable<string, array{string|null}>
144144
*/
145-
public function providerGetCurrentTestOrStep(): iterable
145+
public static function providerGetCurrentTestOrStep(): iterable
146146
{
147147
return [
148148
'No current test or step' => [null],
@@ -153,7 +153,7 @@ public function providerGetCurrentTestOrStep(): iterable
153153
/**
154154
* @return iterable<string, array{string|null}>
155155
*/
156-
public function providerSwitchThread(): iterable
156+
public static function providerSwitchThread(): iterable
157157
{
158158
return [
159159
'Default thread' => [null],

test/AllureTest.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ public function testRunStep_ExceptionThrownDuringStep_StepStatusIsProvidedByDete
360360
/**
361361
* @return iterable<string, array{mixed}>
362362
*/
363-
public function providerRunStepResult(): iterable
363+
public static function providerRunStepResult(): iterable
364364
{
365365
return [
366366
'Null' => [null],
@@ -411,7 +411,7 @@ public function testAttachment_ResultFactoryProvidesAttachment_AttachmentHasGive
411411
/**
412412
* @return iterable<string, array{string, string|null, string|null}>
413413
*/
414-
public function providerAttachmentProperties(): iterable
414+
public static function providerAttachmentProperties(): iterable
415415
{
416416
return [
417417
'Only name' => ['c', null, null],
@@ -711,7 +711,7 @@ public function testParameter_GivenValue_TestHasParameterWithSameValue(?string $
711711
/**
712712
* @return iterable<string, array{string|null}>
713713
*/
714-
public function providerParameterValue(): iterable
714+
public static function providerParameterValue(): iterable
715715
{
716716
return [
717717
'Null value' => [null],
@@ -756,7 +756,7 @@ public function testParameter_GivenExcludedFlag_TestHasParameterWithSameExcluded
756756
/**
757757
* @return iterable<string, array{bool}>
758758
*/
759-
public function providerParameterExcluded(): iterable
759+
public static function providerParameterExcluded(): iterable
760760
{
761761
return [
762762
'Excluded' => [true],
@@ -786,7 +786,7 @@ public function testParameter_GivenMode_TestHasParameterWithSameMode(?ParameterM
786786
/**
787787
* @return iterable<string, array{ParameterMode|null}>
788788
*/
789-
public function providerParameterMode(): iterable
789+
public static function providerParameterMode(): iterable
790790
{
791791
return [
792792
'Null mode' => [null],
@@ -934,7 +934,7 @@ public function testLink_GivenType_TestHasLinkWithSameType(): void
934934
/**
935935
* @return iterable<string, array{string|null}>
936936
*/
937-
public function providerLinkName(): iterable
937+
public static function providerLinkName(): iterable
938938
{
939939
return [
940940
'Null name' => [null],

test/Attribute/AnnotationTestTrait.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,9 @@ protected function getAttributeInstance(string $attributeClass, string $demoMeth
5252
}
5353
$attribute = $method->getAttributes($attributeClass)[0] ?? null;
5454

55-
$instance = isset($attribute)
55+
return isset($attribute)
5656
? $attribute->newInstance()
5757
: throw new RuntimeException("Attribute {$attributeClass} not found in {$demoMethodName}");
58-
59-
return $instance instanceof $attributeClass
60-
? $instance
61-
: throw new RuntimeException("Attribute is not {$attributeClass} instance");
6258
}
6359

6460
protected function exportLabel(LabelInterface $label): array
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Qameta\Allure\Test\Attribute;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Qameta\Allure\Attribute\AttributeInterface;
9+
use Qameta\Allure\Attribute\AttributeReader;
10+
use Qameta\Allure\Attribute\Label;
11+
use Qameta\Allure\Attribute\LabelInterface;
12+
13+
use function array_map;
14+
use function array_values;
15+
16+
/**
17+
* @covers \Qameta\Allure\Attribute\AttributeReader
18+
*/
19+
class AttributeReaderTest extends TestCase
20+
{
21+
/**
22+
* @param string $envLabelPrefix
23+
* @param array $variables
24+
* @return void
25+
* @dataProvider providerVariablesWithoutLabels
26+
*/
27+
public function testGetEnvironmentAnnotations_VariablesWithoutLabels_ReturnsEmptyList(
28+
string $envLabelPrefix,
29+
array $variables,
30+
): void {
31+
$reader = new AttributeReader($envLabelPrefix);
32+
$attributes = $reader->getEnvironmentAnnotations($variables);
33+
self::assertEmpty($attributes);
34+
}
35+
36+
/**
37+
* @return iterable<string, array{string, array}>
38+
*/
39+
public static function providerVariablesWithoutLabels(): iterable
40+
{
41+
return [
42+
'No variables' => ['a', []],
43+
'Variables with no matching prefix' => ['a', ['b' => 'c', 'd' => 'e']],
44+
'Variable with full name as prefix' => ['a', ['a' => 'b']],
45+
];
46+
}
47+
48+
/**
49+
* @param string $envLabelPrefix
50+
* @param array $variables
51+
* @param list<array> $expectedData
52+
* @return void
53+
* @dataProvider providerVariablesWithLabels
54+
*/
55+
public function testGetEnvironment_VariablesWithLabels_ReturnMatchingLabels(
56+
string $envLabelPrefix,
57+
array $variables,
58+
array $expectedData,
59+
): void {
60+
$reader = new AttributeReader($envLabelPrefix);
61+
$attributes = $reader->getEnvironmentAnnotations($variables);
62+
self::assertSame($expectedData, $this->exportAttributes(...$attributes));
63+
}
64+
65+
/**
66+
* @return iterable<string, array{string, array, list<array>}>
67+
*/
68+
public static function providerVariablesWithLabels(): iterable
69+
{
70+
return [
71+
'Variable with double prefix' => [
72+
'a',
73+
['aa' => 'b'],
74+
[['class' => Label::class, 'name' => 'a', 'value' => 'b']],
75+
],
76+
'Variable with non-string name' => [
77+
'1',
78+
[12 => 'a'],
79+
[['class' => Label::class, 'name' => '2', 'value' => 'a']],
80+
],
81+
'Variable with non-string value' => [
82+
'a',
83+
['ab' => 1],
84+
[['class' => Label::class, 'name' => 'b', 'value' => '1']],
85+
],
86+
'Variable with uppercase name' => [
87+
'a',
88+
['aB' => 1],
89+
[['class' => Label::class, 'name' => 'B', 'value' => '1']],
90+
],
91+
'First variable with matching prefix' => [
92+
'a',
93+
['ab' => 'c', 'd' => 'e'],
94+
[['class' => Label::class, 'name' => 'b', 'value' => 'c']],
95+
],
96+
'Second variable with matching prefix' => [
97+
'a',
98+
['b' => 'c', 'ad' => 'e'],
99+
[['class' => Label::class, 'name' => 'd', 'value' => 'e']],
100+
],
101+
'Two variables with matching prefix' => [
102+
'a',
103+
['ab' => 'c', 'ad' => 'e'],
104+
[
105+
['class' => Label::class, 'name' => 'b', 'value' => 'c'],
106+
['class' => Label::class, 'name' => 'd', 'value' => 'e'],
107+
],
108+
],
109+
];
110+
}
111+
112+
/**
113+
* @param AttributeInterface ...$attributes
114+
* @return list<array>
115+
*/
116+
private function exportAttributes(AttributeInterface ...$attributes): array
117+
{
118+
return array_map([$this, 'exportAttribute'], array_values($attributes));
119+
}
120+
121+
/**
122+
* @param AttributeInterface $attribute
123+
* @return array<string, mixed>
124+
*/
125+
private function exportAttribute(AttributeInterface $attribute): array
126+
{
127+
$data = ['class' => $attribute::class];
128+
if ($attribute instanceof LabelInterface) {
129+
$data['name'] = $attribute->getName();
130+
$data['value'] = $attribute->getValue();
131+
}
132+
133+
return $data;
134+
}
135+
}

test/Attribute/IssueTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public function testGetName_Loaded_ReturnsMatchingValue(string $methodName, ?str
3333
/**
3434
* @return iterable<string, array{string, string|null}>
3535
*/
36-
public function providerGetName(): iterable
36+
public static function providerGetName(): iterable
3737
{
3838
return [
3939
'No arguments' => ['demoNoArguments', null],
@@ -55,7 +55,7 @@ public function testGetUrl_Loaded_ReturnsMatchingValue(string $methodName, ?stri
5555
/**
5656
* @return iterable<string, array{string, string|null}>
5757
*/
58-
public function providerGetUrl(): iterable
58+
public static function providerGetUrl(): iterable
5959
{
6060
return [
6161
'No arguments' => ['demoNoArguments', null],

0 commit comments

Comments
 (0)