From 5e23347cf7b869a9bedc75d8de302b7d90075be4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= <theo.fidry@gmail.com>
Date: Sun, 14 Apr 2024 19:34:28 +0200
Subject: [PATCH] test: Tweak the SpecFormatter

- Rename it to `SpecPrinter`
- Passes the `SpecScenario` object for simplicitation
- Add tests
---
 .../{SpecFormatter.php => SpecPrinter.php}    |  37 +-
 tests/Scoper/Spec/SpecPrinterTest.php         | 454 ++++++++++++++++++
 tests/Scoper/Spec/SpecScenario.php            |  10 +-
 3 files changed, 472 insertions(+), 29 deletions(-)
 rename tests/Scoper/Spec/{SpecFormatter.php => SpecPrinter.php} (90%)
 create mode 100644 tests/Scoper/Spec/SpecPrinterTest.php

diff --git a/tests/Scoper/Spec/SpecFormatter.php b/tests/Scoper/Spec/SpecPrinter.php
similarity index 90%
rename from tests/Scoper/Spec/SpecFormatter.php
rename to tests/Scoper/Spec/SpecPrinter.php
index 1e1e218d..1ab39054 100644
--- a/tests/Scoper/Spec/SpecFormatter.php
+++ b/tests/Scoper/Spec/SpecPrinter.php
@@ -14,12 +14,10 @@
 
 namespace Humbug\PhpScoper\Scoper\Spec;
 
-use Humbug\PhpScoper\Configuration\SymbolsConfiguration;
 use Humbug\PhpScoper\NotInstantiable;
 use Humbug\PhpScoper\Symbol\NamespaceRegistry;
 use Humbug\PhpScoper\Symbol\SymbolRegistry;
 use Humbug\PhpScoper\Symbol\SymbolsRegistry;
-use PHPUnit\Framework\Attributes\Group;
 use PHPUnit\Framework\TestCase;
 use function array_map;
 use function count;
@@ -34,26 +32,23 @@
 /**
  * @internal
  */
