From 3925c081104394e6ee9768a1a2b9d7904811aa23 Mon Sep 17 00:00:00 2001 From: Geoff Appleby Date: Tue, 13 Feb 2024 18:42:32 -0800 Subject: [PATCH] Refactor converting HTTPWG rulesets --- tests/Httpwg/HttpwgRuleExpectedConverter.php | 157 ++++++++++++++++++ tests/Httpwg/HttpwgTest.php | 162 ++----------------- 2 files changed, 174 insertions(+), 145 deletions(-) create mode 100644 tests/Httpwg/HttpwgRuleExpectedConverter.php diff --git a/tests/Httpwg/HttpwgRuleExpectedConverter.php b/tests/Httpwg/HttpwgRuleExpectedConverter.php new file mode 100644 index 0000000..09a118d --- /dev/null +++ b/tests/Httpwg/HttpwgRuleExpectedConverter.php @@ -0,0 +1,157 @@ + + * @phpstan-type ExpectedOuterList array + * @phpstan-type ExpectedInnerList array{array, ExpectedParameters} + * @phpstan-type ExpectedTuple ExpectedItem|ExpectedInnerList + * @phpstan-type ExpectedBareValue bool|int|float|string|ExpectedTypedValue + * @phpstan-type ExpectedTypedValue object{__type: 'binary'|'date'|'displaystring'|'token', value: int|string} + * @phpstan-type ExpectedParameters array + */ +class HttpwgRuleExpectedConverter +{ + /** + * Convert the expected value of an item tuple. + * + * @param ExpectedItem $item + * @return Item + */ + public static function item(array $item): Item + { + return new Item(self::value($item[0]), self::parameters($item[1])); + } + + /** + * Convert the expected values of a dictionary. + * + * @param ExpectedDictionary $dictionary + * @return Dictionary + */ + public static function dictionary(array $dictionary): Dictionary + { + $output = new Dictionary(); + + foreach ($dictionary as $value) { + // Null byte is not supported as first character of property name. + if (strpos($value[0], "\0") === 0) { + throw new \UnexpectedValueException(); + } + + if (is_array($value[1][0])) { + $output->{$value[0]} = self::innerList($value[1]); + } else { + $output->{$value[0]} = self::item($value[1]); + } + } + + return $output; + } + + /** + * Convert the expected values of a list. + * + * @param ExpectedOuterList $list + * @return OuterList + */ + public static function list(array $list): OuterList + { + $output = new OuterList(); + + foreach ($list as $value) { + if (is_array($value[0])) { + $output[] = self::innerList($value); + } else { + $output[] = self::item($value); + } + } + + return $output; + } + + /** + * Convert the expected values of a parameters map. + * + * @param ExpectedParameters $parameters + * @return Parameters + */ + private static function parameters(array $parameters): Parameters + { + $output = new Parameters(); + + foreach ($parameters as $value) { + // Null byte is not supported as first character of property name. + if (strpos($value[0], "\0") === 0) { + throw new \UnexpectedValueException(); + } + + $output->{$value[0]} = self::value($value[1]); + } + + return $output; + } + + /** + * Convert the expected values of an inner list tuple. + * + * @param ExpectedInnerList $innerList + * @return InnerList + */ + private static function innerList(array $innerList): InnerList + { + $outputList = []; + + foreach ($innerList[0] as $value) { + $outputList[] = new Item(self::value($value[0]), self::parameters($value[1])); + } + + return new InnerList($outputList, self::parameters($innerList[1])); + } + + /** + * Convert any encoded special values to typed objects. + * + * @param ExpectedBareValue $data + * The expected bare value. + * @return bool|int|float|string|Bytes|Date|DisplayString|Token + */ + private static function value($data) + { + if (!is_object($data)) { + return $data; + } + + if (property_exists($data, '__type')) { + switch ($data->__type) { + case 'binary': + assert(is_string($data->value)); + return new Bytes(Base32::decodeUpper($data->value)); + case 'date': + assert(is_int($data->value)); + return new Date($data->value); + case 'displaystring': + assert(is_string($data->value)); + return new DisplayString($data->value); + case 'token': + assert(is_string($data->value)); + return new Token($data->value); + } + } + + throw new \UnexpectedValueException("Unknown value type"); + } +} diff --git a/tests/Httpwg/HttpwgTest.php b/tests/Httpwg/HttpwgTest.php index bea80a0..bf90eba 100644 --- a/tests/Httpwg/HttpwgTest.php +++ b/tests/Httpwg/HttpwgTest.php @@ -2,26 +2,9 @@ namespace gapple\Tests\StructuredFields\Httpwg; -use gapple\StructuredFields\Bytes; -use gapple\StructuredFields\Date; -use gapple\StructuredFields\Dictionary; -use gapple\StructuredFields\DisplayString; -use gapple\StructuredFields\InnerList; -use gapple\StructuredFields\Item; -use gapple\StructuredFields\OuterList; -use gapple\StructuredFields\Parameters; -use gapple\StructuredFields\Token; use gapple\Tests\StructuredFields\Rule; use gapple\Tests\StructuredFields\RulesetTest; -use ParagonIE\ConstantTime\Base32; -/** - * @phpstan-type ExpectedParameters array - * @phpstan-type ExpectedTuple array{mixed, ExpectedParameters} - * @phpstan-type ExpectedInnerList array{array, ExpectedParameters} - * @phpstan-type ExpectedOuterList array - * @phpstan-type ExpectedDictionary array - */ abstract class HttpwgTest extends RulesetTest { /** @@ -51,16 +34,28 @@ protected function rulesetDataProvider(): array } $dataset = []; - foreach ($rules as $rule) { - if (isset($rule->expected)) { + foreach ($rules as $rawRule) { + if (isset($rawRule->expected)) { try { - $rule->expected = self::{"convertExpected" . ucfirst($rule->header_type)}($rule->expected); - } catch (\UnexpectedValueException $e) { + switch ($rawRule->header_type) { + case 'item': + $rawRule->expected = HttpwgRuleExpectedConverter::item($rawRule->expected); + break; + case 'list': + $rawRule->expected = HttpwgRuleExpectedConverter::list($rawRule->expected); + break; + case 'dictionary': + $rawRule->expected = HttpwgRuleExpectedConverter::dictionary($rawRule->expected); + break; + default: + throw new \UnexpectedValueException('Unknown header type'); + } + } catch (\UnexpectedValueException | \AssertionError $e) { // Skip rules that cannot be parsed. continue; } } - $rule = Rule::fromClass($rule); + $rule = Rule::fromClass($rawRule); if (isset($dataset[$rule->name])) { user_error( @@ -74,127 +69,4 @@ protected function rulesetDataProvider(): array return $dataset; } - - /** - * Convert the expected value of an item tuple. - * - * @param ExpectedTuple $item - * @return Item - */ - private static function convertExpectedItem(array $item): Item - { - return new Item(self::convertValue($item[0]), self::convertParameters($item[1])); - } - - /** - * Convert the expected values of a parameters map. - * - * @param ExpectedParameters $parameters - * @return Parameters - */ - private static function convertParameters(array $parameters): Parameters - { - $output = new Parameters(); - - foreach ($parameters as $value) { - // Null byte is not supported as first character of property name. - if (strpos($value[0], "\0") === 0) { - throw new \UnexpectedValueException(); - } - - $output->{$value[0]} = self::convertValue($value[1]); - } - - return $output; - } - - /** - * Convert the expected values of an inner list tuple. - * - * @param ExpectedInnerList $innerList - * @return InnerList - */ - private static function convertInnerList(array $innerList): InnerList - { - $outputList = []; - - foreach ($innerList[0] as $value) { - $outputList[] = new Item(self::convertValue($value[0]), self::convertParameters($value[1])); - } - - return new InnerList($outputList, self::convertParameters($innerList[1])); - } - - /** - * Convert the expected values of a list. - * - * @param ExpectedOuterList $list - * @return OuterList - */ - private static function convertExpectedList(array $list): OuterList - { - $output = new OuterList(); - - foreach ($list as $value) { - if (is_array($value[0])) { - $output[] = self::convertInnerList($value); - } else { - $output[] = self::convertExpectedItem($value); - } - } - - return $output; - } - - /** - * Convert the expected values of a dictionary. - * - * @param ExpectedDictionary $dictionary - * @return Dictionary - */ - private static function convertExpectedDictionary(array $dictionary): Dictionary - { - $output = new Dictionary(); - - foreach ($dictionary as $value) { - // Null byte is not supported as first character of property name. - if (strpos($value[0], "\0") === 0) { - throw new \UnexpectedValueException(); - } - - if (is_array($value[1][0])) { - $output->{$value[0]} = self::convertInnerList($value[1]); - } else { - $output->{$value[0]} = self::convertExpectedItem($value[1]); - } - } - - return $output; - } - - /** - * Convert any encoded special values to typed objects. - * - * @param mixed $data - * The expected bare value. - * @return mixed - */ - private static function convertValue($data) - { - if (is_object($data) && property_exists($data, '__type')) { - /** @var \stdClass $data */ - switch ($data->__type) { - case 'token': - return new Token($data->value); - case 'binary': - return new Bytes(Base32::decodeUpper($data->value)); - case 'date': - return new Date($data->value); - case 'displaystring': - return new DisplayString($data->value); - } - } - - return $data; - } }