-#[Group('integration')]
-class SpecFormatter extends TestCase
+final class SpecPrinter extends TestCase
 {
     use NotInstantiable;
 
-    /**
-     * @param string[][] $expectedRegisteredClasses
-     * @param string[][] $expectedRegisteredFunctions
-     */
     public static function createSpecMessage(
-        string $file,
-        string $spec,
-        string $contents,
-        SymbolsConfiguration $symbolsConfiguration,
+        SpecScenario $scenario,
         SymbolsRegistry $symbolsRegistry,
-        ?string $expected,
-        ?string $actual,
-        array $expectedRegisteredClasses,
-        array $expectedRegisteredFunctions
+        ?string $actualCode,
     ): string {
+        $file = $scenario->file;
+        $title = $scenario->title;
+        $inputCode = $scenario->inputCode;
+        $symbolsConfiguration = $scenario->symbolsConfiguration;
+        $expectedCode = $scenario->expectedCode;
+        $expectedRegisteredClasses = $scenario->expectedRegisteredClasses;
+        $expectedRegisteredFunctions = $scenario->expectedRegisteredFunctions;
+
         $formattedExposeGlobalClasses = self::convertBoolToString($symbolsConfiguration->shouldExposeGlobalClasses());
         $formattedExposeGlobalConstants = self::convertBoolToString($symbolsConfiguration->shouldExposeGlobalConstants());
         $formattedExposeGlobalFunctions = self::convertBoolToString($symbolsConfiguration->shouldExposeGlobalFunctions());
@@ -78,7 +73,7 @@ public static function createSpecMessage(
         $titleSeparator = str_repeat(
             '=',
             min(
-                strlen($spec),
+                strlen($title),
                 80,
             ),
         );
@@ -87,7 +82,7 @@ public static function createSpecMessage(
             {$titleSeparator}
             SPECIFICATION
             {$titleSeparator}
-            {$spec}
+            {$title}
             {$file}
 
             {$titleSeparator}
@@ -107,12 +102,12 @@ public static function createSpecMessage(
             (raw) internal functions: {$formattedInternalFunctions}
             (raw) internal constants: {$formattedInternalConstants}
             {$titleSeparator}
-            {$contents}
+            {$inputCode}
 
             {$titleSeparator}
             EXPECTED
             {$titleSeparator}
-            {$expected}
+            {$expectedCode}
             ----------------
             recorded functions: {$formattedExpectedRegisteredFunctions}
             recorded classes: {$formattedExpectedRegisteredClasses}
@@ -120,7 +115,7 @@ public static function createSpecMessage(
             {$titleSeparator}
             ACTUAL
             {$titleSeparator}
-            {$actual}
+            {$actualCode}
             ----------------
             recorded functions: {$formattedActualRegisteredFunctions}
             recorded classes: {$formattedActualRegisteredClasses}
diff --git a/tests/Scoper/Spec/SpecPrinterTest.php b/tests/Scoper/Spec/SpecPrinterTest.php
new file mode 100644
index 00000000..0369e705
--- /dev/null
+++ b/tests/Scoper/Spec/SpecPrinterTest.php
@@ -0,0 +1,454 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the humbug/php-scoper package.
+ *
+ * Copyright (c) 2017 Théo FIDRY <theo.fidry@gmail.com>,
+ *                    Pádraic Brady <padraic.brady@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Humbug\PhpScoper\Scoper\Spec;
+
+use Humbug\PhpScoper\Configuration\RegexChecker;
+use Humbug\PhpScoper\Configuration\SymbolsConfiguration;
+use Humbug\PhpScoper\Configuration\SymbolsConfigurationFactory;
+use Humbug\PhpScoper\Symbol\SymbolsRegistry;
+use PhpParser\Node\Name\FullyQualified;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\DataProvider;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @internal
+ */
+#[CoversClass(SpecPrinter::class)]
+final class SpecPrinterTest extends TestCase
+{
+    #[DataProvider('scenarioProvider')]
+    public function test_it_can_print_a_scenario(
+        SpecScenario $scenario,
+        SymbolsRegistry $symbolsRegistry,
+        ?string $actualCode,
+        string $expected,
+    ): void {
+        $actual = SpecPrinter::createSpecMessage(
+            $scenario,
+            $symbolsRegistry,
+            $actualCode,
+        );
+
+        self::assertSame($expected, $actual);
+    }
+
+    public static function scenarioProvider(): iterable
+    {
+        $specCode = <<<'PHP'
+            echo "Hello world!";
+
+            PHP;
+
+        $expectedCode = <<<'PHP'
+            namespace Humbug;
+
+            echo "Hello world!";
+
+            PHP;
+
+        $actualCode = <<<'PHP'
+            namespace Prefix;
+
+            echo "Hello world!";
+
+            PHP;
+
+        yield 'simple scenario' => [
+            new SpecScenario(
+                null,
+                null,
+                'Fixtures/complete-spec-file.php',
+                '[Example of simple spec file] Spec with the more verbose form',
+                $specCode,
+                'Humbug',
+                self::createSymbolsConfiguration([]),
+                $expectedCode,
+                [],
+                [],
+            ),
+            new SymbolsRegistry(),
+            $actualCode,
+            <<<'TEXT'
+                =============================================================
+                SPECIFICATION
+                =============================================================
+                [Example of simple spec file] Spec with the more verbose form
+                Fixtures/complete-spec-file.php
+
+                =============================================================
+                INPUT
+                expose global classes: true
+                expose global functions: true
+                expose global constants: true
+
+                exclude namespaces: []
+                expose namespaces: []
+
+                expose classes: []
+                expose functions: []
+                expose constants: []
+
+                (raw) internal classes: []
+                (raw) internal functions: []
+                (raw) internal constants: []
+                =============================================================
+                echo "Hello world!";
+
+
+                =============================================================
+                EXPECTED
+                =============================================================
+                namespace Humbug;
+
+                echo "Hello world!";
+
+                ----------------
+                recorded functions: []
+                recorded classes: []
+
+                =============================================================
+                ACTUAL
+                =============================================================
+                namespace Prefix;
+
+                echo "Hello world!";
+
+                ----------------
+                recorded functions: []
+                recorded classes: []
+
+                -------------------------------------------------------------------------------
+                TEXT,
+        ];
+
+        yield 'complete scenario without symbols' => [
+            new SpecScenario(
+                72_000,
+                83_000,
+                'Fixtures/complete-spec-file.php',
+                '[Example of simple spec file] Spec with the more verbose form',
+                $specCode,
+                'Humbug',
+                self::createSymbolsConfiguration([
+                    'expose-global-constants' => true,
+                    'expose-global-classes' => true,
+                    'expose-global-functions' => true,
+                    'expose-namespaces' => ['ExposedNamespace'],
+                    'expose-constants' => ['EXPOSED_CONSTANT'],
+                    'expose-classes' => ['ExposedClass'],
+                    'expose-functions' => ['exposed_function'],
+                    'exclude-namespaces' => ['ExcludedNamespace'],
+                    'exclude-constants' => ['EXCLUDED_CONSTANT'],
+                    'exclude-classes' => ['ExcludedClass'],
+                    'exclude-functions' => ['excluded_function'],
+                ]),
+                $expectedCode,
+                [['Acme\RecordedClass', 'Humbug\Acme\RecordedClass']],
+                [['Acme\recorded_function', 'Humbug\Acme\recorded_function']],
+            ),
+            new SymbolsRegistry(),
+            $actualCode,
+            <<<'TEXT'
+                =============================================================
+                SPECIFICATION
+                =============================================================
+                [Example of simple spec file] Spec with the more verbose form
+                Fixtures/complete-spec-file.php
+
+                =============================================================
+                INPUT
+                expose global classes: true
+                expose global functions: true
+                expose global constants: true
+
+                exclude namespaces: [ excludednamespace ]
+                expose namespaces: [ exposednamespace ]
+
+                expose classes: [ exposedclass ]
+                expose functions: [ exposed_function ]
+                expose constants: [ EXPOSED_CONSTANT ]
+
+                (raw) internal classes: [ excludedclass ]
+                (raw) internal functions: [ excluded_function ]
+                (raw) internal constants: [ EXCLUDED_CONSTANT ]
+                =============================================================
+                echo "Hello world!";
+
+
+                =============================================================
+                EXPECTED
+                =============================================================
+                namespace Humbug;
+
+                echo "Hello world!";
+
+                ----------------
+                recorded functions: [Acme\recorded_function => Humbug\Acme\recorded_function]
+                recorded classes: [Acme\RecordedClass => Humbug\Acme\RecordedClass]
+
+                =============================================================
+                ACTUAL
+                =============================================================
+                namespace Prefix;
+
+                echo "Hello world!";
+
+                ----------------
+                recorded functions: []
+                recorded classes: []
+
+                -------------------------------------------------------------------------------
+                TEXT,
+        ];
+
+        yield 'complete scenario with multiple items without symbols' => [
+            new SpecScenario(
+                72_000,
+                83_000,
+                'Fixtures/complete-spec-file.php',
+                '[Example of simple spec file] Spec with the more verbose form',
+                $specCode,
+                'Humbug',
+                self::createSymbolsConfiguration([
+                    'expose-global-constants' => true,
+                    'expose-global-classes' => true,
+                    'expose-global-functions' => true,
+                    'expose-namespaces' => ['ExposedNamespace', 'AnotherExposedNamespace'],
+                    'expose-constants' => ['EXPOSED_CONSTANT', 'ANOTHER_EXPOSED_CONSTANT'],
+                    'expose-classes' => ['ExposedClass', 'AnotherExposedClass'],
+                    'expose-functions' => ['exposed_function', 'another_exposed_function'],
+                    'exclude-namespaces' => ['ExcludedNamespace', 'AnotherExcludedNamespace'],
+                    'exclude-constants' => ['EXCLUDED_CONSTANT', 'ANOTHER_EXCLUDED_CONSTANT'],
+                    'exclude-classes' => ['ExcludedClass', 'AnotherExcludedClass'],
+                    'exclude-functions' => ['excluded_function', 'another_excluded_function'],
+                ]),
+                $expectedCode,
+                [
+                    ['Acme\RecordedClass', 'Humbug\Acme\RecordedClass'],
+                    ['Acme\AnotherRecordedClass', 'Humbug\Acme\AnotherRecordedClass'],
+                ],
+                [
+                    ['Acme\recorded_function', 'Humbug\Acme\recorded_function'],
+                    ['Acme\another_recorded_function', 'Humbug\Acme\another_recorded_function'],
+                ],
+            ),
+            new SymbolsRegistry(),
+            $actualCode,
+            <<<'TEXT'
+                =============================================================
+                SPECIFICATION
+                =============================================================
+                [Example of simple spec file] Spec with the more verbose form
+                Fixtures/complete-spec-file.php
+
+                =============================================================
+                INPUT
+                expose global classes: true
+                expose global functions: true
+                expose global constants: true
+
+                exclude namespaces: [
+                  - excludednamespace
+                  - anotherexcludednamespace
+                ]
+                expose namespaces: [
+                  - exposednamespace
+                  - anotherexposednamespace
+                ]
+
+                expose classes: [
+                  - exposedclass
+                  - anotherexposedclass
+                ]
+                expose functions: [
+                  - exposed_function
+                  - another_exposed_function
+                ]
+                expose constants: [
+                  - EXPOSED_CONSTANT
+                  - ANOTHER_EXPOSED_CONSTANT
+                ]
+
+                (raw) internal classes: [
+                  - excludedclass
+                  - anotherexcludedclass
+                ]
+                (raw) internal functions: [
+                  - excluded_function
+                  - another_excluded_function
+                ]
+                (raw) internal constants: [
+                  - EXCLUDED_CONSTANT
+                  - ANOTHER_EXCLUDED_CONSTANT
+                ]
+                =============================================================
+                echo "Hello world!";
+
+
+                =============================================================
+                EXPECTED
+                =============================================================
+                namespace Humbug;
+
+                echo "Hello world!";
+
+                ----------------
+                recorded functions: [
+                  - Acme\recorded_function => Humbug\Acme\recorded_function
+                  - Acme\another_recorded_function => Humbug\Acme\another_recorded_function
+                ]
+                recorded classes: [
+                  - Acme\RecordedClass => Humbug\Acme\RecordedClass
+                  - Acme\AnotherRecordedClass => Humbug\Acme\AnotherRecordedClass
+                ]
+
+                =============================================================
+                ACTUAL
+                =============================================================
+                namespace Prefix;
+
+                echo "Hello world!";
+
+                ----------------
+                recorded functions: []
+                recorded classes: []
+
+                -------------------------------------------------------------------------------
+                TEXT,
+        ];
+
+        yield 'simple scenario with recorded symbols' => [
+            new SpecScenario(
+                null,
+                null,
+                'Fixtures/complete-spec-file.php',
+                '[Example of simple spec file] Spec with the more verbose form',
+                $specCode,
+                'Humbug',
+                self::createSymbolsConfiguration([]),
+                $expectedCode,
+                [],
+                [],
+            ),
+            self::createRegistry(
+                [
+                    'recorded_function' => 'Humbug\recorded_function',
+                    'another_recorded_function' => 'Humbug\another_recorded_function',
+                ],
+                [
+                    'RecordedClass' => 'Humbug\RecordedClass',
+                    'AnotherRecordedClass' => 'Humbug\AnotherRecordedClass',
+                ],
+            ),
+            $actualCode,
+            <<<'TEXT'
+                =============================================================
+                SPECIFICATION
+                =============================================================
+                [Example of simple spec file] Spec with the more verbose form
+                Fixtures/complete-spec-file.php
+
+                =============================================================
+                INPUT
+                expose global classes: true
+                expose global functions: true
+                expose global constants: true
+
+                exclude namespaces: []
+                expose namespaces: []
+
+                expose classes: []
+                expose functions: []
+                expose constants: []
+
+                (raw) internal classes: []
+                (raw) internal functions: []
+                (raw) internal constants: []
+                =============================================================
+                echo "Hello world!";
+
+
+                =============================================================
+                EXPECTED
+                =============================================================
+                namespace Humbug;
+
+                echo "Hello world!";
+
+                ----------------
+                recorded functions: []
+                recorded classes: []
+
+                =============================================================
+                ACTUAL
+                =============================================================
+                namespace Prefix;
+
+                echo "Hello world!";
+
+                ----------------
+                recorded functions: [
+                  - recorded_function => Humbug\recorded_function
+                  - another_recorded_function => Humbug\another_recorded_function
+                ]
+                recorded classes: [
+                  - RecordedClass => Humbug\RecordedClass
+                  - AnotherRecordedClass => Humbug\AnotherRecordedClass
+                ]
+
+                -------------------------------------------------------------------------------
+                TEXT,
+        ];
+    }
+
+    private static function createSymbolsConfiguration(array $config): SymbolsConfiguration
+    {
+        static $factory;
+
+        if (!isset($factory)) {
+            $factory = new SymbolsConfigurationFactory(new RegexChecker());
+        }
+
+        return $factory->createSymbolsConfiguration($config);
+    }
+
+    /**
+     * @param array<string, string> $functions
+     * @param array<string, string> $classes
+     */
+    private static function createRegistry(
+        array $functions,
+        array $classes,
+    ): SymbolsRegistry {
+        $registry = new SymbolsRegistry();
+
+        foreach ($functions as $original => $alias) {
+            $registry->recordFunction(
+                new FullyQualified($original),
+                new FullyQualified($alias),
+            );
+        }
+
+        foreach ($classes as $original => $alias) {
+            $registry->recordClass(
+                new FullyQualified($original),
+                new FullyQualified($alias),
+            );
+        }
+
+        return $registry;
+    }
+}
diff --git a/tests/Scoper/Spec/SpecScenario.php b/tests/Scoper/Spec/SpecScenario.php
index af26f81b..76908b35 100644
--- a/tests/Scoper/Spec/SpecScenario.php
+++ b/tests/Scoper/Spec/SpecScenario.php
@@ -83,16 +83,10 @@ public function assertExpectedResult(
         SymbolsRegistry $symbolsRegistry,
         ?string $actualCode,
     ): void {
-        $specMessage = SpecFormatter::createSpecMessage(
-            $this->file,
-            $this->title,
-            $this->inputCode,
-            $this->symbolsConfiguration,
+        $specMessage = SpecPrinter::createSpecMessage(
+            $this,
             $symbolsRegistry,
-            $this->expectedCode,
             $actualCode,
-            $this->expectedRegisteredClasses,
-            $this->expectedRegisteredFunctions,
         );
 
         $assert->assertSame($this->expectedCode, $actualCode, $specMessage);