From 000bed4a6fa921f2e5c67564807e94d89b448bbe Mon Sep 17 00:00:00 2001 From: "Deployment Bot (from Travis CI)" Date: Tue, 13 Apr 2021 15:33:11 +0000 Subject: [PATCH 01/16] Deploy phpbench/phpbench to github.com/phpbench/phpbench.git:gh-pages --- .github/workflows/ci.yaml | 187 +++++ .github/workflows/documentation.yaml | 63 ++ .github/workflows/release-phar.yml | 57 ++ .gitignore | 16 + .gitignoree | 16 + .php_cs.dist | 60 ++ .readthedocs.yml | 11 + .travis.yml | 33 + .travis/before-deploy-gh-release.sh | 17 + .travis/before-deploy.sh | 32 + .travis/secrets.tar.enc | Bin 0 -> 10256 bytes .travis/xdebug.sh | 22 + CHANGELOG.md | 334 +++++++++ LICENSE | 20 + README.md | 76 +++ bin/build-phar.sh | 22 + bin/phpbench | 4 + bin/phpbench.php | 25 + box.json.dist | 28 + box.json.gh-release | 24 + box.phar | Bin 0 -> 961620 bytes composer.json | 89 +++ docs/.doctor-rst.yaml | 44 ++ docs/Makefile | 177 +++++ docs/_extension/approved.py | 49 ++ docs/_extension/codeimport.py | 112 +++ docs/_static/logo.png | Bin 0 -> 10205 bytes docs/_templates/about.html | 9 + docs/_templates/navigation.html | 10 + docs/annotributes.rst | 507 ++++++++++++++ docs/assertions.rst | 39 ++ docs/benchmark-runner.rst | 245 +++++++ docs/conf.py | 272 ++++++++ docs/configuration-prelude.rst | 28 + docs/configuration.rst | 553 +++++++++++++++ docs/contents.rst | 36 + docs/customisation.rst | 11 + docs/customisation/command.rst | 25 + docs/customisation/executor.rst | 23 + docs/customisation/extension.rst | 82 +++ docs/customisation/progress-logger.rst | 24 + docs/customisation/report.rst | 42 ++ docs/development.rst | 10 + docs/development/debugging.rst | 25 + docs/environment.rst | 76 +++ docs/executors.rst | 39 ++ docs/expression.rst | 273 ++++++++ docs/extensions.rst | 20 + docs/extensions/xdebug.rst | 45 ++ docs/faq.rst | 47 ++ docs/images/baseline.png | Bin 0 -> 111753 bytes docs/images/blinken.gif | Bin 0 -> 125759 bytes docs/images/html.png | Bin 0 -> 17244 bytes docs/images/profile.png | Bin 0 -> 67640 bytes docs/index.rst | 37 + docs/installing.rst | 57 ++ docs/quick-start.rst | 288 ++++++++ docs/reference.rst | 15 + docs/regression-testing.rst | 65 ++ docs/report-generators.rst | 12 + docs/report-generators/composite.rst | 22 + docs/report-generators/env.rst | 14 + docs/report-generators/expression.rst | 120 ++++ docs/report-renderers.rst | 62 ++ docs/reports.rst | 132 ++++ docs/requirements.txt | 5 + docs/storage.rst | 127 ++++ docs/writing-benchmarks.rst | 61 ++ examples/Annotations/AnnotatedBench.php | 174 +++++ examples/Assertion/ExampleAssertionsBench.php | 51 ++ examples/Attributes/AttriutedBench.php | 162 +++++ examples/Benchmark/Macro/BaseBenchCase.php | 128 ++++ examples/Benchmark/Macro/LogBench.php | 47 ++ examples/Benchmark/Macro/ReportBench.php | 63 ++ examples/Benchmark/Macro/RunBench.php | 37 + .../Macro/benchmarks/NothingBench.php | 20 + .../DependencyInjection/ContainerBench.php | 37 + examples/Benchmark/Micro/HashingBench.php | 36 + examples/Benchmark/Micro/Math/KdeBench.php | 51 ++ .../Benchmark/Micro/Math/StatisticsBench.php | 43 ++ examples/Benchmark/NothingBench.php | 21 + examples/Command/report-generators-break | 19 + .../Command/report-generators-column-override | 22 + .../report-generators-column-visibility | 18 + examples/Command/report-generators-composite | 36 + examples/Command/report-generators-data | 38 ++ examples/Command/report-generators-expression | 12 + .../Command/report-generators-expressions | 21 + .../Command/report-ref-latest-and-aggregate | 15 + examples/Command/run-configuring-reports | 14 + .../Command/run-configuring-reports-extend | 14 + .../run-configuring-reports-phpbenchjson | 21 + examples/Command/run-reports-aggregate | 14 + .../Command/run-reports-aggregate-and-env | 16 + examples/Command/run-reports-default | 17 + examples/Command/run-reports-env | 16 + examples/Expression/arithmetic_1 | 4 + examples/Expression/coalesce_1 | 1 + examples/Expression/comparison_1 | 1 + examples/Expression/display_as_time_1 | 1 + examples/Expression/first_1 | 1 + examples/Expression/func_format | 2 + examples/Expression/func_max | 1 + examples/Expression/func_mean | 1 + examples/Expression/func_min | 1 + examples/Expression/func_mode | 1 + examples/Expression/func_percent_diff | 1 + examples/Expression/func_rstdev | 3 + examples/Expression/func_stdev | 1 + examples/Expression/func_variance | 1 + examples/Expression/join_1 | 1 + examples/Expression/logical_operators_1 | 2 + examples/Expression/memory_unit_1 | 2 + examples/Expression/string_operators_1 | 1 + examples/Expression/time_unit_1 | 3 + examples/Expression/time_unit_2 | 1 + examples/Expression/time_unit_3 | 1 + examples/Expression/tolerance_1 | 1 + examples/Expression/tolerance_2 | 1 + examples/Extension/AcmeExtension.php | 84 +++ examples/Extension/Command/CatsCommand.php | 36 + examples/Extension/Executor/AcmeExecutor.php | 29 + .../Extension/ProgressLogger/CatLogger.php | 69 ++ examples/Extension/Report/AcmeGenerator.php | 48 ++ examples/OutputTest/AssertionTestBench.php | 14 + examples/OutputTest/OutputTestBench.php | 27 + examples/bootstrap.php | 0 .../lib/Command/Handler/OutputDirHandler.php | 65 ++ .../xdebug/lib/Command/ProfileCommand.php | 112 +++ .../xdebug/lib/Executor/ProfileExecutor.php | 100 +++ extensions/xdebug/lib/XDebugExtension.php | 79 +++ extensions/xdebug/lib/XDebugUtil.php | 31 + extensions/xdebug/lib/config/executors.php | 17 + .../tests/System/ProfileCommandTest.php | 70 ++ .../xdebug/tests/System/XDebugTestCase.php | 35 + extensions/xdebug/tests/System/bin/foogrind | 5 + .../xdebug/tests/Unit/XDebugUtilTest.php | 65 ++ lib/Assertion/AssertionProcessor.php | 96 +++ lib/Assertion/AssertionResult.php | 71 ++ lib/Assertion/Exception/AssertionError.php | 9 + lib/Assertion/ParameterProvider.php | 64 ++ lib/Assertion/VariantAssertionResults.php | 110 +++ lib/Attributes/AbstractMethodsAttribute.php | 19 + lib/Attributes/AfterClassMethods.php | 10 + lib/Attributes/AfterMethods.php | 10 + lib/Attributes/Assert.php | 19 + lib/Attributes/BeforeClassMethods.php | 10 + lib/Attributes/BeforeMethods.php | 10 + lib/Attributes/Executor.php | 28 + lib/Attributes/Format.php | 17 + lib/Attributes/Groups.php | 32 + lib/Attributes/Iterations.php | 29 + lib/Attributes/OutputMode.php | 34 + lib/Attributes/OutputTimeUnit.php | 25 + lib/Attributes/ParamProviders.php | 22 + lib/Attributes/RetryThreshold.php | 17 + lib/Attributes/Revs.php | 32 + lib/Attributes/Skip.php | 10 + lib/Attributes/Sleep.php | 19 + lib/Attributes/Subject.php | 10 + lib/Attributes/Timeout.php | 19 + lib/Attributes/Warmup.php | 22 + lib/Benchmark/Baseline/Baselines.php | 68 ++ lib/Benchmark/BenchmarkFinder.php | 121 ++++ lib/Benchmark/CartesianParameterIterator.php | 109 +++ .../Exception/RetryLimitReachedException.php | 9 + .../Exception/StopOnErrorException.php | 17 + lib/Benchmark/Metadata/AnnotationReader.php | 229 +++++++ .../Annotations/AbstractArrayAnnotation.php | 35 + .../Annotations/AbstractMethodsAnnotation.php | 35 + .../Annotations/AfterClassMethods.php | 20 + .../Metadata/Annotations/AfterMethods.php | 20 + lib/Benchmark/Metadata/Annotations/Assert.php | 41 ++ .../Annotations/BeforeClassMethods.php | 24 + .../Metadata/Annotations/BeforeMethods.php | 20 + .../Metadata/Annotations/Executor.php | 31 + lib/Benchmark/Metadata/Annotations/Format.php | 25 + lib/Benchmark/Metadata/Annotations/Groups.php | 36 + .../Metadata/Annotations/Iterations.php | 35 + .../Metadata/Annotations/OutputMode.php | 35 + .../Metadata/Annotations/OutputTimeUnit.php | 46 ++ .../Metadata/Annotations/ParamProviders.php | 36 + .../Metadata/Annotations/RetryThreshold.php | 38 ++ lib/Benchmark/Metadata/Annotations/Revs.php | 35 + lib/Benchmark/Metadata/Annotations/Skip.php | 21 + lib/Benchmark/Metadata/Annotations/Sleep.php | 35 + .../Metadata/Annotations/Subject.php | 21 + .../Metadata/Annotations/Timeout.php | 35 + lib/Benchmark/Metadata/Annotations/Warmup.php | 35 + lib/Benchmark/Metadata/AssertionMetadata.php | 31 + lib/Benchmark/Metadata/BenchmarkMetadata.php | 208 ++++++ .../Metadata/Driver/AnnotationDriver.php | 251 +++++++ .../Metadata/Driver/AttributeDriver.php | 217 ++++++ lib/Benchmark/Metadata/Driver/ChainDriver.php | 38 ++ .../Metadata/Driver/ConfigDriver.php | 149 ++++ lib/Benchmark/Metadata/DriverInterface.php | 26 + lib/Benchmark/Metadata/ExecutorMetadata.php | 39 ++ lib/Benchmark/Metadata/MetadataFactory.php | 128 ++++ lib/Benchmark/Metadata/SubjectMetadata.php | 422 ++++++++++++ lib/Benchmark/Runner.php | 369 ++++++++++ lib/Benchmark/RunnerConfig.php | 430 ++++++++++++ lib/Benchmark/SamplerManager.php | 80 +++ lib/Compat/SymfonyOptionsResolverCompat.php | 23 + lib/Console/Application.php | 50 ++ lib/Console/CharacterReader.php | 44 ++ .../Command/ConfigReferenceCommand.php | 40 ++ lib/Console/Command/EvaluateCommand.php | 119 ++++ lib/Console/Command/Handler/DumpHandler.php | 68 ++ lib/Console/Command/Handler/ReportHandler.php | 61 ++ lib/Console/Command/Handler/RunnerHandler.php | 163 +++++ .../Handler/SuiteCollectionHandler.php | 88 +++ .../Command/Handler/TimeUnitHandler.php | 59 ++ lib/Console/Command/LogCommand.php | 162 +++++ lib/Console/Command/ReportCommand.php | 106 +++ lib/Console/Command/RunCommand.php | 176 +++++ lib/Console/Command/SelfUpdateCommand.php | 90 +++ lib/Console/Command/ShowCommand.php | 92 +++ lib/Development/ConfigDumper.php | 96 +++ lib/Environment/Information.php | 114 ++++ .../Provider/AbstractRemoteProvider.php | 52 ++ lib/Environment/Provider/Git.php | 114 ++++ lib/Environment/Provider/Opcache.php | 31 + lib/Environment/Provider/Php.php | 31 + lib/Environment/Provider/Sampler.php | 62 ++ lib/Environment/Provider/TestProvider.php | 28 + lib/Environment/Provider/Uname.php | 44 ++ lib/Environment/Provider/UnixSysload.php | 37 + .../Provider/template/opcache.template | 23 + .../Provider/template/php.template | 10 + lib/Environment/ProviderInterface.php | 31 + lib/Environment/Supplier.php | 58 ++ lib/Environment/VcsInformation.php | 30 + .../ConfigurationPreProcessingError.php | 9 + lib/Executor/Benchmark/DebugExecutor.php | 88 +++ lib/Executor/Benchmark/LocalExecutor.php | 87 +++ .../MemoryCentricMicrotimeExecutor.php | 13 + lib/Executor/Benchmark/RemoteExecutor.php | 23 + lib/Executor/Benchmark/TemplateExecutor.php | 101 +++ lib/Executor/Benchmark/TestExecutor.php | 105 +++ .../Benchmark/template/memory.template | 79 +++ .../Benchmark/template/remote.template | 72 ++ lib/Executor/BenchmarkExecutorInterface.php | 25 + lib/Executor/CompositeExecutor.php | 61 ++ lib/Executor/Exception/ExecutionError.php | 9 + lib/Executor/ExecutionContext.php | 162 +++++ lib/Executor/ExecutionResults.php | 55 ++ .../HealthCheck/AlwaysFineHealthCheck.php | 15 + lib/Executor/HealthCheckInterface.php | 22 + .../Method/ErrorHandlingExecutorDecorator.php | 34 + lib/Executor/Method/LocalMethodExecutor.php | 36 + lib/Executor/Method/RemoteMethodExecutor.php | 35 + .../template/execute_static_methods.template | 27 + lib/Executor/MethodExecutorContext.php | 41 ++ lib/Executor/MethodExecutorInterface.php | 18 + lib/Expression/Ast/ArgumentListNode.php | 7 + lib/Expression/Ast/ArithmeticOperatorNode.php | 7 + lib/Expression/Ast/BinaryOperatorNode.php | 41 ++ lib/Expression/Ast/BooleanNode.php | 21 + lib/Expression/Ast/ComparisonNode.php | 7 + lib/Expression/Ast/ConcatNode.php | 44 ++ lib/Expression/Ast/ConcatenatedNode.php | 34 + lib/Expression/Ast/DelimitedListNode.php | 41 ++ lib/Expression/Ast/DisplayAsNode.php | 60 ++ lib/Expression/Ast/DisplayAsTimeNode.php | 22 + lib/Expression/Ast/FloatNode.php | 21 + lib/Expression/Ast/FunctionNode.php | 32 + lib/Expression/Ast/IntegerNode.php | 21 + lib/Expression/Ast/ListNode.php | 20 + lib/Expression/Ast/LogicalOperatorNode.php | 7 + lib/Expression/Ast/Node.php | 7 + lib/Expression/Ast/NullNode.php | 14 + lib/Expression/Ast/NumberNode.php | 11 + lib/Expression/Ast/NumberValue.php | 7 + lib/Expression/Ast/ParameterNode.php | 21 + lib/Expression/Ast/ParenthesisNode.php | 21 + lib/Expression/Ast/PercentDifferenceNode.php | 40 ++ lib/Expression/Ast/PercentageNode.php | 37 + lib/Expression/Ast/PhpValue.php | 11 + lib/Expression/Ast/PhpValueFactory.php | 45 ++ lib/Expression/Ast/RelativeDeviationNode.php | 7 + lib/Expression/Ast/StringNode.php | 24 + lib/Expression/Ast/TolerableNode.php | 32 + lib/Expression/Ast/ToleratedTrue.php | 14 + lib/Expression/Ast/UnitNode.php | 21 + lib/Expression/Ast/ValueWithUnitNode.php | 31 + lib/Expression/ColorMap.php | 16 + lib/Expression/Evaluator.php | 23 + lib/Expression/Evaluator/MainEvaluator.php | 52 ++ .../Evaluator/PrettyErrorEvaluator.php | 79 +++ lib/Expression/Exception/EvaluationError.php | 26 + lib/Expression/Exception/ExpressionError.php | 19 + lib/Expression/Exception/ParseletNotFound.php | 9 + lib/Expression/Exception/PrinterError.php | 9 + lib/Expression/Exception/SyntaxError.php | 41 ++ lib/Expression/ExpressionFunctions.php | 60 ++ lib/Expression/ExpressionLanguage.php | 10 + .../MemoisedExpressionLanguage.php | 35 + .../RealExpressionLanguage.php | 31 + lib/Expression/Func/CoalesceFunction.php | 22 + lib/Expression/Func/DisplayAsTimeFunction.php | 26 + lib/Expression/Func/FirstFunction.php | 22 + lib/Expression/Func/FormatFunction.php | 30 + lib/Expression/Func/JoinFunction.php | 19 + lib/Expression/Func/MaxFunction.php | 24 + lib/Expression/Func/MeanFunction.php | 16 + lib/Expression/Func/MinFunction.php | 24 + lib/Expression/Func/ModeFunction.php | 16 + .../Func/PercentDifferenceFunction.php | 18 + lib/Expression/Func/RStDevFunction.php | 19 + lib/Expression/Func/StDevFunction.php | 16 + lib/Expression/Func/VarianceFunction.php | 16 + lib/Expression/InfixParselet.php | 12 + lib/Expression/Lexer.php | 135 ++++ lib/Expression/NodeEvaluator.php | 13 + .../NodeEvaluator/AbstractEvaluator.php | 9 + .../NodeEvaluator/ArgumentListEvaluator.php | 28 + .../ArithmeticOperatorEvaluator.php | 56 ++ .../NodeEvaluator/ComparisonEvaluator.php | 87 +++ .../NodeEvaluator/ConcatEvaluator.php | 33 + .../NodeEvaluator/DisplayAsEvaluator.php | 36 + .../NodeEvaluator/FunctionEvaluator.php | 75 ++ .../NodeEvaluator/ListEvaluator.php | 25 + .../LogicalOperatorEvaluator.php | 54 ++ .../NodeEvaluator/MemoisedNodeEvaluator.php | 47 ++ .../NodeEvaluator/ParameterEvaluator.php | 97 +++ .../NodeEvaluator/ParenthesisEvaluator.php | 23 + .../NodeEvaluator/PhpValueEvaluator.php | 23 + .../NodeEvaluator/TolerableEvaluator.php | 46 ++ .../NodeEvaluator/ValueWithUnitEvaluator.php | 58 ++ lib/Expression/NodeEvaluators.php | 43 ++ lib/Expression/NodePrinter.php | 12 + .../NodePrinter/ArgumentListPrinter.php | 26 + .../NodePrinter/BinaryOperatorPrinter.php | 25 + lib/Expression/NodePrinter/BooleanPrinter.php | 20 + .../NodePrinter/ComparisonPrinter.php | 25 + lib/Expression/NodePrinter/ConcatPrinter.php | 26 + .../NodePrinter/ConcatenatedNodePrinter.php | 26 + .../NodePrinter/DisplayAsPrinter.php | 156 +++++ .../NodePrinter/FunctionPrinter.php | 20 + .../NodePrinter/HighlightingNodePrinter.php | 58 ++ lib/Expression/NodePrinter/ListPrinter.php | 26 + lib/Expression/NodePrinter/NullPrinter.php | 20 + lib/Expression/NodePrinter/NumberPrinter.php | 30 + .../NodePrinter/ParameterPrinter.php | 22 + .../NodePrinter/ParenthesisPrinter.php | 20 + .../PercentageDifferencePrinter.php | 30 + .../NodePrinter/PercentagePrinter.php | 26 + .../NodePrinter/RelativeDeviationPrinter.php | 26 + lib/Expression/NodePrinter/StringPrinter.php | 20 + .../NodePrinter/TolerablePrinter.php | 31 + lib/Expression/NodePrinter/UnitPrinter.php | 20 + .../NodePrinter/ValueWithUnitPrinter.php | 24 + lib/Expression/NodePrinters.php | 42 ++ lib/Expression/Parselet.php | 8 + .../Parselet/ArithmeticOperatorParselet.php | 45 ++ lib/Expression/Parselet/BooleanParselet.php | 29 + .../Parselet/ComparisonParselet.php | 45 ++ lib/Expression/Parselet/ConcatParselet.php | 32 + lib/Expression/Parselet/DisplayAsParselet.php | 63 ++ lib/Expression/Parselet/FloatParselet.php | 23 + lib/Expression/Parselet/FunctionParselet.php | 44 ++ lib/Expression/Parselet/IntegerParselet.php | 23 + lib/Expression/Parselet/ListParselet.php | 41 ++ .../Parselet/LogicalOperatorParselet.php | 45 ++ lib/Expression/Parselet/NullParselet.php | 25 + lib/Expression/Parselet/ParameterParselet.php | 30 + .../Parselet/ParenthesisParselet.php | 27 + .../Parselet/PercentageParselet.php | 24 + lib/Expression/Parselet/StringParselet.php | 23 + lib/Expression/Parselet/TolerableParselet.php | 31 + .../Parselet/ValueWithUnitParselet.php | 24 + lib/Expression/Parselets.php | 93 +++ lib/Expression/Parser.php | 121 ++++ lib/Expression/Precedence.php | 16 + lib/Expression/PrefixParselet.php | 10 + lib/Expression/Printer.php | 12 + lib/Expression/Printer/BareValuePrinter.php | 32 + lib/Expression/Printer/EvaluatingPrinter.php | 73 ++ lib/Expression/Printer/NormalizingPrinter.php | 25 + lib/Expression/Printer/UnderlinePrinter.php | 37 + .../Printer/UnderlinePrinterFactory.php | 24 + lib/Expression/SuffixParselet.php | 10 + lib/Expression/Theme/ArrayTheme.php | 34 + lib/Expression/Theme/EightColorTheme.php | 55 ++ lib/Expression/Theme/SolarizedTheme.php | 147 ++++ lib/Expression/Theme/Util/Color.php | 69 ++ lib/Expression/Theme/Util/Gradient.php | 82 +++ lib/Expression/Token.php | 80 +++ lib/Expression/Tokens.php | 120 ++++ lib/Extension/ConsoleExtension.php | 111 +++ lib/Extension/CoreExtension.php | 131 ++++ lib/Extension/DevelopmentExtension.php | 33 + lib/Extension/ExpressionExtension.php | 351 ++++++++++ lib/Extension/ReportExtension.php | 185 +++++ lib/Extension/RunnerExtension.php | 643 ++++++++++++++++++ lib/Extension/StorageExtension.php | 158 +++++ lib/Extension/config/benchmark/executors.php | 19 + lib/Extension/config/class/main.json | 42 ++ lib/Extension/config/report/generators.php | 59 ++ lib/Extension/config/report/renderers.php | 18 + lib/Functional/Functional.php | 49 ++ lib/Json/JsonDecoder.php | 166 +++++ lib/Logger/ConsoleLogger.php | 54 ++ lib/Math/Distribution.php | 190 ++++++ lib/Math/FloatNumber.php | 34 + lib/Math/Kde.php | 248 +++++++ lib/Math/Statistics.php | 223 ++++++ lib/Model/Benchmark.php | 123 ++++ lib/Model/Error.php | 87 +++ lib/Model/ErrorStack.php | 74 ++ lib/Model/Iteration.php | 51 ++ lib/Model/ParameterSet.php | 56 ++ lib/Model/ResolvedExecutor.php | 39 ++ lib/Model/Result/ComputedResult.php | 83 +++ lib/Model/Result/MemoryResult.php | 98 +++ lib/Model/Result/RejectionCountResult.php | 57 ++ lib/Model/Result/TimeResult.php | 92 +++ lib/Model/ResultCollection.php | 120 ++++ lib/Model/ResultInterface.php | 58 ++ lib/Model/Subject.php | 238 +++++++ lib/Model/Suite.php | 279 ++++++++ lib/Model/SuiteCollection.php | 91 +++ lib/Model/Summary.php | 186 +++++ lib/Model/Tag.php | 41 ++ lib/Model/Variant.php | 458 +++++++++++++ lib/PhpBench.php | 256 +++++++ lib/Progress/Logger/AnsiLogger.php | 39 ++ lib/Progress/Logger/BlinkenLogger.php | 234 +++++++ lib/Progress/Logger/DotsLogger.php | 109 +++ lib/Progress/Logger/HistogramLogger.php | 193 ++++++ lib/Progress/Logger/NullLogger.php | 100 +++ lib/Progress/Logger/PhpBenchLogger.php | 201 ++++++ lib/Progress/Logger/TravisLogger.php | 68 ++ lib/Progress/Logger/VerboseLogger.php | 111 +++ lib/Progress/LoggerInterface.php | 90 +++ lib/Progress/LoggerRegistry.php | 35 + lib/Progress/VariantFormatter.php | 10 + lib/Progress/VariantSummaryFormatter.php | 77 +++ lib/Reflection/ReflectionAttribute.php | 39 ++ lib/Reflection/ReflectionClass.php | 31 + lib/Reflection/ReflectionHierarchy.php | 110 +++ lib/Reflection/ReflectionMethod.php | 43 ++ lib/Reflection/ReflectorInterface.php | 23 + lib/Reflection/RemoteReflector.php | 185 +++++ .../template/parameter_set_extractor.template | 54 ++ lib/Reflection/template/reflector.template | 85 +++ lib/Registry/Config.php | 61 ++ lib/Registry/ConfigurableRegistry.php | 193 ++++++ lib/Registry/RegistrableInterface.php | 25 + lib/Registry/Registry.php | 151 ++++ lib/Remote/Exception/ScriptErrorException.php | 20 + lib/Remote/IniStringBuilder.php | 31 + lib/Remote/Launcher.php | 143 ++++ lib/Remote/Payload.php | 273 ++++++++ lib/Remote/PayloadFactory.php | 54 ++ lib/Remote/ProcessFactory.php | 28 + lib/Report/Generator/BareGenerator.php | 71 ++ lib/Report/Generator/CompositeGenerator.php | 55 ++ lib/Report/Generator/EnvGenerator.php | 78 +++ lib/Report/Generator/ExpressionGenerator.php | 341 ++++++++++ lib/Report/Generator/OutputTestGenerator.php | 66 ++ lib/Report/GeneratorInterface.php | 29 + lib/Report/Model/Report.php | 62 ++ lib/Report/Model/Reports.php | 66 ++ lib/Report/Model/Table.php | 74 ++ lib/Report/Model/TableRow.php | 54 ++ lib/Report/Renderer/ConsoleRenderer.php | 101 +++ lib/Report/Renderer/DelimitedRenderer.php | 122 ++++ lib/Report/RendererInterface.php | 30 + lib/Report/ReportManager.php | 96 +++ .../Transform/SuiteCollectionTransformer.php | 157 +++++ lib/Serializer/XmlDecoder.php | 312 +++++++++ lib/Serializer/XmlEncoder.php | 252 +++++++ lib/Storage/ArchiverInterface.php | 41 ++ .../Driver/Fake/FakeHistoryIterator.php | 59 ++ lib/Storage/Driver/Xml/HistoryIterator.php | 262 +++++++ lib/Storage/Driver/Xml/XmlDriver.php | 125 ++++ lib/Storage/DriverInterface.php | 50 ++ lib/Storage/Exception/InvalidTagException.php | 9 + .../Exception/TagNotFoundException.php | 9 + lib/Storage/HistoryEntry.php | 124 ++++ lib/Storage/HistoryIteratorInterface.php | 22 + lib/Storage/StorageRegistry.php | 28 + lib/Storage/UuidResolver.php | 25 + lib/Storage/UuidResolver/ChainResolver.php | 44 ++ lib/Storage/UuidResolver/LatestResolver.php | 76 +++ lib/Storage/UuidResolver/TagResolver.php | 69 ++ lib/Storage/UuidResolverInterface.php | 18 + lib/SymfonyOptionsResolverCompat.php | 0 lib/Util/Cast.php | 52 ++ lib/Util/MemoryUnit.php | 115 ++++ lib/Util/TextTruncate.php | 34 + lib/Util/TimeUnit.php | 416 +++++++++++ phpbench.json.dist | 41 ++ phpbench.phar | Bin 0 -> 1189493 bytes phpbench.phar.pubkey | 14 + phpbench.phar.version | 1 + phpstan-baseline.neon | 42 ++ phpstan.neon.dist | 14 + phpunit.xml.dist | 12 + .../Assertion/AssertionProcessorBench.php | 58 ++ .../Console/Command/RunCommandBench.php | 74 ++ tests/Benchmark/Expression/ParserBench.php | 60 ++ tests/Benchmark/IntegrationBenchCase.php | 36 + .../Progress/VariantSummaryFormatterBench.php | 58 ++ .../Generator/ExpressionGeneratorBench.php | 53 ++ tests/Example/CommandsTest.php | 86 +++ tests/Example/ExpressionLinterTest.php | 61 ++ tests/Integration/ConfiguredReportsTest.php | 74 ++ tests/Integration/generator/aggregate-csv | 2 + tests/Integration/generator/bare-console | 8 + tests/Integration/generator/bare-csv | 3 + tests/Integration/generator/bare-delimited | 3 + tests/Integration/generator/composite-console | 0 .../Integration/generator/composite-delimited | 0 tests/Integration/generator/default-csv | 3 + tests/Integration/generator/env-console | 2 + tests/Integration/generator/env-delimited | 1 + .../Integration/generator/expression-console | 7 + .../generator/expression-delimited | 2 + tests/Integration/generator/flat-console | 8 + tests/Integration/generator/flat-csv | 3 + tests/Integration/generator/flat-delimited | 3 + tests/Integration/generator/memory-csv | 3 + .../Integration/generator/output_test-console | 27 + .../generator/output_test-delimited | 6 + tests/IntegrationTestCase.php | 38 ++ tests/PhpBenchTestCase.php | 16 + tests/ProphecyTrait.php | 115 ++++ tests/System/ConfigReferenceTest.php | 13 + tests/System/EvalCommandTest.php | 37 + tests/System/LogTest.php | 30 + tests/System/ReportTest.php | 78 +++ tests/System/RunTest.php | 641 +++++++++++++++++ tests/System/ShowTest.php | 44 ++ tests/System/SystemTestCase.php | 107 +++ .../System/benchmarks/set1/BenchmarkBench.php | 53 ++ .../System/benchmarks/set1/IsolatedBench.php | 34 + .../set1/IsolatedParametersBench.php | 34 + .../benchmarks/set1/IsolatedRevsBench.php | 25 + .../set1/ParameterProviderCallableBench.php | 32 + tests/System/benchmarks/set2/FoobarBench.php | 25 + tests/System/benchmarks/set3/ErrorBench.php | 32 + .../benchmarks/set3/ErrorVariantsBench.php | 30 + tests/System/benchmarks/set4/NothingBench.php | 20 + .../benchmarks/set5/AssertFailBench.php | 24 + .../benchmarks/set5/AssertWarnBench.php | 23 + tests/System/benchmarks/set6/TimeoutBench.php | 14 + tests/System/bootstrap/conflicting.bootstrap | 9 + tests/System/bootstrap/foobar.bootstrap | 7 + .../System/env/config_dist/phpbench.json.dist | 4 + tests/System/env/config_set2/phpbench.json | 4 + .../env/config_valid/phpbench.dist.json | 3 + tests/System/env/config_valid/phpbench.json | 6 + .../env/config_valid/shouldnotbecalled.php | 13 + tests/TestCase.php | 32 + .../Unit/Assertion/AssertionProcessorTest.php | 80 +++ tests/Unit/Benchmark/BenchmarkFinderTest.php | 103 +++ .../Unit/Benchmark/CartesianIteratorTest.php | 236 +++++++ .../Metadata/AnnotationReaderTest.php | 86 +++ .../Metadata/BenchmarkMetadataTest.php | 86 +++ .../Metadata/Driver/AnnotationDriverTest.php | 515 ++++++++++++++ .../Metadata/Driver/AttributeDriverTest.php | 328 +++++++++ .../Metadata/Driver/ChainDriverTest.php | 43 ++ .../Metadata/Driver/ConfigDriverTest.php | 83 +++ .../Metadata/MetadataFactoryTest.php | 255 +++++++ .../Metadata/SubjectMetadataTest.php | 231 +++++++ .../Unit/Benchmark/Metadata/classes/Test.php | 28 + tests/Unit/Benchmark/RunnerConfigTest.php | 138 ++++ tests/Unit/Benchmark/RunnerTest.php | 442 ++++++++++++ tests/Unit/Benchmark/SamplerManagerTest.php | 86 +++ .../Benchmark/findertest/AbstractBench.php | 15 + .../Benchmark/findertest/FooCase2Bench.php | 15 + .../Benchmark/findertest/FooCaseBench.php | 76 +++ .../findertestnested/Directory/MyBench.php | 20 + .../Benchmark/findertestnested/MyBench.php | 20 + tests/Unit/Console/Command/LogCommandTest.php | 284 ++++++++ .../Console/Command/UpdateCommandTest.php | 96 +++ tests/Unit/Environment/InformationTest.php | 111 +++ tests/Unit/Environment/Provider/GitTest.php | 110 +++ .../Unit/Environment/Provider/OpcacheTest.php | 70 ++ tests/Unit/Environment/Provider/PhpTest.php | 61 ++ .../Unit/Environment/Provider/SamplerTest.php | 54 ++ tests/Unit/Environment/Provider/UnameTest.php | 52 ++ .../Environment/Provider/UnixSysloadTest.php | 50 ++ tests/Unit/Environment/SupplierTest.php | 67 ++ .../Benchmark/AbstractExecutorTestCase.php | 162 +++++ .../Executor/Benchmark/DebugExecutorTest.php | 115 ++++ .../Executor/Benchmark/LocalExecutorTest.php | 57 ++ .../Executor/Benchmark/RemoteExecutorTest.php | 53 ++ .../Method/LocalMethodExecutorTest.php | 70 ++ .../Method/RemoteMethodExecutorTest.php | 49 ++ .../Executor/ParameterSetExtractorTest.php | 71 ++ .../benchmarks/MicrotimeExecutorBench.php | 66 ++ .../benchmarks/ParamProviderBench.php | 56 ++ tests/Unit/Expression/Ast/ListNodeTest.php | 73 ++ .../Expression/Ast/PhpValueFactoryTest.php | 42 ++ .../Evaluator/ParameterEvaluatorTest.php | 164 +++++ tests/Unit/Expression/EvaluatorTestCase.php | 19 + .../Expression/Exception/SyntaxErrorTest.php | 53 ++ .../Expression/Func/CoalesceFunctionTest.php | 33 + .../Func/DisplayAsTimeFunctionTest.php | 58 ++ .../Expression/Func/FirstFunctionTest.php | 25 + .../Expression/Func/FormatFunctionTest.php | 32 + .../Unit/Expression/Func/JoinFunctionTest.php | 25 + .../Unit/Expression/Func/MaxFunctionTest.php | 18 + .../Unit/Expression/Func/MeanFunctionTest.php | 18 + .../Unit/Expression/Func/MinFunctionTest.php | 18 + .../Unit/Expression/Func/ModeFunctionTest.php | 17 + .../Func/PercentDifferenceFunctionTest.php | 25 + .../Expression/Func/StDevFunctionTest.php | 17 + .../Expression/Func/VarianceFunctionTest.php | 17 + tests/Unit/Expression/FunctionTestCase.php | 15 + tests/Unit/Expression/LexerTest.php | 26 + .../MemoisedNodeEvaluatorTest.php | 64 ++ tests/Unit/Expression/NodeEvaluatorsTest.php | 41 ++ .../HighlightingNodePrinterTest.php | 75 ++ .../Parselet/ArgumentListParseletTest.php | 61 ++ .../ArithmeticOperatorParseletTest.php | 46 ++ .../Parselet/BooleanParseletTest.php | 44 ++ .../Parselet/ComparisonParseletTest.php | 93 +++ .../Parselet/ConcatParseletTest.php | 52 ++ .../Parselet/DisplayAsParseletTest.php | 112 +++ .../Expression/Parselet/FloatParseletTest.php | 41 ++ .../Parselet/FunctionParseletTest.php | 71 ++ .../Parselet/IntegerParseletTest.php | 41 ++ .../Expression/Parselet/ListParseletTest.php | 44 ++ .../Parselet/LogicalOperatorParseletTest.php | 48 ++ .../Expression/Parselet/NullParseletTest.php | 45 ++ .../Parselet/ParameterParseletTest.php | 53 ++ .../Parselet/ParenthesisParseletTest.php | 54 ++ .../Parselet/StringParseletTest.php | 45 ++ .../Parselet/TolerableParseletTest.php | 98 +++ .../Parselet/ValueWithUnitParseletTest.php | 39 ++ tests/Unit/Expression/ParseletTestCase.php | 83 +++ tests/Unit/Expression/ParserTest.php | 19 + tests/Unit/Expression/ParserTestCase.php | 20 + .../Printer/BareValuePrinterTest.php | 45 ++ .../Printer/EvaluatingPrinterTest.php | 61 ++ .../Expression/Theme/Util/GradientTest.php | 140 ++++ tests/Unit/Expression/TokensTest.php | 20 + tests/Unit/Extension/RunnerExtensionTest.php | 50 ++ tests/Unit/Json/JsonDecoderTest.php | 139 ++++ tests/Unit/Logger/ConsoleLoggerTest.php | 42 ++ tests/Unit/Math/DistributionTest.php | 123 ++++ tests/Unit/Math/KdeTest.php | 106 +++ tests/Unit/Math/StatisticsTest.php | 292 ++++++++ tests/Unit/Model/BenchmarkTest.php | 100 +++ tests/Unit/Model/IterationTest.php | 51 ++ .../Model/Result/RejectionCountResultTest.php | 26 + tests/Unit/Model/Result/TimeResultTest.php | 68 ++ tests/Unit/Model/ResultCollectionTest.php | 99 +++ tests/Unit/Model/SubjectTest.php | 67 ++ tests/Unit/Model/SuiteCollectionTest.php | 61 ++ tests/Unit/Model/SuiteTest.php | 151 ++++ tests/Unit/Model/SummaryTest.php | 151 ++++ tests/Unit/Model/TagTest.php | 63 ++ tests/Unit/Model/VariantTest.php | 225 ++++++ .../Progress/Logger/BlinkenLoggerTest.php | 120 ++++ tests/Unit/Progress/Logger/DotsLoggerTest.php | 130 ++++ .../Progress/Logger/HistogramLoggerTest.php | 126 ++++ tests/Unit/Progress/Logger/LoggerTestCase.php | 41 ++ .../Progress/Logger/PhpBenchLoggerTest.php | 174 +++++ .../Unit/Progress/Logger/TravisLoggerTest.php | 57 ++ .../Progress/Logger/VerboseLoggerTest.php | 58 ++ tests/Unit/Progress/LoggerRegistryTest.php | 51 ++ .../Progress/VariantSummaryFormatterTest.php | 60 ++ .../Reflection/ReflectionHierarchyTest.php | 97 +++ tests/Unit/Reflection/RemoteReflectorTest.php | 263 +++++++ tests/Unit/Reflection/reflector/Class1.php | 23 + tests/Unit/Reflection/reflector/Class2.php | 23 + tests/Unit/Reflection/reflector/Class3.php | 30 + .../reflector/ClassWithClassKeywords.php | 37 + tests/Unit/Reflection/reflector/EmptyFile.php | 0 .../Reflection/reflector/ExampleClass.php | 73 ++ .../Reflection/reflector/ExampleClass2.php | 25 + tests/Unit/Registry/ConfigTest.php | 87 +++ .../Registry/ConfigurableRegistryTest.php | 162 +++++ tests/Unit/Registry/RegistryTest.php | 127 ++++ tests/Unit/Remote/IniStringBuilderTest.php | 63 ++ tests/Unit/Remote/LauncherTest.php | 110 +++ tests/Unit/Remote/PayloadFactoryTest.php | 36 + tests/Unit/Remote/PayloadTest.php | 158 +++++ tests/Unit/Remote/launcher/autoload.php | 13 + tests/Unit/Remote/template/foo.template | 5 + tests/Unit/Remote/template/invalid.template | 2 + .../Report/Generator/BareGeneratorTest.php | 23 + .../Generator/CompositeGeneratorTest.php | 54 ++ .../Report/Generator/EnvGeneratorTest.php | 30 + .../Generator/ExpressionGeneratorTest.php | 32 + .../Report/Generator/GeneratorTestCase.php | 79 +++ .../Unit/Report/Generator/bare/bare-vertical | 44 ++ tests/Unit/Report/Generator/bare/bare1 | 29 + tests/Unit/Report/Generator/env/env | 25 + tests/Unit/Report/Generator/env/env_empty | 8 + .../Report/Generator/expression/aggregate1 | 26 + .../Report/Generator/expression/aggregate2 | 24 + .../Report/Generator/expression/aggregate3 | 29 + .../Report/Generator/expression/aggregate4 | 20 + tests/Unit/Report/Generator/expression/break1 | 30 + tests/Unit/Report/Generator/expression/break2 | 22 + .../cols-baseline-has-les-iterations | 31 + .../cols-baseline-has-more-iterations | 31 + .../cols-baseline-subject-not-existing | 31 + .../Generator/expression/cols-baseline1 | 30 + .../Generator/expression/cols-selection | 36 + .../expression/cols-selection-baseline | 27 + tests/Unit/Report/Generator/expression/cols1 | 16 + tests/Unit/Report/Generator/expression/cols2 | 25 + tests/Unit/Report/Generator/expression/cols3 | 27 + tests/Unit/Report/Generator/expression/empty | 23 + .../Generator/expression/include_baseline1 | 30 + .../expression/subject_time_specification | 26 + .../subject_time_specification_with_baseline | 31 + .../subject_time_specification_with_nulls | 31 + .../Report/Generator/expression/suite-empty | 7 + .../Report/Renderer/AbstractRendererCase.php | 45 ++ .../Report/Renderer/ConsoleRendererTest.php | 57 ++ .../Report/Renderer/DelimitedRendererTest.php | 52 ++ tests/Unit/Report/Renderer/console/default | 29 + .../Report/Renderer/console/title-description | 31 + tests/Unit/Report/ReportManagerTest.php | 99 +++ tests/Unit/Serializer/XmlDecoderTest.php | 179 +++++ tests/Unit/Serializer/XmlEncoderTest.php | 52 ++ tests/Unit/Serializer/XmlTestCase.php | 249 +++++++ .../Driver/Xml/HistoryIteratorTest.php | 147 ++++ .../Unit/Storage/Driver/Xml/XmlDriverTest.php | 133 ++++ .../UuidResolver/ChainResolverTest.php | 60 ++ .../UuidResolver/LatestResolverTest.php | 107 +++ .../Storage/UuidResolver/TagResolverTest.php | 101 +++ tests/Unit/Util/MemoryUnitTest.php | 120 ++++ tests/Unit/Util/TextTruncateTest.php | 37 + tests/Unit/Util/TimeUnitTest.php | 335 +++++++++ tests/Util/Approval.php | 127 ++++ tests/Util/IterationBuilder.php | 42 ++ tests/Util/TestUtil.php | 195 ++++++ tests/Util/VariantBuilder.php | 66 ++ tests/Util/Workspace.php | 127 ++++ tests/bootstrap.php | 7 + 739 files changed, 47178 insertions(+) create mode 100644 .github/workflows/ci.yaml create mode 100644 .github/workflows/documentation.yaml create mode 100644 .github/workflows/release-phar.yml create mode 100644 .gitignore create mode 100644 .gitignoree create mode 100644 .php_cs.dist create mode 100644 .readthedocs.yml create mode 100644 .travis.yml create mode 100755 .travis/before-deploy-gh-release.sh create mode 100755 .travis/before-deploy.sh create mode 100644 .travis/secrets.tar.enc create mode 100644 .travis/xdebug.sh create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 README.md create mode 100755 bin/build-phar.sh create mode 100755 bin/phpbench create mode 100644 bin/phpbench.php create mode 100644 box.json.dist create mode 100644 box.json.gh-release create mode 100755 box.phar create mode 100644 composer.json create mode 100644 docs/.doctor-rst.yaml create mode 100644 docs/Makefile create mode 100644 docs/_extension/approved.py create mode 100644 docs/_extension/codeimport.py create mode 100644 docs/_static/logo.png create mode 100644 docs/_templates/about.html create mode 100644 docs/_templates/navigation.html create mode 100644 docs/annotributes.rst create mode 100644 docs/assertions.rst create mode 100644 docs/benchmark-runner.rst create mode 100644 docs/conf.py create mode 100644 docs/configuration-prelude.rst create mode 100644 docs/configuration.rst create mode 100644 docs/contents.rst create mode 100644 docs/customisation.rst create mode 100644 docs/customisation/command.rst create mode 100644 docs/customisation/executor.rst create mode 100644 docs/customisation/extension.rst create mode 100644 docs/customisation/progress-logger.rst create mode 100644 docs/customisation/report.rst create mode 100644 docs/development.rst create mode 100644 docs/development/debugging.rst create mode 100644 docs/environment.rst create mode 100644 docs/executors.rst create mode 100644 docs/expression.rst create mode 100644 docs/extensions.rst create mode 100644 docs/extensions/xdebug.rst create mode 100644 docs/faq.rst create mode 100644 docs/images/baseline.png create mode 100644 docs/images/blinken.gif create mode 100644 docs/images/html.png create mode 100644 docs/images/profile.png create mode 100644 docs/index.rst create mode 100644 docs/installing.rst create mode 100644 docs/quick-start.rst create mode 100644 docs/reference.rst create mode 100644 docs/regression-testing.rst create mode 100644 docs/report-generators.rst create mode 100644 docs/report-generators/composite.rst create mode 100644 docs/report-generators/env.rst create mode 100644 docs/report-generators/expression.rst create mode 100644 docs/report-renderers.rst create mode 100644 docs/reports.rst create mode 100644 docs/requirements.txt create mode 100644 docs/storage.rst create mode 100644 docs/writing-benchmarks.rst create mode 100644 examples/Annotations/AnnotatedBench.php create mode 100644 examples/Assertion/ExampleAssertionsBench.php create mode 100644 examples/Attributes/AttriutedBench.php create mode 100644 examples/Benchmark/Macro/BaseBenchCase.php create mode 100644 examples/Benchmark/Macro/LogBench.php create mode 100644 examples/Benchmark/Macro/ReportBench.php create mode 100644 examples/Benchmark/Macro/RunBench.php create mode 100644 examples/Benchmark/Macro/benchmarks/NothingBench.php create mode 100644 examples/Benchmark/Micro/DependencyInjection/ContainerBench.php create mode 100644 examples/Benchmark/Micro/HashingBench.php create mode 100644 examples/Benchmark/Micro/Math/KdeBench.php create mode 100644 examples/Benchmark/Micro/Math/StatisticsBench.php create mode 100644 examples/Benchmark/NothingBench.php create mode 100644 examples/Command/report-generators-break create mode 100644 examples/Command/report-generators-column-override create mode 100644 examples/Command/report-generators-column-visibility create mode 100644 examples/Command/report-generators-composite create mode 100644 examples/Command/report-generators-data create mode 100644 examples/Command/report-generators-expression create mode 100644 examples/Command/report-generators-expressions create mode 100644 examples/Command/report-ref-latest-and-aggregate create mode 100644 examples/Command/run-configuring-reports create mode 100644 examples/Command/run-configuring-reports-extend create mode 100644 examples/Command/run-configuring-reports-phpbenchjson create mode 100644 examples/Command/run-reports-aggregate create mode 100644 examples/Command/run-reports-aggregate-and-env create mode 100644 examples/Command/run-reports-default create mode 100644 examples/Command/run-reports-env create mode 100644 examples/Expression/arithmetic_1 create mode 100644 examples/Expression/coalesce_1 create mode 100644 examples/Expression/comparison_1 create mode 100644 examples/Expression/display_as_time_1 create mode 100644 examples/Expression/first_1 create mode 100644 examples/Expression/func_format create mode 100644 examples/Expression/func_max create mode 100644 examples/Expression/func_mean create mode 100644 examples/Expression/func_min create mode 100644 examples/Expression/func_mode create mode 100644 examples/Expression/func_percent_diff create mode 100644 examples/Expression/func_rstdev create mode 100644 examples/Expression/func_stdev create mode 100644 examples/Expression/func_variance create mode 100644 examples/Expression/join_1 create mode 100644 examples/Expression/logical_operators_1 create mode 100644 examples/Expression/memory_unit_1 create mode 100644 examples/Expression/string_operators_1 create mode 100644 examples/Expression/time_unit_1 create mode 100644 examples/Expression/time_unit_2 create mode 100644 examples/Expression/time_unit_3 create mode 100644 examples/Expression/tolerance_1 create mode 100644 examples/Expression/tolerance_2 create mode 100644 examples/Extension/AcmeExtension.php create mode 100644 examples/Extension/Command/CatsCommand.php create mode 100644 examples/Extension/Executor/AcmeExecutor.php create mode 100644 examples/Extension/ProgressLogger/CatLogger.php create mode 100644 examples/Extension/Report/AcmeGenerator.php create mode 100644 examples/OutputTest/AssertionTestBench.php create mode 100644 examples/OutputTest/OutputTestBench.php create mode 100644 examples/bootstrap.php create mode 100644 extensions/xdebug/lib/Command/Handler/OutputDirHandler.php create mode 100644 extensions/xdebug/lib/Command/ProfileCommand.php create mode 100644 extensions/xdebug/lib/Executor/ProfileExecutor.php create mode 100644 extensions/xdebug/lib/XDebugExtension.php create mode 100644 extensions/xdebug/lib/XDebugUtil.php create mode 100644 extensions/xdebug/lib/config/executors.php create mode 100644 extensions/xdebug/tests/System/ProfileCommandTest.php create mode 100644 extensions/xdebug/tests/System/XDebugTestCase.php create mode 100755 extensions/xdebug/tests/System/bin/foogrind create mode 100644 extensions/xdebug/tests/Unit/XDebugUtilTest.php create mode 100644 lib/Assertion/AssertionProcessor.php create mode 100644 lib/Assertion/AssertionResult.php create mode 100644 lib/Assertion/Exception/AssertionError.php create mode 100644 lib/Assertion/ParameterProvider.php create mode 100644 lib/Assertion/VariantAssertionResults.php create mode 100644 lib/Attributes/AbstractMethodsAttribute.php create mode 100644 lib/Attributes/AfterClassMethods.php create mode 100644 lib/Attributes/AfterMethods.php create mode 100644 lib/Attributes/Assert.php create mode 100644 lib/Attributes/BeforeClassMethods.php create mode 100644 lib/Attributes/BeforeMethods.php create mode 100644 lib/Attributes/Executor.php create mode 100644 lib/Attributes/Format.php create mode 100644 lib/Attributes/Groups.php create mode 100644 lib/Attributes/Iterations.php create mode 100644 lib/Attributes/OutputMode.php create mode 100644 lib/Attributes/OutputTimeUnit.php create mode 100644 lib/Attributes/ParamProviders.php create mode 100644 lib/Attributes/RetryThreshold.php create mode 100644 lib/Attributes/Revs.php create mode 100644 lib/Attributes/Skip.php create mode 100644 lib/Attributes/Sleep.php create mode 100644 lib/Attributes/Subject.php create mode 100644 lib/Attributes/Timeout.php create mode 100644 lib/Attributes/Warmup.php create mode 100644 lib/Benchmark/Baseline/Baselines.php create mode 100644 lib/Benchmark/BenchmarkFinder.php create mode 100644 lib/Benchmark/CartesianParameterIterator.php create mode 100644 lib/Benchmark/Exception/RetryLimitReachedException.php create mode 100644 lib/Benchmark/Exception/StopOnErrorException.php create mode 100644 lib/Benchmark/Metadata/AnnotationReader.php create mode 100644 lib/Benchmark/Metadata/Annotations/AbstractArrayAnnotation.php create mode 100644 lib/Benchmark/Metadata/Annotations/AbstractMethodsAnnotation.php create mode 100644 lib/Benchmark/Metadata/Annotations/AfterClassMethods.php create mode 100644 lib/Benchmark/Metadata/Annotations/AfterMethods.php create mode 100644 lib/Benchmark/Metadata/Annotations/Assert.php create mode 100644 lib/Benchmark/Metadata/Annotations/BeforeClassMethods.php create mode 100644 lib/Benchmark/Metadata/Annotations/BeforeMethods.php create mode 100644 lib/Benchmark/Metadata/Annotations/Executor.php create mode 100644 lib/Benchmark/Metadata/Annotations/Format.php create mode 100644 lib/Benchmark/Metadata/Annotations/Groups.php create mode 100644 lib/Benchmark/Metadata/Annotations/Iterations.php create mode 100644 lib/Benchmark/Metadata/Annotations/OutputMode.php create mode 100644 lib/Benchmark/Metadata/Annotations/OutputTimeUnit.php create mode 100644 lib/Benchmark/Metadata/Annotations/ParamProviders.php create mode 100644 lib/Benchmark/Metadata/Annotations/RetryThreshold.php create mode 100644 lib/Benchmark/Metadata/Annotations/Revs.php create mode 100644 lib/Benchmark/Metadata/Annotations/Skip.php create mode 100644 lib/Benchmark/Metadata/Annotations/Sleep.php create mode 100644 lib/Benchmark/Metadata/Annotations/Subject.php create mode 100644 lib/Benchmark/Metadata/Annotations/Timeout.php create mode 100644 lib/Benchmark/Metadata/Annotations/Warmup.php create mode 100644 lib/Benchmark/Metadata/AssertionMetadata.php create mode 100644 lib/Benchmark/Metadata/BenchmarkMetadata.php create mode 100644 lib/Benchmark/Metadata/Driver/AnnotationDriver.php create mode 100644 lib/Benchmark/Metadata/Driver/AttributeDriver.php create mode 100644 lib/Benchmark/Metadata/Driver/ChainDriver.php create mode 100644 lib/Benchmark/Metadata/Driver/ConfigDriver.php create mode 100644 lib/Benchmark/Metadata/DriverInterface.php create mode 100644 lib/Benchmark/Metadata/ExecutorMetadata.php create mode 100644 lib/Benchmark/Metadata/MetadataFactory.php create mode 100644 lib/Benchmark/Metadata/SubjectMetadata.php create mode 100644 lib/Benchmark/Runner.php create mode 100644 lib/Benchmark/RunnerConfig.php create mode 100644 lib/Benchmark/SamplerManager.php create mode 100644 lib/Compat/SymfonyOptionsResolverCompat.php create mode 100644 lib/Console/Application.php create mode 100644 lib/Console/CharacterReader.php create mode 100644 lib/Console/Command/ConfigReferenceCommand.php create mode 100644 lib/Console/Command/EvaluateCommand.php create mode 100644 lib/Console/Command/Handler/DumpHandler.php create mode 100644 lib/Console/Command/Handler/ReportHandler.php create mode 100644 lib/Console/Command/Handler/RunnerHandler.php create mode 100644 lib/Console/Command/Handler/SuiteCollectionHandler.php create mode 100644 lib/Console/Command/Handler/TimeUnitHandler.php create mode 100644 lib/Console/Command/LogCommand.php create mode 100644 lib/Console/Command/ReportCommand.php create mode 100644 lib/Console/Command/RunCommand.php create mode 100644 lib/Console/Command/SelfUpdateCommand.php create mode 100644 lib/Console/Command/ShowCommand.php create mode 100644 lib/Development/ConfigDumper.php create mode 100644 lib/Environment/Information.php create mode 100644 lib/Environment/Provider/AbstractRemoteProvider.php create mode 100644 lib/Environment/Provider/Git.php create mode 100644 lib/Environment/Provider/Opcache.php create mode 100644 lib/Environment/Provider/Php.php create mode 100644 lib/Environment/Provider/Sampler.php create mode 100644 lib/Environment/Provider/TestProvider.php create mode 100644 lib/Environment/Provider/Uname.php create mode 100644 lib/Environment/Provider/UnixSysload.php create mode 100644 lib/Environment/Provider/template/opcache.template create mode 100644 lib/Environment/Provider/template/php.template create mode 100644 lib/Environment/ProviderInterface.php create mode 100644 lib/Environment/Supplier.php create mode 100644 lib/Environment/VcsInformation.php create mode 100644 lib/Exception/ConfigurationPreProcessingError.php create mode 100644 lib/Executor/Benchmark/DebugExecutor.php create mode 100644 lib/Executor/Benchmark/LocalExecutor.php create mode 100644 lib/Executor/Benchmark/MemoryCentricMicrotimeExecutor.php create mode 100644 lib/Executor/Benchmark/RemoteExecutor.php create mode 100644 lib/Executor/Benchmark/TemplateExecutor.php create mode 100644 lib/Executor/Benchmark/TestExecutor.php create mode 100644 lib/Executor/Benchmark/template/memory.template create mode 100644 lib/Executor/Benchmark/template/remote.template create mode 100644 lib/Executor/BenchmarkExecutorInterface.php create mode 100644 lib/Executor/CompositeExecutor.php create mode 100644 lib/Executor/Exception/ExecutionError.php create mode 100644 lib/Executor/ExecutionContext.php create mode 100644 lib/Executor/ExecutionResults.php create mode 100644 lib/Executor/HealthCheck/AlwaysFineHealthCheck.php create mode 100644 lib/Executor/HealthCheckInterface.php create mode 100644 lib/Executor/Method/ErrorHandlingExecutorDecorator.php create mode 100644 lib/Executor/Method/LocalMethodExecutor.php create mode 100644 lib/Executor/Method/RemoteMethodExecutor.php create mode 100644 lib/Executor/Method/template/execute_static_methods.template create mode 100644 lib/Executor/MethodExecutorContext.php create mode 100644 lib/Executor/MethodExecutorInterface.php create mode 100644 lib/Expression/Ast/ArgumentListNode.php create mode 100644 lib/Expression/Ast/ArithmeticOperatorNode.php create mode 100644 lib/Expression/Ast/BinaryOperatorNode.php create mode 100644 lib/Expression/Ast/BooleanNode.php create mode 100644 lib/Expression/Ast/ComparisonNode.php create mode 100644 lib/Expression/Ast/ConcatNode.php create mode 100644 lib/Expression/Ast/ConcatenatedNode.php create mode 100644 lib/Expression/Ast/DelimitedListNode.php create mode 100644 lib/Expression/Ast/DisplayAsNode.php create mode 100644 lib/Expression/Ast/DisplayAsTimeNode.php create mode 100644 lib/Expression/Ast/FloatNode.php create mode 100644 lib/Expression/Ast/FunctionNode.php create mode 100644 lib/Expression/Ast/IntegerNode.php create mode 100644 lib/Expression/Ast/ListNode.php create mode 100644 lib/Expression/Ast/LogicalOperatorNode.php create mode 100644 lib/Expression/Ast/Node.php create mode 100644 lib/Expression/Ast/NullNode.php create mode 100644 lib/Expression/Ast/NumberNode.php create mode 100644 lib/Expression/Ast/NumberValue.php create mode 100644 lib/Expression/Ast/ParameterNode.php create mode 100644 lib/Expression/Ast/ParenthesisNode.php create mode 100644 lib/Expression/Ast/PercentDifferenceNode.php create mode 100644 lib/Expression/Ast/PercentageNode.php create mode 100644 lib/Expression/Ast/PhpValue.php create mode 100644 lib/Expression/Ast/PhpValueFactory.php create mode 100644 lib/Expression/Ast/RelativeDeviationNode.php create mode 100644 lib/Expression/Ast/StringNode.php create mode 100644 lib/Expression/Ast/TolerableNode.php create mode 100644 lib/Expression/Ast/ToleratedTrue.php create mode 100644 lib/Expression/Ast/UnitNode.php create mode 100644 lib/Expression/Ast/ValueWithUnitNode.php create mode 100644 lib/Expression/ColorMap.php create mode 100644 lib/Expression/Evaluator.php create mode 100644 lib/Expression/Evaluator/MainEvaluator.php create mode 100644 lib/Expression/Evaluator/PrettyErrorEvaluator.php create mode 100644 lib/Expression/Exception/EvaluationError.php create mode 100644 lib/Expression/Exception/ExpressionError.php create mode 100644 lib/Expression/Exception/ParseletNotFound.php create mode 100644 lib/Expression/Exception/PrinterError.php create mode 100644 lib/Expression/Exception/SyntaxError.php create mode 100644 lib/Expression/ExpressionFunctions.php create mode 100644 lib/Expression/ExpressionLanguage.php create mode 100644 lib/Expression/ExpressionLanguage/MemoisedExpressionLanguage.php create mode 100644 lib/Expression/ExpressionLanguage/RealExpressionLanguage.php create mode 100644 lib/Expression/Func/CoalesceFunction.php create mode 100644 lib/Expression/Func/DisplayAsTimeFunction.php create mode 100644 lib/Expression/Func/FirstFunction.php create mode 100644 lib/Expression/Func/FormatFunction.php create mode 100644 lib/Expression/Func/JoinFunction.php create mode 100644 lib/Expression/Func/MaxFunction.php create mode 100644 lib/Expression/Func/MeanFunction.php create mode 100644 lib/Expression/Func/MinFunction.php create mode 100644 lib/Expression/Func/ModeFunction.php create mode 100644 lib/Expression/Func/PercentDifferenceFunction.php create mode 100644 lib/Expression/Func/RStDevFunction.php create mode 100644 lib/Expression/Func/StDevFunction.php create mode 100644 lib/Expression/Func/VarianceFunction.php create mode 100644 lib/Expression/InfixParselet.php create mode 100644 lib/Expression/Lexer.php create mode 100644 lib/Expression/NodeEvaluator.php create mode 100644 lib/Expression/NodeEvaluator/AbstractEvaluator.php create mode 100644 lib/Expression/NodeEvaluator/ArgumentListEvaluator.php create mode 100644 lib/Expression/NodeEvaluator/ArithmeticOperatorEvaluator.php create mode 100644 lib/Expression/NodeEvaluator/ComparisonEvaluator.php create mode 100644 lib/Expression/NodeEvaluator/ConcatEvaluator.php create mode 100644 lib/Expression/NodeEvaluator/DisplayAsEvaluator.php create mode 100644 lib/Expression/NodeEvaluator/FunctionEvaluator.php create mode 100644 lib/Expression/NodeEvaluator/ListEvaluator.php create mode 100644 lib/Expression/NodeEvaluator/LogicalOperatorEvaluator.php create mode 100644 lib/Expression/NodeEvaluator/MemoisedNodeEvaluator.php create mode 100644 lib/Expression/NodeEvaluator/ParameterEvaluator.php create mode 100644 lib/Expression/NodeEvaluator/ParenthesisEvaluator.php create mode 100644 lib/Expression/NodeEvaluator/PhpValueEvaluator.php create mode 100644 lib/Expression/NodeEvaluator/TolerableEvaluator.php create mode 100644 lib/Expression/NodeEvaluator/ValueWithUnitEvaluator.php create mode 100644 lib/Expression/NodeEvaluators.php create mode 100644 lib/Expression/NodePrinter.php create mode 100644 lib/Expression/NodePrinter/ArgumentListPrinter.php create mode 100644 lib/Expression/NodePrinter/BinaryOperatorPrinter.php create mode 100644 lib/Expression/NodePrinter/BooleanPrinter.php create mode 100644 lib/Expression/NodePrinter/ComparisonPrinter.php create mode 100644 lib/Expression/NodePrinter/ConcatPrinter.php create mode 100644 lib/Expression/NodePrinter/ConcatenatedNodePrinter.php create mode 100644 lib/Expression/NodePrinter/DisplayAsPrinter.php create mode 100644 lib/Expression/NodePrinter/FunctionPrinter.php create mode 100644 lib/Expression/NodePrinter/HighlightingNodePrinter.php create mode 100644 lib/Expression/NodePrinter/ListPrinter.php create mode 100644 lib/Expression/NodePrinter/NullPrinter.php create mode 100644 lib/Expression/NodePrinter/NumberPrinter.php create mode 100644 lib/Expression/NodePrinter/ParameterPrinter.php create mode 100644 lib/Expression/NodePrinter/ParenthesisPrinter.php create mode 100644 lib/Expression/NodePrinter/PercentageDifferencePrinter.php create mode 100644 lib/Expression/NodePrinter/PercentagePrinter.php create mode 100644 lib/Expression/NodePrinter/RelativeDeviationPrinter.php create mode 100644 lib/Expression/NodePrinter/StringPrinter.php create mode 100644 lib/Expression/NodePrinter/TolerablePrinter.php create mode 100644 lib/Expression/NodePrinter/UnitPrinter.php create mode 100644 lib/Expression/NodePrinter/ValueWithUnitPrinter.php create mode 100644 lib/Expression/NodePrinters.php create mode 100644 lib/Expression/Parselet.php create mode 100644 lib/Expression/Parselet/ArithmeticOperatorParselet.php create mode 100644 lib/Expression/Parselet/BooleanParselet.php create mode 100644 lib/Expression/Parselet/ComparisonParselet.php create mode 100644 lib/Expression/Parselet/ConcatParselet.php create mode 100644 lib/Expression/Parselet/DisplayAsParselet.php create mode 100644 lib/Expression/Parselet/FloatParselet.php create mode 100644 lib/Expression/Parselet/FunctionParselet.php create mode 100644 lib/Expression/Parselet/IntegerParselet.php create mode 100644 lib/Expression/Parselet/ListParselet.php create mode 100644 lib/Expression/Parselet/LogicalOperatorParselet.php create mode 100644 lib/Expression/Parselet/NullParselet.php create mode 100644 lib/Expression/Parselet/ParameterParselet.php create mode 100644 lib/Expression/Parselet/ParenthesisParselet.php create mode 100644 lib/Expression/Parselet/PercentageParselet.php create mode 100644 lib/Expression/Parselet/StringParselet.php create mode 100644 lib/Expression/Parselet/TolerableParselet.php create mode 100644 lib/Expression/Parselet/ValueWithUnitParselet.php create mode 100644 lib/Expression/Parselets.php create mode 100644 lib/Expression/Parser.php create mode 100644 lib/Expression/Precedence.php create mode 100644 lib/Expression/PrefixParselet.php create mode 100644 lib/Expression/Printer.php create mode 100644 lib/Expression/Printer/BareValuePrinter.php create mode 100644 lib/Expression/Printer/EvaluatingPrinter.php create mode 100644 lib/Expression/Printer/NormalizingPrinter.php create mode 100644 lib/Expression/Printer/UnderlinePrinter.php create mode 100644 lib/Expression/Printer/UnderlinePrinterFactory.php create mode 100644 lib/Expression/SuffixParselet.php create mode 100644 lib/Expression/Theme/ArrayTheme.php create mode 100644 lib/Expression/Theme/EightColorTheme.php create mode 100644 lib/Expression/Theme/SolarizedTheme.php create mode 100644 lib/Expression/Theme/Util/Color.php create mode 100644 lib/Expression/Theme/Util/Gradient.php create mode 100644 lib/Expression/Token.php create mode 100644 lib/Expression/Tokens.php create mode 100644 lib/Extension/ConsoleExtension.php create mode 100644 lib/Extension/CoreExtension.php create mode 100644 lib/Extension/DevelopmentExtension.php create mode 100644 lib/Extension/ExpressionExtension.php create mode 100644 lib/Extension/ReportExtension.php create mode 100644 lib/Extension/RunnerExtension.php create mode 100644 lib/Extension/StorageExtension.php create mode 100644 lib/Extension/config/benchmark/executors.php create mode 100644 lib/Extension/config/class/main.json create mode 100644 lib/Extension/config/report/generators.php create mode 100644 lib/Extension/config/report/renderers.php create mode 100644 lib/Functional/Functional.php create mode 100644 lib/Json/JsonDecoder.php create mode 100644 lib/Logger/ConsoleLogger.php create mode 100644 lib/Math/Distribution.php create mode 100644 lib/Math/FloatNumber.php create mode 100644 lib/Math/Kde.php create mode 100644 lib/Math/Statistics.php create mode 100644 lib/Model/Benchmark.php create mode 100644 lib/Model/Error.php create mode 100644 lib/Model/ErrorStack.php create mode 100644 lib/Model/Iteration.php create mode 100644 lib/Model/ParameterSet.php create mode 100644 lib/Model/ResolvedExecutor.php create mode 100644 lib/Model/Result/ComputedResult.php create mode 100644 lib/Model/Result/MemoryResult.php create mode 100644 lib/Model/Result/RejectionCountResult.php create mode 100644 lib/Model/Result/TimeResult.php create mode 100644 lib/Model/ResultCollection.php create mode 100644 lib/Model/ResultInterface.php create mode 100644 lib/Model/Subject.php create mode 100644 lib/Model/Suite.php create mode 100644 lib/Model/SuiteCollection.php create mode 100644 lib/Model/Summary.php create mode 100644 lib/Model/Tag.php create mode 100644 lib/Model/Variant.php create mode 100644 lib/PhpBench.php create mode 100644 lib/Progress/Logger/AnsiLogger.php create mode 100644 lib/Progress/Logger/BlinkenLogger.php create mode 100644 lib/Progress/Logger/DotsLogger.php create mode 100644 lib/Progress/Logger/HistogramLogger.php create mode 100644 lib/Progress/Logger/NullLogger.php create mode 100644 lib/Progress/Logger/PhpBenchLogger.php create mode 100644 lib/Progress/Logger/TravisLogger.php create mode 100644 lib/Progress/Logger/VerboseLogger.php create mode 100644 lib/Progress/LoggerInterface.php create mode 100644 lib/Progress/LoggerRegistry.php create mode 100644 lib/Progress/VariantFormatter.php create mode 100644 lib/Progress/VariantSummaryFormatter.php create mode 100644 lib/Reflection/ReflectionAttribute.php create mode 100644 lib/Reflection/ReflectionClass.php create mode 100644 lib/Reflection/ReflectionHierarchy.php create mode 100644 lib/Reflection/ReflectionMethod.php create mode 100644 lib/Reflection/ReflectorInterface.php create mode 100644 lib/Reflection/RemoteReflector.php create mode 100644 lib/Reflection/template/parameter_set_extractor.template create mode 100644 lib/Reflection/template/reflector.template create mode 100644 lib/Registry/Config.php create mode 100644 lib/Registry/ConfigurableRegistry.php create mode 100644 lib/Registry/RegistrableInterface.php create mode 100644 lib/Registry/Registry.php create mode 100644 lib/Remote/Exception/ScriptErrorException.php create mode 100644 lib/Remote/IniStringBuilder.php create mode 100644 lib/Remote/Launcher.php create mode 100644 lib/Remote/Payload.php create mode 100644 lib/Remote/PayloadFactory.php create mode 100644 lib/Remote/ProcessFactory.php create mode 100644 lib/Report/Generator/BareGenerator.php create mode 100644 lib/Report/Generator/CompositeGenerator.php create mode 100644 lib/Report/Generator/EnvGenerator.php create mode 100644 lib/Report/Generator/ExpressionGenerator.php create mode 100644 lib/Report/Generator/OutputTestGenerator.php create mode 100644 lib/Report/GeneratorInterface.php create mode 100644 lib/Report/Model/Report.php create mode 100644 lib/Report/Model/Reports.php create mode 100644 lib/Report/Model/Table.php create mode 100644 lib/Report/Model/TableRow.php create mode 100644 lib/Report/Renderer/ConsoleRenderer.php create mode 100644 lib/Report/Renderer/DelimitedRenderer.php create mode 100644 lib/Report/RendererInterface.php create mode 100644 lib/Report/ReportManager.php create mode 100644 lib/Report/Transform/SuiteCollectionTransformer.php create mode 100644 lib/Serializer/XmlDecoder.php create mode 100644 lib/Serializer/XmlEncoder.php create mode 100644 lib/Storage/ArchiverInterface.php create mode 100644 lib/Storage/Driver/Fake/FakeHistoryIterator.php create mode 100644 lib/Storage/Driver/Xml/HistoryIterator.php create mode 100644 lib/Storage/Driver/Xml/XmlDriver.php create mode 100644 lib/Storage/DriverInterface.php create mode 100644 lib/Storage/Exception/InvalidTagException.php create mode 100644 lib/Storage/Exception/TagNotFoundException.php create mode 100644 lib/Storage/HistoryEntry.php create mode 100644 lib/Storage/HistoryIteratorInterface.php create mode 100644 lib/Storage/StorageRegistry.php create mode 100644 lib/Storage/UuidResolver.php create mode 100644 lib/Storage/UuidResolver/ChainResolver.php create mode 100644 lib/Storage/UuidResolver/LatestResolver.php create mode 100644 lib/Storage/UuidResolver/TagResolver.php create mode 100644 lib/Storage/UuidResolverInterface.php create mode 100644 lib/SymfonyOptionsResolverCompat.php create mode 100644 lib/Util/Cast.php create mode 100644 lib/Util/MemoryUnit.php create mode 100644 lib/Util/TextTruncate.php create mode 100644 lib/Util/TimeUnit.php create mode 100644 phpbench.json.dist create mode 100755 phpbench.phar create mode 100644 phpbench.phar.pubkey create mode 100644 phpbench.phar.version create mode 100644 phpstan-baseline.neon create mode 100644 phpstan.neon.dist create mode 100644 phpunit.xml.dist create mode 100644 tests/Benchmark/Assertion/AssertionProcessorBench.php create mode 100644 tests/Benchmark/Console/Command/RunCommandBench.php create mode 100644 tests/Benchmark/Expression/ParserBench.php create mode 100644 tests/Benchmark/IntegrationBenchCase.php create mode 100644 tests/Benchmark/Progress/VariantSummaryFormatterBench.php create mode 100644 tests/Benchmark/Report/Generator/ExpressionGeneratorBench.php create mode 100644 tests/Example/CommandsTest.php create mode 100644 tests/Example/ExpressionLinterTest.php create mode 100644 tests/Integration/ConfiguredReportsTest.php create mode 100644 tests/Integration/generator/aggregate-csv create mode 100644 tests/Integration/generator/bare-console create mode 100644 tests/Integration/generator/bare-csv create mode 100644 tests/Integration/generator/bare-delimited create mode 100644 tests/Integration/generator/composite-console create mode 100644 tests/Integration/generator/composite-delimited create mode 100644 tests/Integration/generator/default-csv create mode 100644 tests/Integration/generator/env-console create mode 100644 tests/Integration/generator/env-delimited create mode 100644 tests/Integration/generator/expression-console create mode 100644 tests/Integration/generator/expression-delimited create mode 100644 tests/Integration/generator/flat-console create mode 100644 tests/Integration/generator/flat-csv create mode 100644 tests/Integration/generator/flat-delimited create mode 100644 tests/Integration/generator/memory-csv create mode 100644 tests/Integration/generator/output_test-console create mode 100644 tests/Integration/generator/output_test-delimited create mode 100644 tests/IntegrationTestCase.php create mode 100644 tests/PhpBenchTestCase.php create mode 100644 tests/ProphecyTrait.php create mode 100644 tests/System/ConfigReferenceTest.php create mode 100644 tests/System/EvalCommandTest.php create mode 100644 tests/System/LogTest.php create mode 100644 tests/System/ReportTest.php create mode 100644 tests/System/RunTest.php create mode 100644 tests/System/ShowTest.php create mode 100644 tests/System/SystemTestCase.php create mode 100644 tests/System/benchmarks/set1/BenchmarkBench.php create mode 100644 tests/System/benchmarks/set1/IsolatedBench.php create mode 100644 tests/System/benchmarks/set1/IsolatedParametersBench.php create mode 100644 tests/System/benchmarks/set1/IsolatedRevsBench.php create mode 100644 tests/System/benchmarks/set1/ParameterProviderCallableBench.php create mode 100644 tests/System/benchmarks/set2/FoobarBench.php create mode 100644 tests/System/benchmarks/set3/ErrorBench.php create mode 100644 tests/System/benchmarks/set3/ErrorVariantsBench.php create mode 100644 tests/System/benchmarks/set4/NothingBench.php create mode 100644 tests/System/benchmarks/set5/AssertFailBench.php create mode 100644 tests/System/benchmarks/set5/AssertWarnBench.php create mode 100644 tests/System/benchmarks/set6/TimeoutBench.php create mode 100644 tests/System/bootstrap/conflicting.bootstrap create mode 100644 tests/System/bootstrap/foobar.bootstrap create mode 100644 tests/System/env/config_dist/phpbench.json.dist create mode 100644 tests/System/env/config_set2/phpbench.json create mode 100644 tests/System/env/config_valid/phpbench.dist.json create mode 100644 tests/System/env/config_valid/phpbench.json create mode 100644 tests/System/env/config_valid/shouldnotbecalled.php create mode 100644 tests/TestCase.php create mode 100644 tests/Unit/Assertion/AssertionProcessorTest.php create mode 100644 tests/Unit/Benchmark/BenchmarkFinderTest.php create mode 100644 tests/Unit/Benchmark/CartesianIteratorTest.php create mode 100644 tests/Unit/Benchmark/Metadata/AnnotationReaderTest.php create mode 100644 tests/Unit/Benchmark/Metadata/BenchmarkMetadataTest.php create mode 100644 tests/Unit/Benchmark/Metadata/Driver/AnnotationDriverTest.php create mode 100644 tests/Unit/Benchmark/Metadata/Driver/AttributeDriverTest.php create mode 100644 tests/Unit/Benchmark/Metadata/Driver/ChainDriverTest.php create mode 100644 tests/Unit/Benchmark/Metadata/Driver/ConfigDriverTest.php create mode 100644 tests/Unit/Benchmark/Metadata/MetadataFactoryTest.php create mode 100644 tests/Unit/Benchmark/Metadata/SubjectMetadataTest.php create mode 100644 tests/Unit/Benchmark/Metadata/classes/Test.php create mode 100644 tests/Unit/Benchmark/RunnerConfigTest.php create mode 100644 tests/Unit/Benchmark/RunnerTest.php create mode 100644 tests/Unit/Benchmark/SamplerManagerTest.php create mode 100644 tests/Unit/Benchmark/findertest/AbstractBench.php create mode 100644 tests/Unit/Benchmark/findertest/FooCase2Bench.php create mode 100644 tests/Unit/Benchmark/findertest/FooCaseBench.php create mode 100644 tests/Unit/Benchmark/findertestnested/Directory/MyBench.php create mode 100644 tests/Unit/Benchmark/findertestnested/MyBench.php create mode 100644 tests/Unit/Console/Command/LogCommandTest.php create mode 100644 tests/Unit/Console/Command/UpdateCommandTest.php create mode 100644 tests/Unit/Environment/InformationTest.php create mode 100644 tests/Unit/Environment/Provider/GitTest.php create mode 100644 tests/Unit/Environment/Provider/OpcacheTest.php create mode 100644 tests/Unit/Environment/Provider/PhpTest.php create mode 100644 tests/Unit/Environment/Provider/SamplerTest.php create mode 100644 tests/Unit/Environment/Provider/UnameTest.php create mode 100644 tests/Unit/Environment/Provider/UnixSysloadTest.php create mode 100644 tests/Unit/Environment/SupplierTest.php create mode 100644 tests/Unit/Executor/Benchmark/AbstractExecutorTestCase.php create mode 100644 tests/Unit/Executor/Benchmark/DebugExecutorTest.php create mode 100644 tests/Unit/Executor/Benchmark/LocalExecutorTest.php create mode 100644 tests/Unit/Executor/Benchmark/RemoteExecutorTest.php create mode 100644 tests/Unit/Executor/Method/LocalMethodExecutorTest.php create mode 100644 tests/Unit/Executor/Method/RemoteMethodExecutorTest.php create mode 100644 tests/Unit/Executor/ParameterSetExtractorTest.php create mode 100644 tests/Unit/Executor/benchmarks/MicrotimeExecutorBench.php create mode 100644 tests/Unit/Executor/benchmarks/ParamProviderBench.php create mode 100644 tests/Unit/Expression/Ast/ListNodeTest.php create mode 100644 tests/Unit/Expression/Ast/PhpValueFactoryTest.php create mode 100644 tests/Unit/Expression/Evaluator/ParameterEvaluatorTest.php create mode 100644 tests/Unit/Expression/EvaluatorTestCase.php create mode 100644 tests/Unit/Expression/Exception/SyntaxErrorTest.php create mode 100644 tests/Unit/Expression/Func/CoalesceFunctionTest.php create mode 100644 tests/Unit/Expression/Func/DisplayAsTimeFunctionTest.php create mode 100644 tests/Unit/Expression/Func/FirstFunctionTest.php create mode 100644 tests/Unit/Expression/Func/FormatFunctionTest.php create mode 100644 tests/Unit/Expression/Func/JoinFunctionTest.php create mode 100644 tests/Unit/Expression/Func/MaxFunctionTest.php create mode 100644 tests/Unit/Expression/Func/MeanFunctionTest.php create mode 100644 tests/Unit/Expression/Func/MinFunctionTest.php create mode 100644 tests/Unit/Expression/Func/ModeFunctionTest.php create mode 100644 tests/Unit/Expression/Func/PercentDifferenceFunctionTest.php create mode 100644 tests/Unit/Expression/Func/StDevFunctionTest.php create mode 100644 tests/Unit/Expression/Func/VarianceFunctionTest.php create mode 100644 tests/Unit/Expression/FunctionTestCase.php create mode 100644 tests/Unit/Expression/LexerTest.php create mode 100644 tests/Unit/Expression/NodeEvaluator/MemoisedNodeEvaluatorTest.php create mode 100644 tests/Unit/Expression/NodeEvaluatorsTest.php create mode 100644 tests/Unit/Expression/NodePrinter/HighlightingNodePrinterTest.php create mode 100644 tests/Unit/Expression/Parselet/ArgumentListParseletTest.php create mode 100644 tests/Unit/Expression/Parselet/ArithmeticOperatorParseletTest.php create mode 100644 tests/Unit/Expression/Parselet/BooleanParseletTest.php create mode 100644 tests/Unit/Expression/Parselet/ComparisonParseletTest.php create mode 100644 tests/Unit/Expression/Parselet/ConcatParseletTest.php create mode 100644 tests/Unit/Expression/Parselet/DisplayAsParseletTest.php create mode 100644 tests/Unit/Expression/Parselet/FloatParseletTest.php create mode 100644 tests/Unit/Expression/Parselet/FunctionParseletTest.php create mode 100644 tests/Unit/Expression/Parselet/IntegerParseletTest.php create mode 100644 tests/Unit/Expression/Parselet/ListParseletTest.php create mode 100644 tests/Unit/Expression/Parselet/LogicalOperatorParseletTest.php create mode 100644 tests/Unit/Expression/Parselet/NullParseletTest.php create mode 100644 tests/Unit/Expression/Parselet/ParameterParseletTest.php create mode 100644 tests/Unit/Expression/Parselet/ParenthesisParseletTest.php create mode 100644 tests/Unit/Expression/Parselet/StringParseletTest.php create mode 100644 tests/Unit/Expression/Parselet/TolerableParseletTest.php create mode 100644 tests/Unit/Expression/Parselet/ValueWithUnitParseletTest.php create mode 100644 tests/Unit/Expression/ParseletTestCase.php create mode 100644 tests/Unit/Expression/ParserTest.php create mode 100644 tests/Unit/Expression/ParserTestCase.php create mode 100644 tests/Unit/Expression/Printer/BareValuePrinterTest.php create mode 100644 tests/Unit/Expression/Printer/EvaluatingPrinterTest.php create mode 100644 tests/Unit/Expression/Theme/Util/GradientTest.php create mode 100644 tests/Unit/Expression/TokensTest.php create mode 100644 tests/Unit/Extension/RunnerExtensionTest.php create mode 100644 tests/Unit/Json/JsonDecoderTest.php create mode 100644 tests/Unit/Logger/ConsoleLoggerTest.php create mode 100644 tests/Unit/Math/DistributionTest.php create mode 100644 tests/Unit/Math/KdeTest.php create mode 100644 tests/Unit/Math/StatisticsTest.php create mode 100644 tests/Unit/Model/BenchmarkTest.php create mode 100644 tests/Unit/Model/IterationTest.php create mode 100644 tests/Unit/Model/Result/RejectionCountResultTest.php create mode 100644 tests/Unit/Model/Result/TimeResultTest.php create mode 100644 tests/Unit/Model/ResultCollectionTest.php create mode 100644 tests/Unit/Model/SubjectTest.php create mode 100644 tests/Unit/Model/SuiteCollectionTest.php create mode 100644 tests/Unit/Model/SuiteTest.php create mode 100644 tests/Unit/Model/SummaryTest.php create mode 100644 tests/Unit/Model/TagTest.php create mode 100644 tests/Unit/Model/VariantTest.php create mode 100644 tests/Unit/Progress/Logger/BlinkenLoggerTest.php create mode 100644 tests/Unit/Progress/Logger/DotsLoggerTest.php create mode 100644 tests/Unit/Progress/Logger/HistogramLoggerTest.php create mode 100644 tests/Unit/Progress/Logger/LoggerTestCase.php create mode 100644 tests/Unit/Progress/Logger/PhpBenchLoggerTest.php create mode 100644 tests/Unit/Progress/Logger/TravisLoggerTest.php create mode 100644 tests/Unit/Progress/Logger/VerboseLoggerTest.php create mode 100644 tests/Unit/Progress/LoggerRegistryTest.php create mode 100644 tests/Unit/Progress/VariantSummaryFormatterTest.php create mode 100644 tests/Unit/Reflection/ReflectionHierarchyTest.php create mode 100644 tests/Unit/Reflection/RemoteReflectorTest.php create mode 100644 tests/Unit/Reflection/reflector/Class1.php create mode 100644 tests/Unit/Reflection/reflector/Class2.php create mode 100644 tests/Unit/Reflection/reflector/Class3.php create mode 100644 tests/Unit/Reflection/reflector/ClassWithClassKeywords.php create mode 100644 tests/Unit/Reflection/reflector/EmptyFile.php create mode 100644 tests/Unit/Reflection/reflector/ExampleClass.php create mode 100644 tests/Unit/Reflection/reflector/ExampleClass2.php create mode 100644 tests/Unit/Registry/ConfigTest.php create mode 100644 tests/Unit/Registry/ConfigurableRegistryTest.php create mode 100644 tests/Unit/Registry/RegistryTest.php create mode 100644 tests/Unit/Remote/IniStringBuilderTest.php create mode 100644 tests/Unit/Remote/LauncherTest.php create mode 100644 tests/Unit/Remote/PayloadFactoryTest.php create mode 100644 tests/Unit/Remote/PayloadTest.php create mode 100644 tests/Unit/Remote/launcher/autoload.php create mode 100644 tests/Unit/Remote/template/foo.template create mode 100644 tests/Unit/Remote/template/invalid.template create mode 100644 tests/Unit/Report/Generator/BareGeneratorTest.php create mode 100644 tests/Unit/Report/Generator/CompositeGeneratorTest.php create mode 100644 tests/Unit/Report/Generator/EnvGeneratorTest.php create mode 100644 tests/Unit/Report/Generator/ExpressionGeneratorTest.php create mode 100644 tests/Unit/Report/Generator/GeneratorTestCase.php create mode 100644 tests/Unit/Report/Generator/bare/bare-vertical create mode 100644 tests/Unit/Report/Generator/bare/bare1 create mode 100644 tests/Unit/Report/Generator/env/env create mode 100644 tests/Unit/Report/Generator/env/env_empty create mode 100644 tests/Unit/Report/Generator/expression/aggregate1 create mode 100644 tests/Unit/Report/Generator/expression/aggregate2 create mode 100644 tests/Unit/Report/Generator/expression/aggregate3 create mode 100644 tests/Unit/Report/Generator/expression/aggregate4 create mode 100644 tests/Unit/Report/Generator/expression/break1 create mode 100644 tests/Unit/Report/Generator/expression/break2 create mode 100644 tests/Unit/Report/Generator/expression/cols-baseline-has-les-iterations create mode 100644 tests/Unit/Report/Generator/expression/cols-baseline-has-more-iterations create mode 100644 tests/Unit/Report/Generator/expression/cols-baseline-subject-not-existing create mode 100644 tests/Unit/Report/Generator/expression/cols-baseline1 create mode 100644 tests/Unit/Report/Generator/expression/cols-selection create mode 100644 tests/Unit/Report/Generator/expression/cols-selection-baseline create mode 100644 tests/Unit/Report/Generator/expression/cols1 create mode 100644 tests/Unit/Report/Generator/expression/cols2 create mode 100644 tests/Unit/Report/Generator/expression/cols3 create mode 100644 tests/Unit/Report/Generator/expression/empty create mode 100644 tests/Unit/Report/Generator/expression/include_baseline1 create mode 100644 tests/Unit/Report/Generator/expression/subject_time_specification create mode 100644 tests/Unit/Report/Generator/expression/subject_time_specification_with_baseline create mode 100644 tests/Unit/Report/Generator/expression/subject_time_specification_with_nulls create mode 100644 tests/Unit/Report/Generator/expression/suite-empty create mode 100644 tests/Unit/Report/Renderer/AbstractRendererCase.php create mode 100644 tests/Unit/Report/Renderer/ConsoleRendererTest.php create mode 100644 tests/Unit/Report/Renderer/DelimitedRendererTest.php create mode 100644 tests/Unit/Report/Renderer/console/default create mode 100644 tests/Unit/Report/Renderer/console/title-description create mode 100644 tests/Unit/Report/ReportManagerTest.php create mode 100644 tests/Unit/Serializer/XmlDecoderTest.php create mode 100644 tests/Unit/Serializer/XmlEncoderTest.php create mode 100644 tests/Unit/Serializer/XmlTestCase.php create mode 100644 tests/Unit/Storage/Driver/Xml/HistoryIteratorTest.php create mode 100644 tests/Unit/Storage/Driver/Xml/XmlDriverTest.php create mode 100644 tests/Unit/Storage/UuidResolver/ChainResolverTest.php create mode 100644 tests/Unit/Storage/UuidResolver/LatestResolverTest.php create mode 100644 tests/Unit/Storage/UuidResolver/TagResolverTest.php create mode 100644 tests/Unit/Util/MemoryUnitTest.php create mode 100644 tests/Unit/Util/TextTruncateTest.php create mode 100644 tests/Unit/Util/TimeUnitTest.php create mode 100644 tests/Util/Approval.php create mode 100644 tests/Util/IterationBuilder.php create mode 100644 tests/Util/TestUtil.php create mode 100644 tests/Util/VariantBuilder.php create mode 100644 tests/Util/Workspace.php create mode 100644 tests/bootstrap.php diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 000000000..6b125edfe --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,187 @@ +name: "CI" + +on: + pull_request: + push: + branches: + - 'master' + +env: + fail-fast: true + TZ: "Europe/Paris" + REQUIRED_PHP_EXTENSIONS: "dom, iconv, json, pcre, reflection, spl, tokenizer, xdebug" + +jobs: + composer-validate: + name: "Composer validate (${{ matrix.php-version }})" + + runs-on: "ubuntu-latest" + + strategy: + matrix: + php-version: + - '7.2' + + steps: + - + name: "Checkout code" + uses: "actions/checkout@v2" + + - + name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + php-version: "${{ matrix.php-version }}" + tools: composer:v2 + + - + name: "Validate composer.json" + run: "composer validate --strict --no-check-lock" + + php-cs-fixer: + needs: + - "composer-validate" + + name: "PHP-CS-Fixer (${{ matrix.php-version }})" + + runs-on: "ubuntu-latest" + + strategy: + matrix: + php-version: + - '7.2' + + steps: + - + name: "Checkout code" + uses: "actions/checkout@v2" + + - + name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + extensions: "${{ env.REQUIRED_PHP_EXTENSIONS }}" + php-version: "${{ matrix.php-version }}" + tools: composer:v2 + + - + name: "Composer install" + uses: "ramsey/composer-install@v1" + with: + composer-options: "--no-scripts" + + - + name: "Run friendsofphp/php-cs-fixer" + run: "vendor/bin/php-cs-fixer fix --dry-run --diff --verbose" + + phpstan: + needs: + - "composer-validate" + + name: "PHPStan (${{ matrix.php-version }})" + + runs-on: "ubuntu-latest" + + strategy: + matrix: + php-version: + - '7.2' + + steps: + - + name: "Checkout code" + uses: "actions/checkout@v2" + + - + name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + extensions: "${{ env.REQUIRED_PHP_EXTENSIONS }}" + php-version: "${{ matrix.php-version }}" + tools: composer:v2 + + - + name: "Composer install" + uses: "ramsey/composer-install@v1" + with: + composer-options: "--no-scripts" + + - + name: "Run phpstan/phpstan" + run: "vendor/bin/phpstan analyse -c phpstan.neon.dist" + + tests: + needs: + - "composer-validate" + + name: "PHP ${{ matrix.php-version }} + ${{ matrix.dependency }}" + + runs-on: ubuntu-latest + + continue-on-error: ${{ matrix.allow-failures }} + + strategy: + matrix: + php-version: + - '7.2' + - '7.3' + - '7.4' + dependency: + - 'lowest' + - 'highest' + with-examples: ['yes'] + allow-failures: [false] + include: + - php-version: '7.2' + dependency: 'lowest' + with-examples: 'no' + allow-failures: false + coverage: xdebug + - php-version: '8.0' + dependency: 'highest' + with-examples: 'no' + allow-failures: true + coverage: xdebug + + steps: + - name: "Checkout code" + uses: actions/checkout@v2.3.3 + + - name: "Install PHP with extensions" + uses: shivammathur/setup-php@2.7.0 + with: + extensions: "${{ env.REQUIRED_PHP_EXTENSIONS }}" + php-version: ${{ matrix.php-version }} + tools: composer:v2 + + - name: "Add PHPUnit matcher" + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: "Remove friendsofphp/php-cs-fixer" + run: composer remove --dev friendsofphp/php-cs-fixer --no-update + + - name: "Composer install" + uses: "ramsey/composer-install@v1" + with: + dependency-versions: "${{ matrix.dependency }}" + + - name: PHP Info + run: php --version + + - name: "Run tests with PHPUnit" + run: vendor/bin/phpunit --verbose + + - name: "Run benchmarks with PHPBench" + run: bin/phpbench run --report=env --progress=dots + + - if: matrix.with-examples == 'yes' + run: bin/phpbench run --profile=examples --report=env --progress=dots --iterations=1 --revs=1 + + - if: matrix.with-examples == 'yes' + run: bin/phpbench run --profile=examples --iterations=1 --revs=1 --report=catordog --executor=acme + + - if: matrix.with-examples == 'yes' + run: bin/phpbench cats --profile=examples diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml new file mode 100644 index 000000000..0ee6ccb24 --- /dev/null +++ b/.github/workflows/documentation.yaml @@ -0,0 +1,63 @@ +name: "Documentation" + +on: + pull_request: + push: + branches: + - 'master' + +jobs: + build: + name: "Build" + + runs-on: ubuntu-latest + + steps: + - name: "Checkout code" + uses: actions/checkout@v2 + + - name: "Set up Python 3.7" + uses: actions/setup-python@v1 + with: + python-version: '3.7' # Semantic version range syntax or exact version of a Python version + + - name: "Display Python version" + run: python -c "import sys; print(sys.version)" + + - name: "Update apt" + run: sudo apt-get update + + - name: "Install Sphinx dependencies" + run: sudo apt-get install python-dev build-essential + + - name: "Cache pip" + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('docs/conf.py') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: "Install Sphinx + requirements via pip" + working-directory: "docs" + run: pip install -r requirements.txt + + - name: "Build documentation" + working-directory: "docs" + run: make -C . SPHINXOPTS="-nqW -j auto" html + + doctor-rst: + name: "DOCtor-RST" + + runs-on: ubuntu-latest + + steps: + - name: "Checkout code" + uses: actions/checkout@v2 + + - name: "Run DOCtor-RST" + uses: docker://oskarstark/doctor-rst + with: + args: --short + env: + DOCS_DIR: 'docs/' diff --git a/.github/workflows/release-phar.yml b/.github/workflows/release-phar.yml new file mode 100644 index 000000000..dd77fc1b4 --- /dev/null +++ b/.github/workflows/release-phar.yml @@ -0,0 +1,57 @@ +on: + release: + types: + - created + +name: Append phpbench.phar to release + +jobs: + build: + name: Compile and upload Phar + runs-on: ubuntu-18.04 + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set PHP 7.3 + uses: shivammathur/setup-php@v2 + with: + php-version: '7.3' + + - + name: "Composer install" + uses: "ramsey/composer-install@v1" + with: + composer-options: "--no-scripts --no-dev" + + - name: Compile phpbench.phar + run: bin/build-phar.sh + env: + GPG_SIGNING: 1 + GPG_SECRET_KEY: ${{ secrets.GPG_SECRET_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + + - name: Check existence of compiled .phar + run: test -e build/phpbench.phar && exit 0 || exit 10 + + - name: "Upload PHAR to Release" + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./build/phpbench.phar + asset_name: phpbench.phar + asset_content_type: application/octet-stream + + - name: "Attach signature to Release" + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./build/phpbench.phar.asc + asset_name: phpbench.phar.asc + asset_content_type: application/pgp-signature diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..3f70e71e4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +/composer.lock +/vendor +/docs/build +/build +/.phpbench.sqlite +/phpunit.xml +/.travis/phpbench-phar-private.pem +/phpbench.json +/.travis/build-key.pem +/.travis/secrets.tar +/.php_cs.cache +/.phpunit.result.cache +/tests/Workspace +/coverage +/.phpbench +/**/*.pyc diff --git a/.gitignoree b/.gitignoree new file mode 100644 index 000000000..3f70e71e4 --- /dev/null +++ b/.gitignoree @@ -0,0 +1,16 @@ +/composer.lock +/vendor +/docs/build +/build +/.phpbench.sqlite +/phpunit.xml +/.travis/phpbench-phar-private.pem +/phpbench.json +/.travis/build-key.pem +/.travis/secrets.tar +/.php_cs.cache +/.phpunit.result.cache +/tests/Workspace +/coverage +/.phpbench +/**/*.pyc diff --git a/.php_cs.dist b/.php_cs.dist new file mode 100644 index 000000000..2fdf9f2c5 --- /dev/null +++ b/.php_cs.dist @@ -0,0 +1,60 @@ +in([ + __DIR__ . '/lib', + __DIR__ . '/tests', + __DIR__ . '/extensions', + ]) + ->exclude([ + 'Attributes' + ]) +; + +return PhpCsFixer\Config::create() + ->setRiskyAllowed(true) + ->setRules([ + '@PSR2' => true, + 'void_return' => true, + 'binary_operator_spaces' => [ 'align_double_arrow' => false ], + 'blank_line_before_statement' => [ + 'statements' => [ + 'break', + 'continue', + 'declare', + 'default', + 'die', + 'do', + 'exit', + 'for', + 'foreach', + 'goto', + 'if', + 'include', + 'include_once', + 'require', + 'require_once', + 'return', + 'switch', + 'throw', + 'try', + 'while', + 'yield', + ], + ], + 'ordered_imports' => true, + 'concat_space' => false, + 'method_argument_space' => false, + 'no_unused_imports' => true, + 'ordered_imports' => true, + 'php_unit_set_up_tear_down_visibility' => true, + 'phpdoc_align' => [], + 'phpdoc_indent' => false, + 'phpdoc_separation' => true, + 'no_superfluous_phpdoc_tags' => [ + 'allow_mixed' => true + ], + 'fully_qualified_strict_types' => true, + ]) + ->setFinder($finder) + ; diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 000000000..d8a9006af --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,11 @@ +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details +version: 2 + +sphinx: + configuration: docs/conf.py + +python: + version: 3.7 + install: + - requirements: docs/requirements.txt diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..6638bf61e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,33 @@ +language: php + +cache: + directories: + - "$HOME/.composer/cache" + +stages: + - deploy + +jobs: + include: + - stage: Deploy + + if: (NOT type IN (pull_request)) AND (branch = master) + + php: 7.2 + + before_install: + - source .travis/xdebug.sh + - xdebug-disable + + script: skip + + before_deploy: + - ./.travis/before-deploy.sh + + deploy: + provider: pages + skip-cleanup: true + github-token: + secure: "cVKcMHHv6MV1HiP78hYPyBx6VIy+DzIv6HS0QM/g3gHnE7EWopu9mQjku2DQtQWAjqVrIPL3ClvZxJH5MjDd4/kB6kWQDGX5O2+CaqzhaJgSpT0htFLbiClqK1uvl2QnHoqwfnhIux230AWDtQfod1g1p60nryo65pLG0HGHqXU=" + on: + branch: master diff --git a/.travis/before-deploy-gh-release.sh b/.travis/before-deploy-gh-release.sh new file mode 100755 index 000000000..b399ba952 --- /dev/null +++ b/.travis/before-deploy-gh-release.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# set -x + +export PATH=.:$PATH + +# Travis won't deploy ignored files +sed -ie '/^\/phpbench.phar*/d' .gitignore + +# Install PHAR dependencies and remove strip other dev deps. +rm -Rf ./vendor +rm -f composer.lock +composer install --no-dev -o + +wget https://github.com/box-project/box/releases/download/3.8.5/box.phar +php box.phar compile -c box.json.gh-release + +echo "Done pre-deploy build" diff --git a/.travis/before-deploy.sh b/.travis/before-deploy.sh new file mode 100755 index 000000000..9485a49d2 --- /dev/null +++ b/.travis/before-deploy.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# set -x + +export PATH=.:$PATH + +# Travis won't deploy ignored files +sed -ie '/^\/phpbench.phar*/d' .gitignore + +# Extract the private key (needed to sign the PHAR) +openssl aes-256-cbc \ + -K "${encrypted_d58d55177063_key:?'Encrypted key not assigned'}" \ + -iv "${encrypted_d58d55177063_iv:?'Encrypted iv not assigned'}" \ + -in .travis/secrets.tar.enc \ + -out .travis/secrets.tar \ + -d +tar xvf .travis/secrets.tar -C .travis + +# Install PHAR dependencies and remove strip other dev deps. +rm -Rf ./vendor +rm -f composer.lock +composer require "doctrine/dbal" "~2.5" --no-update +composer require "padraic/phar-updater" "^1.0" --no-update +composer install --no-dev -o + +# Build the PHAR +curl -LSs https://box-project.github.io/box2/installer.php | php +box.phar build +ls + +# Dump the version file (used to see if PHPBench needs an update) +sha1sum phpbench.phar > phpbench.phar.version +echo "Done pre-deploy build" diff --git a/.travis/secrets.tar.enc b/.travis/secrets.tar.enc new file mode 100644 index 0000000000000000000000000000000000000000..b3641d585b25bd663b1de4053dc4fc6b46b71af6 GIT binary patch literal 10256 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 W&QZi^RU|=sk6^T)%aZE(Ych}c;SOs6 literal 0 HcmV?d00001 diff --git a/.travis/xdebug.sh b/.travis/xdebug.sh new file mode 100644 index 000000000..1914365bb --- /dev/null +++ b/.travis/xdebug.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# The problem is that we do not want to remove the configuration file, just disable it for a few tasks, then enable it +# +# For reference, see +# +# - https://docs.travis-ci.com/user/languages/php#Disabling-preinstalled-PHP-extensions +# - https://docs.travis-ci.com/user/languages/php#Custom-PHP-configuration + +config="/home/travis/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini" + +function xdebug-disable() { + if [[ -f $config ]]; then + mv "$config" "$config.bak" + fi +} + +function xdebug-enable() { + if [[ -f "$config.bak" ]]; then + mv "$config.bak" "$config" + fi +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..9aebfb148 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,334 @@ +CHANGELOG +========= + +1.0.0-beta1 +----------- + +B/C breaks: + +- Most configuration option names have changed. All options are now prefixed + by their extension name, e.g. `bootstrap` => `runner.bootstrap`, `path` => + `runner.path`, `extensions` => `core.extensions`. See the configuration + [documentation(https://phpbench.readthedocs.io/en/latest/configuration.html) + for a full reference. +- Removed `time_unit` and `time_mode` configuration settings, as they are + replaced by `runner.time_unit` and `runner.time_mode`. +- Environment provider `baseline` renamed to `sampler` to avoid + concept-conflict with the runner baselines. + +Improvements: + +- Removed "summary" line from default progress output. +- Automatically detect time or memory units by default, added meta-units + `time` and `memory` +- Unconditionally enable `xdebug` extension (previously the entire extension + was hidden if Xdebug wasn't installed) + +1.0.0-alpha9 +------------ + +B/C Breaks: + +- Extensions grouping related functionalities have been extracted from the + ``CoreExtension``, this will change the location of some constants used + (e.g. ``CoreExtension::TAG_PROGRESS_LOGGER`` is now + ``RunnerExtension::PROGRESS_LOGGER``. +- Renamed `travis` progress logger to `plain` +- Removed awareness of `CONTINUOUS_INTEGRATION` environment variable + +Features: + +- Added `--working-dir` option +- Option to include the baseline rows in the `expression` report. +- Progress output is sent to STDERR, report output to STDOUT (enable you to + pipe the output) +- Allow `--theme=` selection and configuration. +- Allow benchmarks to be configued in the config (`runner.{iterations,revs,time_unit,mode,etc}`) +- Include collected environmental information in the report data #789 +- Allow providers to be enabled/disabled via. `env.enabled_providers` #789 +- Support `@RetryThreshold` annotation, attribute, and + `runner.retry_threshold` configuration. + +Improvements: + +- "local" executor will include non-existing benchmark classes and bootstrap +- Configuation options have generated documentation +- Preserve types in env information +- Make default true color theme compatible with light backgrounds. +- Added `vertical` layout to `bare` report (`vertical: true`). +- Removed `best` and `worst` columns by default from default report. +- Default to showing all columns in expression report +- Standard deviation in `default` report is shown as time +- Relative SD is color gradiated +- Trunacte long syntax error messages + +Other: + +- Automatically sign PHAR on release + +1.0.0-alpha8 +------------ + +BC Breaks: + +- Removed `table` report generator, it is replaced by the `expression` + generator which is now used to produce the `default` and `aggregate` + reports. The output and configuration is largely the same, but some features + have been removed. +- `html` and `markdown` output formats have been removed temporarily. + +Features: + +- Introduced `bare` report generator - provides all raw available report data +- Introduced `display_as_time` function to handle formatting time with + throughput. +- Null `coalesce` function introduced in expression language + +Improvements: + +- Dynamically resolve timeunit / precision from expression (progress/report) #775 +- Support specificaion of display-as precision in expression language +- Allow the display unit to be evaluated (for dynamically determining the unit based on the subject's preference) +- Make the display unit a node - allowing it to be pretty printed. +- Improved memory formatting (thousands separator, use abbreviated suffix) + +1.0.0-alpha7 +------------ + +- Support true color expression rendering #767 +- Added `expression` report generator - will eventually replace the `table `report used + for `aggregate` and `default `reports. +- Added `--format` to customize the summary shown in progress loggers +- String concatenation for expression language +- Show debug details (process spawning) with `-vvv` +- Support Xdebug 3 + +Bug fixes: + +- @OutputTimeUnit doesn't propagate to default expression time unit #766 + +1.0.0-alpha6 +------------ + +- Support for PHP 8 Attributes + +1.0.0-alpha5 +------------ + +Backward compatiblity breaks: + +- `--uuid` renamed to `--ref` and `tag:` prefix removed #740 +- No warnings - if assertion fails within tolerance zone then it is OK +- Assertion DSL has been + [replaced](https://phpbench.readthedocs.io/en/latest/assertions.html) (only applicable vs. previous alpha + versions) + +Features: + +- New [Expression Lanaugage](https://phpbench.readthedocs.io/en/latest/expression.html) + +Improvements: + +- Show difference to baseline in progress loggers. +- Highlight assertion failures. + +1.0.0-alpha-4 +------------- + +Bug fixes: + +- Numeric tags cause an error #717 +- Benchmark errors cause reports to error +- Undefined console formatter `subtitle` #729 +- Missing formatters not defined in correct place #727 + +Improvements: + +- Colourful indication of success/failure/warnings when assertions are used. +- Allow multiple paths to be specified in config +- Add type restrictions to config values + +1.0.0-alpha-3 +------------- + +Backward compatiblity breaks: + +- `BenchmarkExecutorInterface#execute()` must now return an `ExecutionResults` + object. +- `TemplateExecutor`: expect an `array` for the time measurement result instead + of an `int`. +- Extensions use the Symfony `OptionsResolver` instead of provding an array of + default values (which is in line with how other parts of PHPBench are + working). +- Executors accept a single, immutable `ExecutionContext` instead of the + mutable `SubjectMetadata` and `Iteration` +- Renamed the `microtime` executor to `remote`. +- `OutputInterface` is injected from the DI conatiner, `OutputAwareInterface` + has been removed. + +Features: + +- Introduced `remote_script_remove` and `remote_script_path` options to assist + in debugging. +- Added `local` executor - execute benchmarks with in the same process as + PHPBench. + +Improvements: + +- Decorator added to improve error reporting for method executors. +- Benchmarks executed as they are found (no eager metadata loading) +- Allow direct reference to services (e.g. `--executor=debug` without need for + a `debug` configuration). + +1.0.0-alpha-2 +------------- + +- PHP 8.0 compatibility + +1.0.0-alpha-1 +------------- + +Backward compatibility breaks: + +- DBAL extension removed. +- PHPBench Reports extension removed. +- Removed Xdebug Trace integration +- Removed `--query` featre (only worked with DBAL, too complex). +- Removed `--context` (depreacted in favor of `--tag`). +- Removed `archive` and `delete` commands. +- Assertions now accept a single expression rather than a set of + configuration options. +- Type hints have been added in most places - possibly causing issues with + any extensions. +- Assets (storage, xdebug profiles) are now placed in `.phpbench` +- Services referenced via. fully qualified class names instead of strings. + +Features: + +- Configuration profiles +- Xdebug extension is loaded by default if extension is loaded +- Baseline: Specify baseline suite when running benchmarks and show differences in + reports #648 +- Assert against the baseline +- Show PHP version and the status of Xdebug and Opcache in the runner output + #649 +- Add `@Timeout` to enable a timeout to be specified for any given benchmark - + @dantleech #614 + +Improvements + + - All assets now placed in `.phpbench` directory in CWD (instead of + `./_storage` and `./xdebug` + - `--tag` implicitly stores the benchmark (no need to additionally use + `--store`) + - Decrease benchmark overhead by rendering parameters in-template - + @marc-mabe + +Bugfixes: + + - Use `text` instead of `string` for envrionment key for DBAL storage - @flobee + - Numeric tags are not found. + +0.17.0 +------ + +- Support for Symfony 5 +- Dropped support for Symfony < 4.2 +- Minimum version of PHP is 7.2 + +0.16.10 +------- + +Bug fix: + +- Fix PHP 7.4 bug for baseline script (@julien-boudry) + +0.16.0 +------ + +BC Break: + +- The ExecutorInterface has been + removed and replaced by the `BenchmarkExecutorInterface`, + `MethodExecutorInterface` and `HealthCheckInterface`. +- The Executor namespace has been moved from `PhpBench\Benchmark\Executor` to + `PhpBench\Executor`. + +Features: + +- Support for named parameters #574 + - Replaces `params` column in reports with `set` (showing param set name) by + default + - Progress loggers show param name. + - Serialized XML documents have a new element `parameter-set` to contain + parameter elements. + +Improvements: + +- Various CI and code quality fixes, thanks @localheinz +- `groups` column no longer shown by default in reports. +- HTML report changed from XHTML to HTML5. +- Changed PHPStan level from 1 to 4. + +0.15.0 +------ + +- Minimumum supported PHP version is 7.1 +- Renamed Factory => MetadataFactory +- Replace Style CI with PHP-CS-Fixer, fixes #537 +- Allow any callable as a parameter provider. Fixes #533 +- Remove benchmark dependency on JSON extension, use `serialize` instead. + Fixes #534 +- Allow Executor to be specified per benchmark/subject. +- Allow `@ParamProviders` to return a `Generator`. Fixes #529. +- Fix computation exception with `--stop-on-error` and multiple variants #563 + +0.14.0 Munich +------------- + +### Features + +- Assertions [docs](http://phpbench.readthedocs.io/en/latest/writing-benchmarks.html#assertions) +- Added `--disable-php-ini` option +- Report if opcode extension is enabled in env. +- Show enabled PHP extensions in env. +- PHP 7 only + +### Bugfixes + +- Fixed merging of PHP ini config +- Fixed Blackfire integration (#480 @torinaki ) + +### Improvements + +- Internal refactoring! +- Aggregate report: `diff` column now shows multiplier (`x` times slower). +- Various travis imporvements @localheinz +- Various CS fixes @villfa @Nyholm +- Microtimer optimization @marc-mabe +- Symfony 4 support @lcobucci + +0.13.0 Mali Mrack +----------------- + +- Bumped minimum requirement to PHP 5.6 +- Allow custom subject pattern #449 +- No exception for empty file. +- Allow failure on HHVM due to https://github.com/lstrojny/functional-php/issues/114 +- Prevent division by zero #451 +- Use non-logarithmic scale for diff column #445 + +0.12.0 Split +------------ + +- Column labels +- Non-strict JSON parser (e.g. `--report='extends: aggregate, cols: [ benchmark ]'` +- Dropped JSON schema, introduced Symfony Options Resolver. +- Show better exception message when beforeMethod is static +- Stop on error `run --stop-on-error` +- Allow additional extension autoloader to be configured +- Allow configuration of launcher. +- Refactored annotation reader (allow namespaced annotations, possibility to add benchmark annotations to PHPUnit tests). +- Initial Xdebug function trace support +- Container split into [independent library](https://github.com/phpbench/container) +- Fix skipping benchmarks diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..b6e7e436e --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2015 Daniel Leech + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 000000000..e31beebc8 --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +

+ +

+ +[![CI](https://github.com/phpbench/phpbench/actions/workflows/ci.yaml/badge.svg)](https://github.com/phpbench/phpbench/actions/workflows/ci.yaml) +[![Documentation](https://github.com/phpbench/phpbench/actions/workflows/documentation.yaml/badge.svg)](https://github.com/phpbench/phpbench/actions/workflows/documentation.yaml) +[![Latest Stable Version](https://poser.pugx.org/phpbench/phpbench/v)](//packagist.org/packages/phpbench/phpbench) +[![Total Downloads](https://poser.pugx.org/phpbench/phpbench/downloads)](//packagist.org/packages/phpbench/phpbench) +[![License](https://poser.pugx.org/phpbench/phpbench/license)](//packagist.org/packages/phpbench/phpbench) + +PHPBench is a benchmark runner for PHP analogous to +[PHPUnit](https://github.com/phpunit/phpunit) but for performance rather than +correctness. + +Features include: + +- [Revolutions](https://phpbench.readthedocs.io/en/latest/annotributes.html#revolutions): Repeat your code many times to determine average execution + *time*. +- [Iterations](https://phpbench.readthedocs.io/en/latest/annotributes.html#iterations): Sample your revolutions many times and review aggregated + statistical data. +- **Process Isolation**: Each iteration is executed in a separate process. +- [Reporting](https://phpbench.readthedocs.io/en/latest/reports.html): Customizable reports and various output formats (e.g. + console, CSV, Markdown, HTML). +- Report [storage](https://phpbench.readthedocs.io/en/latest/storage.html) and [comparison](https://phpbench.readthedocs.io/en/latest/regression-testing.html): Store benchmarks locally to be used as a + baseline reference, or to reference them later. +- **Memory Usage**: Keep an eye on the amount of memory used by benchmarking + subjects. +- [Assertions](https://phpbench.readthedocs.io/en/latest/annotributes.html#assertions): Assert that code is performing within acceptable limits, or + that it has not regressed from a previously recorded baseline. + +See the [documentation](https://phpbench.readthedocs.io/en/latest/index.html) +to find out more. + +Installation +------------ + +```bash +$ composer require phpbench/phpbench --dev +``` + +See the [installation instructions](http://phpbench.readthedocs.org/en/latest/installing.html) for more options. + +Documentation +------------- + +Documentation is hosted on [readthedocs](http://phpbench.readthedocs.org). + +Community +--------- + +- Follow [@phpbench](https://twitter.com/phpbench) for the latest news. +- Join the `#phpbench` channel on the Slack [Symfony + Devs](https://symfony-devs.slack.com/join/shared_invite/enQtMzM3NDA1NzEyMzg0LTgyNGYwYjFjMjY5YjllYWZkYTY2OWM4MDQzZTgzMmNjNGI3ZDJhYzE2Yjc4NmFmM2JiOTZjODg2MGJlM2RjMDU) + channel. + +Screenshots +----------- + +Default output: + +![phpbench-standard](https://user-images.githubusercontent.com/530801/92305757-14aeac00-ef82-11ea-87b1-077afc72f0f4.png) + +Blinken logger: + +![phpbench-blinken](https://user-images.githubusercontent.com/530801/92305786-4a539500-ef82-11ea-8a2c-db67968113b5.png) + +HTML report: + +![phpbench2](https://cloud.githubusercontent.com/assets/530801/10666918/bb61e438-78d4-11e5-8add-454c51261aa8.png) + +Contributing +------------ + +PHPBench is an open source project. If you find a problem or want to discuss +new features or improvements please create an issue, and/or if possible create +a pull request. diff --git a/bin/build-phar.sh b/bin/build-phar.sh new file mode 100755 index 000000000..5c572d349 --- /dev/null +++ b/bin/build-phar.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -e + +mkdir -p build +cd build + +wget -O box.phar https://github.com/box-project/box/releases/download/3.11.1/box.phar +php box.phar compile -c ../box.json.gh-release + +if [[ "$GPG_SIGNING" != '' ]] ; then + if [[ "$GPG_SECRET_KEY" != '' ]] ; then + echo "Load secret key into gpg" + echo "$GPG_SECRET_KEY" | gpg --import --no-tty --batch --yes + fi + + echo "Sign Phar" + + echo "$GPG_PASSPHRASE" | gpg --command-fd 0 --passphrase-fd 0 --pinentry-mode loopback -u 15E1F8E2B149E6F5 --batch --detach-sign --armor --output phpbench.phar.asc phpbench.phar +fi + +cd - diff --git a/bin/phpbench b/bin/phpbench new file mode 100755 index 000000000..20a487e1a --- /dev/null +++ b/bin/phpbench @@ -0,0 +1,4 @@ +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +if (is_file(__DIR__ . '/../vendor/autoload.php')) { + $autoloader = require __DIR__ . '/../vendor/autoload.php'; +} elseif (is_file(__DIR__ . '/../../../autoload.php')) { + $autoloader = require __DIR__ . '/../../../autoload.php'; +} else { + echo 'Cannot find the vendor directory, have you executed composer install?' . PHP_EOL; + echo 'See https://getcomposer.org to get Composer.' . PHP_EOL; + exit(1); +} + +PhpBench\PhpBench::run(); diff --git a/box.json.dist b/box.json.dist new file mode 100644 index 000000000..076be86ac --- /dev/null +++ b/box.json.dist @@ -0,0 +1,28 @@ +{ + "algorithm": "OPENSSL", + "chmod": "0755", + "compression": "GZ", + "directories": [ + "lib", + "extensions" + ], + "files": [ + "LICENSE" + ], + "finder": [ + { + "name": "{\\.[php]}", + "exclude": [ + "tests", + "test" + ], + "in": "vendor" + } + ], + "git-commit-short": "git_version", + "intercept": true, + "key": ".travis/phpbench-phar-private.pem", + "main": "bin/phpbench.php", + "output": "phpbench.phar", + "stub": true +} diff --git a/box.json.gh-release b/box.json.gh-release new file mode 100644 index 000000000..e857432fe --- /dev/null +++ b/box.json.gh-release @@ -0,0 +1,24 @@ +{ + "compression": "GZ", + "directories": [ + "lib", + "extensions" + ], + "files": [ + "LICENSE" + ], + "finder": [ + { + "name": "{\\.[php]}", + "exclude": [ + "tests", + "test" + ], + "in": "vendor" + } + ], + "git-tag": "git_tag", + "intercept": true, + "main": "bin/phpbench.php", + "output": "build/phpbench.phar" +} diff --git a/box.phar b/box.phar new file mode 100755 index 0000000000000000000000000000000000000000..ce6b69a6d4f0959c710f397193472b99398f675f GIT binary patch literal 961620 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

6tp?N zzIR_hOyy{dR^*P$jaKHj%a2xW^{JQ?j;^rZ)kZ6z^P7)W75&<11!NfYRvqnXeeeFc zqooSo#nB}SX>>7?4P2$gajCsZ_b(9!g>Z2Z(DIj(0mC*EQw4rtDT3={vO>%Rtv3Gu%20>{&NhfT<6d{P_u&HkRvVCM(Vaq3EM3JD zWuqsqMsW_Lp{Nc7xZZeRv7Bhdsart9u-nv&NrlR^O9w!jm@8?Qv}ywHu-YVCDpF4y+6&X-Bj!3hu7riE=;d(p3(TbNc-O0?+&ZEQDduc__k zBImUmOIF%7)1w$CnE~?n6yL-NrQFegn0}a!j(-nGU?{0$Zo|W!E{kz0NAFp3rMN@S zkF(G3+`e;X`}6Vf=VSi`?|qv0zvTW!0!V3qYr6V950}pPznWWMs=KDP^fv>cLA;(N zQ<|A~f-#p3<%U{$e!EO973CmnidGfA(CdO_L3=GZt}2M>_(v(Gg!~Fln&}RjEpDx= z*DNhx#^aTUgwmxkw9vYSbSVvm85A zY7d##GGPS?R+QCW-ud$qV8K}k`8*;V^4kPI%6C@WcWwkO~R%nk6TFdz})** zfF><4;uo>`u`q*kVuTgf(HU5A%!QW>YKuE~2i#P+smCFj6-V@z+w%KLBiFd8Q_N+` zcGs|*$&J=&3=KiqNP>7C-(~Iz?44g?6oRYTSscr!Hx3E^>7=W1%Q^i9SXUzV0#2b0mNR22w|3;lhte%|HA7T-Z%!UW#D zLvo9#0Hs0fOYd`SqDE?acQ}GGPMlxJuBkGm&42E<+WAXN%5t2^2QS$spN}8!9)3ka ze0gyAmGt@vVk%mXZ#`)}#v6Wlf5&%Kr z7?3f}c?k`6PY(`yBh;5*Ds<7pwG1#`v|qh@OG`-#yiSDLc=JXHWmIHIL{zBKlCrMC zD*LdRpxf=?DKq?v)^ae|nPy%e1R)PNS0#;AuST|~inIlGV@V*=&_HDnz^C;wV4TMk zng>dN+|%0yNzx$*XEugkVE-^}o@)-HgwW1F`Qn$L1jKbz$qODwK|;+Kh-NySI}lp! zA_LJp(y8CtYta;!8VY6qX`>*G{(@1sGCsu+S|-l)YYilhrj=D;}sLx%ou4Mh}KU(%_^khifwIjfJM`oIg;?8)Y^;`hH>UV zQm+Y33&oZ(pDuFEYAy*CWD4`ZU~;gC47@WWhzLNmevRhO)laE2HfhNmvdy4XXry2R z)R@9&v=qBrD*bE-c%R#xNj@KA-njLInmW!s+l}hUCb0MFz#%VInGkJiet_H@QenX^ z=)k^J!$2<8y4ngX*&VYFv-nBQ;bozrU6KPS4T^G~j*qNd(fevKX56)M<)Bsz9@F;ZlDIyEELzdQ4P8?z?Dov|Pi*Ib zln^vMqJB;~AoXk|`?w?dCe`7Ad%Dv_2V4QF?KRg{5fl4T=~uZYs=@3DW9A52XZ z)h$vnP}!TMStHCS|EVgj#ChD01=crO>xrGi80Iz;+j`?C6EOa^RhQuITsY`$YFWpm zibS*abgBC773VaaFk9U|bIoqbBt45qLKT3}Ts>y%{qE85u%b}pK;hbt$R|`^D9Z7; ztqHvQNEPPV2D2j%s^E5ToRnyIJ(-+^mTlat^d9$);CPrJseaS~?6mh`2v-QaAK*Jl z90FFg5CKNiVaZKd^<@yI>*467hF9ZtTka~DUvC*0cd-s0CAW-X&IFkTJTuG7(BCe~{&F0cQ-)e0? z=FyiY;notn8|b~=UII|)mI4w!H;G(&B1z_t1}R#Ofd@$X*Bk2{pCrrQ^uV3K zp|J*lqF_ikd~bsHVOsRy*Jz z4qkbI3~OVV@w$7H{zsR;7)`O}2w3=dJczZ`Yr1ns{Ze zd4V_9n>c+T$Vmk6Nb`zf_UONAokg4j}!;n803QpBZ0eitF?~_4cU$CugmtLt+M1R{w$oyo2GOwbX+? zv_ff^K%z7?GqfCjGMS+l+aKJ9Rs$fFvd`%(p*mURK*wAQ4@c0vpB?mu2L4VUD9$x|t`z*Ayw9rTI-JZ621;Ds# zyS&cZE1{W$%`qaVCRcXg1kx`al|1al=-~hv4a=6XqMw5SYuHu^F-k{t1kc!MRNSL` zuZdVInONqV+1p;h@e7ayTnbB{RN*T+X4>C`7cvMWLRkzImzK2}2tsliVYCyC1He+n zp)jd}8&^x%JW~bOt1U9VQX3~7Gn-$>h_T7far!U4CFwG4;G)J1a1B?j+*xscnBSQ= zwb)7VENQpBL;xqJDzuNnFy2QMQx`|9bPmSY!>RF5F?8TIP+z24t*#{f|37>0+Zfex z>0r&lbgLOV|;}WHXD$LBs=kgurf#k%(XP5Xe4Z6=YIC*_p3kM)qT#H z839hRyU`1nb53`4b#--Bb#-;Ma#w{(nr33=up6IWdkOx>8EY!a+-1xQC3c``k|%Jp zL4r9?3lL!|9=tmGoR@NqC>?5OMsL6kDmO|TvGS6>)gyK~)6h4Z(OXu13x!1qNB%;` zNkZ?s=}|I_iK*uSi<>uP!IKoJ23oaG(cg21>ii7d;BlDdz%@olX3zf=2sQNny*DTK zbE>$6CC3{58m;geOBP}Cb!CD3A1XU!G6&J>)~^a*J-_PW*;cS1+U5B;5ld)j2YkqS zIJaX;T17=x`}K+hN`G@Qel_9}lxfXXAcqC0G6?$PCVjRsz*yWjkmbW*JjF?4yy^cH z%SIE`OZFCY{$!LR@bc+H{muhkw!m2KzoRwG1LPtr64-34XA3={1Ol4`4tm!OA#jjU zE1-jBAe2bIbP_V-BG9bPCtiv2Zf>Pa=Z2#ag#LPTd^{ZQBE32iYt8c41X80zCk%cH zv=$+&Cu+awJ$$^owYk5u``zK**0YV>4g3_sL3^XhAu@LD9v-gsZ?CQ0M#GcKb1W%u zzZ{KkzY%0aQ?8*fx5RXo_Rro7&ev74n|wMveDwIq*5RS3mw(rhxi~%Bk~XIAriak< zxCZm|?C^MWz6`!^fqpbOLq;~V1pd*w7&SfvO?#n&tCC1g3znAYj`HVX8-*D|I3hfY z%2@hIW<#0#zSSUTOBXK3Ja~YeJAD;LU0X_AHQOL8+N`WnpSD0cpj%i~&G&9grT&_+ z8I0&X@lGV0Hrbv5*gPpBR0;DX3G2lz)jT36=KpAmzje>8p5hmF9H#@^|>v%z%wBNRRh-RpC+h_!mF z=iwV>fu~90i1}X4?aS%8Oq${NE%&}xj{sB*V-D5lc=b6%2@GX@nwp_sz(3{%vIJCy51xc%Rb zhN`u)o5%e-%58yCxk=&^*1C8k@RH&K*i`Kl?}S4w1}R*Qnh{~RVJ<%|ZW7`EmC$UX z79skG#Q$4Hr_@a!d#s!*F#yS8_!tA?aAyrc*|U}@fXWW%R~~|z?hs6mvjc%KbPx`w z!O>2Phl4Q#?(ZEUL}pla8dp5BCaZ zltlW4DH2}`D)EKSax>dsVcidR1UBlX@OcJVtbv5EtuGMAhf3GuM;Fv0jdyN2K7_@ZEM9D&VnLxxgVas8R2Rvikyi$YojJ-{16d+hk5w?+V$oXgve`Xf1Q>v zI>i9^$B3A;RB%}{;Qb8PFzTt$Ft4+S50w9wuc-GyO*5$syK3V=4oOT2Fi! zXbOiKApPns)%AnP7C+^3qH#s=)lHO2256QRTLPCMq*1ewgk*$)A~FH-Kakx-0jsYBW1C(Rz^c`Is`b#lj*)nIx44C#Qpy4&a zq7F>i*r-+aR9SK#n`ojYF{)N4j{S%Z{ax zItvrY#uBBtU!SvZ0hjm*5;K2OdkCCLjYYT>9Az?}{D2e-N}!A9?z?`QyR8S$pMQV) z;?vXH&}9-aW0GzL3b7Y#zzHj>QYBSr|AQ@Pvu(wMmmFuRG!n5PlkKn80}HDfbfN=9 zsY+&+QzcIc|0^yS>}rFY<6rTP8XI~wW*0kn4F00aZ{s!M?_VPxOZ2v7omO3#r8>sb z*O~6M^-kB+jq$%C_FabuXp}hMNVBiKF^W^UFLKB;D`hw(6{wFJJ!)wF_Ljda-$%%7-zw$^; z9k6zWMxh;obnF8MCHS$sk}{@C4Vp(KZu$w->nNZ;RX3j zhZ=7X&1OO4Vo}IKj!7^b&FN1eX+OIGgCGu=5K8P~?HVIg^hp*kJ75I*!N&Rd;9dG! zc2x%ZR}~lHy1e2=HKC3K^QO7uf}mO_Ncj-6(zEI1e#|s+8sa7&L=#}m9N`4us4rzhHH47yir{z zKfJ#EzhJQkm$<(*ANEykVIaO1nNbZIMkW5$V{v{tE2`n>lqYN@gx4k{9_gL4O?OUF zyusP{my_v8e4mxCa((^Vt=$JZdyn_OJH#*f2aj7a%4*?2sd0?ew@_+n^D5RymT0O_ z%w-=E5Y>(MGaVHGG@=;6*4H)LFd~6aRNz_xNu-eC!T1>g;aDyqH6GH9Ss~GB88%CS z9aEf8{z6INfTbxBQesGFD&0eOVSY+z5jy;>ldpX%H&F>Of;>458GUl7hgJ@uN03F@ znx_Lvsi>~3J+Ug&XX&(w#U~KrXw9UzpyvqDe8mYd!zB#1!T#}laJFeI5cZ1tFADfcT&E?F6wY>)Brfcb%6z z9v%0n*MMAE9vw-|V2rlX6*#~=4W0#;6(fXWpR)$Yx)H&by$ECSNk*Y8(?iTqHd$nT z3Wxd_dTf7M_T7^*a$-_{QGd7bio&Y2x)CDl+bB^Qw$16~^a4S=GlHcsd(gZz)hSj) zKv=_BJO~eIF99LI;gd0?vsNedN?YieN{mWsr&*(1>=r~TC2E;k%#fX~&3tP&v03PW z5#@trBu2Q{*0)|HD-_W^@6PD@x0B{}cq7r*dK)mq&&2Hb?K}H3CInz$v4TtmvnB=M ztllrBmf?g;8FYZw*O9D1H?W8`S+p|U69d53l8xF$cxy zh^LeRK=S6}CI?BM&teX;1_)WAYmf}5Z*>0kJ;=(8O$*27bFwC6#2yUJX*~4OD)Qyv z{EwsKi#K4;&;BEr^3nP5 zFZgD;K@wuhu%R>#t)8JAHi=RhY{i5pQKa;c7Vm~<(!cNyc~y?!Fej|F2<>Dry?CnU z$)-<+Rt*NOY+Nf!c0}dWVAKP|`#2$5Y2*RnYmAXT}RxeDhsw1SU zOEmMkzQM?&SrmKP)m4n04p`l}q~h9pDW^1w_URIjsH#HbFKCAAxoQ1AflV-S}0;8(i%mb4&j6xjU z{0qmz+AEe0CN@}P#>k4B%`9S4FF=D0*sv6>A&N-!2Fn0@QLP@Tlfk>vDy;?Q;P@?_mfWxvJnlxU^IUS1SW4j(DE|)x4%T6`bNvo1EuM)RCaE?Y zK-CDM?2fN=;5qc40nPLdWc15p)4M!g$$pI(`ouOgh@ug~RRub#yM6lMo(oc;07fG0 zk3E^Z9vvkY{&N4j$tB`U#|M3Q3UTiVEDAa4;ew(EVe5HO#hI!+Y|~$xLu%OcHjF#V zz#b$H(m}ZVhHs%hM-!gKQPhheLm-L5$$Sl(FCF2MvPLx_Kt}}yVkENm8+@BlhW1&0 zbu!^e4trSwXFt}-`_li=2|2qIuw&41B82*L2q}=H;C3}OYF6R`3!ZPXjx47#!~n-g zn>#854&$SpM_5m;PI2whJ8>RiUc#fhwe#RzW(!# zW#&^kSUu$ZiC?ZCtm7`l8~DBQ<&D3M5QUPaCl~6PP{mw~L=#H_MF&5;aZ*|aNVSh` z4JExbZ*l6UjB(+i@_}BzS~~+1aI{=t!l6rQ)Ar*XwddXkT&GxqPuLel929!whMA)$ zccc0i7NlY#53>nG#FPL22ExU>_GdglEw>g5FFJ6(!3%y9j?qqxk zv#8+vz+Mf$Fb60jku8KaHk~wl#KC=6i8avur-d&m>lU5A_;(q>f+oaueiYyuLD7e`Bb!67c%O%LN=jD1Dn+Kv~U&ebOO&2m-(KrrwkYd*O=XxEGNue9eVVbfV4VauDke3J^*^9 zh^F_|i55B$JI%y3Gm6H8KdD@cM+Xh2&WYG}o^k5Z5V_VLo(#^WNZch^QE`%wTOz#% z_mEb`axU*wese!&S$AVdE5|$dXxTQ}IUk-5z|v!0YmNM?Gh5;Sev)8i?jsg67J&Jf zzw?it^tXm()2Ij7d<&5@I=wu_R>bK`#5N&{h=vOCOim@#1Qt(RD`sST>@4&q^$P3* zb&*{x@q^y;J%>uzB1=8ajbJ*pPQyUzT-a>5?r}oq2BU=y3O3qCFj*HG{*O8+so`H^ zP%`jpP~wLV8;EEnn zdGRKab?NgsGgpvbdv4Op5n-OHbQ|-@)*~@7G2b!47H_wOoGLZUJGVHbN^?2>Q9s?q zp(TONf3e=OWZZ4~S1odE6gD-kYjp@SoGmp<-?)7P{l3wAegg!&f&8w9|N3vHH*VlY z)Y1T;APjZA7Rp_7`$#$-%FKe_yJ50?{dZGothBK@TB~bG)5&al@AhxsNbJ78n(;}1 z%?4T+DKouFiY-iy8k^n?oi|RYXwEHyQdM6V=~s;`SKIAi8-8y6*%gY6RA|%5$@!|H)(~UosK-P>~;;?f*dOaEs?DTP}sunOkF8oWcO-MH0Lu5AIr7fJs7h7zp zX#Q5$E4~bJg=hK){{d0TUDqS{=?1T!KrJy;o0dQu&Hl)!I<6AxwkR)tYYqBuZ;sEu z32pNEcWn*Rf;MH7o6wec2=~h`zsv_}|Ia<{tHi#1i5v3r3IL||179e9Xe@!k%Im6n zr`*^UB%ZSg4M5E$RSm70jq^dewCzUl%tA|_GZh%d*a2Xc#V+}hMe9jvpgj|wAuojGaUd!{mo{nuYrq$8aeRz&af%&L_; zxYj^Z1{UpCQV`$L#cbkb4nZS22wcD>cbntYbhOQLhKX8RcO=tD>lRE@)~cP%Ba(e_ z=B=$crCFa$V;Onp;gno|3lt2Vym_U~9XAuzLKVMPXAMH=ZgT0(Y+0?H51_IF2 z!Z8S`W^9&zgL*b3?LNn&!!3OtRe^H`5gm=~{NBqUn12Yj^a-5uuMy#yx`y-(ioR^R zYk0T`3PTpcf~ckMDb&EeFM!&dXamE5tyqe7or0l(JWLu>j`irQ-~y{jUy*_;;tO0L zcqwW*9clDcem-lDS?~(&V1-J?P7DmVTHHXh%lam5w3GSKZTqXa%!@5)G{HZJu^fxc zRkq^{d

(cZ{^3uZLqM1VG4-_CQndX+$ixdXO9r`KO^4e1jSgZOfKX>$qj_X9>#v z+YN4eNoo9VtqX@+`)L!;XSJ%As&E&X^Ct5=aFUN$$EzNzD&XVqH0ixnpYTcENKlIo zLLqNhz{5viK?w1+STGNZ7kQau)Ny_gXB!#V-lujjitK(eBwW|~p+BLblUnxEQVpLC zzHCYsRa6Ig175X%u?_-TP%I9@re1T=q_@dP6e|g$8Iml0y=1pn3MhE&CW8;(4$i@b z&GJnt%T!Ta4S9`z z6FZ@Jk)k4V$8%W)UBYk?B`jDlJVj?jKhT{0L%n4V`(y6F4Z7qBcV`3 zct5`59%R)sN__(6hM#_l&04GY*laekCEE5!%ZkUTh>QOKTIUFU%;yewix`L4BRZWo z_Y}t@Ac#nI77Ro)K`kv$Owd4cAwnT6)4O)S4jTl41xTNti;gMdN{3aj3y?sMItldRcP zo*7MowA+_(lx*XCTHKA}v1|$T1_#n``V}+JNIIM=CihVWY3{9<7%ayof*spw2|p z8P;^G=O~A(A}}4*Dv@MGbO{Q)wrl(TVq}y6sD~QhWo{S+Q55(VZkl7Jebcy*BuXU8 z_js}~*Bg693mj z24{?nnDT<8D732qgnq~igd-Yqolp8!xm-FEp!EeX?IJ)_2hq!6Z_tx2@M;eM>kC4Y zKr-Zt3`LJt&RwX{hp>t0hX{ZsJcrT(qam=IdLpm_`C2Hb!`G=~{$Lzh@^_qoS;WT9 zhifEVP_v*TCJgVe$?+Vn^fl&PD&>VJ9kHbV4aUy0Y+vD`*RDHwiv|@IA}u7}QSydx z(WHRFmAnlMVH4PDw;kp``odn*yA3KoJ4e_hf+mH3X6ev)SxW=TM!3o49D)dJ;lq|) zFa-p7@!SE@yo*7`5uyR@Sk{Mv0Vp)}LJ}2qdMN<>n5=+gR=Dv~H9jU#=fx#~9VKWj z-4B4AL^K@JsV=N-h)*qX9fe1hPRLMsb3XYIHAqhJqOh7eHe@FTIL6Ka!N24Er+Ho6 zLaR7BAzznsGYqzxVlb?f6oKEro`|dZz zGV79Af*O~H{X;J?{iqj}26OBGiJM8We)`w-qv2>#y$)IK=fg#B>N`Z;(GXVi4dZu? zccoLb{Gol3AM>()4tv<{)@<0qPX|AxdpEJnv|Fvivd}Bey=LzGe3CYcYezLjrQb({ zV}034U4B@-&y@P{{yABWh1GFhIc9LOdMRqvx7HCgDfQG6nqecvZ?40VByA7mhWeIk z7bo_Gl4Wh)dM>Y-^%5?wa%GOw%kstQIXZ$ z+b}7p+XnkgY6)T#bi;)l1LWc?0wSssaOBMTd^9<^JRM8K!K`9>xO3;#rWCiBexEHi zt6_%YFTIp|>F~~9V7L1M(duv)W9x+wY9H?ciCcB6G^B_j&rM|S5CFxu_R6s1AOwS;b1%ZvQ}mTy*KbU(`ypbC1yV(H z-NVvGk`_udhO4|qMn)_;<@4Bya#f8gWG2^fe5q-MC(|LjUZioAQl2|BhIx7Q4wPNN z{;K2>PS5nIr=c@Q+#L4Fp}VF~%o_c zw*ok}JfkJ8VKRZWn+!NSLq1OIhlbXOgxu508f|2gxtQ+@Ay$jd!v02&Xh^I$w*tp{ zOsj{BO9v*-9HWG*o9)IX2|eAn@^K~>Kz2Ar&K;dhEQnX^#qWo_O8SQ(vRoJ6um#DC zQN_F9?Lp0AOX_HNast~>1A$3}ylDaVzC>fDB9?8Dgu%hfi_!Q}bE~GCcB}kHsUlI% z2t7CSUp!v|`VvYhNpTQ~gLr|lpzkl(|IADBR?z1!p+Lxrv@}LY(5_R)X@rvnUihYD zhR?F6z$sVu99yC_NpXnf1i3;Qoa!Q|3mQgAPt&otJcXy}mB`~_SkGmA5Cpx#X#p`M zZW(*Af7MKWZbgKo_}s?mYV7F0wXDE_eb`1K#=JA~2CeTo3v+v~{^;Chaw*05i0qd2 z%_g|)YmsjnP$G*CnR609SOJLgUva82_*-C#%vi!1Bnudnz)7Ms<&b7W^^^A`iig&2 zHb&M<3E+-i0@Kh%4sf%ZK+A5kHryR>L1nJ~^gAQu$Ct?bEOJrC&TYh`qTA?XQqm59 zNOT(&u>ytG+Li<)$wrMEZqkxqZpHIhemyxK{T0)HaPnYs4t`+}s;<0!V>g%GJfBRb zI3A;ANO9$M0S`a>`45*&iP~tRtS1qEJT4;>0Ojv|&Y)M?;S6n+j-@v@Z_2+@Y%uIxl54O_5GCp3!D2#M)b7^h`lJ-kF9TUXLYa(;;h$DE`^ z*DVMi^i2AIS^6mq(+eg9gM(?wV`hwBI`|!WM7M$*wf4)f>3mb`K>|trE^46EcH1?v zRY9*v+>~H*i6@Xx2$wJJxaLdr*`cZ|v<{%Ir-t&F1cy<+zj`V}fdc}7vk)66ywy2_ zH-X~rH*3wJ=Rzj@Ur)|wFr|}-ZU4THD~%xC zk54C|{DQDyTG+A?Z(ll*GW z8xRT)X(bv{tM@BB$cYFJa{xh|W~Ym@c&}qF7)gqgF4{BE5wI2YNsY)7oz`+n91Wh% z#0e7;Tp^$+nkNCV5?^ zea+bI(`f~Q;WKH?_Z98Piep>b?w56zB{(_QVcgZ70GbCk}`X4#JxO=@2T8!fRl%=#T3|=qyG`%-?8twswH?wt`2@Qb9O^nVnwQ8zcHpf1 zla23ozS%!~xb!#XTYG!>h%H#t9S0n;2rcdk{!$c1fH^I*ekk0m^xNnlh*az z!IUIJu#tJRM|a?It(&dQjzEL! zT)|b$vnsX?M|bnmCI#y9hEnGfRXI6+24gwo@CVw3B?7b=^@5>!Y8h{fB55UVR;`h$4& z+bfEJ!qH_WlhST3PBW@rjZP3nvM8n);*Ys4H!93%uR{WvdtD8!aF`Hg2CjM+4CoA{ zsdBJ8e04IEgxEN!gQL_JX`4sHF@lV=7B}0M&O}IV938=+A0g;L6Y-`Gvc2Q0N2_iz z035keFZmuoTCh}V8Irkiu1sKlj8m{exL|B1rzTYX2b#<}CJ{DmF~ChWW>82N1*}v; z&dD|%!9qn6%%+%TkzXR5C^Px-X7RC-ODu42G=4Rad)ehjT|9{imcRL;I&MlCU|v^b?O`&Bx^!QuzJM@O&= zx4s_n4YX@d;t^)jDAqIp9Ake3>XXU%wNhh&@IqO4mdPR;Alox~1v5Rc5-}n@jNA z4oQwpMpE2%8?E_Q2-95V^@#Gddo`^)~9ZztBB-b^=pGH$$00jrDP*|Xfs9Lg;J1t^AKOR|D*Z`8qb^P3`I3Zl6) zETXL9t9ycd<`XOgsvyW!`9h*yNp*-KY;FcQQDR$iJT6ICLQNd}D&O;^Q(;c;waw7s z*&JSrA$FH}6pDxnXh9L_vS*>;J1u+zHqOwFw9BXoU@e8g2eI(yE*n!*MQ;By;P zTwaktRCzT*Gv~aa92}#2tEOp~EJOwkta7avG)9xhOI*o*@w{I(8v>$IlDT6H1sPXh zwMZwW?gAav;PSd7rEjAl4f^|&2_jO)??4h8EDH?JtD$6=C9P76$Ek@Su;E(?f=zjLNkE$S3oh(d~oDJU|_6(srs zBHf~}78B=5O?m|00H%ksjFg|+QZ33&dv(O%vc7rI{f$3$Sp1=k9 zlHqHCsVu94(do?4$t2Mg4IEq0Rf)2@q6|)z${Z8S=oTutg^?&RntsbUygU5MDosnvqUK#y{k21IyVL4Wmy%ZiO$38G zhD!%4;&-}B&Tu1+!1!nmJ$K;2XrAN2gRW6YVu#FI1z~i8$q;gH%mAYhfsTpsG>gW2R>vn+!gigu!F~lNnMn}unrK#VAUu{8;V*)mJ z!QSfFh)sT^75kz@`t?@slxngX&#T`g67tgM+nMXW_vyvF^`9~_6-$_)alLxKmyQliw+|n9PE?>V%b8T8Vxocayx)}oq zyWyso1zvQ@G}#c*cQQqkXR*V_n=SMq3|x4N0a|`JnZT5aWxE>)n<2=Em64QDK0F58+HBW6X}xB9?$iS$RP#tgQ6Up=orAH+jC;AQQ!y*|LS6&ElEq z>NyF9Q>);Lysnj6INiizT41z`t<>y7T&|6MFpn#0b1Z^34!1rv`MdleTb^y)UEqH; z-i}olI9NgotxzD4YH$7GW>~T^0dPY(C()?d}$?W8d9+2;G&=((XLl zf4sB3@dWR1idM@#-aFjb-QD;O?|w@!on0#@5hfw@?{spE5XbZt76AS6#hdBT`AD{D z0Ve*{!xpVTX$DM5@W6meAv_&}ZUzf4`}77|gI0z*wOl$8fEa}aNe|w7he7e=2uN-; zt-=h$Qs;i}e{!osAMR;X)s+?wJ_J1h7c(x3g(VxG(xwVF<9o_t^vdXMF9iZ#^8aL- zmQ;yE%2pv%nd>|!SrDW3NI-;cqxoo}1g!E5sq2lVZtBIVO7hHiaIr5}?^4{E&|y{s z8yXUlr=3E|+?t`McU{n%6TF2?6=zk5>kgj?W*Hke>hdPK@g~_XFid@1WpglQ3>TWl zrmM5FtPl~`#mKrId}X?LaZXxacLo-o^2~tbd>f}?+q9e!0V`((^f4_o$_b&by<9Ew z>#l56_!IGz+Yo|If{Pki53^}+$U70h==QUlBfzQ;eH%G5MG0t5?Icqo(PGBBDVbEA4U3GE7vfZ4fXiBj>mns%{RhV_Ay^ zHATaAEW*Vn8775ogd~8;uoxg_CcQNaCA@KuX4qY;2Q(@>nPkyTkmqAqcdBeksgE%; z>~|34T@uY}HCht*iB7p)C>QHie3zZ}xpmpQ7+nlclYf#AF@>UxklI+Hi4CyEZLlzA zR`SCnT#C<_c<$c-8GMA0U?CL;5p2q$fHsED*3>)-wE@ii4r7p-%U@Hw1!v(q(wG=V zfo<^Km@0qk9q{BKgmGaLOkwvr0*&% zGlEUd3@EBp+i(SuM%9rlGG}=cSQ7ZpuyN3cr>=BVil*LR&Rx?;KaMWm^u*v2>uhks z`$gVqST#IPFf+YqXeq?0b&e&;G+*_D5~?N*oxv;kotlcXQ1cm~Xmn{XgP0?M>8^kU zZX?wwvP_(WjZUV)NpCVnw4Tl>F*zbGB0|;2*GUy{p>H~CjCll*El8q`kb31Z$IIk) z78v?D^fp5G8Ds<9!7`|k1~}Ua?TQ%o#)d&__b&jGsz62lkMB84$kP+=yW`@^@yO|v z2Hd8s(}R1!HI1NW-FNEa;;ZkHIJL}a_8iLi*TT(2LXF`}Ngu|pa7i^O;Bu?(Vt`=I z?1s%rDpZEVm%V3SKRbN9y?^+0<4>UYk@&&uRs-rYn)T_?)qFiqY{5MPqM6T15#4|* zc672TsFMi;mY%i7#Gz$|Ln@Nv)2QOS}t(b$v8BZCH% zc1L;A-&ug@H|XRa!O8w}K>{h~d8JtYDt3ucN~{ux)k!fgpoEf`rE&ta4L4ZSsH1W3 zU8D-^`(C6qOdQ8Zkc>hY?WWb(fhpX#h?pn-_^xKJ6v)VQpf!HQI0ix zl8lzzlLfX2BFtJQB-*s;H)SiVNwZX`>jR@bpIPF0qy%fjDU2u$4U4MnRyH~$kKzGR z2N9Yuc1gW(0pvyZC*qb&v(kivG+TEi0lBJvGct*G)88VdUAO70z_W5-Exfh%Y;V!I zqp8=x59U)(8}gm~G-(bOHMCz%l{l+v58a~dKKoRu<7M>rK6OjG%wsLXviEL0Ih&5m z;23&{O48xYNXayos8f%N<5tO&J!fO?yQD=QX7w!1U>VLGC2>Kdy3hM-uA6x<@Y^!> zim{eArW6}rTwhyTyWWSL*C@Uta?66GtoU;+|AH0l6^@av^L%BB{PS zcYOKb#Bv*1*toTC$w+6%+`^ROI!q8n#Gc=n3PKJm@IR=M-zL8^>2X)8qBmGl%v>n8 zBGm^OduVsNt6{g0Ts|S%3ELve8ei`qgH_!Wz=J9Mnd0_^r&&F{a9L}(Q0eOAbhZR& z_Pz8@E|-t%>&Qg$U}x{~{&$DlJG)Oep4^f(FQMd?R`2WYmk9gN+-w+V&MLdGWx0Po z`0>!PwlOrA%Vr^`PeJ{L8zzT5FHv;1t-J8TVzj0YC#UFyIHJU0&KOzW3U4jJ+W4_1 z*~pvE6@?)xU+lxlk!OfNpA@;7G+dB6#K$pcA;3p)(pNtEx$b!gsOroZP|Sa|5QLh2 z<|ipsCZw}mMZJd2k9ImLf^p!JEzUwYsJivJL26#H$wAO!DGE-zR|5xzLH=|@3gyiD z$z>be4nAKkjNXbuB`4TljR@D_rK85n)+K?84gv(GmuYrAm0Ad0A5|Jhiy<3pBs6MN zt1vgmwk`&G3Cp$xvQagE$X1b&JA@c0<}gt-aNygGC*N#A=xuL7n4}N`eVaFdbD)8K zNgJ9HZV(}*ZQL(<|A7v>(NqT_xT*}>FuF^gD%ku81zda~y^fQzi=ED7LWq*N6LiBG!ody^nMg z+7l#3_pX8W&Po%>FtM&DT=M33DxX%^ehevn5rf2^04bd&5gW}vNJmqHbWKU5&GhVK zbb%9s%Ll)#tbWNjn-d&GJjGeUbYy}D`A|E(Np7vfSECcS*mGIAAkQmyVj^iV0>!<; z`%vBM;u2-q{;w!+Lo4Kz7y}86pu5OeOydypT*C+~5Xo})-#;vrMW^~l2xLj!haG)<%k=uqo5IMCw02MLHJAj@!GK^xh2K%1w#ocUa%IAY+uWdt#BK`92{MJVN-Z ztv?+iclYD%ufU-F<>%iYOkdpma%K6;^@Hgz%U|C6 zs759N6B#L&pif}P)_uuu;oevY?H>IVo*Zd2IlT%==@DBrkLdo);_&c2B(s~rV|21SRX94k) zA8R-pIXXLPam&e@8g&izxdiqA^zqC zvTn+Kiy)R~7Woaf2yx4tUYM9n@uK@PJNq78t-577H5JD@Z8+X>9HR!DfuhQQwf+a= zX4zNn%07|D`dx#ue2&|e>yyww! zHdV&NnBDQ~ES=xO1)@$iwNKr>D3eXL%-&BF!+3Z-JcqnugzdBG`2k)WY}jF4{}{h@ z(LqCI9Rg}T8NMAN3Uzkm?#_o?>$ZvWB9oJ$!bmRABzsk*SRw67Rw_jglV*9m{b+|8 zh4)evG*_%>{l8+ZKW^-9i+;Xhz3m-f z3D4ZjYPxr^bt@1>hi+c2?+#n4KHPfn4Gf8yb)}44bO^d;51MZ~NCo!T1@1O8;gVKo#S|tQLu2`%CbmcTOc} zKCyjzOC6(7#99edOv5x^&Ae%Y-_qz!Uj{YGC#O&YiyE&4qb%u*mYPZ@6P&v{p%8`X za*A7!2Cw0%w=Tnuy}-_3k}J|z+C$|Id;{C(3b79M=@RPc9%sqz42*h$kjDBX_fgL7 zEMI9^GJ>N=e2mOxmmLdPUnt8c19Q!zW~Z`2jM8tH_Tr+o!fCCl&$5c!qRifr(HGB- z>vrPvEzI{Rp_QIjc2d^<2eox;9m4(CEUAlcWHY6`Hjz(rZ0Lo zezP>acU#`jY*kF!X@$?u3|dUK0M8~T%=wV{1NSEjXP6cQ$NB^Dzz9-UZH9oa&IthL0tvDM}~U)u2uT5+Ps% z6@$&74EWiOCg+&s$0)?L`YkgSn;msc$io?0mXo*5Y2Y&NY&h1QYOf_}M?wlSDz%N@ zDwY77dffnMf7uw)*F?&>+{ZP4V-dj%G@xP)PPWuDgi9yht*-17*Rc_X332Ra>~L3+ zG-(ujxo7a91BkF@w4G5@`laW@8IfRN6o6DJyIjz;AgLhLQBY-i%~$}O^^~fLiWrU0 zZG>nk)ZQM(snD(Hql+CKNS_Ps)q!rTu2c-f!!(+lvjYWLz-deJ#fOG7tSQwPC3??8vYr-ba7sub$xbgmW!Zms@ORk^b)!WuwTV?f2Bm^Bh7+ z<}Ieq)$ZDcT2#cM#%`r+3$;>{%UUBnry;3iIj;2R8lhBIM9wvwR=l`8AB%ddfM-F| zf>NtGB4c#4JeuYj6q?p-u`$=uOheeuXaek)i-$I9yX6nxJl@)u*met0JK!Dtx*5dE3SmojH<)tPDUxZw~kaH6lA+SpqdXt`#VCS3tXW#5wFfIGo zHCO^ES=@wUW;A57ba5fb|Cs5Uro?;CV;5U0O&J|u~@DVY7keaLWjt00xk;9Kvks| zHVw?{*TFK#C4PZ)j^YdIg4QLg4Y|Z?Qoo)VEn>Q6NRjc;hMose`&J0^8m9T>d-sxI zx&Y8Nk>3J1dHt^dy`ddyxa#xnLKkwaChXNUh>cf6VNh+FlE`a-YVtHHU0KV}!&7&< zd*tbwQ#~qNmDD6=db;t&yhqGB15su7)%Z&}H)Rh5HpTJMqc`**L(B5T(EwYND$$ap zMWU&wcGnSps-AKR7+v2ap@>5$q8XCv1?^q}3*1C76ZG!JAJ-Qu`0UBX{WwkvA#y?_}k@zE<01%tD9IIFedIf$`Bp%GZH{)_<$f-Z+St)Q=J~)uh zQMm{UNeVMe{K{hJi{5XVpluUPvKeZ#1#hFxgpYjyXGToyt*{~9BU57hYZXUnE?n6R zEr7Se#;%9Y#yQ4vhE)S<^J;aMF7;k_xh}(?yGEtNovtnDK62}Gq19w}i>2|y$@ot) zk-YC}t*cW;r&Q(9z{E;+Is_rS>bbW9q^4eBlWPrVxnu@_rE7KMCl|AEheGe;B&V+9 zHr4rXI=MVY+|`n{FZ)t`{jJw6V_Z_HL(14xlsamWfFapS-HY1{O3LwYc+7k4v^A>3 z5fbCS;#F_h_SMu&9%O9d+loTh=`RqNJs}0uJUe#3Hjyi+WDwx}+nk)>*t>g5)h28S zG|U5mK}m4&JE^o}i!^N?P)&OrSN}yPq+PJZ)f5bPj2)KsnDqERUzy?KPH6HSkTkOZ zXMugOtx0dTcAoI~u=~oG-8_8$K=(klgsHF-}%r12_#XLZ*^9eBVc`c z_;`0~bAJa~V{hx(#_k4wN)Hif8n6G-t}{UzXgnOhUG8se?>*k!*+wDksrKElC!C5oODf6opZlq|GTZ{Ei?iu)7R-AQTE;!C|LJT8f=l+(8akcB&@6 z;HSxc>`Sb)#yX5J8OPXG9v!FM+9s11^6M|wtsJtUXv*aAbFl6jXx*dB5HQ`O2Dr)B z@78JbVk!0C=My}2dJJ}g9dDfn7=4Q@&!1=s<|-SP7n36p=7je*O3$Ziw;y6#9pb6XryB9qS!|z zTb(ncmGv-xouk-qWzxhV1#;g~@*Y9Gshpai#M&srn!kTOSpDn9>i>6V^?w~AEj!L0 z+$!I!e7ba-OE?E^NDxup8#&aAKo>DTJZz@!utU}vUN?l^rUXm3S`eY4OR-fBDpShB zfBXYWLzD#?*F{kRc7?eJyqXtcoY7mRH=$KSV%iblBb7eS^QvI>XtpR28E9`nFU7{Q}mLzNRGjH`Xhp0 zOl~;J#(-El@SYvyF40nCOdnBwmG~mts*XgnFs-frJ2l)OWG-N#qiu)IyA>nPd}6JN zy*IDc$7UsGpJGPz+hi*Lom6XJxP*M*WBH zpL2dR{aQw*rD+`*;XS8XLposUTKy$0P9sDFqH6g;bM!a?g6hHTtU=wY;FhanL&4a0? z7K#a~Sr(`msZ-(i3)FiC5}7V~ zOml%`+abO6t=^y6i$C}Dt_TfU&T%F@7XEUAgN%PpSo|5Ov5$Ly4v7C5Ju>%mxF{}6 z3LWwa{f{~iiaGKvjVERsF=}=)RO`5-wn|tD-PWIheL+oqXYgGb7c7^#t7a_lY;K^* zsR??9&@R3)*Paf3+AzHX-D$rFvSdyFLgq}xaB1M(YCO8x2@Q=-FHhlmJbek)0Z%5F z>7;#+-jfUL$0)m1Vk(u#nE3B2j0%VrJKH2%hUrR!RI6!UnO#{)hT+XGIllSnYS?Zk z{o5i?BhdFWe1XmE2(`yNop}JKBqp%@o)^)gK5odGaOkkB4wm{y@FN}A{|_!lC&xTx z*^W{w{pM`zr{U4%1x48--1I32GnKpXuQ}X#Cs#DK24_e#bR?#Huy2sh4kpmM@J6=< z!wa~6cX5pXgZ;x>xFCr7W1OrMm>6WJohgc>znW{}dSD}C14hmdfq_E11YY|cvFH<= z=$e44x@Qx;tF& z{BkTT+{9IaN~meJenKWFp<-*Fq}(Q#@yY0!F3S=MM2|`$4;Uqr6ACVf!SSJW(j9*G zi1t!3^X}2d>qNKX<$^lGUke=}sj0Y1R|`Q&vwB}V-`v{M*P!;-Xda6baVH{5nAC&5 z{5)b_GZoQa!u2qyXT*NifXJ3TRV@g-Q)S)`xZ~#lQh+a z0d5~KzP{CaH@WN~wbSLvF%sU7rz0Z6POaa&E%mSUdY|^h0|CmNQl-NSJd_EF!x->E zOov$C^6F;;eoJat;VEtp9QSc;mTs)50U-JA5aI);Fm`BI`*fPFn>rgHdG4{eSw=^r zi<5U1JY!BE!Rxq=xNC#e@$uTVYwR?tArzaLZz|LSD zcRTj)iKF%YKGtmpxd+qubTpPTPBK|xhf5(iSO*uEQ;A=>cU$Yx_sEun-*bct2zr6- z9?YLa%^;fcddDQhwLcC|(7I%6;0jVD#?LlP1x)}(v;O?euq&wPWmhm=H!ft=3Y6gyLE2qL5hq}PeU^*M)p}DR%J;XfVGWW>o{*+jBC^ZJWXJkbO z2eQS?q4vN`bWwZAXH{ByWZRRq@cr{X?XN>UV%bMdlsn*W{V*IK_vz?U8LRKkCRj-J zU+9F3#l#+vvj&v?inbiU&hj0OL@YrRPbmAIZV)2_A@R=j#Z_;cel6nz#g&fb!O#1y zRm%hln$GqOxCR`0cOk$ zs;Ri~G0^nM#d(kfcvL24kp+7!RB^zs0BO3sxBu|*HnYrp_FI7SZ_xq}JQy{gu4gI2 z{PfC1>ph&IW(HFhBSOhdE^p*Ty*AJ;6f26B8HZU~6ln#8#VNX0l!GF#-h_2V{Rsqi zM_T>KCz%qq{whu@S$(D8h`o2GuO{Pn2Urdw2j>`C;J_Dq=?4sGOpf5!gh6^SIvpm? zUhe0B617W{DGBEc3`}Y!YpM54JBn3_HAriehM+jYPEKG5YCy+T7tFfQJlHrMoIwwR z67p9a%J7UpRw}!{ragO=LM~-(>dF|wK_}R|crqIQKnqOxh>Oki-BPI{vN66KjUVD( zStbXoJ`8^H58=kDqVDjgqmxUylUes1!KVQ-k4~m6yOhcC+e8$7*JrE|E(RbrkZOfI zfu%LG#1z(gS8W)4o!>MUa92%aN-Kg~5@pW@7jL>Mfp4D8_&jDg6UXQLym$IpirJ|>6M6LrW)wLBP2Q&EuvlByc8v&3^LD{AkPq>X&)S#E3W zj^d0=(X>prmiKnmO>P8!-Uq{Pu+|c@I0>=tsv?eBIs=LHDDyXub0=mGawxnd4&f1# z%@qkH&x~fC(ukLQ!vV0(9ZN&JC@7gg7Td!>KFl+5*V_od5cesDx#urC=3P*>3P^(Z zp?76rj04;dfGLhYzcvLt#NrttN-}N|f#1L1NBF!7;eZEMAO=us?qi|GbA-K{jKYo~ z{Qdi1TnR!|`(CJ=d)(RlYNPpkvHAHBQuDSW*xGp`$0E>VIkX^0K?_k%ss{)*mOcjV z%7WBCF3zr;!->U2<_5qngJmXz=@(_(ZIjQAGK8446G3MY4$riHUc3R9-`;>r{Lp~E z;zah}i#LEC)NKGue-)YuZ`o3KT)eS8svGZ$=$_Dk>!Kz3u{h9&)Kd;rlvV)hN{nua z;S~nOew!Xg`d88J04H8Tm}@3++e)L9+syj8qh+xf@lP@vvt5<^ zF_qfA%5jEkla^c}{JQ^OSw_V#XR{JsdOT$NIHnK-2(1H9r z2jJFE2=zmne&CaT&Z(o&Ve8ZHJf|#5nRYzbmi@>4V)2@s*s-1tXc|u4d-7EPDdj5x ztmomVz%sxxYTA3S75*|8;1UJU3*5uERn%|XKMGSyYJdU7A<5ciuC+jzBFXB}o5y1q zs&E2g-wSDcMv^`Tahv!BD6nNuI5+hR5fS%@kM;qC&~P7~IQ$O=@YgtBxc%h2LyyIO zytSvUa#PB%%D6c6#kXq&xx+$>1ko*aj%jE1$=i^oWm@|i6*7{IiQN#pkXDFnLVUp` zAb+i|AE8r7-h~3PL4sb|RDQ_~M~;#eO-@|;mIY#T+y{$PJRq-7QT^ooRsCj11*8@K z0s~EKzCQdZ;#05eHW+6>Oxg)+HT7JTHcg|^)4@-zpxp*i(6%w)M0@WMY&0(p*<ukDoL-q#gb%r>W9HMoi2ZWkBpoV?6;9W5kS;Xj5^h7p`CiP`=nL)D(Ve zHU;KL4RW^B4K7g4^;K)LVw5zSRm9XtDSJq{H0`P&b_SVj38MNd zxSBXwVxQP+lMdXcntD%5O47`PpDNIjb<=3O?5a3yPOTu`(s=uZozYL&KX4vtD%@=- zCk#k4w2tbzH&|hB@pQx2kas88WOnXvvsEC`;i#Zv<^i$ zEF-f_(C!@lp;jm;+Z6V`(4*hJ3m{A-t_>f$HdUT+5eN&?5J^MlKxOnOE8vOeR_Gqeik3kp*P9({%LiL6!8OU+ny(6VmGVo zdu{5siIX@Zu^gu+28LiO5L|rPG+W73cf>qAn!wYq*Dz{utCG8tZDKSL2GZ>TOG&dQ z5#_7*k4_;dmPI+_?JV`1@a)S-GVJnM*cJzKXyN9?MY0S9RSGOi#>|quLieka$xDbG za;3h4KTY59D*!><(WKf3k2V<+f`eeT9J*5|bF)`KUnFO~C9c zWS)Fmy^j?X(hMSS!=5E+;o$t@(a8WQyhy>@lI-vH^Y3rJ_*6Ins$=iMi`XDZq?Nq= z-?$s)T!XB@DS*z~HsmFki$F+xm1Mcet?{un$Ux-cy}}Fqk)}{VKWO`4&ok+Uv;Z{8 z5kcP>&tiP<_wEQ=;JyJU2jL3;s6^s5$RzX1wJ)LS{M{x}r0fk5%fvWSrOT?IDa2WY zJVzZ+hgUz(szcVYD)MvXTAEv04foI>Tng48Gywt}beOZE6aHP%{)r6ensSvr*_?8m z%jpQMXx#-So|FsAtq+`d0!}>JI`Q~CZ{kUre}ai8n$CyXSVnLcR`|e4Cr23na)~V` zmAPxaPL5~Rak_pDtX|HCPs-V&vx;1l_P7;ipZ9%%;IqeOPO=kyYW&0J4M*MPjr{!J zdDE__s#R2T)|jHw1r=0NRwD8$@G_r;A56d6T0T<}njncF@N?SDVa3jU9Nh;gK=xNz zqlb_RSkqoN?&Kg;_lOdsWk074(9L#0*pq1o#I2nUI8A)oQ29jmcG^HNOr+5z1My*P zaziTb4PCWu>K14h{3qF!pfsgon6+T*m`-pqWn$GSV2GjBK zGNU7)wN|li0k~qXZ>4v;_q#h_4>PlH!vG<$Nx1k*%Jf!G@vXOdhwS7bX`%xX_a^u| zXU$#6R_+y$!gPW;`D`I(RaD5VQje~yM!go^^lIA6YF4zeq9Z6Z5InbvSV1EM_)~~g zftkr&1vp?Fn5m1MxM1PRzR5@R{)^iGk}1h6`4~dI+p5M`H~FAj)+Q}*x*7*3$lqLW z4Ub0?qKVhA&WuX|(&~dYH-Kk4F4h)aBmJnI=(A!Gyzeb_LSVs+Qsxlw&9A)NSDLoK zl^BAwrKiCGoQ++$Z*xuM@M&|(UDlnY&9#{-*z>FwIIfJ}@j5k>WE0?v)iGQx}0RT2|8m8o^gA`c$?>SRQ2I zV|SG{k6pbN*^OK4_D)yvBF3nz5LUe8Io(tGpy>)fr$_09aKK009y!+y!iBa8_hduS zgw33dJNM=saA2d4XimMY_N_?e8{Ol3FqMs?uI)W`tK8zNHuDl)v+t(=a(av#?O{lY z5=Lt+qP*43G#dNGL4~UwZE&lO8)U++%R;;L_5JUjZ5=+?dw8oCUp(B{{o~_pEC=E4 zz(y%Ni13e8AzB={RP8BVw&m^T3_GW+1)>U@ZI6|#?!56l#1t`b$v$- zu3~)z54#6x4op%2Fk$Z_xd+Uv{*ABqLG@@;cO$g1xw*BshbwXS9}8vy%3xt2n_q8i zf7J@(Xki#ncOE`|^tctsX$F!EzOSyD;ao`!u-*Ks%Jnk2Qs%gUKyYs>yB zOz82&o!&1!*&%{;DW_9Huu(ke;iyKJ4<~4N`Q4#w+DBk-l`$byjf$|(C zi%EhuYfAi^Kd0;}jVi&GVo_PsZCpHtK>BI9zq*B<%GRtqfsfSgpD{V51**N|es8z) zW*bJu0>ag=MGT5=mIVYC{@~m!sWJP=Mt>%=?1inq?H%;LU%~~sVE}B%9{>Ee zo)$v#JF7Rt4@S2elLM}fa%t!}am+o@W&TO}0=f=m;*t^6c~Lvb9MhlwhFWe>|4a2i z_NwanYJ3^gOUg&&ktN|9IEfN_0oC(*d^uP3{NEtv-(|TtM@7t#@*DrTFqCdFzqvS& z8FGF51~de{~9&XS7#78&2W@#gl8R|f~dIQK<(Qcm*6UP%@&2_{o{A!qd?bl-brMA=TjB zP4(-Bx)@ExXH^D%icf29|5#ByI!^4C{t5I%P#B>KyY6u^s`FT*EM+J?Uvp7m^YYgZsTa2~bkyf&iI?QIZK>yleEx7ZoY{6Y@v)XvyAfbP%xS%jO z#RFI}w0ihv5rHM!lQnSgZaC?M{MFJ7cq9m^-ySseNq$4#a(Ia>HWxq1-as$n z{y!WXL`H*q{0Aoh`BVM{QSlqQWqNb?mHb4O>H(urq{(uk1F+00}35q)2ki$Id$tSSg39q#H&1JpeQ<*L6`IFj}9Kb`X~!dg zD$;hz5)nf0HTw`nq32RnL+Mm&J#dod+rc+u>?KHIGbF{m93~LZS|5zV%#CYv*LYr- zl-{M(u2$+1GL2oHXY4ZmbK zE$1t+qjRelb4x*!za34GV-9K%AA@;p^{M$19`5&yEhq4}bGN@I2T^e*0FH}e%{+OX zX-5?oiD=`>eHk%|*L(I>w4CFiDh}ef?ksHZ4TaK@KPe&jo_ZmTgpMvYI_<8+^dS+} zhN!adBX$&3?_t2DrsC|-$%&)W+lVh=mOy0qO9c~`8DVNJ{0GjFnNzEZ^q}rlSs))^ zneudR-I)bCORn*?<&p2{5_*%HA_E)s(g-JhzvU;5(_ZVMbH`fe+)0SE&K+Z1tkR_K zDI=s#p!FUnk3m?y|6fRcCo^Z^8MCQWXqhnyt@oZWn6YxGKeB7yw2pF4BzGb&xDx&| ztl%VCWq0eVtv`WI={iw)`_+@32Y6{Wc&0&xWz5`u`ey>dk}&1tT6!j2wxB7^Y_&z7 zG!q=@-gKvwiF}bj*lIzoQNaPY$ytKDHP0PyLQM;Tu4L(!&d>C(LnC@}07)CA2vKK5 zEd~z{FD9Cuv}I6r!@WCAfPc?qiD>W`5LZ$2>cd;5r(R}*NyXS1{?%O>O*hNqI$iJ@ z1-l?Z3Ts?c7o=363j&y0&soWM0!&Gu@-sLwt>tH=ud-7ciJeN2#n%NP=EF~kzcqPR zoKo4dAWWvwXe)hv^mZox}L2AT$=DTP)VK}CRf0e3&*gGs4s+& zImpXU=mEgZ6&L5YYluNeusxU#7N`9Dnt?b_tM^TiwDTqK6~J+V1)NP{byEWIF>#2o z1doq!L)i(z4>6?MgfEFaQ|xjMx_*QYnv+3j zfYzLqk-e)%JExf~77kM~XnQ>MgBX`AF~;0N#I?RUJzptK`@^jgp>jwu! z?>g8cWthPc$AnE;I*%&OIUgLL9MA8zF$ccu0ciR?dFrxg;L8OYK)E>$NbsYomW=<; z3xY>EV7H8cMVTE0y|OB*vav@VSH_BRkA|LDoaSD*n<`rH;wTiXxQ+okmO z!Ol0^2=V`pW&b<9{ogyk-`eFX%z{*ZZ*$|><86T|v=$}SZV~ufix4+E!QW!hrMU;H z_gFvhD0B5zN=qJq>H~_0*FmMuThx({K;hSi;^KwK`+j-t=il7=+m|cL&qt@zpDs5Z ze)Hnf6}>W+I+tEBaY{pM0CfH%@~8#_n%4^4@JSEno{Nn#hZu0%WD~0gpA)=k)!8H zKc7#B&&;|ECG2y?(@~}fY47MqU5)E1!zc7LHx$*1c6?W^t#y=3bG04W8cgCKPv0lJ z&H0NJj5ZM@5rjMV4##+HQ;rNRlQ$Jskz?iJ0F~&%g5-eJFVoP-x+Js&#+}iQA*sQ;&%k5F1E8T0=dqi1fG6JURe;5SmS&4^Jm=ht1X-k|FZ9 zH3P<$fCgb3!vei@IV~W%TgEQ;9_JLf*i;06hGlo2p6FS6=xCr`(w7N?GMih+0yq^O zP0|3tEzAEyWl|yM^DpPiwKt37=g{8<8{(nCgBkt3KgSG$W?6wm4rP3yw7p?Q$ z{cVX{*ad1aJun|-X{0dIWTc@Z_dEnW%murW3YWjA2_yFjA*}|fojcShL^BrSUN_`Z zLR+$RLq{W@&28kfMk8Q4(ahj!Z6c}W)~>*4rMYWGeZ@j^u+gE_&&0-^ixaMt5*xnk zyiW#Bwz+*@{cLfpeuL&fsCmOdtc+SEFL9>WFmk{?$^ta+Xn)H+w7419YS_Bt7BgkW z_h;^)?JCx=ueu5*`>CryX&-f!Dx9Pw-qrWv0ea@|{tY zWZFA$7Dx$R5tWhdo`)t?C)(q$ws&^74!_;p!_hFwN}gj3?AyZ~c-ib5Lk(P6%Y{Oa z!gBYPomG$nNO-6DHlXBEVLy!2`f;d3-TKWA)wF)ogh8#JjmypLE-);XR;VB6p zny>Y|9m>bvB5ML8!kV3B40QV!I&yezMEh4VYV-0ir)@r>)!L{9$+Sa;rmU~|%3z_O zIzF}?Gt5LwqYG0^uK0R;G&NOhvEoVNngCi^Qnc`c@7*9XOL> z54W!M9jdBUnETp-pyn)`(G|^gNFOhf+6_7Nko}6ZIJx$`~tpm9Ye@p9Fdx>=28K>s( zLodkvGT2Rz{j`htTrn?v6G_F2vs#9-{D3w^%g<;{W>)L;5=D#~Ux#mpF*sWFvs{x) zX_Rf|#uY?fccIo>W*jvxPRiroz*wS&+gg8!av2$`w@}kHS96Lb4?}2L3?XScFH#ea zaW2MDBuN{9_3(BpY&+L|74R)BxUtdJgn#?MCLoSB&7^5-C49bOLrr?oz-enk44)5% z4oqnzwKZU$rXSn}EmpV*@o(TGF=GYv+zBK#=P+^2WoCFwj){1iTxF0S;@Hw_Ag4;i zKHlnS7RVL3KSWYGx)5E2!PP87AQM+;xNp3CCJDp^SG}h5PO&hv4p}<7jkla9yr}a( zzUck;j7p{EpEpqz4=kX(3;0=K&o>Joo)%$fGR0`jLp)aMM%>1|HX331p}v{y1#|N> z>fq>2Z#j)ICK5v-B@x>hyQAK~rC{^h6cCCvzq)$&>uZ`N3Z+V+$iJFpX@@NzY@SS} zI7)=@E&UOv3<{tv|8m6{LGTaWZOiGKY@*nzFsbf6l!^&8%XE$J)np3EHvV3`Nrp4e zX|kP+58mmqvf;5D>21<*f&LbG!scSpg~#R_=8X9OG-9H$xmdKZ)Pr}(aXfm38zA4G z7Db}6xmYwfTRj~!dE1;JI?;^vXEaoIG`K#ZVH#eyTlu97tf8s|Ebjq#VQBCIYqga| zDc{M{&olbeCKGVrhc|$z2E!U>a`JX~{IK%QiZp5*9Mi>qdTaxNSwj*0jE#F4&ViRfxiqz*KzMEY3PH|@!jz%DZ zC;Uyl$*UgEj`a}BYLp4s)6^BdZ{_8)nPo$c(SwPDd>RDUZXrH9ru=dXf;HW9h3Wk|O4yj`~5m2SWh{g>U0|y)D=Yw|<+hnw* zDl!gq3fM^GG2qLhD|=@rkMQpC_|=3*bL2bjlww2S6K@*@G~v5?(4CgvRSbTZecdsX zI)m2Nt0~v}@qB`5hD-^_nlQfTF)^f1Gv^#mhRij!FwrSUF0t}Hh8{klbE%LyI-_WJ zS`=^cu+kvgFlVR8rpp0e`KriWL$^ZEhr5~56)7({4#EP1!nejmY%k**mh(=QMZd%@ zJ#+_7fw^36)kNBL0>;zot75Rn;q3>qsoN z?Ce@Nkgip$$&ufRQ~o*wr(%8I|F`Auf4RMKu(oz{`SveMEB}V5aSTJAc-jx=1(|;K zkVjs5D0mooo8I2#*%`PACWET2^LK#*2o)Oxl1J58aM^w60gi4SG&Ba~HRt5f;DRf< zEzd6zy!;{`7({{2ggINMWmxWSPY_^Yn0jV{-m3}n3i;Heeb$^j1?tv?(7w|sPgU=p z?uR_e89IHw1*6qAO(gjwJPA4m>U*?LdVh(2pc~d+gB09i_A17H_K|DA1Wo*T{=xij zUJV=~W$knJcxij?OUt$E5&+<@tk533K&fr<0oez7F4G6`Z`udQZs@J_2~wJm4lc+k z$}D@WE-l3p%X?hQq14`XZk2H2%?jdLD;E57VD%ER|YWail|kwC{l& z;`jcU&i>h|<4Q;VSm$3S{jiyd5WRHfP|k=j1p8kn{r+{*@3jc9#1j9&lYU567!LTU zjq^UjFfGC00`NbTgKc4HDRqsiGGEw0T)JJ#wFkADJ4Ixd-T~Lfe~bo6h~#^(lS!U5 zWxW5=8{!ICf4lki3D#xw7s$QG{*51l+0?z%u`kT0EnbJ0gQFkJaKhngrhb4XHiK zmyaO9+}okTi*B3oczEs|U&akqcqjdZ5BGkOa^9);wds@g@S)N1w#DN^cb-2I<>c5b zFzU9W`Z0wDmiiufM4^#odiWsI$e-Vd(V+x&e8i7uo{s*SocPJNcMJG;h}!(OKC%-= zC&)(_V8+TYcZquzrywqdGGz+F%^s?+Oxm0v?DXxB5{K(^luSv4x=T{oc{^BFU0|z+ zt?Cgf{Ck_>rI~OgX@qu0C$|WMKzhQSke~p^m}-Ri_mn}F8xTy`9`2s_@#*Es#Ry^I z&k!+MvaBymYq>MXHiXE38y)HL?(j7ZzfXHS;cxH9lc#hv*%wl&()Owbx-Aj$k7n&K zGA@9R^pl`8nB5C50(i^SW7bqQbiB}UTjHBtc;3^D7PjEoQ*5|mUgLizM7dqc8NAh*xP~tuLu}795VunH1{}1(Y!3MvJ>@2+ z6-7)rVLr~B5}qdTEci<4<&Eod=cBMAXBV67l2HFxer&^TF@^+VF3yvAuycY+@}(&k z2sWA&`HbKDc?6^NFp~|#E?a46aQvS^olG0HMY#p2@;fb-Um|TJ^rynA-#!_RUtheb zzTxO7+i8DGcB#G?o9f#}I7?iB+eR3bBI5%LwXm+SH=F_qZ<87JtQeyJh6v2N?BzOb zOKA*Wlent&8W6<#RU`D1JHWRaC6>f=O1aFnZCTr#RT-ToEIKFi3jb$F8HXB$R1afU zTDOU|ihf=3hv7R})VEd=2Ifc-6-o`jr~x2hxVi=0Y||I8jkHIyVe>3G*E^t`4}ZkT zGr`2+_^UaPI8&S1q4$&nTe_Bbvz;C={H?C`xj|2>uUt%e)Z4p_4gbT+BYK@ z-ZP*eNV??)NmIWJsrs4lg{42#X(UFBF6Ag;8GniYVZJxSZ21~|0c#FnLk!wVCnHm4 zW_gQPuC2}eo!!SD#r?XBx0_t6dFk0C{S2E z1U`w_ShFq%eOGixUce#GV({jy-cJgtdhcYQT2rB<@QosE_!ogrWVY$M>BaCg;yAPa z5t$jy&{$M`NZb;{`P8L88ql+`z8)cbsDztoZf5JZWz|zLZnw`MErA+5ob7O3u$n&r z#8Ar#!Xy-_*3_YpO@@ZG5Jg;?dYO%rK_`n=7(*DhF>Ft>M6Er4UpmW*Dz(eU1#j?i zXyBU)oF>pLWmW0y7k;zf)kF=Si1EIfRSZ zl$k;5<&jq?04J7dX(17X%PY=}k0D)ErqawUc`vbKH`ls|ttD)&2F;eK)u1g9Ywx2; zN#^Yn8_JD- z(l(IJzyD-jD7*IQKo8|S9FB1qB?arv$rut$YBxW0espW*G*9xS(GBgb6UEm`c1Eu} z>7mV~BC5Zr)g2FxQa#ZMIR!}x_j|hFx-Ek&;efwWqN0#UBd_Mjk zZfM++6ANr7?(U(}BpBnEjUTKb$)-)35ki34ATg3K@h17-_xnAMdfbO39A_sp)1KW9 zy0>oCsZ*!UyH0T#$vEMZ^w;%?h{T*q;W`=;nB8CE9pRzp#Wr4|hN{3pi*4>+A+-J_ zZ9Z(h%`8#h)F0x|P3lOFwR;~`5M{ZZ>kVbHF#2Q*)}a2BC6U~Un(gLFrR^TsN^0iY zd@9Pg&O?_ute9)$waRKeY36FajUP)F>+q3h5Q)A-<#z%MH@|`}rH>&$``rty`ZBH zwEu>zt%VCjn*qf*(T;4K4q90N=Qs8%Z-w-fO4S8t(JCsKbo{RjnBbVe0vR_6{8jxQ zc@z@LXYRZ3wbw8wlegKE3kliC#5BDr(JWA6f1hL*4p8gUfV8l!)Ua zZbHv4J%)X+F0;LtDB1t$`$G}X$#rJ_oJW~~EjY~b_Sq?8H2AdKAU*$!_qaMV?;tyx ziv|xt`ax%L*GE#Wj9K!&J8Bqes=pkh2e*RJ1=*>Y_Q6%>qhR#V%J_>aDvkt24*Xl% z1NoPd2Rp>q!El2^7{kb!B3o&&t=`2Yo|}o%J*VCw65^=zhyue!5V>j&gD%9)U=YO& zBYVJ}UOtRkt+)+q&vDdHHWR}$h0A2 z?7?hI=al=a0N5%6U=L;kP}&;53Ia>xizu)MWeBn)TG=NerZX}Z>UTQ9CWuPXL34sF z&ou9qdDFD}2DP!eA?ucmOV0S(CE^VIOgn>NAseiFe#$y$XBYI%bW!la4j_g+-HJ7X zrFK3jIxj+hIGKr^>RTSS5&B_Houa?EtJgHaxU;i_@@3Gw1vB6gMrfi{=E|fFMfAR( zS4HmAR7R(a9oho#6$Qs8+w+@DRA<+A;o;E$btX%my6_SS(*5)1*ZuK$G=7SK&ifXj zo^2!=Sj?-UpA^ITT^Pn2!*gpvjOf3zwKVf?NWDBXejK1GAJMUTd(ChqYev?}EHgqb zOJ$HbA(i`<>$z|$Z7{sNl8WNimp-4I_0P|H7svj70bnhCZ$^J0@)`XFiGzYM2)yI` zHt53;PbyFcCH{)EDsO8*3tde|kWyV;ln^j_$xDWQ>~ z$F}m#CQ_aTOkEK))VRc$mXN)Q7GpEHz^ppCwvhTlKNh-zi>nxqP&pFj2YJ6#0U7eL z&N*5&*rs zj!>3$)Pr9L9r3S2`a#N{343K3l?{u%wJryjQeAi4f|C#RN-8g|`QkzS3;G4w0ZYCa zL^3)q8k=0652m;dqJ1zv82)@P{`p{dhZjO5eiztfntiK%;9fEG*TFqpySVx1aBuh7 z&i>(xmxtTWclW=?0P24cwVItO<~#)O4R8iu?UU0#Eu4T1TNKk`6()ztULg`>IjfK zpTBvrQ@uqUw*r2*^8&;bybjWK>-(?taquNc&kJ6sFV>|n+K@K^<7=~ z$Z5!rc(urhTD2}Fy5y6-0A34eC@3<4@X4Pi}qSS4TKYC1^isYuHqvR>Q4knq5!=i=?PGUI*eLWpDMS^}%*oym> zr=#=HH8wp@M!nqQSj0l~SA%_Bj-a27uvuEYcc(}e$q=i$QbK+L!i+vpLw!acO4;-J zIGP+c0Rf^8Un{z@8O#{kwuyXgw3$qf`;f2RXQTUeGCyQewFnul&}MM;K@tqb>iFW} zQlc2KPh5TJV}`eUfa`8YDq5j)ZMjk)+MJY;1{y?oAL1@4ICP+^ zw(k*Pra#~{UFs6bi7JIM7IetqkAjA8%KccKYUNQ$FwFW9rt2Dqev2Ltk-{e@r4v6? zMXgqS<8=#_V)xNbyYz+-@OTOw-D`2x}5}F-OqL z-Qws9#oOSQJ(at1ZQSA-uA501$a5!j17ULq&jUTWt^_RKo-AAUtF6`LgW>j%P%{|> zUoUP?+P5cnmha+lr_M~`9b2m0JVP-~L_zX*{MZ8M^)L4Ij0E-3rW%xsDMlS#O{tRKa0fzbNIn(;sA9|p0YGt0Ty>74@pbY& z2e(w>blW>YF15Iyyl2@@ebj^NA7jIEk6KTxgnAqMlV^#K>Ep{cNO@NUu}hq@uYH9N zL}GSknI%y~FG5HaUN)F~)km6m_dE^}?P#+rQsltgNEA<7<67DtOm+~Bc&~0rBDH;D znpKpbbo%u`O7T@!le$L_2YPCNUeD}gINuUB7of)Smp>uJC( z7oBaVrq(yZH^b5OFpv#RI(UhXm&GkySq>$Jo9g{WC{^W0MP^whhV6b(dGniye4tGK zDLC8W_hX1r>zbw_dUADgGWapM8mzSn(^amuI_m26RtOEqZ-_j<=c+2u;ThetuC$gl zQU&aH!aw{GIfVZ$R9dUo!{eiU@~T>bS<{oodGvMciQSd=H>zJFQbY|L?9C_&aL{iR za_}Kt$6bW^lPDp_q&6}b4yf6;TXbh0@?0IVveZgl(VECq@X=xaM|He0>ocZV--YX` zz5^H#=qVX7O~OffW?YG>6WosikW~ejXJR0*8^s5o0UPd-WphF-=7)7crw4!2(SX=D z{S{Y|KcmGbQs;_%LFCH+d8ZlFR&yI}kNU|2bN%7lc8f@1)$#QrJOQXcBiMOBzG(@8 zLV#5D(%~X|wUFi}v=af_KQbsA#=*$j|7Ty0Av)>dwakqzzi3=IZ<5sV@D7NSm@(x~ z0Dyy~XJAupAo`nW0*qp8_$H~ediO0Yf zHt=%W8~Xev$0Cd?mbectl$*tXB7n-A@O63TrdmV70?Jd#W913{JL3J1;h0eXlf5r(VOHYK!8IEOrstMiB8C#PLv#;*gHl8ImF+xr-zo`!N+@>yF13@ z+O6LCfczZ)>7Y&;3Vd@i$_0cHV?QYqh)+ovW}uy20$u6sb$>f;voc*=B`}t=%e2do zY#K-p4QBSfz4_|U0MQjNMmS>-4yvSF;JbHsRaJo+FC#Ov3qfOd4SGwwnkvi z{oN5FCF{pxeZT#6bQm}t2dQjjQf(LUvoLw463Hp=$=H86|%2-R;#mb~N9$Zc# z@q5?DWDuFOAHI!Pj5ZIR{RySX1iSFnUrurRVj`Y}1C^JTU4!@RR(YKtQBItCKt0++ z-nq!lNJMrBsg`{LTp_MH1Bz{QV)$Q3SNw07?Nkj8JTmxaP#IJ=!2M`2?2g~*9wniv zri^!~WhFaU00R2Uct_3SJ=B zI_M$zv%$}dn_4PNXrB79DYJA}@!$o*Km&a%ao|+Z-N$hfGhqD1wH@G2o(l$xIb%Q10FK5Lfy= zMB{;=3e%e{AWgnG8obumH^ae?(m@b}8QY;a4E$g^<+Wkr@$-=XW%bdqdWqF9i{Z+% z&19%`2~1i~_)Dk~|IIoQ(Wn$@-vH}=jaC568Hz%cv&XYUG^Nnn-a*2sIJD$ z0MRYO?P)#91N7khGg!vy`@nQ@Wycs>6SqjpcU*F?h$@PX5#1kO^{ke$&O0F!l5+W@ zGQnf%2z}jz1$;6C-S>e0L^7#bYsdY!YkVPEytrhHTdG9SbeMLQQMI%ZdxEK*{t(8K zLSYLPxtmEU@0UgbhZZ={n13^CYu0-vZ_RyQfy4+xj2m?=uCEU!yVG~2t4Zpo56I%6 z=&Jfl2dw`Z;;%e0iaRGLS~OD{a>grEk)(Zo=bH>2>|NQ!a%X$G+r{x4d|9Dt4V>7-mk9L2raRWj zFRff-xGXt0pdcoam2d|0oty2iaBtyb{nN~~#3C5t-IqbW^OpJc3X7IsWT9l2 zXBf$(#QOUF&hzb;-|QbEPjPF9Zi(XK;{NuZFy=kcr=2QXZ<(!^CHzw6<+wEmimjq7 zlb}Z_PLps}h6%ffO`=MKd+F%E1a8?-7Ee6$kPlIZTajyfeB48Ie!7NaizlFAcDxXb zm;BH_qALs>^YAP_XQeMIK7?{%qD%3O5m;dFCw$=DYe*)lE^3BJhlrj^&ulI;Ct;=? zb4tp_B_j^I%Rov{DlnRe@ea{hq(F53dWAUQz4kI5$FiN3(29VB%Cl{rp;U`X6&$L= zxMof#Bpw~W$gM7&$$T1t)CEDZBZBQb;sFqpRs(=_JVC()`>Np!;hnmT1wZUkm*bi| z{a*38=eS)9c(!KP2uRa*K9V3#*W?-s9=yo_@q~Xu-d|&bVvFe3OWHGE`18+;`+vFf zSL2T>{xOp28Wx8o!%Y5rk-M6Xy(98&U&A91$QxZWniS%#^ChTsK?DdsWQCH~d{NJ2 z2yopp$fb)lOW>C=R(EbyF$vml6iz{L6S9zf$H|3%rrVT{3fZ{|V^Y}jD!H*fvNF^W z4Ip|<^lQ+phYkmmeaH|==#Gyd^5v2AfS!`ps=_{wSr57Z{~vVp*{b}4zAp75dbNI`f8^`Ecw5L-h7GOf~)e++TL6#IL3m(r)( zuU^?x(0d%LtcaJj-OP%}8mH7?`!;(I;GF!j=NO$l%mrHZj@yHLEa7E2qOvL3L{W3? z5@E15Tmq7~$KP_>E-0`yIXNVy9ZsglNTi_tSc8;Bdq92*djJBXRMc&+P*OgQPEMd; z5!Di!NjOxpuCJ{b<%F69hC%5kyroutope#9}2PQn))Zk7vfv*27uWB3Ur(~->k z{J6knY`BWHpmD;KjS+6i1XmUh9kGXms{+`gP??VI7Kk?tPTt{dcUruIy;khwTm{bj zydB_fO}SO`ZFf8nUmt4Tuq*zpZkiwt5m)$N*50o%@(l6g{speyec~Pp<&ovh@F7gU z7vDUi0fO9jY=lg$cH@d{I@m3lotOQf*rJHy|IP15`9zEK#l8)SI-4$iWn)xj4KQK2 zin1UjVV5#x43c#%;NU^0)|;sZt~!J1h^9p3RjiGbljNEQ&eV2Cx?RzPyHpid+oRfy zTZfG4o~Sq2#QirK(bd-&S+`x_zNYGlM#CiE!SD(`8+cn26*efH*yyT_nS%6@>Uj~$ zpxh<8bfWQ^q8r=e_!CaQtgkPm?*3vk`yA(5<@6lDaTSe&s8&`#KOS(kub`fD_BA@L z^q+930QpQUH_oPB*UdGh*m~lqFR87yErjEIIQ@yboy)ZfA8sOhb18wWJ+xs@QqrvS zCX{-HQ#5+hvSY)Z2&q-Sag`u!T~YRaD@CuYx&@^mV4_?RJ7cs!NV6p6YBIgWHzt4k z@gb3|@{(`Ayc*)zsclzz%Hc!YgC9m+DMgIyTp?W0#BKm6dBaTah)mg-MD7)_&98Qo zE}5vo!R27Krf3bxdu9$O*Z7y;Vmwrm5|gazv5=QsqGkhitXwYP&c=&VCT)Cn{MwcT zM(GXstV^xLfaOJ;GJxs5B__!npJq2e1f!loCVH|GluMbQW;LVZH@DYi$GDNrqi@`* zwm(t?{kOB$iTw94UO^y>+M`V;CYtJbn#~iFT@Uiy+r_`Lo=QhaZu~t{i?ek4#d|2{ z70mlmEOHIrg;@S#--MXmV$D+z0M;`6vs4(hqaa8$!YcN7(V|J|WyQpvEn2iw@x3@EKDYJZzYgz#k1FAl!eSEIe9{d-W3%lm3%1)CasVQ30)4whq~vA z^OxrN;2GxtMAAx?~VmP6_ zdM%M}W$3{x`J2ERg9c0YAN)z$fRm4y`(qmd2`xi9V>BlUraBt_SsG&bq>KvRO4LN&{lz=! zo)3=k6%MX>5+FxeD(*k{H)-;;J3PI*g}wfoxX&*~NG|~~c6GSVwfW`|rY2dgT9sLvGwPoo;vH0YCgpGj0 zGdgx32Zki)4_&hBAr4U?MGB6nGZbhY^yuiqMHiTXcA^;mdVtVIJMczif~D~9+i~FT%L_&EUjJ0K1N$xB4n{l|t~mNkQJ#JIfM0&5c>hklZKHy&p905t z&ft+l0i8Y^JKbZ&3ru{IQ_oGe!HBJ*7@=tpyOp=N`T{ zLgv!A<8g3~bOTIXvAM5Moy+$HHY0YayjGk(LKfobSb~JMzYE@gfFQMWIT8dFC*#pY z0rpX}mj}aTf$*WCeD3#R@Ckb4bMR>UURUOu@hJ<#iOtY`izBGw)dXfIOxeTXnx*>> zbzQc0zmWq=#rfa@yND#nGi%a97l!-`GeqUqnkKqSDvL|OMeJV@{imR+@$?FNkdHyz z>CZ8Syu>{z$3oH%b#bxlVEhWIq$6trpg;f*b(wZYdhdAAe~YQwE64sZ6YZD|is$sX z<^Rgwy)0N@9E~ELUc08;T^cCvH2_<;pC+*JU|t?CZN6%C5fY13Ez%tGSuSxNU3BiG zsv7Df4B#-pDu-_F7O)jjf&~6-GT@99#0AboR;exyWOCbkDy!K$2{JGz9NACQA8#TU zBBF?A3xWiTHVH8jm2^UdQw@LLe)0Hl|0#7u920o5jk5%>DrcQ6N&S7gGH~eP56u$@ zgG$&PYFJ{pA-1uNQ$S2sR!2%|&~a*}r`cmP-v{MkTOk&#f8aBFS&`|pKn`C)F~B&c zJ6TVQ=tX%`?6j&@gnk7C&b#LP4zX^%3?|;ewj?zR=ImW5-JGQ+&LJH2OkKM*xw@3= zKo3ReO(d&@nj5_GN0PvONGTAXGme^NfLC{2mc_>EITG$AN4CU|>L>6&^6)y$5o?WL zOgKIJm<7yJKUu!*D=Bf~D}?%MlEwodSynmui2Gv|((5kjd)16r2K(Y_GKKk9I~Ix7 z#;aKzE}cgljaYmEp;$#Nnv_19S%3p@uOkl!5i_JPU@KpOu^+40?sVX?M6jzfG5T69 zz8_r`1iYXwDBGu5Eyrck)P2y9h=IN{JsTZ6E8z?^IN?i{qC&QG1l>4V$xc@cDW=mU zP}f>%mGx(N4aDx6zXDjMA>>ZE)I-^L$8kc7FJii00}|xKZ#7_;tSh((LXg3L!jeYs zmQ!E8fVr2W-BRl#E}jLSq3r0gp|&m;0P=2hEzwUd1r*P&B%1R`gfE6KLCvm7sh&KK zTv6#fw@>l>V$ehCJ=%y15bI(MeKzq^xkz%$EYFC3euCZa>DRG+IaG-~mr9-3FcU@M zvYW+)6Bamw=Zd^YI>u-%96>JDzFkLx+uFAL?^dzgZiSLsR1<((_#?^z0m@()1!C7p z^$qG#NF$f%NGaOx;eu?caKQ4Uo`u|3TN|xaBf4*m6MOOEB-tmGu9oRD zAywu@K8;1cZXLb5#I<~fV)CI>Pa-=TngBa$xr@`*D8Ococl^r=FeWI4P)ODtzm&pq zWQiaN?;zj9-w@y7?$b#Ws+&2U@Hy{Z4WTb}Ml)*+Xaj-WyjwWGT!1t<2gW0%x9oGH7u7$cJKXp#TQf zStPQqBQ~|6tg}%92@?sz)7sQA@AA|{!p;!Uv=d=G?CM_InE<6U)7yuKPriAvwU4~i z!$S^jD7dP_JNHCAL$8bv&^^|jVY!|!O5Z_sAdlhfyP=w z{v6OR;aTszNgafe{gDF_ST`+p=}=fYHkouq`lAUUmuVX8#HT;107-hknwqBpzgVVn2)*4wEMgp%e$SM#psUZBAIjO>Sj|gO zF*t@LJvbTAbVsd!%dWzF#se7HL7_AFYBy8ZC&Jw zhtN%83mlJtF*suURk%NFjV`qO~kXKHhtRU&JFM< zk&Q!ylL54F6F50zGK5C|e@+YyDr2&at~44bSYQSdLbV#e04=|yCze!Km-vnNETdD^ zQ&nGo1vX^^{5oX12&l}+Eb@jal_ylKbp}&3-|_hf8MReu8C$;|J9#e)t##mJzS5<| zyrHm+5c9z>eG^?|OPa{MOu1}vx#ST==i>|_Z8&P!fJV zXEeBol2@nVs?9nBjaE)n^ApAVl2C#7g|##Y$X^2LBQKRLW^7f@ENRp~#=T$5pUiZ? z1(jb|Qb6r%9^~`leW0TeQ(K}ODo+9n9xAm8F&Gqx-0{Slin9#rw6|Iw3dzk{6{@|$ zd`)!PG83@CW1W#p>0`s!5M{1HRBWU7vK?TLS?u#e84PvGgX;OT59cBhXeWZq4EPrs zV?xjCR-YQ;YQtf0{p)-sfd8^=De^my9D*v&o0fPsoHqFY}X0^wJaV7h5 z5&2>xg1Kf$i@AYuBDIVhk?Is@DlfDWe;wi45JcAa1w?(Fxh8F~BtX+JI3`KRS6or zNe+8EUwysv?3w0sczT2l73Sw8gW{ytKF*iXl{uR}5&U($G;DZMkqs^0U5j1k``b0J zNcrWxjN-WFvshvHE7{Ep2MWz1|a$d&hNk?>`_dg!uzPo@}LM@+5Lk0@)K5Dx-N03@@i1!QRa~?_R{lWPh(y{aH=^%h77z{UYPY_SRTblS) zG3~^=t3A@W+u(&u5c+FzQ6>d0fK-g_VwAHu_Wu6&#Ra&%XOS@zNU~v> zYGygz1#*R^%pOmC*X)}t6nSn4Ao&rvSiDKny;!J~o<<6VcJgj+%r-z}7Sg0xV|6Ny z3E6^E;ofV>3(5xZh(CTcl3N+QRv)6WMWm0oHOil1Rait~De_{qc~s@o;0hi=5uRP zLAoVTE`W9gUU|3x|Y#ANG8f4R{Q^P}hfq)$*I#EfFj;yX6Gm|k>Pe#t#O}9z{ zcO}*$gJX4eH66jybX?Bqj6w|hHZpi_<4i?yc)0a!b8qkPkbfp=3(&K5HVg9wtlmql z8C0RV8j`6y5g&@+;?=1gSPIN0Bk z%Sbv2yG))moKW(E^Y&fijLQg(^M!w+nz!3L;o9cujLFf;qha}M*0rCLg*8mOw!N?! z$}I9j`LJ=`HNfPVFZqTHE`*XrZ*GvtRAt5`9ZrO2Rg?24pI6Mijw^c@c06Uf#14{V zW2UJjc_X%c_t7#t>#5FF+|8@oyYe1Y@xI^PKD29f&?op{9q_3J7*)~q>5NI_Cf08| z6JC}h({P4v6vy?$X`~`|e(!8l(5s;mTK~5NeF=4u>{;C}Iz1#A5m?{^vf@ANUbIvhM-6Hj zo7CjOSf>SOJXNy#CPL0l9S-^h?1G6rj;XT3GYHE(3>jl1aGr(=Fw(=m>+9)7^ugof zLMIfLtM4hCGM?8>L#A&EnB>Q%lXPK|Wlj^XoVh0&l+Izqtz_(~o~y-X4K5w%e@Tie za3<8^Wj>QvPB~6ZR{sdaiI@rIk|nX}(N~MP!=_}%!!*z%icdGhexWMMCgFv0lK< zFGJ-UE=hsJQgGWyxi}S~DwD$fXP`T$uI}aZYTVxrq0>l_F|j9r`@xfY(tT<>J(ukT&Kp@C7sQqo zaNz0zGgv!*3R?DRuz^P_UzWTCB|0m!>Ddsh+_7%l9VIfHfpc3((4kHp>6ICa?Y_Kl ztZE(EmiNE{4wuj?*tyRbVvgp<_9Q-0{JoQi-a78$tu!R-)ZoP(e6RE9!X!IPXnomg zhZqSdJX8G?7Hh3XWqe2g#^>0&>^q+x2qdmi@<9i8tWt5UOi^$IB!#v(7ZCnlPa{vI zPs90vq;=Sesln_yBrV@_8i7u3 zGL!#$mF*PuSrN8JD8vN*3%YPF7B5#!N>E84F0?2Gp2Bl+^CVxnD$dd+$zzKEfPbr- z1C)_;afXuGEUbaoO7!6!YDM7M>8a#7{qr&0nbJ|fKvyq-khLd5u_wc&|8d%JF{>z~gY-SHQuhT;7b`ox2&K95NPN_bXU88?h8 zGcD5s2KoRoi*82kS2z;J zG`aPghfnMI4QOm%eIA+1gnEYCsk$tKnUZv^8ebV4`2`&CpyePJ~aUu@R ztmUm)T@4{O60-@Eg$cSmf~8fxl+b`gLk#eWSW$^oT?kqCa<`R}s!hF`HrUW@mAAl; zX9OKWH|koG>S$5d6}C;sNX^^8IA^O&tx&Uv!sZTZ{7CJ^@OfGFOJQ?W-NX9i(dt#O z6$2Rr`9;|(yu-sH+ES&7SWvV~!nkxiXt5W3mt8$>opdj)%+%rGi_PcTd%K%k+lPm% zh;gON0BUM0FM7i8T?9(PE~#Y{*t(hewLI-``^C5D<#2EN6|Pthz@shYdGM*}w}uk@ z5Q!}DK9+ujMa)Z>RpZ9_bHDpp@dAwu{oRgqV7W`e8u8hGo&xL&QKrfYa|i3kKUW!pI+1F8l!A#Ja&>f$z%!i(Y5X~-iCoL_ zlxvd3ypXB1v5U)`c8P35rw$_pa_}}8MoB?mA8*=a<{$|qZ=}I4+g5*MT5# zvaMbnqF+d7<*B1v09)<@faZbK(r2>N3(#}HBUEk3w))*HG?u*+jWqFQ5u_&&4IYo5}83GT-{IlP^K!&egyQ>Zi zMPR82i_BOS&Dl(O8Onr?Er>cK=@d)<-5=Etl095W1 zly5r4eL2BcWyMA-ECV*VM9szGf9;*&9$?()m;O@*`g|~y5-rL+$v!>4?`vq^WG{~U zw8dfA>%Zik-&$FDabz5<@}gJMOVkl6BN)PwRB_TpF3`)6Kq#`m-8wn%o}TuPx1g+` z{7h0n(uYZ>$SdcA^1)bZ{AU_nd41ti#I0ZLm48m6c*V?g@H<@NDQYwSw3G&Lm(W@k z01YYE9p@Ynukh3xbT$YPd)HU;RcAv7)kN$4k?E>v6hRQs*$p~72W4NBV!kya;kKAK zmR-7NaI!ZS`v1fgvS8=E?i7X0+VSoLC?Mn!#uH%X5Nd%}xGe>xKR`0KMxJqlfZn_7 zEoCb$p_KI@9H<_j{;PSrSHw4+i0eR_LAEb|7_5kCSy;L00H;L$F9K;L+*ynBk=6>e zp8@IsLnQP7LSwISV54Ry6H{`Pvews2l$@=pE-1Eru$h++#s|aYyT!8nFW!fhO7oohgNtu;Eh+_L2$yCf4Kef8FvX+S9|1!Comy~!i{e4 zU=Y0Oqks}%G!)h#P>~L7#B`hn$f4@RVL|;dGU0It-L)Eu=kxASb9CPEvLF zmelwG{zO@KOq1kcv7bs*HJd-5{y&GXB%?u#oFx*i~<$YHrt9^Z+%7JwyV zitA-0U{Xw53Mfbs@XHT(KItqU2&vdKe+3HMz|j$AO-`>{TLisSzYN7W_CA`8K8z5) z3z*T>cjC|mT5FNS0@n2*Fv0@Tz7+a!S#=V7$VGUCRFWS_1YR6oXnm1s1*+lRNXGF$ zlifp7Nx{RN^#U@sG4p#`?YnGg3 z92tm?HaN91^1`bl;L*`56Aauy=6APXu7mLnOH2`sE?3bM)!1kzI?Y5&S690GP!swFO zdI?BaPXQz4+*^Y0Fz*N_Xpiiq0*7_j%lLojg$yCKAiK`H(S^hem|y&I*nftjxvs>) z0}>$TN<`~2@A2adu&l@D&=m(W`ot<#CZtHM!|DQHn)n~PCaTinM&xFb1MR=)|2S=v zJ~(^FWTk81^h?12d_%{(a{owJP+8^zhZiCyYqp0|fso>A!_J63V4L_z04!{*z|6v~ zokd?WFh2wJv)9v*4_{XV!BmzC)|_3$mRNUD5lZ-PL8VgNHoh?Oj_OeCh0mHcRpCSU zmM|vxx?;YBBBm9%xrIK0W&s%`z0@`!J%|ECjZFwAuAY}ysFziu*A>fg|Lb-V_V_y8 zkRYo2cMwW44iuqXu|LsP^`#Os#4}EVo3iS|fc1$6OR*CXWr1 z`^e#seIgSUb1&@?JTYrUBDj!-5kCn$kr_0YCF)KV#XK2&UA5MN*T-oKTr(Gy_vrPS zMXS2lRRUE3K%`>nU8}bK=^2H6q+>cxBP!DAWE>KTUb6dxUW<-0ykUEIr^(P9EokRi zd*vSU2*wm}&`b7lHkjL%&V1_DDAkv-pyU!tyl9NsQC$i6auLj^l)eS#0_XGZf|gu2 zoX}i(5E1bUG9x(rp?c%e4rDMP->=Mw3{kSm{Qlz%!uIP=IzQZLA51>EqkpdL zsqX~r(!K>2x$buRu|JGlgq{|G-Y`q(a7Q=)BbgTV#p?%f+nV1`K=7M!5VsK^07C-8 z^2+_6q>o=#9>7hb-khkc+K_^;Iw_HO`GB$nex+@JQIr}NJ=S3eETxl?$^wdXlol|! z>1HJo02$T-Y$y1t1Z;quH1r81N}mmAZtPo2OVMxrb^q;P_|?;cN24EsGTuTWNtYqV z6dEj^d8qpW^~NeZKeZ;$$>@$Hteq5@^=C? z!=r}}jvk&o{L{UYFeyea^J8Qb&P2Yq^SVihg)#vR|*?K?PI!9tPP z&Fv*#UW^*AcbC|MewuIiHm)l^2E8xZpWIc6y)v`Q-n;J5`ZUgs06F;9`Z|&1!Bm@I zq|Isw1uOnoLutk+t+XNO23wLm-FvQU3LJ@0A#>mcKg9Vz#Q57Sc(cdmEcLD=B~|e( zeIINxuZX?@8>`YlQj}*m+5QoCtoG!fWOF|}-ZQ(KSGZL5E$CiIueqbI`;ZZc7Eh(= z>~;%5_N|pIn++XyAft|?AY%LGyu%^oJ^-1DsC3ELZt0<8o&oiCLg3H4h;`yNpl`;5 zcI#|9y_~GCtsRYi?7(#UCu%;ef_I%=9jy*VeEMJw^djw7|2YBm5?JqUqS?r{&M9LE zEyA9kMWQOQ_YiEP_*gj;367t&M4B|l#6nw{TANtV29%m7gVQU$3Rj;&b)5Oh?%CyD zzxQl#lr(?em8k(tmnZ2vGSWB?B5aP* z+Zcx^acx+!X+I4v)G1Ki!+xv!K?7m%7F*HzIHp;nm5?9S%8a|ltphOLkI;H93Y7>Y?F%=b3(2b3 zBoeB-2x9CUk^v?MGWwynm{!HfhLEYgq>$7oUhgmpk$E70sw^0*96166!*v^Jr>0HyV z^(SD>2aFUG)$bILeCkOndc^WUKK}wG44MqP$a928Jq1nSR&ttrYW7vlxrAG*jk9|O zm+ZlAcX}rOqYuk&+7C8z;_UAoA=uWf`oVD;8g zu&Hh>6~#VJw%X|E#P7+*{ivxbA@eHWNGYfj!#3ro92*LC?@jjK)@wR;1FNp2^*;ii=Js8~W!?&+(y=B}^Ali_r=DVU6q)Tz5XY zCIpIn!5V61V7K9_`@xVw=>GW5x}h3$BTC&}e(yh4kt;s6VSu1DXRo)GFc%KuplP|u zpF2l6Ossr}`QutO0>pJX>JuU?9)KW3x0V1BlQVHY1)VS5Lc5aItGWyqsA&S?L3JSc z1bt`+x0X=1l|2i_@$mrZa$Hsj&!Rhg#~QT2rh0ZJSm6rT7u~_|=g!5i1)4ChE1hB+ zkigpwhINcOBm!Wza<`Un70;{ZqfTNL^X zo}=d@1u+ncrEwYh(|$LDCG)U(e_0+5u2_{g4H^t z*{45u#`ExIvzO&D^1B7)O27ebPIwEk--RrjQ#T2DN})s@DhR^>;G727CixeRV%_mv z3E#Cc;z@UKUN?!!^y=v6PVlS5(PopYEJ8-E>_``p_6C0iBE`8Ct$Hk^$(l4HkgU<` zU0VWf2Z0*N^U#N+2LMb?NOyuUNo*JwS#U>O((t~kZW4dIzW!?ae}A*{YWp!AeA(8C}79+&!=M^>UZDt8P9s@T8S)7z8-ZfSn0$lMLIr z+`mdv%w5?7x^Er%82S$S^yaf~wsG*`#Wu$vPm%5f>I!Qqp(6Cpeazl3ZrD55P8g-M3e7XYf{tEBHrlZgXJ{ z?exOC0X^Yj<=6tW1aL&onXM;h85o&UXP2~ddT+Os&QLlJwv2hfOecm)3)iWdF5C(# zrKG*Htgaur0jsg%3dgH%?#S%j3d~CFD7%HGZkq`HvMmwle(C``mb;TJ;@l$4vU9nu zg(w>u$zDVwc&3T3aH2yhf=_8hUq%QNB0<^hFLFE|J$p+?4C53WC1KUh0hlc;Nil7E3pzy;5VAHD27TO%lWXz*zC zW30z1m=Al-1lEN0)b*;pBmK@8-2{0kB)}Cs_u7j|(sU<04*f1p% z7`k@9E4kmM$bhjoRzkNyKOt4K5olPxQj0wr_rQd>4B@GTZsb$t4m2Xyv0(C|6a26I zR9xy7%`p`fv07|x1wE}9MaX!7<_(uHw1e24qbDLEzLUUh_?c||1PNFwoWOwhjd3?q z7A1Mfa4mR%aNqKmX*+dBf+qzAr>sLhA`xIe2}e=Mp2vxqb^=yF8l@2ritD0y5ma+z z``7Wwz}v9~+>n-7Mf#L*d-q&x!X-O3>BnV>%i1OxQOiwa;|MotRx~q4BCs}X+8U@# zgGt$QvOAWIT_$D_$d*6}*A?VT)zc?whyLt+9!- z`#5H@&`^)}&_-HE2Q5JTlb@$HrN$CL5sVo#5TNQO!mQOQ!C$3On?(dx$s zlXyhOAs2}gQx~fXVe8M?rI;v#P`mnpVTBa~Z8%fU7hOd2V|qR$jAJ4Y=lyOPa;SCZ zr4LCjU-gG@`N1|-Uzq1}K=|ab8GT(|7IUCoo|EDj=$q$J)40mPW1IyWo$8NypNKB| zemNN>$G(oVKZf}|p753$3RM16Po4xke^-eyU~+qX(X%D;yw)7YS#F zU%tQrO~!i*=I6ruIKpFcAb28Puo{6l-_SfP1dhYP*(mPbE*&hH^YkbB5Q}2Ddwx1X zxv#T}<@MqVaV*NiL3ffqIO-0;9I8(`1R#Gr>368iW)I=pdUFnCDSPOqOZu>P27h4o zz}1`#9BM{5C(cZVv@t5?_L*{6_v{b~8T9e&m0g)M=x3cD$HyH=`1A{S_Zc2d9}xlB z=klQZAv}$6?B(C_Nx5B;hLkpjTdF_if%)_y^xV=&vF&>E#zh!rBKTZPWBy>3Y)?E0*){$VmzHYX>Z z#;|6a&)3(fbFn-o%d>C#mV)gNheT`vadm_^y2D#b29cUnMVlaq!9qUDAK(W_^hHLe z1W5RV(G`EoA#d@dqqH&ibOf@2YKycKeHz@9r6$z(8z@NE7Q2Rjw9~G&YK8|t zx^LjZfTO(Moebc=M`sA{>mR}nK!MS#3{ITQW{BM+iLEtrg6D>3_CW>0`cnu%aS}$u za?9i}sFu5OV-z!FNG z6^~dTvWtrgDZQd|JT4>1Gs`TS49(ZhU}?c5=peHUsRNy)_Te*L%EHL4qc}MxHto;be1AQUT#*P`p1BV;Sk>E1TA8@oaYYvXLAIH7EmbvD0@|Y*kQTD&P$f z;9N`t?C-GI`y#T=O^$J+pJNdaF_j4u16XE6l3|^&YS1~}Oh?9(-K%`JI2&E}u@lM1 zphexnAtd%IL?@!0yggwx;6F)zHVS9K#Mj`2DmXT)gcS=HwX;(aMn}M$=fq@*uv#3- zl}d>Qb9Ib+VS$oF8rSDj`EcaQa}zo-vGKV3E&(XeK1s!St@LdndM@y-C2k6j)9VgW zVHHLMBFNhbKS&-KB2@VxZ5;Ly-TnUW1=#u(Y0)XF(_Og2J$q1gtU+8~cUZnuK{1P-;IZfkaFxBM9-drY%{w7a=-E z*OLMwLu9yQ5m6OlX@1U62o2nciBs5>n!u007)X#UXk*hEr(^-{27@zTsN`@ zL^CvoI6#sKi1Xy?T=WG?hC|SysI%F0&Bz0ldnj;Ksc_KYSR05LNcpl88pl&y_`y zE3_p|?f>@2&|gnvfn9_V3cX}|h;*ag;G(OEE|HX~xC-M3^L26dF0VTdpWr(pYi9p;Gi??_K3JXp zIAv_kzE8)jSPZ1E$kc6iCKq9#_0F#}w+|{L-23JA=rpg!|H6?H&TvK(9LmU9c~0!+ zwrkqow+Zu<&i=KN~(9*C|zb>o}55Lt9AzcV(@lw3}>t9AJ7NIE+Dy+ zbwJ00=3@d$o@gTEH8S=f70@r~Zo}LpKFkxUG`SNhJDa`RecXh#6S^kbmQGv< z^m#0uNjK=t9jXvE7I+iHAveqf7RWIfH|&pq?^T$k5$vGEXNq57g?;2_O1>p6+fM;lK0h6Gjr9r2ASgjO|Ax0>iO6t%-e~6a+jvEJ{KTN z$t)Kw%yWh5XD-Qq^QKp4Y+kQAE$X-rDFD43BVeNg{c?YxZ_MBjxP`ll5hXX{&^R8g zd^y(yaC3wYDH3qC=v;!DEKVfi9tBWvQ2paR^VZ zLCvOD5A4WHjKf?+mE zVS0<~pYj$iIU=GcYBw$zpaujNs%cCZ#_`$;fg&+SS3K0mqMHZ%mg5CnBuVYqB3)EC z78<-;AVz=^9pVnZ0Mm-BJ6S{=te7JEnf_1eNwfpO2j2`w*Gj5T1gSwI8{Urs8Iq&2 zBBxQHEkXnA7;MR(+_v&F5e((up^a@G=?B7HpGPW6`KCv8t9wLQXPuY;Um_)V(5zdP z_A=>cZd<5Q!WZNW$b2JA0msJ9ik=+-aG->LZJS*aso}xB`wu(!?sstMS>JV|%6&J@>YBx(GVP(XX=?xY_cQ8`Ry*kbB zI~Sef;_3Qe9j3l>i+sy4qRlr0Kcf_pG~3euZP)KBIMNj?=!BCq#6eZagARB<@o4<6 zVP*RdAO5BP>8B4*?jPU()5D|QgU^mX`}E}awBFFsi>}?_3%p!$p_tZ%%h?iea2f`ucnA-cy7%8|i|3#=J%H*AOp2)K;!jG?K*Aa?cMhm$ zDZ@*ej;Jl}uswn4_2g>UgJNWBD4tShf5}`kYlttchrqLaoC?A&6kxaKHDJ3Y4M~LO&>{1Az z(XMc{(E@A&wp%obA}3%t#a)laZ>SczLZUo9Zb?iC&W1^FQ->7`uGa;qrh?DOR z-X)%1TrxUl+AN|3k9jB&XdL9$ z5>My~PKYKM^ctuWTU9(fc1qWHp*k(38KgO>$F@CVlgScfJ|;UX-8}*OVTc}56KXaR z$G{QP))xQaS(m3{v0yWNPxN?AM#@3_c(JchHYQ~iizT7U6Rau1WW#BRLm+Xfx=xLD zinBowv}uVNDW4#$S+r@ZouA)Pi-YD9T`UqSPfmrpCq>LTp-!LyC+sEK9W*RkaOFH2 zQL>ythgJRFX;@jX_6{xDCe;>om}a zus>i!N=#~3YBYNX8vg;DKoyMzb;q>Ly`~!oE&xP6Eak)@5mG~z*ci16#x!~F$clx{ zN-1gqhh@#xe9cGv_fxTa@osI3I#3eVM6{S8-L-u@ST246ZH8w4Y7-<5{}|rt%ev_A zt)+HA0%?9CKC29#`>RDdu!N)OcV(v!O7BQ2Yci_0_%yb#7^}XCOpd$-Kmd&p^+r$&w(4m6}j=7neVR_8K5vv8(f#C#-%_&}~y zCxvlJ5fqKZGHe1Wgugw20Hs26*kWo5_n;-v04v?gOYq&hu-WNWE#3}bkg3-pPM_Io zD1&x=eNB2=9rmZJdxikcT7QUSH3WN+94G(HLIfp2hP37?Lgq5ax%hxriwTZmR3aS& zT0iJvJfQ0weuoXCW(PVr~w1@`a{<9+!jw zxPazr`xjw%&$hcn$Cy1;hyW{wqV5)1{LBE2o!EI_%#o5;y)2jeEwP1 zRwSSqldqYSIFd-qBD74BNqzyW&X7eGB#7>KYnH5#C>`*MrV6eK81|dma6u_jUBY!U;nF89ox9(Hh|n=m=Tw(qSanG>AMIm!lI$+@+**OMR zylTA-Uf@Ox)S=(0CQy3(0#VfWEiz&UT{Vj=9>K?+*s9d(AR*P2G|)4hyLc@}hW`sz z;!uz&HpBg^+%^rTuv=0cmnJC;khg=;)g(`Evx*(4HIG|HdAToolB3Z&jG??#tfA(_ z@gt@fVi+rB5D!ADD2)YNTIH6Z23>ksUYAMu0S-$ODm^7nCfnISX#y-Rvyj#qMn}aO z2izb-1F4*%q>CC5yo2h|6~fHS<4aav^fp|O&f48xAEK6ScK=lh15w$4Df3Y7@u1yK~=@QJ(<7eMj1cwqzE5w`Rq3&pQBEtcE#xGbmD4QyX^fb?a(5W{TM4Hx`= z&4brZA@7zFML7-tm!-ibC)M=Bm^~##amr(WCd&)jUmV$b+;wbze+%u6(`FX%0R5CddfZDS{+Qv~xGEx<+gf3jZ4lVyH zJ!gwc2&%}JB4#aXL9Xg`8J&sXdMu>x{jEqk8bO0K4dKobs_UHN5U|%^PN3eD=O|9s z*{~eHa-+dyTaWS69|Pdfl4&+S8uH~Q_9KtmRX>${a=s$c)GExUL(Dci#2b1qy{jbm$?h(`z`Lz_gr|$qv zmHa4(T2g2TnKAHeUcexfv;AT{+h(#y99Gs9I-GUs<0@5UG><}hIB(jpiAWZYs0)^#&nj#jVuW*N2Y_jSIUvNpP+0j&J4ARb=$lZHCu~F@rp@)>Bcms*nlavtEV5lP*9YB8XLyxHuG41 z9EDVBW+)X(=ZaH-yku%f3AsJ=A+2T2q*<60lrlp|(zmT%>I)E33c6K7Fu|u^%lxCO zzPgu0lisr&1Mem<#9%Gu#!%w^M}>HeSLnJ{%;9iUqIQ+2=G&o)w0=bcn4xvB5*=-2 zI-+&Keu-=F>j1=T;+u{x>r6Z@Y-TC*)}mD$wLvINZ@Ed0-|VG`!4ijI)Ua-ub~O*; z>;AixZgmAxvS7szN-sSC2bWjOp9t#~qv&&o@^HJ@v;?&xR};V_T#wxdf?ZlUmLghl z00DmN$DOsgfuq_*qp-Naj4&b_%o=tfN z6FQPzg)gX?gm19kP#bD_Y@mrafrJSfk{gJ!Om6U_@qgfQM~sbSes$UtHH%#IO`d2 zgAB6(Y0-G#I4VrB()kSXxPBBEyqH%ihCop zAlq$PRf#Azn9pC(&t8R=Xlnt#p$2HF)lKU-&Z)_mzE=L9S8mj2zeSbBq%xxxb|tsx zztW9Tuq5@8FPL6DgC$p4madu7>r!f>4Js7;4isB9VAx8@F15UYM6IZG_wYXgnl|zB z^Q9{MQWpdwgWNIeLW$}U`$GLMk>S-q7ef~^q8N%iu53ldd25hge-`;iBB*ubw9uzr zGS=DPS$!p=L8TUZ1~7CW?z)@ZM@GlG(nReIK7ROzDxaSH*@Bk9J6Z9Bl?MF3H=rvo zwtj<80zPl>jgumUTk|wDJ~8`hAM+RllC`Pdj8vhUydZyVnPXx`IZqqUPvVHB_`$B_ zvEe0ti!kT~3rvuGVl*aC+Bghj?`U?NCS7zomxJD$pF1I^a3rNA8RKQh5>eqH;!%ic z>$XA9iIWi-0&=0IMBy!5YH*~PPNc<QX z0a?|P+*_WfdN{mZdq@UTUlD)|!)eT0jvGiK1S%-R6`9Gmy;kRz{!Qg(RgJBNrN2s zvYzAa>>X~tdbRm|@pCbsA9DsweTY~D6g^-B2i`bNBM?|y!Z50bVFuS%qKE_HRIeHP zxRExjl&{YnH-8}CY6QWr2#O<9j8_suJ}hz*q1?Kmpo;?ct$gj55Q+nsk|xTGKmm3w ztgF&CtY|jgpIlsrOFCNHx&dhCCQO2)C!1A+&XA0`KKDDiC_)rC;;K!%Hphrch;|Dv zAUhoaY{R`t2Lf;O5(`MLLw2lsnPs~P1J;2eC&Rb*E}P>{@dbzph7VOEiOA_E98_X+ zhu7WnH!!}Xqr1rk+<#Pkly;45jl69ClGN>AV%`2Fx+}7}{cLYed$S9K=eTQpe{Rdl<@v_${$-u?43uLTrY_lN_YGOb6TXp_lXe?;sP)d#B5+?rDaA0g%u`*!FLPIkN7_={5ecG&g zEwvsGCJ^iIcn%(Md<5fBR0=7ZlS6t&E7rkvt5GMR*-XlmWyy2PHU(yG1SLO%kq z(D=|506~C@ppdt?UZd$~nHtAE?dne`0T46|DMiecRwa(MPnxEd<;NxXK?Zn^AO&PP z>7SnjmCWX4IdU0LoemSGf9{++WKs8vdY^OROy84%n8hag7<*yGlBkbq+M(2&1@KVa zuCbU*Yz2DZ^ptvl<30`uS(Q~JSmNXfdn~(0M}^YF8}4DVzytbM&t8p=X9L*g;%P@YThZJn@JM(Y?szNBpR=QeLhPr?o31fT z6ERg#iP9f%4eqdieUNH&S4ZcAUZ~N4t^U3_j?{>P_efnpx%N@k$5a8~21UE5VUWbZ z>{jSu7ss~iC!+NQcejKMY;|x=Aqqz%o_yo$tOrBj^>84PuAC?mwocu`psMZu!M;43pf#o_yD)|947a=#WUxJtWDJV zA#GPY#q-+6tsm@aA7VVwca1<>Li<6xgQG7=VH8PFQF_B(-7@WOJ#JWW(X5PdeF4@% zG-(_}X+c*+hI(+bCYLF}5t&}^b%$Ez8nj6lOup5U)e`L8U1~dZYL-P*BQS{FvA~Eb%|dnl zG00ApPgPSbK*K5Ufkft`Uw>ihCR9E2=s*<3W$;#tb>J+x=opa@US3T>rNQM@gn(S4@Zmx!Fg+}=u{4cO zsG6qRIxIdgmqZ!(S(dFRX^YNNUZ+&7q@-~I#9J<8$!KKIC`O#%2T(DIO8xjuyftXt zl8Sqc=WVzdSx+ z3-Sv~=DdkO-L8W6jE_o7WoP+F%LQY8y?5Q=#_JXK@`FU}%V|+io;$?mp6(HWfJE*0x_}v5u9umLnYKwnFIHU5o-e za6=z~64Qclh65y00GBjaYa&#;XP0~Z-m}3`8lA#{s!6|hJ~%qq8o#@o9=zJyoagrk ztG-Dz!s27U_4Q+-3G0<*DzxWKL&BwRmdx0q|R-Jj9Y{h=8!3hE#Btb5RUC@H{tzl{^~<{hNoo z`61;!N>EzgGytvt#5LgOapCfYn}CJfSg5hUwO+mcIn(d8OoB}wc)lw`6Ck_Eg&7#? z+vj(&OpmO*9I*D<`XkJ4GC99nT=ckM?iQyPm#|TLa9?;W)uzHk9TYm7PJZr$vx90Z z@f>GO!E$B-K{THtCMp%>U{s?F5cVD|4jvYTjf}ITxZvR8>Vm*OxcAv#Q2!8B<$y{W z^X0RY$x&{b#p-B$x^_J3P1c^i{BHl{8pfQk{BhL5{fwns-QZk5cSa@WLHz`{RxT?z z8A@WY&*fmg7{3eAV0yeBp)twFss_h|jx@c70~1klrgj7vAaAE!aj%3JlcmDZa6*y* zYoRgV+}4O^?_+Qb1(K3J{AeF>9y&}rD9+b(;AnVtafEI`+CtR;*r&x*6OucX{Teq3h?Ju&7M@aBY`#T&Wtd#W z?3&y#I)4n&=!7ZFBqFTHhHHRdDV9@ci1L|_XICj3Dlb`a$N*H=lcgES#r>{> zB=tDSsR(L>h$`IlNgD%=I~&3!Ay_0)%OV?i`Rl`O^J67R;?h^~F0$Lm!-F(RHVktG z#k>!rME-2HHJ z5WLsEo4J{JeX*=z;wrR_SszRkTv)X727m`{Z8@meh&6EQ$N^0G$f2aM^*>kTcW3Q9 z1wPUEg9GOV-{>Zv_rXhX5Y4s{wl#470nG#xwz=M)*$FnLrlus~#S)9s-IMpyXgz?1 z7q?B|uhQziUDg6b&#Bh~ETIV{G=9_o0jndZ*9}Ci!Zl{uxFO+sh>LYdv63F98UIkI z83NltS7t#OXh@b;jd;4zV5>N|kdI+s25aJXYpEF2|B050t)7!Wh+o#J`U6cy6HkpL zNF|G}MMzfT>hDA2^bz2A%l>&-_JzJAoOHOxs5f23hj#+}j zDD!68ydTcqaAP1X@#xPSP@edxu{NZ5lSX7SV{H!{!So{*KG?af+) z)bdd&ss>ZxgIY!3SvND^bo)cG5f0n2(L)o*JPz(*+*%hh&KDseb2OH-MG0Spi-QHB%you)<;V{{ zU(>ci&z>K4Up?X#9be+uT$Tt30*1Q79Rry2t~?t~g8z)UO{SJmc9u1zx$+Tx%@vLukd8Wou`V$@FrTFs!Kijw&ItDp zDw3F0=puIIO7dC~qkqttIFZ`f zI7WyC#l+GafFvh}k^gF>RqrXMT7P*}UW~9X}<6*!BgO|`Jkh6)Eiy6KUmE{ay#N=h987$m^QBQ?; zr@6i)GO6zn`)zbjEZ~}OXe35a9SN}t6j?=P3=*RlT`WXZ!VYK67hH#l+M7dOT#&^) z_gSFS99J8Ke<|neJ}e z9wZPh)*~F3uR%yS`!mJKtdM9A-F!<(!F=wNZ+YH}D(B)m+eh!!o+WAoWyZD+=*j&m zVnO^;pfD|N-D?LnSgiD&ch-u}%oeB_?*up4XXBO3fwBc4zr&EoD#nop+)xKMQ^uWZ zVUb**DEcDgl3lk=qY9_^McfH1sO=l%yQeuyuqK^YvDwtUqYKOhQxe*p2 zms+5igOCHMuc)iclRjHFOXKy?|4^-NJT8 zXR{<88&FPgLvtl0pGES?KcukJIdKf8%V@f~d0$0UrE9Q!I>QB-$N)GHRjlwSg#8Bf zxk%bcehn-EyIYYTphCIK&}vr0fzv}iPfYirBqdmaZaKQ6f-PPq1=&JICs62R0yxiX zK5qzNu%kFR0)=a^!W>0>N-hTvI^!4{#zGDP`@>E3$4nr?*mV$U(vJ+vz=NcsRB+Jg z3`d;+d4dfUu22sk|Kxx*c;&`-XE?zuZkQc8cOAevnh(wwpRa{}*z1noXn)bijaTrp zTRKuAzQIk85gSEK03`Mxt&sB!x=Q2P3CHBkp4D$G2`HIlm^fYT^ep9HGl9foHnv2F zt2l*&t=H6N5;6of{91Nf+fs;E))yVHvzYBA-NF7w@)zk3%D+(?XKwGvDVBz=KfeIQPc`UpMLP>mImH^NVRYwB@Fa>JOHR?-bG znx9ag%ket;D~?BC3ZmiJy;-1oLkpq-qf@C0_O`!!MDP@oDg>)J;sfZf>A;%)yvSj! z3c!;Lzc&k+E4rqG5(TVKpq6>*cD<>I-gB1~UOXjjaF@~4s=gIT27Y1NoWLlH6sSLgF7D0i;R zfmC6>q@dA@C#g%CC^NKYoylecsoO}q}&5k(%6YISI}`+$^o5Y@<00U6$-B*g|hU~ z-sSld*i1V}fY4^u0y|n>+inVPYP73*+JXHSIFo;Rj9u->E2K(8zfi?Eo?jQApSSl2 z)XKPf?a!7hXcSI_95PM@kKk1genUze8#~{vF$Y(9!>a6de-zs0HN`u z&^7j?RUQVI+;c`zp$Ds2EQDp$9J{xd5)iYOEz3oaA~9Nf!cbE)wAY@K^X~Pd|J33c z8eDfdJ!}0UP_SL*D?Y}_suZT`C$St`6GgR?>_&1F4hoKf(_Lw8wr(u7B86T|0|9e} zLr1b66TD?$_(?k7hw*S_(-*=iE6p)iCWiQYx!&siRV2^AvDNp(GW$xcwNQvxitPQz z6;wa(6C@GW1dcMj&O~fT&!;EjZDt) z81&qBmwSe|qz&&sZ~cN@AZv&XpbfKWVXH|uUb`guJg;ElFW%juYKl|3>{P^2c*8x$ zr;W9aD^C0HsjM;n$Co6XYPzWeR+XL2>s&^)v+jU$>%}e_aUf_~XGkjdV{vMu8fA`I zNplh*7q+v3zaTGCLOO@TmJz-u`bMjkpAKOne7m*Rma;8UxV)WCJtKWpq2|&U2AD%# z0;Up&23%&+hp^IE+k=Zl!=_93T|EgV{bw;B?dkOoz%nCAi2y>DvcOQg$FTD%gu@{` zHQAZ54Euh>@`N~>LnhLF4S~qFIcacqo2k;E!fNt<%gI6jv)RQ7k6MnC6?K&8hBr@& z5^N~B4{6xh_zA(?K@-{|m{u8LIBcIT`%$dTAoG)=%gbncz^jS|T zoLI_ULTB$iV=Oy&xTqeXx12nDreX%z9&4@r;{ypBXO%&NX(t=XDxfB;8k$l7Q%xnx z^eVoPUt2CVJEP8Q$T=De{gf+pg={d6W&|nPN{VvhY4S)IdY#-xH3aB<-AnGYS%l{8 zl+XxATo0jk+@PmDfwpqxNvUQqK_yeUxg53~KF>PRCh*>jh65xtfMwN)(|RodPo+O% zE80TxD1DGK_*xY;7!t}5UzvO4K~u0^w$-(&*G}Ps2m7MFT3Tj5+B97pRCTQtHef zx0mWTbvdBJp!nK*RLuAlw zZ^&2quJ|yPq+5P8R_awlwE_j@&L-yCj`mFxZfZ37$2iVUPxOn?H7sH|J}mara76zs z=X&5x82P!DWC&&)@*|=O&){3pbI0C+sd^u+c*8k#v)r;TTahlXXK`=J?$h0ZoiOZ* zvKyL%<2yEVFY}!}xm{R({imaIR`6@a`A;o}*!+7r=FL8jZBj%B{xJiPCyJ*<065ec zF9Jtsnu62%zH`wzE}pIr)+h5^-Cqr+tOLj^>zFWUfj~l8n*Y@hO}8|gDBbO+IAwo6 zIxRY=*@4Tp-u+E%h0gGP@pOChF;yS!`HP};8|4O3vGDd&WS!Y&Z_*_&)*b;OY_fi5 z^VbiMAZhaA0{@XAXW_5fHbr1q&l%voA7qQcL^p9%1r~v3zl*Dz=PX)_OLZ(|h>#)C z=}fz)6I~<~hBGFM?r*XR=y~2mrAhNFYh^Hq0%3Qps)@O3s5&Q8c+=4e;EGq;3xCJ{c#9OTvLh4R_mKP{?cl4B1O#zE_)%}1%O&%CmO zR%=yex!S^?y2qoTTOm#x=5^?vl{6w3${dmKoBrXW)sGJ*AEQjZ*0c1HvybMouJY0} zszet!)}1OuT&V2CN#c)@wkhljfPt&ZXN-NRnmsP!!pa4eb9Ur~jai3sV3(M$nB!m4 zZEE_EhVq47Sf1Z)9zTCwnY(;&hmoAhD;$KN`lG9oXqy3;*R4bGXdNOqM5rx!(_)v& zz_BUzfY+4OgoFNZ+L0ii7ksH2AG%Bzg|E}!0VSZhCa&~cE`pY&;^%}eY+t++_nHZW z=!lm+;BiuX#aTkb`YrC7cVT2$cTY2>DMcZPf4zGS4z0@(fKX`XKe>-BIKeZbvQcuCqm?z zrR`;q(_LHV-SKuQEu`+Ky7%)pG)xqhye7fWb2wvjTT35{RzeHEN&N z3tkhrRi8PEMc|0Ku`To&`GJ^g`nn>nl}Fwvy5ueuWhI<(l8k5ro7bf#(TICQUDGV- zoNxWsm)zM@%{!(QeQ9;WxrZ?!G>=u#YZ`-l1QwVo$au~kGI1664Yb@Vp)>JzdB!i)-2(2={8T>+=!%7kd3SoIhUvftOwFCI9}aw zOgRnFKYpd+m1*1-0~FVKp1P7ZGJ51zF@+V2A1PNYn2L3&M6R8`Ucnb4n$wjRTP4c* z+5OiW*9#8f29ZYfW)B2YEHfbH58=k!BE8p{DDy|Kuf#;Gj3O?lRIhj(-DENo_ax=x z@UwhLlJ8tDVr~7YyMuJWn5R29&xjx)8YKA+&PBJ0YKxH>6-*FsUOL+}Gba|jm!f!D z#(MI4Sx4h{`q#1mF3r-1f;Ra}|KJ zZ3;?72dH&k?49+Gx-&JAiM9~BA_qg2a}DPuJj~k^KmY79MXs%@X_BxymduA%4NsiF z06LS{Rmy(7W8+S(KYY{@?lieNf&`*_P(Hm|+`C&m5D=p&xoGmL4}Xr(tk&y7s1r^{ zmVGXk|M1bWc{hBp;3agm+B!&)pKBcTm`5}nI^ZJV=Mc#ai-4LBn14C=Ty3{-=sExN zJ&>rgxoOZj3|mI(ynu7~FxMm!f(tFub`kwZ9wBvMj%yw}2Tf+N z3vZFaun290@t?rO#*k0Yb`rT}s#sLCBG^3;r;DR% zFxl3ErF#2&BS1LGH%88yFg)k8$>z4#>A1q`X z_2XjMSMv5`xsFt|Ss>Px0A`B&@GzsH@KmPizY;p?Y}gdqp<6)~tqr4M&*b5ZQS;E#-_5?3po;Cug|uXyk1+}EkZ@-_~NFY%mnvR^TX2ZO7n{$KKwGp>)6M}7AiX|uF*p^B^Ng0=KcmS@r=Z@z%@4zJ#@4y@IecdxV zyV{lia>xY-uV%V?dS<$NdV0FEZ((I|stv^sb_J5R*5w4C^Znr(lEgFYYuDVWO7M)I zqqN&z|DubmvN^l6Tu*APymY7!Xw|va>}7s(dkTOfMo zWq>cDhsl)R`R$fF{F>=@(>~l!Q;XCmNm);3@CHPB|A_$)|{%7%7$>v~7=oC2WTXSnHw| zIT9F7AxzwFX>A9n0jOrF_ln$(Vuh0oS4i%1fM`HP!S2EXl=uQzsJ+@=~3h*xqM&gh=%%qBKtt%Ef=CtabB@ z`&p1HOqXd8I&^mJPm@nfKZQU*dv;;EBxa;qUeYl!GEJPnSOEj1D57{`Rw|dV%1wpj z%IX|Mrrq4{ybF`a#BI|`ns4L1XwL*;8-E8?^DgcjG>HzN}_h#G!taVI&9VB2p{7{t$wr@H{O_CbZj&`rXC;09YF0g4KZ3+afFWh<}gZX z2LK)J#`U=Kz6zb*X#nW*63QT%(6qv&6W5OFVPaZGNvqk8K)@d0HsZ#90yNQK)aW4b zSOeco^cH^@_%G{q!4)2X{zNIPHCykK_{~Ab95m~D5x(w5pe@|3M@DdAT(x=_AL0c_ zco@DReTiv;;!Fu z_F`jAG!Yh7UqU8jALgqF2DR@r9rMOHFr^OZj>4AT<4}O#ma0*rYu8#vvtBuq0X!tf^ zOF49j-x%>o`GwjKDdbS^Z~&>wZ^!~KpKxwrJnnQ@J_6^EDPr(5lEM|MQ-NbGPre_F zo;f9$5a(ShLo9f(RYbYa=mo+dmQhcNEB35pv1;PF-_$ z2XT@2yJKW35t)5H9*m~4+@2wYs)2)UlkyZPsvzGEQ#Ve!7dXE?nLaMP=Tm2|O^Q@; z%UoO{@E_dhqwpzoD9fqI9Sm)6JwHGTsnItlES*Mxv=sV^@Jq%in<$kt*4%scb`iWx zZc@bkAjf}fO6XE7lHpmPj#22MOeXabO>32$FqI&mHo7MxVXPly3eE*Z+POTkf`B@^ zuvHP}hZBRGJ_to#j+E*nk4axdeFZ*!#HD24j}Q*dufT)e3x{F@B)<;&+>4u1Wa?5W z$0HU3c4v1%7I&E~1pt$@9K5^oEMf>*Bs{faDJ0VCPgREQ5^3_ipaQ|{h_<@1pG?4}9mDf}wI6CyN3|wan&nqlzp3^kO zWlI-oxLd>;QON7D!Zq3q*u%w@7rSuc9HH_AOh|>cfFpv+0`qwo!)HxHAVa1j2e1(? zUJlk-Sd;!3+X|Hd#sdZl$oW7WBC?RRt{re?z$+I(c^U*>j-oJGI!~n9mdn$^=3Siu z`z&1j!G3Z3BZ-D{--AnpB^M#rx;UUvb&_{Ki_FlBq}71La^M=ADT${iGt?76ArbJY zL{gU<&lxNwhfc4pGVP>TCe9o*_CECooFAkI$%6NDY=hz!kZvRXB;*c^cPD02zvxJ| zU{}%vCaiU8O%pGzs0nI zUn?hm(6dZn-rd~>)4}wT&WfWE&1ZAiy&hb^t7zJCyW|&Qar5Uo`i6FDp5*o5(k%|H z0|x1$)?E9tqhuXQ!gCC=H~ey~iI+1Q)NUC#3%XOBj37&clM_jNxd9AnS0ZvLOB<9)N^1@tbn;83CyK8|*y)eloi%0>~2j$K&qC#rDRpPH%s)eJAA3 z#+T)w=LwzZ1TG}NKXf<# zym7jYFKCkcwDxjs?X`Sge_UHI0$jGG5PHGRaUcuEGQJv+N7cX$5Kfxkyv7{>-TGTE zU!o+yOT(oE6OW7OQ7Vckh73;3t&(4tI z=IjiY;Kapyvs5|F{C#=nE z)B*nT)5gPv$~Q-XajbyWrhd>1(}@{OGjj;nPWMXgGYw-|v}UpdTT?!|qch83VQGA; ztzl>yvqCqNEEZk=6avF@^%0={2j#jrs@PFG+Y4Jq-brX@>O^=|2lGHESD&72m3bLb z_}yksYMWhM_v z!48CRV?hm zf9$1H;lVGdW1LpL37hFp>+3m;{DJ@IA{$&I4l&qe#8y3@N>rQF1zXm8vk|bDl{ysFCd0_^mpR zY{<4$$jp9c&R*3~8`ugOW$tOw?Y8-mO^JUp}}tPV?~ tc-b2)0Pp^@{ docs/configuration.rst", + "make -C docs html" + ] + }, + "suggest": { + "ext-xdebug": "For Xdebug profiling extension." + }, + "config": { + "sort-packages": true + }, + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpBench\\": "lib/", + "PhpBench\\Extensions\\XDebug\\": "extensions/xdebug/lib/", + "PhpBench\\Extensions\\Reports\\": "extensions/reports/lib/" + } + }, + "autoload-dev": { + "psr-4": { + "PhpBench\\Tests\\": "tests/", + "PhpBench\\Examples\\": "examples/", + "PhpBench\\Extensions\\XDebug\\Tests\\": "extensions/xdebug/tests/" + } + }, + "bin": [ + "bin/phpbench" + ] +} diff --git a/docs/.doctor-rst.yaml b/docs/.doctor-rst.yaml new file mode 100644 index 000000000..218b969cc --- /dev/null +++ b/docs/.doctor-rst.yaml @@ -0,0 +1,44 @@ +rules: + american_english: ~ + avoid_repetetive_words: ~ + #blank_line_after_directive: ~ + blank_line_before_directive: ~ + composer_dev_option_at_the_end: ~ + correct_code_block_directive_based_on_the_content: ~ + deprecated_directive_should_have_version: ~ + indention: ~ + lowercase_as_in_use_statements: ~ + max_blank_lines: + max: 2 + no_blank_line_after_filepath_in_php_code_block: ~ + no_blank_line_after_filepath_in_xml_code_block: ~ + no_blank_line_after_filepath_in_yaml_code_block: ~ + no_composer_req: ~ + no_inheritdoc: ~ + no_namespace_after_use_statements: ~ + no_php_open_tag_in_code_block_php_directive: ~ + no_space_before_self_xml_closing_tag: ~ + ordered_use_statements: ~ + php_prefix_before_bin_console: ~ + replace_code_block_types: ~ + replacement: ~ + short_array_syntax: ~ + typo: ~ + unused_links: ~ + use_deprecated_directive_instead_of_versionadded: ~ + valid_inline_highlighted_namespaces: ~ + valid_use_statements: ~ + versionadded_directive_should_have_version: ~ + yaml_instead_of_yml_suffix: ~ + + versionadded_directive_major_version: + major_version: 1 + + versionadded_directive_min_version: + min_version: '1.0' + + deprecated_directive_major_version: + major_version: 1 + + deprecated_directive_min_version: + min_version: '1.0' diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 000000000..8f8e230b2 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,177 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PHPBench.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PHPBench.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/PHPBench" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PHPBench" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/_extension/approved.py b/docs/_extension/approved.py new file mode 100644 index 000000000..8ce46a57b --- /dev/null +++ b/docs/_extension/approved.py @@ -0,0 +1,49 @@ +import pprint +import re +from typing import List + +from docutils import nodes +from docutils.nodes import Element, Node +from docutils.parsers.rst import directives +from sphinx.directives.code import LiteralIncludeReader, dedent_lines +from sphinx.util.docutils import SphinxDirective + +class approved(nodes.General, nodes.Element): + pass + +class CodeImportDirective(SphinxDirective): + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = { + 'language': directives.unchanged_required, + 'section': directives.unchanged_required, + } + + def run(self) -> List[Node]: + document = self.state.document + + try: + location = self.state_machine.get_source_and_line(self.lineno) + rel_filename, filename = self.env.relfn2path(self.arguments[0]) + self.env.note_dependency(rel_filename) + reader = LiteralIncludeReader(filename, self.options, self.config) + text, lines = reader.read(location=location) + text = text.split("\n---\n")[int(self.options['section'])] + + retnode = nodes.literal_block(text, text, source=filename, language=self.options['language']) # type: Element + + return [retnode] + except Exception as exc: + return [document.reporter.warning(exc, line=self.lineno)] + +def setup(app): + app.add_config_value('approved_include_approveds', False, 'html') + app.add_directive('approved', CodeImportDirective) + + return { + 'version': '0.1', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } diff --git a/docs/_extension/codeimport.py b/docs/_extension/codeimport.py new file mode 100644 index 000000000..3db461c20 --- /dev/null +++ b/docs/_extension/codeimport.py @@ -0,0 +1,112 @@ +import pprint +import re +from typing import List + +from docutils import nodes +from docutils.nodes import Element, Node +from docutils.parsers.rst import directives +from sphinx.directives.code import LiteralIncludeReader, dedent_lines +from sphinx.util.docutils import SphinxDirective + +class codeimport(nodes.General, nodes.Element): + pass + +class CodeImportDirective(SphinxDirective): + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = { + 'language': directives.unchanged_required, + 'sections': directives.unchanged_required, + 'dedent': int, + 'php_opening_tag': False, + } + + def run(self) -> List[Node]: + document = self.state.document + + try: + location = self.state_machine.get_source_and_line(self.lineno) + rel_filename, filename = self.env.relfn2path(self.arguments[0]) + self.env.note_dependency(rel_filename) + reader = LiteralIncludeReader(filename, self.options, self.config) + text, lines = reader.read(location=location) + lines = text.split("\n") + + if not 'php_opening_tag' in self.options: + lines = self.removeOpeningTag(lines) + + if 'sections' in self.options: + lines = self.filter(lines, self.options['sections'].split(',')) + + lines = self.filterSections(lines) + + if 'dedent' in self.options: + location = self.state_machine.get_source_and_line(self.lineno) + lines = dedent_lines(lines, self.options['dedent'], location=location) + + text = "\n".join(lines) + retnode = nodes.literal_block(text, text, source=filename, language=self.options['language']) # type: Element + + return [retnode] + except Exception as exc: + return [document.reporter.warning(exc, line=self.lineno)] + + def filter(self, lines: List[str], sections: List[str]) -> List[str]: + sectionLines = [] + parsing = None + for line in lines: + if frozenset(sections) & frozenset(self.parseSections("endsection", line)): + parsing = False + + if frozenset(sections) & frozenset(self.parseSections("section", line)): + parsing = True + continue + + if parsing: + sectionLines.append(line) + + if True == parsing: + raise ValueError('No closing section found for %s' % (sections)) + + if None == parsing: + raise ValueError('Section %s not found' % (sections)) + + return sectionLines; + + def parseSections(self, sectionType: str, line: str) -> List[str]: + p = re.compile(".* %s: ([a-zA-Z_,]+)" % (sectionType)) + match = p.match(line) + if None == match: + return [] + + match = match.groups()[0] + + return match.split(',') + + def filterSections(self, lines: List[str]) -> List[str]: + sectionLines = [] + for line in lines: + if "section: " in line: + continue + + sectionLines.append(line) + + return sectionLines; + + def removeOpeningTag(self, lines: List[str]) -> List[str]: + if lines[0].startswith('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 literal 0 HcmV?d00001 diff --git a/docs/_templates/about.html b/docs/_templates/about.html new file mode 100644 index 000000000..c2aa7c6b6 --- /dev/null +++ b/docs/_templates/about.html @@ -0,0 +1,9 @@ + +

+

{{ theme_description }}

diff --git a/docs/_templates/navigation.html b/docs/_templates/navigation.html new file mode 100644 index 000000000..83c4d3107 --- /dev/null +++ b/docs/_templates/navigation.html @@ -0,0 +1,10 @@ +
+{{ toctree(collapse=True,includehidden=False) }} +{% if theme_extra_nav_links %} +
+
    + {% for text, uri in theme_extra_nav_links.items() %} +
  • {{ text }}
  • + {% endfor %} +
+{% endif %} diff --git a/docs/annotributes.rst b/docs/annotributes.rst new file mode 100644 index 000000000..761a40567 --- /dev/null +++ b/docs/annotributes.rst @@ -0,0 +1,507 @@ +Annotributes +============ + +Configure your benchmarks with **Annotations** or, if you have PHP 8, **Attributes**. + +.. contents:: + :depth: 1 + :local: + +.. _metadata_revolutions: + +Revolutions +----------- + +When testing units of code where microsecond accuracy is important, it is +necessary to increase the number of *revolutions* performed by the +benchmark runner. The term "revolutions" (invented here) refers to the number +of times the benchmark is executed consecutively within a single time +measurement. + +We can arrive at a more accurate measurement by determining the mean time +from multiple revolutions (i.e. ``time / revolutions``) than we could with a +single revolution. In other words, more revolutions means more precision. + +Revolutions can be specified using the ``@Revs`` annotation: + +.. tabs:: + + .. tab:: Annotations + + .. codeimport:: ../examples/Annotations/AnnotatedBench.php + :language: php + :sections: all,revs,benchTime + + .. tab:: Attributes + + .. codeimport:: ../examples/Attributes/AttriutedBench.php + :language: php + :sections: all,revs,benchTime + +You may also specify an array: + +.. code-block:: php + + /** + * @Revs({1, 8, 64, 4096}) + */ + class HashBench + { + // ... + } + +Revolutions can also be overridden from the :ref:`command line +`. + +.. _metadata_iterations: + +Iterations +---------- + +Iterations specify how many samples should be taken - i.e. how many times we +run the :ref:`revolutions ` and capture time and memory information (for example). + +By looking at the separate time measurement of each iteration we can determine +how *stable* the readings are. The less the measurements differ from each +other, the more stable the benchmark. + +.. note:: + + In a *perfect* environment the readings would all be *exactly* the same - + but such an environment is unlikely to exist + +Iterations can be specified using the ``@Iterations`` annotation: + +.. tabs:: + + .. tab:: Annotations + + .. codeimport:: ../examples/Annotations/AnnotatedBench.php + :language: php + :sections: all,iterations,benchTime + + .. tab:: Attributes + + .. codeimport:: ../examples/Attributes/AttriutedBench.php + :language: php + :sections: all,iterations,benchTime + +As with :ref:`revolutions `, you may also specify an array. + +Iterations can also be overridden from the :ref:`command line +`. + +You can instruct PHPBench to continuously run the iterations until the +deviation of each iteration fits within a given margin of error by using the +``--retry-threshold``. See :ref:`metadata_retry_threshold` for more information. + +Benchmark Hooks +--------------- + +Method hooks +~~~~~~~~~~~~ + +Any number of methods can be executed both before and after each benchmark +**subject** using the ``@BeforeMethods`` and +``@AfterMethods`` annotations. Before methods are useful for bootstrapping +your environment: + +.. tabs:: + + .. tab:: Annotations + + .. codeimport:: ../examples/Annotations/AnnotatedBench.php + :language: php + :sections: all,beforeMethods,afterMethods,benchTime + + .. tab:: Attributes + + .. codeimport:: ../examples/Attributes/AttriutedBench.php + :language: php + :sections: all,beforeMethods,afterMethods,benchTime + +Multiple before and after methods can be specified. + +.. note:: + + If before and after methods are used when the ``@ParamProviders`` + annotations are used, then they will also be passed the parameters. + +Class Hooks +~~~~~~~~~~~ + +Sometimes you will want to perform actions which establish an *external* +state. For example, creating or populating a database, creating files, etc. + +This can be achieved by creating **static** methods within your benchmark +class and adding the ``@BeforeClassMethods`` and ``@AfterClassMethods``: + +These methods will be executed by the runner once per benchmark class. + +.. tabs:: + + .. tab:: Annotations + + .. codeimport:: ../examples/Annotations/AnnotatedBench.php + :language: php + :sections: all,beforeClassMethods,afterClassMethods,benchTime + + .. tab:: Attributes + + .. codeimport:: ../examples/Attributes/AttriutedBench.php + :language: php + :sections: all,beforeClassMethods,afterClassMethods,benchTime + +.. note:: + + These methods are static and are executed in a process that is separate + from that from which your iterations will be executed. Therefore **state + will not be carried over to your iterations!**. + +.. _metadata_parameters: + +Parameterized Benchmarks +------------------------ + +Parameter sets can be provided to benchmark subjects: + +.. tabs:: + + .. tab:: Annotations + + .. codeimport:: ../examples/Annotations/AnnotatedBench.php + :language: php + :sections: all,paramProviders + + .. tab:: Attributes + + .. codeimport:: ../examples/Attributes/AttriutedBench.php + :language: php + :sections: all,paramProviders + +The `benchMd5` subject will now be benchmarked with each parameter set. + +The param provider can return a set of parameters using any `iterable`. +For example the above could also be returned as an array: + +.. tabs:: + + .. tab:: Annotations + + .. codeimport:: ../examples/Annotations/AnnotatedBench.php + :language: php + :sections: all,paramIterable + + .. tab:: Attributes + + .. codeimport:: ../examples/Attributes/AttriutedBench.php + :language: php + :sections: all,paramIterable + +.. warning:: + + It should be noted that Generators are consumed completely before the + subject is executed. If you have a very large data set, it will be read + completely into memory. + +Multiple parameter providers can be used, in which case the data sets will be +combined into a `cartesian product`_ - all possible combinations of the +parameters will be generated: + +.. tabs:: + + .. tab:: Annotations + + .. codeimport:: ../examples/Annotations/AnnotatedBench.php + :language: php + :sections: all,paramMultiple + + .. tab:: Attributes + + .. codeimport:: ../examples/Attributes/AttriutedBench.php + :language: php + :sections: all,paramMultiple + +Will result in the following parameter benchmark scenarios: + +.. code-block:: php + + // #0 + ['string' => 'Hello World!', 'algorithm' => 'md5']; + + // #1 + ['string' => 'Goodbye Cruel World!', 'algorithm' => 'md5'[; + + // #2 + ['string' => 'Hello World!', 'algorithm' => 'sha1']; + + // #3 + ['string' => 'Goodbye Cruel World!', 'algorithm' => 'sha1']; + +.. _metadata_groups: + +Groups +------ + +You can assign benchmark subjects to groups using the ``@Groups`` annotation. + +.. tabs:: + + .. tab:: Annotations + + .. codeimport:: ../examples/Annotations/AnnotatedBench.php + :language: php + :sections: all,benchTime,groups + + .. tab:: Attributes + + .. codeimport:: ../examples/Attributes/AttriutedBench.php + :language: php + :sections: all,benchTime,groups + +The group can then be targeted using the command line interface. + +Skipping Subjects +----------------- + +You can skip subjects by using the ``@Skip`` annotation: + +.. tabs:: + + .. tab:: Annotations + + .. codeimport:: ../examples/Annotations/AnnotatedBench.php + :language: php + :sections: all,skip + + .. tab:: Attributes + + .. codeimport:: ../examples/Attributes/AttriutedBench.php + :language: php + :sections: all,skip + +Sleeping +-------- + +Sometimes it may be necessary to pause between iterations in order to let +the system recover. Use the ``@Sleep`` annotation, specifying the number of +**microseconds** required: + +.. tabs:: + + .. tab:: Annotations + + .. codeimport:: ../examples/Annotations/AnnotatedBench.php + :language: php + :sections: all,benchTime,sleep + + .. tab:: Attributes + + .. codeimport:: ../examples/Attributes/AttriutedBench.php + :language: php + :sections: all,benchTime,sleep + +The above example will pause (sleep) for 1 millisecond *after* each iteration. + +.. note:: + + This can be overridden using the ``--sleep`` option from the CLI. + +.. _metadata_time_unit: + +Time Units +---------- + +Specify *output* time units using the ``@OutputTimeUnit`` annotation +(`precision` is optional): + +.. tabs:: + + .. tab:: Annotations + + .. codeimport:: ../examples/Annotations/AnnotatedBench.php + :language: php + :sections: all,benchTime,outputTimeUnit + + .. tab:: Attributes + + .. codeimport:: ../examples/Attributes/AttriutedBench.php + :language: php + :sections: all,benchTime,outputTimeUnit + +The following time units are available: + +- ``microseconds`` +- ``milliseconds`` +- ``seconds`` +- ``minutes`` +- ``hours`` +- ``days`` + +.. _metadata_throughput: +.. _metadata_mode: + +Throughput Representation +------------------------- + +The output mode determines how the measurements are presented, either `time` +or `throughput`. `time` mode is the default and shows the average execution +time of a single :ref:`revolution `. `throughput` shows how many *operations* +are executed within a single time unit: + +.. tabs:: + + .. tab:: Annotations + + .. codeimport:: ../examples/Annotations/AnnotatedBench.php + :language: php + :sections: all,benchTime,outputMode + + .. tab:: Attributes + + .. codeimport:: ../examples/Attributes/AttriutedBench.php + :language: php + :sections: all,benchTime,outputMode + +PHPBench will then render all measurements for `benchTimeItself` similar to +`363,874.536ops/s`. + +.. _metadata_warmup: + +Warm Up +------- + +Use the ``@Warmup`` annotation to execute any number of revolutions before +actually measuring the revolutions time. + +.. tabs:: + + .. tab:: Annotations + + .. codeimport:: ../examples/Annotations/AnnotatedBench.php + :language: php + :sections: all,benchTime,warmup + + .. tab:: Attributes + + .. codeimport:: ../examples/Attributes/AttriutedBench.php + :language: php + :sections: all,benchTime,warmup + +As with :ref:`revolutions `, you may also specify an array. + +.. _metadata_timeout: + +Timeout +------- + +Use the ``@Timeout`` annotation to specify the maximum number of seconds +before an iteration timesout and fails. The following example will fail after +0.1 seconds: + +.. tabs:: + + .. tab:: Annotations + + .. codeimport:: ../examples/Annotations/AnnotatedBench.php + :language: php + :sections: all,benchTime,timeout + + .. tab:: Attributes + + .. codeimport:: ../examples/Attributes/AttriutedBench.php + :language: php + :sections: all,benchTime,timeout + +.. _metadata_assertions: + +Assertions +---------- + +You can annotate your benchmarks with *assertions* which will cause PHPBench +to report failures and exit with a non-zero exit code if they fail. + +For example, assert that the :ref:`KDE mode` is less than 200 microseconds: + +.. tabs:: + + .. tab:: Annotations + + .. codeimport:: ../examples/Annotations/AnnotatedBench.php + :language: php + :sections: all,benchTime,assert + + .. tab:: Attributes + + .. codeimport:: ../examples/Attributes/AttriutedBench.php + :language: php + :sections: all,benchTime,assert + +You can also specify assertions from the command line: + +.. code-block:: bash + + $ phpbench run --assert='mode(variant.time.avg) < 10 hours' + +See :doc:`assertions` for more information. + +.. _cartesian product: https://en.wikipedia.org/wiki/Cartesian_product + +.. _metadata_format: + +Format +------ + +Override how the variant results are formatted in the progress output. + +.. tabs:: + + .. tab:: Annotations + + .. codeimport:: ../examples/Annotations/AnnotatedBench.php + :language: php + :sections: all,benchTime,format + + .. tab:: Attributes + + .. codeimport:: ../examples/Attributes/AttriutedBench.php + :language: php + :sections: all,benchTime,format + +You can also specify assertions from the command line: + +.. code-block:: bash + + $ phpbench run --format='"This is my time: " ~ mode(variant.time.avg)' + +See :doc:`expression` for details on using the expressio language. + +.. _metadata_retry_threshold: + +RetryThreshold +-------------- + +Set the retry threshold (the deviation beyond which a sample will be +considered invalid and retried). + +Use to create more stable sets of iterations. + +.. tabs:: + + .. tab:: Annotations + + .. codeimport:: ../examples/Annotations/AnnotatedBench.php + :language: php + :sections: all,retrythreshold,benchTime + + .. tab:: Attributes + + .. codeimport:: ../examples/Attributes/AttriutedBench.php + :language: php + :sections: all,retrythreshold,benchTime + +Good values are generally 10 or less, the above threshold is 20 because the +examples are executed in the continuous integration environment and may cause +delays. + +.. _cartesian product: https://en.wikipedia.org/wiki/Cartesian_product diff --git a/docs/assertions.rst b/docs/assertions.rst new file mode 100644 index 000000000..74d2ae180 --- /dev/null +++ b/docs/assertions.rst @@ -0,0 +1,39 @@ +Assertions +========== + +Assertions are executed against each variant of the subject and can be added +to your benchmarks as follows: + +.. codeimport:: ../examples/Assertion/ExampleAssertionsBench.php + :language: php + :sections: all,bench_time + +Above we assert that the KDE mode of the average iterations times is less than +200 milliseconds give or take a tolerance of 10%. + +.. codeimport:: ../examples/Assertion/ExampleAssertionsBench.php + :language: php + :sections: all,bench_time_baseline + +Above we compare the current variant with a :ref:`referenced` variant. + +Data +---- + +PHPBench provides the aggregated iteration results for the current `variant` +and, if a :ref:`reference ` is specified, the `baseline`. + +.. codeimport:: ../examples/Assertion/ExampleAssertionsBench.php + :language: php + :sections: data_access + +Each metric (e.g. `variant.time.net`) is provided as a list of numbers +corresponding to each iteration. You will need to use an aggregation function +(e.g. :ref:`expr_func_mode` or :ref:`expr_func_mean`) to get a comparable +value. + +Assertion Language +------------------ + +Assertions are created using the :doc:`PHPBench Expression +Language`. Each assertion must be a comparison. diff --git a/docs/benchmark-runner.rst b/docs/benchmark-runner.rst new file mode 100644 index 000000000..892873a0a --- /dev/null +++ b/docs/benchmark-runner.rst @@ -0,0 +1,245 @@ +Benchmark Runner +================ + +The benchmark runner is a command line application which executes the +benchmarks and generates reports from the results. + +Running Benchmarks +------------------ + +To run all benchmarks in a specific directory: + +.. code-block:: bash + + $ phpbench run /path/to + +To run a single benchmark class, specify a specific file: + +.. code-block:: bash + + $ phpbench run /path/to/HashBench.php + +To run a single method of a single benchmark class, use the ``--filter`` +option: + +.. code-block:: bash + + $ phpbench run /path/to/HashBench.php --filter=benchMd5 + +Groups can be specified using the ``--group`` option: + +.. code-block:: bash + + $ phpbench run /path/to/HashBench.php --group=hash + +.. note:: + + Both ``--subject`` and ``--group`` options may be specified multiple + times. + +.. _filtering: + +Filtering +--------- + +The ``--filter`` option accepts a regex without the delimiters and matches +against a string such as ``HashBench::benchMd5``, so all of the following are +valid: + +.. code-block:: bash + + $ phpbench run /path/to --filter=benchFoo + $ phpbench run /path/to --filter=HashBench::benchFoo + $ phpbench run /path/to --filter=Hash.* + +.. _overriding_iterations_and_revolutions: + +Overriding Iterations and Revolutions +------------------------------------- + +The benchmark runner can override the number of :ref:`revolutions +` and +:ref:`iterations ` which will be executed: + +.. code-block:: bash + + $ phpbench run /path/to/HashBench.php --iterations=10 --revs=1000 + +You may specify these options multiple times. + +Overriding the Bootstrap +------------------------ + +You can override or set the :ref:`configuration_runner_bootstrap` using the +``--bootstrap`` option: + +.. code-block:: bash + + $ phpbench run /path/to/HashBench.php --bootstrap=vendor/autoload.php + +Assertions: Overriding and Toleration +------------------------------------- + +Use the ``--assert`` option to introduce or override assertions: + +.. code-block:: bash + + $ phpbench run /path/to/HashBench.php --assert="variant.mode <= 10 microsconds +/- 10%" + +This will assert that ALL variants must have a mode less than 10. For more +information on assertions see :doc:`assertions`. + +Failing assertions will cause PHPBench to exit with code ``2``. If you want to +tolerate failures (f.e. in an unstable CI environment) you can use the +``--tolerate-failure`` option. + +For more information about assertions see :doc:`assertions`. + +Generating Reports +------------------ + +By default PHPBench will run the benchmarks and tell you that the benchmarks +have been executed successfully. In order to see some useful information you +can specify that a report be generated. + +By default there are two reports ``default`` and ``aggregate``, and they can +be specified directly using the ``--report`` option: + +.. code-block:: bash + + $ phpbench run /path/to/HashBench.php --report=default + +See the :doc:`reports` chapter for more information on how you can +configure reports. + +.. note:: + + If you want to suppress all other output and only show the output from the + reports you can use the ``--progress=none`` option. This is especially useful when + piping a report to another program. + +.. _retry_threshold: + +Retry Threshold +--------------- + +PHPBench is able to significantly improve the stability of your benchmarks by +retrying the iteration set until all the deviations in time between iterations +fit within a given margin of error. + +You can set this as follows: + +.. code-block:: bash + + $ phpbench run /path/to/HashBench.php --retry-threshold=5 + +The retry threshold is the margin of error as a percentage which is allowed +between deviations. Generally the lower this value, the higher the stability, +but the longer it will take for a set of iterations to be resolved. + +By default the retry threshold is disabled. + +You may also set the retry threshold in the +:ref:`configuration `. + +Changing the Output Format +-------------------------- + +By default PHPBench will output the reports to the console using the +``console`` output. The output can be changed using the ``--output`` option. +For example, to render a HTML document: + +.. code-block:: bash + + $ phpbench run /path/to/HashBench.php --report=default --output=html + +Example of HTML output: + +.. image:: images/html.png + +See the :doc:`report-renderers` chapter for more information. + +Storing Results +--------------- + +You can store benchmark results locally: + +.. code-block:: bash + + $ phpbench run /path/to/HashBench.php --tag=foobar + +Then generate reports: + +.. code-block:: bash + + $ phpbench report --ref=foobar --report=aggregate + +To dump the benchmark results to an XML file use the ``--dump-file`` option: + +.. code-block:: bash + + $ phpbench run /path/to/HashBench.php --dump-file=report.xml + +Then generate reports: + +.. code-block:: bash + + $ phpbench report --file=report.xml --report=default + +Progress Reporters +------------------ + +By default PHPBench issues a single ``.`` for each benchmark subject executed. +This is the ``dots`` progress reporter. Different progress reporters can be +specified using the ``--progress`` option: + +.. figure:: images/blinken.gif + + blinken progress logger. + +.. code-block:: bash + + $ phpbench run /path/to/HashBench.php --progress=classdots + +The built-in progress loggers are: + +- ``verbose``: The default logger, format: `[R] I P ` ). +- ``plain``: Similar to verbose, but without any animation, useful for CI environments. +- ``dots``: Shows one dot per subject (like PHPUnit). +- ``classdots``: Shows the benchmark class, and then a dot for each subject. +- ``blinken``: Highly visual progress logger. +- ``histogram``: Shows a histogram with 8 vertical levels and 16 bins for each + iteration set. + +All of the progress reports contain the following footer: + +.. code-block:: bash + + 3 subjects, 30 iterations, 30000 revs, 0 rejects + min [mean mode] max: 0.84 [1.13 1.12] 1.66 (μs/r) + ⅀T: 33987μs μSD/r 0.16μs μRSD/r: 14.92% + +It provides a summary of the minimum, mean, mode, and maximum subject times, given +microseconds per revolution. ⅀T is the aggregate total time, μSD/r is the mean +standard deviation, and μRSD/r is the mean relative standard deviation. + +.. warning:: + + These summary statistics can be misleading. You should always verify the + individual subject statistics before drawing any conclusions. + +Configuration File +------------------ + +A custom configuration file can be specified with the ``--config`` option. See +the :doc:`configuration` chapter for more information on configuration. + +Exit codes +---------- + +The following exit codes can occur: + +- ``0``: Everything was fine. +- ``1``: Errors encountered in benchmarks. +- ``2``: Assertion failures. +- ``255``: Internal error diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 000000000..e512a14f7 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,272 @@ +# -*- coding: utf-8 -*- +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) +sys.path.append(os.path.abspath("./_extension")) + +# adding PhpLexer +from sphinx.highlighting import lexers +from pygments.lexers.web import PhpLexer + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'approved', + 'codeimport', + 'sphinx_tabs.tabs', + ] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'contents' + +# General information about the project. +project = u'PHPBench' +copyright = u'Daniel Leech' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +# version = '0.11' +# The full version, including alpha/beta/rc tags. +# release = '0.5' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# This will be used when using the shorthand notation +#highlight_language = 'php' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# -- Settings for symfony doc extension --------------------------------------------------- + +# enable highlighting for PHP code not between ```` by default +lexers['php'] = PhpLexer(startinline=True) + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +html_theme_options = { + } + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +html_show_copyright = False + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'PHPBenchdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'PHPBench.tex', u'PHPBench Documentation', + u'Daniel Leech', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'phpbench', u'PHPBench Documentation', + [u'Daniel Leech'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'PHPBench', u'PHPBench Documentation', + u'Daniel Leech', 'PHPBench', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/docs/configuration-prelude.rst b/docs/configuration-prelude.rst new file mode 100644 index 000000000..ea3995257 --- /dev/null +++ b/docs/configuration-prelude.rst @@ -0,0 +1,28 @@ +Unless overridden with the ``--config`` option, PHPBench will try to load its +configuration from the current working directory. It will check for the +existence each of the files ``phpbench.json`` and ``phpbench.json.dist`` in +that order and use one if it exists. + +.. code-block:: json + + { + "runner.bootstrap": "vendor/autoload.php", + "runner.path": "path/to/benchmarks", + "report.outputs": { + "my_csv_output": { + "extends": "delimited", + "delimiter": ",", + } + }, + "report.generators": { + "my_report": { + "extends": "aggregate" + } + } + } + +.. note:: + + Typically you should use ``phpbench.json.dist`` in your projects. This + allows the end-user of your library to override your configuration by creating + ``phpbench.json``. diff --git a/docs/configuration.rst b/docs/configuration.rst new file mode 100644 index 000000000..5337108d8 --- /dev/null +++ b/docs/configuration.rst @@ -0,0 +1,553 @@ +Configuration +============= + +.. include:: configuration-prelude.rst + +.. contents:: + :depth: 2 + :local: + +Core +---- + +Extension class: ``PhpBench\Extension\CoreExtension`` + +.. _configuration_core_debug: + +core.debug +~~~~~~~~~~ + +If enabled output debug messages (e.g. the commands being executed when running benchamrks). Same as ``-vvv`` + +Default: ``false`` + +Types: ``["bool"]`` + +.. _configuration_core_extensions: + +core.extensions +~~~~~~~~~~~~~~~ + +List of additional extensions to enable + +Default: ``[]`` + +Types: ``["array"]`` + +.. _configuration_core_working_dir: + +core.working_dir +~~~~~~~~~~~~~~~~ + +Working directory to use + +Default: ``"\/home\/daniel\/www\/phpbench\/phpbench"`` + +Types: ``["string"]`` + +.. _configuration_core_config_path: + +core.config_path +~~~~~~~~~~~~~~~~ + +Alternative path to a PHPBench configuration file (default is ``phpbench.json`` + +Default: ``null`` + +Types: ``["string","null"]`` + +.. _configuration_core_profiles: + +core.profiles +~~~~~~~~~~~~~ + +Alternative configurations:: + + { + "core.profiles": { + "php8": { + "runner.php_bin": "/bin/php8" + } + } + } + +The named configuration will be merged with the default configuration, and can be used via: + +.. code-block:: bash + + $ phpbench run --profile=php8 + +Default: ``[]`` + +Types: ``["array"]`` + +Runner +------ + +Extension class: ``PhpBench\Extension\RunnerExtension`` + +.. _configuration_runner_annotations: + +runner.annotations +~~~~~~~~~~~~~~~~~~ + +Read metadata from annotations + +Default: ``true`` + +Types: ``["bool"]`` + +.. _configuration_runner_annotation_import_use: + +runner.annotation_import_use +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Require that annotations be imported before use + +Default: ``false`` + +Types: ``["bool"]`` + +.. _configuration_runner_attributes: + +runner.attributes +~~~~~~~~~~~~~~~~~ + +Read metadata from PHP 8 attributes + +Default: ``true`` + +Types: ``["bool"]`` + +.. _configuration_runner_bootstrap: + +runner.bootstrap +~~~~~~~~~~~~~~~~ + +Path to bootstrap (e.g. ``vendor/autoload.php``) + +Default: ``null`` + +Types: ``["string","null"]`` + +.. _configuration_runner_env_enabled_providers: + +runner.env_enabled_providers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Select which environment samplers to use + +Default: ``["sampler","git","opcache","php","uname","unix_sysload"]`` + +Types: ``["array"]`` + +.. _configuration_runner_env_samplers: + +runner.env_samplers +~~~~~~~~~~~~~~~~~~~ + +Environment baselines (not to be confused with baseline comparisons when running benchmarks) are small benchmarks which run to sample the speed of the system (e.g. file I/O, computation etc). This setting enables or disables these baselines + +Default: ``["nothing","md5","file_rw"]`` + +Types: ``["array"]`` + +.. _configuration_runner_env_sampler_callables: + +runner.env_sampler_callables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Map of baseline callables (adds you to register a new environemntal baseline) + +Default: ``[]`` + +Types: ``["array"]`` + +.. _configuration_runner_executors: + +runner.executors +~~~~~~~~~~~~~~~~ + +Add new executor configurations + +Default: ``[]`` + +Types: ``["array"]`` + +.. _configuration_runner_path: + +runner.path +~~~~~~~~~~~ + +Path or paths to the benchmarks + +Default: ``null`` + +Types: ``["string","array","null"]`` + +.. _configuration_runner_php_binary: + +runner.php_binary +~~~~~~~~~~~~~~~~~ + +Specify a PHP binary to use when executing out-of-band benchmarks, e.g. ``/usr/bin/php6``, defaults to the version of PHP used to invoke PHPBench + +Default: ``null`` + +Types: ``["string","null"]`` + +.. _configuration_runner_php_config: + +runner.php_config +~~~~~~~~~~~~~~~~~ + +Map of PHP ini settings to use when executing out-of-band benchmarks + +Default: ``[]`` + +Types: ``["array"]`` + +.. _configuration_runner_php_disable_ini: + +runner.php_disable_ini +~~~~~~~~~~~~~~~~~~~~~~ + +Disable reading the default PHP configuration + +Default: ``false`` + +Types: ``["bool"]`` + +.. _configuration_runner_php_wrapper: + +runner.php_wrapper +~~~~~~~~~~~~~~~~~~ + +Wrap the PHP binary with this command (e.g. ``blackfire run``) + +Default: ``null`` + +Types: ``["string","null"]`` + +.. _configuration_runner_progress: + +runner.progress +~~~~~~~~~~~~~~~ + +Default progress logger to use + +Default: ``"verbose"`` + +Types: ``["string"]`` + +.. _configuration_runner_progress_summary_variant_format: + +runner.progress_summary_variant_format +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Expression used to render the summary text default progress loggers + +Default: ``"\"Mo\" ~ display_as_time(mode(variant.time.avg), coalesce(subject.time_unit,\"time\"), subject.time_precision, subject.time_mode) ~ \n\" (\" ~ rstdev(variant.time.avg) ~ \")\""`` + +Types: ``["string"]`` + +.. _configuration_runner_progress_summary_baseline_format: + +runner.progress_summary_baseline_format +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When the a comparison benchmark is referenced, alternative expression used to render the summary text default progress loggers + +Default: ``"\"[\" ~ \n\"Mo\" ~ display_as_time(mode(variant.time.avg), coalesce(subject.time_unit,\"time\"), subject.time_precision, subject.time_mode) ~\n\" vs \" ~ \n\"Mo\" ~ display_as_time(mode(baseline.time.avg), coalesce(subject.time_unit,\"time\"), subject.time_precision, subject.time_mode) ~ \"] \" ~ \npercent_diff(mode(baseline.time.avg), mode(variant.time.avg), (rstdev(variant.time.avg) * 2)) ~\n\" (\" ~ rstdev(variant.time.avg) ~ \")\""`` + +Types: ``["string"]`` + +.. _configuration_runner_remote_script_path: + +runner.remote_script_path +~~~~~~~~~~~~~~~~~~~~~~~~~ + +PHPBench generates a PHP file for out-of-band benchmarks which is executed, this setting specifies the path to this file. When NULL a file in the systems temporary directory will be used + +Default: ``null`` + +Types: ``["string","null"]`` + +.. _configuration_runner_remote_script_remove: + +runner.remote_script_remove +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If the generated file should be removed after it has been executed (useful for debugging) + +Default: ``true`` + +Types: ``["bool"]`` + +.. _configuration_runner_assert: + +runner.assert +~~~~~~~~~~~~~ + +Default :ref:`metadata_assertions` + +Default: ``null`` + +Types: ``["null","string","array"]`` + +.. _configuration_runner_executor: + +runner.executor +~~~~~~~~~~~~~~~ + +Default executor + +Default: ``null`` + +Types: ``["null","string"]`` + +.. _configuration_runner_format: + +runner.format +~~~~~~~~~~~~~ + +Default :ref:`metadata_format` + +Default: ``null`` + +Types: ``["null","string"]`` + +.. _configuration_runner_iterations: + +runner.iterations +~~~~~~~~~~~~~~~~~ + +Default :ref:`metadata_iterations` + +Default: ``null`` + +Types: ``["null","int","array"]`` + +.. _configuration_runner_output_mode: + +runner.output_mode +~~~~~~~~~~~~~~~~~~ + +Default :ref:`output mode ` + +Default: ``null`` + +Types: ``["null","string"]`` + +.. _configuration_runner_time_unit: + +runner.time_unit +~~~~~~~~~~~~~~~~ + +Default :ref:`time unit ` + +Default: ``null`` + +Types: ``["null","string"]`` + +.. _configuration_runner_retry_threshold: + +runner.retry_threshold +~~~~~~~~~~~~~~~~~~~~~~ + +Default :ref:`metadata_retry_threshold` + +Default: ``null`` + +Types: ``["null","int","float"]`` + +.. _configuration_runner_revs: + +runner.revs +~~~~~~~~~~~ + +Default number of :ref:`metadata_revolutions` + +Default: ``null`` + +Types: ``["null","int","array"]`` + +.. _configuration_runner_timeout: + +runner.timeout +~~~~~~~~~~~~~~ + +Default :ref:`metadata_timeout` + +Default: ``null`` + +Types: ``["null","float","int"]`` + +.. _configuration_runner_warmup: + +runner.warmup +~~~~~~~~~~~~~ + +Default :ref:`metadata_warmup` + +Default: ``null`` + +Types: ``["null","int","array"]`` + +.. _configuration_runner_subject_pattern: + +runner.subject_pattern +~~~~~~~~~~~~~~~~~~~~~~ + +Subject prefix to use when finding benchmarks + +Default: ``"^bench"`` + +Types: ``["string"]`` + +Report +------ + +Extension class: ``PhpBench\Extension\ReportExtension`` + +.. _configuration_report_generators: + +report.generators +~~~~~~~~~~~~~~~~~ + +Report generator configurations, see :doc:`report-generators` + +Default: ``[]`` + +Types: ``["array"]`` + +.. _configuration_report_outputs: + +report.outputs +~~~~~~~~~~~~~~ + +Report renderer configurations, see :doc:`report-renderers` + +Default: ``[]`` + +Types: ``["array"]`` + +Expression +---------- + +Extension class: ``PhpBench\Extension\ExpressionExtension`` + +.. _configuration_expression_syntax_highlighting: + +expression.syntax_highlighting +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Enable syntax highlighting + +Default: ``true`` + +Types: ``["bool"]`` + +.. _configuration_expression_theme: + +expression.theme +~~~~~~~~~~~~~~~~ + +Select a theme to use + +Default: ``"solarized"`` + +Types: ``["string"]`` + +Storage +------- + +Extension class: ``PhpBench\Extension\StorageExtension`` + +.. _configuration_storage_driver: + +storage.driver +~~~~~~~~~~~~~~ + +Storage driver to use + +Default: ``"xml"`` + +Types: ``["string"]`` + +.. _configuration_storage_xml_storage_path: + +storage.xml_storage_path +~~~~~~~~~~~~~~~~~~~~~~~~ + +Path to store benchmark runs when they are stored with ``--store`` or ``--tag=foo`` + +Default: ``".phpbench\/storage"`` + +Types: ``["string"]`` + +XDebug +------ + +Extension class: ``PhpBench\Extensions\XDebug\XDebugExtension`` + +.. _configuration_xdebug_command_handler_output_dir: + +xdebug.command_handler_output_dir +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Output directory for generated XDebug profiles + +Default: ``".phpbench\/xdebug-profile"`` + +Types: ``["string"]`` + +Console +------- + +Extension class: ``PhpBench\Extension\ConsoleExtension`` + +.. _configuration_console_ansi: + +console.ansi +~~~~~~~~~~~~ + +Enable or disable ANSI control characters (e.g. console colors) + +Default: ``true`` + +Types: ``["bool"]`` + +.. _configuration_console_disable_output: + +console.disable_output +~~~~~~~~~~~~~~~~~~~~~~ + +Disable output from both STDOUT and STDERR + +Default: ``false`` + +Types: ``["bool"]`` + +.. _configuration_console_output_stream: + +console.output_stream +~~~~~~~~~~~~~~~~~~~~~ + +Change the normal output stream - the output stream used for reports + +Default: ``"php:\/\/stdout"`` + +Types: ``["string"]`` + +.. _configuration_console_error_stream: + +console.error_stream +~~~~~~~~~~~~~~~~~~~~ + +Change the error output stream - the output stream used for diagnostics (e.g. progress loggers use this stream) + +Default: ``"php:\/\/stderr"`` + +Types: ``["string"]`` + diff --git a/docs/contents.rst b/docs/contents.rst new file mode 100644 index 000000000..92712d7ea --- /dev/null +++ b/docs/contents.rst @@ -0,0 +1,36 @@ +Welcome to PHPBench's documentation! +==================================== + +PHPBench is a benchmarking framework for PHP. Find it on Github_. + +.. toctree:: + :maxdepth: 2 + + installing + quick-start + writing-benchmarks + reports + benchmark-runner + regression-testing + reference + extensions + customisation + development + faq + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + +.. toctree:: + :maxdepth: 2 + :hidden: + + index + configuration-prelude + + +.. _Github: https://github.com/phpbench/phpbench diff --git a/docs/customisation.rst b/docs/customisation.rst new file mode 100644 index 000000000..c889dc084 --- /dev/null +++ b/docs/customisation.rst @@ -0,0 +1,11 @@ +Customisation +------------- + +.. toctree:: + :maxdepth: 2 + + customisation/extension + customisation/command + customisation/executor + customisation/progress-logger + customisation/report diff --git a/docs/customisation/command.rst b/docs/customisation/command.rst new file mode 100644 index 000000000..b0535c67a --- /dev/null +++ b/docs/customisation/command.rst @@ -0,0 +1,25 @@ +Commands +======== + +Commands let you add custom CLI commands to PHPBench. For example we can add a +command which prints cats to the console output. + +Create a new `Symfony Command`_ similar to the following: + +.. codeimport:: ../../examples/Extension/Command/CatsCommand.php + :language: php + +Register it with the :doc:`DI extension `: + +.. codeimport:: ../../examples/Extension/AcmeExtension.php + :language: php + :sections: all,command_di + +You can then run your command: + +.. code-block:: bash + + $ phpbench cats + 🐈🐈🐈🐈🐈🐈🐈 + +.. _Symfony Command: https://symfony.com/doc/current/console.html diff --git a/docs/customisation/executor.rst b/docs/customisation/executor.rst new file mode 100644 index 000000000..8bd6fe9bc --- /dev/null +++ b/docs/customisation/executor.rst @@ -0,0 +1,23 @@ +Executors +========= + +An executor is responsible for executing your benchmarks. It accepts an +``ExecutionContext`` and returns ``ExecutionResults``. + +PHPBench comes with two executor variations: + +- The :ref:`executor_remote` executor executes you benchmarks in a separate + process using a generated template. +- The :ref:`executor_local` executor executes you benchmarks the same process + (sharing the runtime environment of PHPBench). + +This executor will return a constant configured value for each iteration. + +.. codeimport:: ../../examples/Extension/Executor/AcmeExecutor.php + :language: php + +You can register it in your :doc:`extension ` as follows: + +.. codeimport:: ../../examples/Extension/AcmeExtension.php + :language: php + :sections: all,executor_di diff --git a/docs/customisation/extension.rst b/docs/customisation/extension.rst new file mode 100644 index 000000000..5d29146a1 --- /dev/null +++ b/docs/customisation/extension.rst @@ -0,0 +1,82 @@ +Extensions +========== + +PHPBench allows you to create your own extensions, allowing you to register new: + +- :doc:`Benchmark Executors ` +- :doc:`Progress Loggers ` +- :doc:`Commands ` +- :doc:`Reports ` + +Create a new extension package +------------------------------ + +.. note:: + + This is optional if you do not wish to distribute your extension + you can skip this step. + +Create a new project, for example: + +.. code-block:: bash + + $ composer create-project my-phpbench-extension + +Include PHPBench as a dev-depenency: + +.. code-block:: bash + + $ composer require phpbench/phpbench --dev + +Create the Dependency Injection Extension +----------------------------------------- + +First, create a dependency injection container extension: + +.. codeimport:: ../../examples/Extension/AcmeExtension.php + :language: php + :sections: all + +Then it can be registered in ``phpbench.json``: + +.. code-block:: javascript + + { + "extensions": [ + "PhpBench\\Examples\\Extension\\AcmeExtension" + ] + } + +Registering and Retrieving Services +----------------------------------- + +You can register new services which will be integrated with PHPBench via +_tags_. + +For example, to register a new :doc:`Command `: with a configuration +parameter: + +.. codeimport:: ../../examples/Extension/AcmeExtension.php + :language: php + :sections: all,command_di + +Note that: + +- The container is a PSR-11_ container. You can get any registered service + with ``$container->get(<< service ID here >>)``. +- The parameter name is prefixed with the name of the extension (``acme.``) + This will help prevent configuration conflicts. +- A "tag" is used to integrate the new command with PHPBench. + +You can activate and use your extension as follows ``phpbench.json``: + +.. code-block:: javascript + + { + "extensions": [ + "PhpBench\Examples\Extension\AcmeExtension" + ], + "acme.number_of_cats": 8 + } + +.. _PSR-11: https://www.php-fig.org/psr/psr-11/ diff --git a/docs/customisation/progress-logger.rst b/docs/customisation/progress-logger.rst new file mode 100644 index 000000000..7ffea387a --- /dev/null +++ b/docs/customisation/progress-logger.rst @@ -0,0 +1,24 @@ +Progress Loggers +================ + +Progress loggers show the real-time progress of the test suite. + +The progress logger has many methods which are called during the lifetime of +the PHPBench run. + +Create a new progress-logger class similar to the following: + +.. codeimport:: ../../examples/Extension/ProgressLogger/CatLogger.php + :language: php + +And register with your DI container: + +.. codeimport:: ../../examples/Extension/AcmeExtension.php + :language: php + :sections: all,progress_logger_di + +Run it with: + +.. code-block:: bash + + $ phpbench run --progress=cats diff --git a/docs/customisation/report.rst b/docs/customisation/report.rst new file mode 100644 index 000000000..757de25d1 --- /dev/null +++ b/docs/customisation/report.rst @@ -0,0 +1,42 @@ +Reports +======= + +Report Generator +---------------- + +The report generators are responsible for generating an object structure which +can be rendered to a result. + +Reports consist of tables, which consist of rows, which in turn consist of +cells - which are infact :ref:`Expression Language ` Nodes. + +Example Generator +----------------- + +.. codeimport:: ../../examples/Extension/Report/AcmeGenerator.php + :language: php + +And register with your DI container: + +.. codeimport:: ../../examples/Extension/AcmeExtension.php + :language: php + :sections: all,report_generator_di + +Use your new generator: + +.. code-block:: bash + + $ phpbench run --report=catordog + + Cats report + =========== + + Are cats really cats or are they dogs? + + This table will explain + +---------------+---------+ + | Candidate Cat | Is Cat? | + +---------------+---------+ + | 🐈 | Yes | + | 🐕 | No | + +---------------+---------+ diff --git a/docs/development.rst b/docs/development.rst new file mode 100644 index 000000000..145738c98 --- /dev/null +++ b/docs/development.rst @@ -0,0 +1,10 @@ +Development +=========== + +This section documents how to contribute to and extend PHPBench. + +.. toctree:: + :maxdepth: 2 + + development/debugging + diff --git a/docs/development/debugging.rst b/docs/development/debugging.rst new file mode 100644 index 000000000..c6e64074c --- /dev/null +++ b/docs/development/debugging.rst @@ -0,0 +1,25 @@ +Debugging +========= + +Remote Script Templates +----------------------- + +PHPBench renders a template to produce the benchmarking script and to gather +information about the benchmark execution environment. By default +this will be rendered in your system's temporary directory and subsequently +removed. + +This makes it difficult to inspect the rendered template. + +By adding the following configuration in your `phpbench.json` you can +render this template in a local directory and ensure that it is not deleted. + +.. code-block:: json + + { + "runner.remote_script_path": ".phpbench/script", + "runner.remote_script_remove": false + } + +Each remove will be rendered in this directory and can be executed with ``php +.phpbench/script/remote.template`` for example. diff --git a/docs/environment.rst b/docs/environment.rst new file mode 100644 index 000000000..33449ac58 --- /dev/null +++ b/docs/environment.rst @@ -0,0 +1,76 @@ +Environment +=========== + +PHPBench will try and record as much information about the current environment +as it can. This is facilitated by "environment provider" classes which +implement the ``PhpBench\Environment\ProviderInterface`` and are registered +with the ``environment_provider`` tag in the DI container. + +This information is recorded in the XML document: + +.. code-block:: xml + + + + + + + + +This information can be readily viewed with the :ref:`report_env` report and can also be +displayed when using the :ref:`table report generator `. + +GIT +--- + +**Class**: ``PhpBench\Environment\Provider\Git``. +**Available**: When PHPBench is run in the *root* directory of a GIT +repository. + +The GIT provider will provide VCS information, including the branch and +vesion (i.e. the ``commitsh``). + +PHP +--- + +**Class**: ``PhpBench\Environment\Provider\Php``. +**Available**: Always + +Provides the PHP version. + +Uname +----- + +**Class**: ``PhpBench\Environment\Provider\Uname``. +**Available**: Always + +Provides information about the operating system obtained through the +`php_uname`_ command. + +Unix Sysload +------------ + +**Class**: ``PhpBench\Environment\Provider\UnixSysload``. +**Available**: On non-windows systems. + +Provides the `CPU load`_ for the following time periods: 1 minute, 5 minutes and +15 minutes. + +Sample +------ + +**Class**: ``PhpBench\Environment\Provider\Sampler`` +**Available**: Always + +Provides sample measurements, by default it will provide mean times for +executing the following micro-benchmarks (1000 revolutions): + +- ``nothing``: An empty method. +- ``md5``: Calculation of an MD5 hash. +- ``file_rw``: File read and write. + +These measurements can help determine the relative speed of the system under +test compared to other systems. + +.. _CPU load: https://en.wikipedia.org/wiki/Load_(computing) +.. _php_uname: http://php.net/manual/en/function.php-uname.php diff --git a/docs/executors.rst b/docs/executors.rst new file mode 100644 index 000000000..93c9ae823 --- /dev/null +++ b/docs/executors.rst @@ -0,0 +1,39 @@ +Executors +========= + +Executors are the classes which perform the work and take the measurements. + +The default executor is ``remote``. + +.. _executor_remote: + +``remote`` +---------- + +Executes you benchmark in a separate process for each iteration. + +This benchmark records: + +- Time in microseconds +- Memory usage + +.. _executor_local: + +``local`` +--------- + +Executes the benchmark in the same process as PHPBench - only useful if you +have included PHPBench as a dependency in your project and it shares the same +autoloader. + +This benchmark records: + +- Time in microseconds + +.. _executor_debug: + +``debug`` +--------- + +The debug executor returns a constant set of results, and is useful for +debugging. diff --git a/docs/expression.rst b/docs/expression.rst new file mode 100644 index 000000000..2c7d99a20 --- /dev/null +++ b/docs/expression.rst @@ -0,0 +1,273 @@ +.. _expression_language: + +Expression Language +=================== + +PHPBench has a general purpose expression language which is used when writing assertions. + +.. contents:: + :depth: 3 + :local: + +.. _expr_comparators: + +Comparators +----------- + +.. csv-table:: + :header: "comparator", "description" + + "<", "Less than" + "<=", "Less than or equal" + "=", "Equal" + ">", "Greater than" + ">=", "Greater than or equal" + +Examples: + +.. literalinclude:: ../examples/Expression/comparison_1 + +.. _expr_logical_operators: + +Logical Operators +----------------- + +.. csv-table:: + :header: "comparator", "description" + + "and", "Logical and" + "or", "Logical or" + +Examples: + +.. literalinclude:: ../examples/Expression/logical_operators_1 + +.. _expr_string: + +String Operators +----------------- + +.. csv-table:: + :header: "comparator", "description" + + "``~``", "Concatenate" + +Examples: + +.. literalinclude:: ../examples/Expression/string_operators_1 + +.. _expr_arithmetic: + +Arithmetic +---------- + +.. csv-table:: + :header: "comparator", "description" + + "``+``", "Addition" + "``-``", "Subtraction" + "``/``", "Divide" + "``*``", "Multiply" + +Examples: + +.. literalinclude:: ../examples/Expression/arithmetic_1 + +.. _expr_time_units: + +Time Units +---------- + +The base unit for time in PHPBench is microseconds. By specifying a timeunit +you can represent other units - internally the unit will be converted to +microseconds: + +.. csv-table:: + :header: "unit", "description" + + "microsecond", "1,000,000th of a second" + "millisecond", "1,000th of a second" + "second", "1 second" + "minute", "60 seconds" + "hour", "3,600 seconds" + "day", "86,400 seconds" + "time", "Automatic" + +Examples: + +.. literalinclude:: ../examples/Expression/time_unit_1 + +.. _expr_memory_units: + +Memory Units: +------------- + +.. csv-table:: + :header: "unit", "description" + + "byte", "1 byte" + "kilobyte", "1,000 bytes" + "megabyte", "1,000,000 bytes" + "gigabyte", "1,000,000,000 bytes" + "memory", "Automatic" + +For example: + +.. literalinclude:: ../examples/Expression/memory_unit_1 + +.. _expr_display_as: + +Display As +---------- + +Parameterized values (e.g. `variant.time.avg`) are always provided the base +unit. You can use ``as`` to display these values in a specified unit: + +.. literalinclude:: ../examples/Expression/time_unit_2 + +This will force the unit conversion to happen only when displaying the +evaluated expression. + +You can also specify the precision: + +.. literalinclude:: ../examples/Expression/time_unit_3 + +Will ensure that the result is displayed with the given number of digits +after the decimal point. + +.. _expr_tolerance: + +Tolerance +--------- + +In practice two runs will rarely return exactly the same result. To allow a +tolerable variance you can specify a tolerance as follows: + +.. literalinclude:: ../examples/Expression/tolerance_1 + +With the above values within 8 to 12 milliseconds will be tolerated. + +You can also specify a percentage value: + +.. literalinclude:: ../examples/Expression/tolerance_2 + +.. _expr_functions: + +Functions +--------- + +.. _expr_func_max: + +max +~~~ + +Return the max value in a set of values: + +.. literalinclude:: ../examples/Expression/func_max + +.. _expr_func_mean: + +mean +~~~~ + +Return the mean (i.e. average) value in a set of values: + +.. literalinclude:: ../examples/Expression/func_mean + +.. _expr_func_min: + +min +~~~ + +Return the min value in a set of values: + +.. literalinclude:: ../examples/Expression/func_min + +.. _expr_func_mode: + +mode +~~~~ + +Return the `KDE mode`_ value in a set of values: + +.. literalinclude:: ../examples/Expression/func_mode + +In PHPBench the mode is generally the best predictor. + +.. _expr_percent_diff: + +percent_diff +~~~~~~~~~~~~ + +Return the percentage difference between two values + +.. literalinclude:: ../examples/Expression/func_percent_diff + +.. _expr_stddev: + +stdev +~~~~~ + +Return the `standard deviation`_ for a set of values: + +.. literalinclude:: ../examples/Expression/func_stdev + +.. _expr_rstddev: + +rstdev +~~~~~~ + +Return the `relative standard deviation`_ for a set of values: + +.. literalinclude:: ../examples/Expression/func_rstdev + +.. _expr_variance: + +variance +~~~~~~~~ + +Return the `variance`_ for a set of values: + +.. literalinclude:: ../examples/Expression/func_variance + +format +~~~~~~ + +Format values as a string - uses `sprintf`_: + +.. literalinclude:: ../examples/Expression/func_format + +join +~~~~ + +Join values with a delimiter + +.. literalinclude:: ../examples/Expression/join_1 + +first +~~~~~ + +Return the first element in an array, or throw an evaluation error. + +.. literalinclude:: ../examples/Expression/first_1 + +coalesce +~~~~~~~~ + +Return the first non-null element from the given arguments: + +.. literalinclude:: ../examples/Expression/coalesce_1 + +display_as_time +~~~~~~~~~~~~~~~ + +Similar to :ref:`expr_display_as` but also allows specification of the "mode" + +.. literalinclude:: ../examples/Expression/display_as_time_1 + +.. _KDE mode: https://en.wikipedia.org/wiki/Kernel_density_estimation +.. _standard deviation: https://en.wikipedia.org/wiki/Standard_deviation +.. _variance: https://en.wikipedia.org/wiki/Variance +.. _relative standard deviation: https://en.wikipedia.org/wiki/Coefficient_of_variation +.. _sprintf: https://www.php.net/manual/en/function.sprintf.php + diff --git a/docs/extensions.rst b/docs/extensions.rst new file mode 100644 index 000000000..503b0af87 --- /dev/null +++ b/docs/extensions.rst @@ -0,0 +1,20 @@ +Extensions +========== + +PHPBench can be customized with extensions. See :doc:`customisation` for +details on creating your own extension. + +Built-in Extensions +------------------- + +.. toctree:: + :maxdepth: 1 + + extensions/xdebug + +Community Extensions +-------------------- + +Make a pull request on Github_ to add your extension here! + +.. _Github: https://github.com/phpbench/phpbench diff --git a/docs/extensions/xdebug.rst b/docs/extensions/xdebug.rst new file mode 100644 index 000000000..6d5f9a4de --- /dev/null +++ b/docs/extensions/xdebug.rst @@ -0,0 +1,45 @@ +Xdebug +====== + +The Xdebug extension allows you to easily profile your code. + +Installation +------------ + +The Xdebug extension is enabled by default. + +Generate a profile (cachegrind) +------------------------------- + +The profile command is very similar to the run command: + +.. code-block:: bash + + $ phpbench xdebug:profile examples/HashBench.php --progress=none + + 3 profile(s) generated: + + profile/_HashingBenchmark::benchMd5.P0.cachegrind + profile/_HashingBenchmark::benchSha1.P0.cachegrind + profile/_HashingBenchmark::benchSha256.P0.cachegrind + +A single profile is generated for each subject in the benchmark and placed in +the directory ``profile`` by default. + +The generated profiles can be viewed with a cachegrind viewer such as +`kcachegrind`_ (linux) or `webgrind`_ (web based). + +.. image:: ../images/profile.png + +Automatically launch a GUI +-------------------------- + +It is possible to automatically launch a GUI for each of the profiles using +the ``--gui`` option. + +By default PHPBench will attempt to locate the ``kcachegrind`` executable. If +you do not have ``kcachegrind`` you can specify a different executable using +the ``--gui-bin`` option. + +.. _kcachegrind: http://kcachegrind.sourceforge.net/html/Home.html +.. _webgrind: https://github.com/jokkedk/webgrind diff --git a/docs/faq.rst b/docs/faq.rst new file mode 100644 index 000000000..15d516ad3 --- /dev/null +++ b/docs/faq.rst @@ -0,0 +1,47 @@ +FAQ +=== + +PHPBench is slow on Windows +--------------------------- + +Process spawning on Windows is more expensive than on Linux/MacOS, PHPBench spawns +many processes. Actual benchmarking time however is not affected. + +PHPBench's output looks corrupted on Windows +-------------------------------------------- + +PHPBench makes use of ansi escape sequences in most of its progress loggers. +The default Windows console does not support these sequences, so the output +can look corrupted. + +You can mitigate this by using the `plain` logger, which does not issue any +of these escape sequences. + +Why do `setUp` and `tearDown` methods not automatically get called? +---------------------------------------------------------------------- + +PHPBench supports the annotations ``BeforeMethods`` and ``AfterMethods`` which +can be placed at the class level and/or the method level. These methods are +plural. If we were to automatically add ``setUp`` to the chain then the +annotation would read one thing, but the benchmark would do another (i.e. +execute the method indicated by the annotation and the "magic" setUp method). + +If you want to support ``setUp`` and ``tearDown`` you can create a simple base +class such as: + +.. code-block:: php + + /** + * @BeforeMethods({"setUp"}) + * @AfterMethods({"tearDown"}) + */ + abstract class BenchmarkCase + { + public function setUp() + { + } + + public function tearDown() + { + } + } diff --git a/docs/images/baseline.png b/docs/images/baseline.png new file mode 100644 index 0000000000000000000000000000000000000000..22612904a97833563c5e99b32e9a047be71efdca GIT binary patch literal 111753 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 z

jz271+`C9)ujLt@`sTODvjb{6=O!^j#^HzLE_%kY(Yk`n5rn99=@-%rdObA?Lh z`vs91fkD`^s0q2kQN`58CJvCW5X}KA~k2QlYEZYq;|yQ2PDN7TJWBgI*8MPb!>6rvI!N^tm>#E8kKC&xc=y z?=R9T;!_E%cj0M21gjsXH7^vXC@^t-A<+XfgjM=f2sacvZ8%*>@aEpq^AJ(IK;T2_ zjE;cTwQJ9=eAnR12I8Ym$B!564kMbw-QvRS4w!Oe$7Y4Ok34Suf?WY_X^D@@7d7%Y#aa3&Ir;bxaeQyMs>|XHkaKs{!^ugwZK=0`8W52AvMx9kgrwyxLA^ z@o)eF*jtDuLVHyH z-iY~3ZDb;-77!3llOw0NuOnjgb)Mbdjg1*ts{U#&O?9+gaN`Wt0#GO8Wp<)uj}p5dyzvSnb*K( zW=-tiQHCfUrG~l<5~kzFkK1^Hj91iW?+DtiHQjg>H7K;!^80IXJKXS z#mUFm>6;b>Z2!JGI48auz%gwtmvWoF1GiPl5_D1v3kxW^|1b95Jgn!vZU425$y_2s zQ9_EeB4nr}G*KFf5TU_Xkw~#5GDn4I(4di|LM0(n8PX(8=2R$4rAfo@^6uX|D7KA-pd8qV{)&a2p5G$uUU)4%0ok&XbPG{id}R5?AoAIQc) zM_va-Q&!uo(m(Wa_n6mhpR1wgcSKe__!~d;cL7PI%Osu*K9U3JJDjE&Fi+cCX4tS{ zdG(9*<;2caG!IUd1t;j%ZU6iCYpQ>ADQ=y(PxA#1g(5AICWkBGg=HRX{w6KDIGZuRmkK=bStjAc9}Z`9aC7V zdr{Fi^{sYkp~AT)-NZ^E$2U$@=)K@uJoVeIp7#k1h6!7<*l5TaBm!gB z)MmRZiNeGGoG8Hsg-ULnS`!p$r>YhA*UQx_De4f9rFGcx1 z9$|L&XL7vrTHKAuPyD@8Jfl;ms=Ag_Lix3hv7D zV&1!%w33sP^XA$*eZMp-Oc7}QckN1L%}I@ADd}4n2*wxn{CQz5)8`{&Yc0a35;*ub zLnI`!43ThFW|^OG-|Xdb*y`h3;tcEjlCB?rnW{HgB4kCe*QOH`=ppG(lCx=|=|k*o zZL2%06Z~wC|)kJCZU-y;CYfx==|BUG+V+VwR^>$N$(EZ&tDwt z(`Mp5TRXr={^+4Y%>b_JUaq!iGFg$iMX7enuxY#1w!Yol(0KFaO^)-PJ?(UjFV+Fks7u{W|ip0uvx_wpQ^IE^UF4!nHQPi`>l_O zb(WXXrbnS^6&=&{`$@k$qss+d`Cs9KV+R2Zx7(Psz_<(DEN*fLgl$LjtZ zRn^@=U5rY{8byq*{FfGB>{$LSzJMYJv__0LPjAeRS$uxlvFC5+?w>2 zyzRZR(+3S!dQnw{LBY9!VlZN3+o&m~j<6uOl>&-VleW0IZ~xqoaxy6RBRw^#N!a30 zV?TOMY;PGGYTK^+R)Z3nJ+CO|-I9yEwL_VC-q98J{0kJv;##Z+2$E^V821!_!Qkto=EN2wEMS)v2T$%e$YGG;PBQE-xDeN^WioyK$q5 zk5bjXY@Ns7pMBFMetdd1VyN(ShL)9;GeZXt#-H!Xsf}~@TbO=vED3n35qtWeQBv|H zYCDxxm+bc(c4&U~pnXzi&y&?tSgE3=|eHADrOmv13Bz!auM*23TAT z80PmzX;rm-?BMHSx{z0Xcbd*so6lv{b$#&win-h;B+V>q_G7=q>(?(Ock0F6q;j^m zvumN^#NJwOsBgshJu$a}bkuiAw9aT-QEqaeMwZoZ&Ruo`Vc@&M_P1HiRaf!1B+gT7 z#j2i?&|B0`NDzN8>-cl#-~VQSAIBWimeGj;8KIT|`7aGRmbK6JJf@`7 z{%UIT0pq~wLz`~&P0RCYGdPtLDfLs6eV0_mcHm9~h|t|XGdh22%$t{4Rb>mBgw-uJ zUSt#Nud@i|jj?0KC@^uZPf9o?y3u-c?GC9hsRHVAUCu_=gKHl3t@iT_1r1z<))G+N;@mgdgbL@Uw&rh+6 zy}IP$%f$m&Exab-Y)&Ph+#}BwTz}HAV?o-2x&mCIP9p{fcS?-m- zWZi;3-*?WQ>HpWJG3Th6cvW_aJ!EX4Ias+d43rIsS71cA@Cz3{qL>YdVyR0=`{&1p z4pVD?dzG-iz3}0~OJEUV-XG~xo%PqOS;HL=oI-AF@xbiWObU~!d@L#@aZ|Z``SQ~c zQ|S|tG4>5!{YK=uk0r&Kg=P+~@su^Wc&DHztwBj)L~yPrbYC$|CC-#qfc*@kU>8m-?P8vv*}u6Stj>} zz7SuA3p2K9C0EA9ICqxqW|zqMfyS1W3W=Xi+vk|b59Wb0g1rL?S6az z^w{8h3`q*v%<$~lv%7cqE$$WG&mz2C?ehA1!&+C7;G8qx${|lR*vFuybHHhQ%(n^V zE$3tFnm=~<3;P_XQrPT5&lrB@93J9@MMXo8JP2Ms_5uHbQi~(pP(=MP$ZUEQ&h~o) z3Vggoq0FE_@lm&u^?9*ua&H;d*KhPm4+&XKxo2|8KzOY5LB#_U2!#{}Z{NOUQIPzf z)(%(~k)XJpg7NL};n`O*H*VO_gmtz-z|`n~qII^muXv?6GO+aOmRrlIC)xgWlu}iZ zxjfJhA3pS!!BmTg-lyia!ZGW!EKh(ny$s_))j*9r*3*p+n^MxERg6UmY$Cv=>=no# zP}9Ps93Nm=oFqhK2=jRdeq!x?#?KF&=C*r#z~<@~7x!mmXtX@FbnDTh2YHhI>>Vgk z%&N3Us8i+`YU%JlQl~8x;qxu37`0l&`=rRS(s}05WP5=1*=&}qX)hxG(~$c-&lMKG z1h{VL*`r6Gnv>VEKtKk zogV0AZ}Zb)>qbbQcx1lt<<_i>?2*u9J0Rw8Y@D&8WEOU#~qAFn=KcR_N1CnqQ z>V1KM!Y?h9lf!o(F8uz<*xKIf;6VV2Mi2)Bef_kp@4Cv|#|_DU7tu&BVPPM5CIX?_`oqD*B%h0G8rglzrcHro&)TkD zJ$j%>)r%5J$Hf6tbuSGqL@SWp|5ik|aRODe<5_CAUj1Ke9>>e?z`Y26P~pG9<6yo{ zrvda7Z<#FY`BFJZ)AAkm=0c#bLhahMYfqm&1GDIC{W7^^l8zePHxAdrRwPhzpj1qa z0|TW_Jkn<&YJ1b0bxLj=0Db?7_)qE1ox4wSbV$7s7BdrI!Q68Q%6u%*-o2Nl?bt*C zzj9@ewrmPODZx_W*w_QcCb{+9_jdk8{QmK^__WUDjk>W@L}c)NI=NCN3lC?W7s-5* zqT+}8mIhVdGiQd1i5WjDv9Fc%!rork=(VSaP28N~(vlK>k!_TUpPyvk%oudjY3o*2 zPyxflv#=pF`Nlzjbd(JEQ2xq(F#o%D?P@UO6f8?|j4)^h+r&+&^+b}r*X8w=JQJ>^ z#GHM+48hzKmliTQQM|Ig#d$~o+;>91deK3m2Z%}K^eCK6{$(fL)>2Y|IuNqqhv3SO)a#=QqskPQq8K(`q%m(X#ULIkRWm z?umUEA7%bA$?}wk`MHDiDQqBOk%WxrNk9WDE2{us5EW`7lU)rN*%?NI_c%p^wfnL+ zNidtw%nY8h+uYcAjf2CJ%KA|NxR|Hpv=?DFhK-H~D|zVoh^07~gAlwO0Von;9yO+) z1nu)2JWPKtPy`UmH1LA$VQSuvd&A`B@U#U&)hY8aHhd9!ML zz?SLLr#Duz(e|2Oc9vdut-l6AUu#~oJ zCdTlDDtJw8*+H4g(0HtuFCry5eBCJ2JX>zJJj~48pHnHkepn+=?@}Z>Y$}gcOUa-> zFB7mV&yygq^63YM50sK(9j>rInRrV*Uj5|@sMokDQ|iHhA%p6^_1|&MTyNpRgXw{` zd;G<~pLcN%NQwOULl54=K!0UV2LOHb5zJag;Vj78Um%5Cp8MoxP1X0Au&(y()0`) zI8d3b0swLRiF4=AlPxx^TL*WzjU-T4_-N#l*Q)38DQF@iBG?iLZRmu2VmP(BPrm)U zj;yf*1c%(EOP5lOBh*;GX3f11-!TlN5gaKky)w|^s&Jy8eKB1u>x0+bEHyP(UMK+Z zpVOxcc7|}w1i_VAD@3ReL1rKWhtq2Y=bSip%qn5ceAP4U!g7x;n+6RYd|&x!-X5zz zx`aH0Q95gVRdjVP1bunG!pHsKmg5ca)!UR6lmwI5?a?eVQ!>laiGd^b1Mu z+pphb0w7uQT+b!jr>Lr?v;WD>ZMKxS*T}wVlY}3jsah>78lp$JFxJ!5<1cbX)G$nD z8xC@hU%vKPDcBWVaZ<)(i(mo|ZvXs6S0jBtUtbn`xI?b2V$JHJ#tG+;PVC!v7lblK z<7>#3Ppco>{aO|@_%AQf=20MSUyI`3Ad)$9_;8g!ujjbrpP}eNrtI$8#WsVRq8~3g z!AHmbTe7dezh1mHZs`+7t%vk+wps)>h36w+*YM@2_j(>iL<_Pws zDEAc{yU1Ns1yk5qW_^}-LF1s^zl)}n(TMSKe{_kmQj>JF6n^V2^R2aew4LCDl2)4# zTY&BnN3|}pv(cSvLxf8qZXoIQJ1(#yKFu_m^na+s9VGv%XjNR1cr5GP4` zwE!+T{G7v{h&#$h>4&6yb=gZtG{v~t&Aldl$doBlsvWqEL-U4e21P`yV@)2Qv%}Xz zv!%aro1kO26hsYjyjs%#_xrT%ZKb_$$NTygXi%IHvABxmMXg-<`Zq=?EN%1A2{(&m z|6?BSLtKk1mjng$`5R5ITT8_mv~j%uo8nzUu2Xle;?^m&nc()y4G6#Ss-#3%>4nbi zPgT`PN=nZkKYqoE0%}{+?J5AYT%IwWI&xykE0b^Ec3;1KTzhe8=}$83*WjLgC9iV& zv>p>ySoY=j%2ii4i@Kw?4Dtqw64CZ}N>M%%cC7gbl=AY7H{F~EwLyx;W3rd9Ls?w> zqLGmM*gBLQnZ^!Rb}-Rd)O`Jl2{!mS&V^(g< z#d3R)mzNi@@(a%e=yCu4{hV45x$^Sz*wYJWtuXcSf73zkJa>ECVd+49H-$~;4@uAL zd5udrzf9I=BXu=LM4;WS*?4C+#m9^^7(LM7!i-#UR(AH`fpv6>K$~O%oa6c&&>Aif z7_foe&Q}f|GUPQ6i%){A(6Ha&6mku2uvr}nV%{*$=l%S)({NHQAAazD_|4Fn{`vOP zHoOhmKOtbegExf{ORfU^U8&v2k8MI^F&A5nriNX*_A_U4SZ&p&n%a&5pynE! z?mtM_u}s+^$m1aTfD>UDiQ%TP&tDSIJCq6hep5|3pr|$KVU9+QP(U)VY5n>}3cWJ5 zE+KPKI8r>%k@3u_95HOzAT4?b+eyoHb-kS*gDra=Jjnmbd6+PLdJ%93q&?r|4U2~1 znXa5VjaPiIXzX0IQ+82+;xV84697>CX^OB7+9;`2c$UA?hOKV-Fc}+4EL4=EIYFoi z#i%odZC;Yfqg7PuAjWL=ZLx`YWdr(@Vb4D+o*EOPqp)9?0aN7rhfeQFhs373;w>XP#*`WwG1o8#i27+d|T_ zuX9(2qJxMA%aa;3=&tfndzUMxMm|OINQxv#M6}dXHn26P7@`@%RG{Nur!Sv*RYy)i zUS3&Q*%n0{>5M(*Q-PN$-Pq=Qv!|@d#332GejFYc8Xqq_9~xqS3l~<(P)R8$Dw;G& z6ee6SPgLvHG}7SW>}C3_!Z`F=p>WB{m5cl6UrQECP37ncZ=<2gCv+h^RRtPKf(j5j zJpgun53ZLAF}ENpb^4Ff$B*}yYIJ911QiL_O-l#EIG*J|35j)fcF{2=##sf1Jdh2N z<0!Dn%)GXS(&&J=%a@0FVL{q+^Oi1A$G+CoRCgav3XpI=jO6jpz8re#eVUrn1p=DV zs&6JRSxUgd$So&1T7^#^P|N2ZnZv$AXAPq{bJDp_XhMz0wz#rONK5<_dl?8rlgEt7 zq{7|3`6zrinC5az%g>)bi)nT5dY2B_?yGK7Ve)k*AfK)v5fanwbo%r;Lf@T#lHG&$ zb7soihuX0|OD&YJXc2d1z2ql8Jtzt9nEq*|gY!Dcdy(fzp~$o zCFPIpRL@V@nsfK&!2)T5R~ltHVQ!e$l9s(*h_l$Ri}Xfem-9YNmit6Jefm_O-&ip@ zYt~N|nGpTvZ5zycof=ZBYWiZbKLsCQINQTH_gb7@RQSa;V1j)!*)DdxF>=JL9UFfb z_U$Lp#VFS7wb5LMNtCxXmODIJDu#Ml!%VMu7UmFh@voP?h%-xCC4{Qeo^OGwuy5wX z6*rB3e)A_}{@Hg-d1n#z_3c91nf$IN?01Vk85KCtk+nQt)+j}WRv)9mUnjX>S6TSO z9Fr1CQ-_-~PYI7U($QeiBGjAMFe;Fb*?~+(d_FcHIyUI^F ze8r8c?eZSjowMK5C?H+r$Vww)@fI&pd#*;3)x~k~QMDCG0ndirtX!?<8RyCo4vpWw z%PHpiMJ+?aZ)iRGW(xY?+%Wu6&00tG>MI!%v&EQa73Df}`zJkiL(xW&L17JVkw$X1 z*NnpR8X=aD7dTmj=!sUT1t_=1^(~D;(w+qGL!f6H!Pn9o@vNKVSXfxN@I%O28K|8| zztkQ@bj`#H(QXzgInX9rkI{_&0G>Rj0V6<9Mtoac~c8(c=Et`q-tyou`u zYwNph@p?n@KS}C~MxQQc%x`tKMEdEm z0mylDV5{k?t+gLN-d8q2ZJU=i2VPi5Zq3hq{YOlTyLq#jE2CGLrALwYqp7Kr(gFr& zne|?tjh8iX;Jm8Cb)DJ4l=1R&<_IP365bpcJu(j3-gBUrwZerz7F?QP^)BMT z!Gq#jta|l(G9>0){B_%Rx6%H}4B#O{& z@hbMX{={`QHuMxikpRvy+S3i0($&G1-|vQg=~z-%pQ$iZzbxO13qV$fOJ?s>IT-+4 z285O--y&}5zS%vxC@icDSCW@MS-kj*+Xx;DX{ltV6?vCcEn%liB_$<=9FBeQ?K;!k z*~@yr1ZdnA;))yJvj;|WNNi0 zKUc;xYRBtMEny%PU#hAOX@`=C^$*^1Zzm@_ethVqzkS>6LWR+zh4~eLKn@~^&E>1U zkC*)TkmyMGko7cGx3yy!fwC3>0Dvozrcb|qd%nLvz(>;2In;2cM@9Cu2CXVPTZw>{ zrIwb%hYwe{(W~SF7#9k^<@J?@79JB671u5wom6w!-ePfV@r8Z6`!}YXgnCOktG*`{ zEi}hVaijR<+sFy)w)D9Yx{#JnmqSg_t>sYzgaZVVm;mv@WPcsPC%^lcj~{c$`Fi@~i37DA=Z3w7Hanh-_2=ZyJHLVYf^95%`ud8?uGEkMzOKTb z*(}Q~t!%CL?$q6`t_TV7m|g>r$8^cX5trlQkaHaTb;^oP3isjZjvb>{gL@Pojo9H$ zvy5{uvIdHnV@HqL#yY9i+srS-Z5$1cn70jex*3zXTJQL^?WP+*5u@b)OUZp@H%>60 z1~SSk1< z02E-%S3!Sf!(xr_4pmex+Ka&Dh_>uL&8wi0Bp50$h17#(Wh=b3ffvYkk@qt!Opq|z zX6KDq0Ht;P<3H)q%**tP$%+WzT)`?Fu7v9*HHcxbC*1WT58l&3k3H_rMMtFjl><0! znmHr{y%|6R#g6r40aXN>BjwT7Eto*Tq4}iH=Xnnr%+3!dG;E_SJi&%eo(_4nB6xHacy83z!%><3~94Z(>xvs_lT7$;fKPBY`Nhvsy_ZD-#7Aj1b(Y5mumHV2wJ{H8OYzb9WHJdnuhfI{8xcqutc(`_X!c?Sh zsbYl`01J*v4w8_l`10i^S1G$wamI{S4k4pR^f;qNzHmb6 zi+NA_u6fy$I*730r!9*vLe0HxCoK<+6Tj5>H#%)dh@J zza8k2n%sZ*uxIz~ESpYCNg+{VNPok=P(b`SIGTfmv-VTqDM*a^ovoOPQ`!%TmR5Lwj|C>oherjf@7?I9>Lc zzs)REkbG4<0dJv00X_BcI5WX#yrQY{+E8Ou7c-dsyi{A z7LaO^5b^PiUY3HQYjqcGZ*3J$j%C{A+hO^oCte?hDPWn}=G@R8QgU;}3&4j*JU3ZH zC4bB1+e$BMJv}{egWUwNJ9qA&j-vr%v~ZEPl=*n|*aGBh(^CJdeC;|giXdOJ9WLl3 zre)dwU(44XRo16`-S>P-*u98*8jlJJd>6a<1_q+M6PCe^myTvcJt9IHORp#I;r z`p9`P$R1a)0VXlA9`L8Qwnxl^%6hv2UZRzI-tTM>6oD7i5~;Z;jbJX1#K#K|5`-L* zIkt2D$qFz^Ov{Fxz)Z&hgj-h+pe2{E_-ir)?Dj9u{bjm&I5HV&*ajj zOI`Q9WtNHeCP-gVT-T(h(oD^qElb!P>v~O##sp>>5?e9&TJR}%Lo-=Y%9t_p4lGGx zw!$*4h{R5!T0<9rStjgpOW6ut;@umlICOw0m0~r5jr|rz{y6J=pppy5?Uxuj6vD)R z(gnBLj<|RAaeo44%F4b6T|Bc);@p^s52yQF_X(?8_Mv!we|7h^sIahk4qt3by`A@& zq+IJ)Ds8uVwb8Se6Sm(J@U~cse*O9dUbp~UsK#niVlHLGv-|gLxU*&*vnF*EYBqT! zENx?%kd_W`+9Cr37?mIb>$?l9OSaBWmW?dj`25bCULPclFB@(fi@?8)BCoQt65L|` zqCczA-J@m<4V7(Cay~3w{%iNZEA}h^2!cLxcau{!dvxjY_Z!oN3rruM(W%U4v;cUtNngk`JFUE9+@T_`h9q`7ojXxKSuxW?X_h0=f7`Zd5KzY9=FYtf7^RT%PIc$}WTk)J zTI}p_%i#BV0w6GYVBE@d%RRBPxaWrlA{ZUh+52I873qly=&jIo2?^g>SHnEo-}KR( z(GB!Ak&)mgspH0P?FI$?ISMt>tDe1wT>iS+VIEKKm)0PrJv$=p^7FQPDph3_o+p9A zo;=Nv>-4TmnJIP7;f!5kv|QfJM`9-)g^3UC%YG#H9hL(IoZw)_PWB(%I<-Pl|1c8K zux>Sk1ST+0_zIhti3tp(tjutiQtQ*jRB6_%-4xDw%h^r!3LUc+>m!f%)a#ZSGhu+p zfqQ~R#4X4`@YJhs5%0%l?bitimguVtM0s&(Y zFI{s*Ur?K08q_c3dwrbl*UPf`4*U9yeQ$E{(IPpq?=5coG>uwRBt9#azL>89BPuvOie+%cwVY7>&v z%1ZWzO=ocG=pN$k3-|3d+6NHb9a#PLMti)oO z>^c6vW5m2^B^hF+LSop03YY!QK9{@f72`i$XRl<4fBUYbPUG{_jNCisZ8vfVLQZ0O zZ_{(<%l8%>FmSp})x(ZqSOcmoW@Do+WhGaf2!c8qas(wOGLZ0jOzRKQqBSm+F5 zO)kfm{dZcj-?=U(`-|z%18aU{*p6|a!vh?#?bRouNB8c3x3oc&XHR?-T9u1%it*j` zC31xk{*-TIu#7> z`_81C5^kryzCLdYb{16hl`_SJn|i0vhAq^ww^xt}r(Nd$I3H8pwX3d&d2NDr<(}-P zPlZ9Yr@>%51q5C?%i5A;{gkDl4+I|NiL-`j*QTX09Tx3;AL3bi$^*GHPUM*#Xi3~E z$|DAe$qc%7{h;Yz-riXZ?&yoKIyB~7(|WqL6H>Dtc9eh=44)CzS|`-HI9mu}2ha$k z8nsx3at#Xwwx*Bs{6!0%43Ce`KoO4C3h_LZlT&qf%NAEhjZ`Cf0{}r*E+gx5ZdyLm z=`oYC;cuSkU5FH2t6LGBRasv#c`j2Kw1u80MK?@|J*D+M+LMSy|Ei{@hJrY%ZQtc< z8|QUw+IDgNvjEfoA}xy+q-BCkFQ|mFHV@b)`N9TX`|T>mO;)ue@jC zvNH08$qH{^@vJRn|4IC2HboWLLe>T^6Zt*PT8I4n3PF(Rg{tFuB4c7UzaZ~*QH@5) zfa;AY(9+v`(zInSIxQ2^5?-3@ZbCM}NEpAQGoo&~Hp-MGaumxnNK@87HCl7;CBGO2 zYvZ>!8fXTmPW82#T=M*REn~4PMLH!`1de+bHi-X(d17c_kVh&Fs9bAn%e& zET(*W#tJTelNWBU0gH-?&I|;WzlUJPO+8KXihcA1!*QKE>dKLaWsl!sWR?me^g1{9 z&vD~E(Pe90Jo)GYYP+NVQki8g&bb^Ln?_SkHys%sZV;>8Z z;@fZj;HNF4g`^@d1lUCpDiguTu-@!O_wv_K$ zH-$=yN3>J^>Vc1Ns&W5?IE) zHWbRAKsJjRG)NN~0&}N+)*MwH_&7Pan`vphrnP`Yo&z`C{P&Koz_)0^_rXPOZo-mh zI|m2FAxBWH#*~Qf1h*nu@uYP&EDlT5&uZu-rFW?j(9riEKA`-Y4hz}V<_6J*8u`|( zTQHJ(2bZy@3rV-vfKk#^Qo+HUZ7oh9@hoPWX%#0{-`|8ILwEY$$;#xd{u=77&2P^q zX3yOR+6OFdMh_M}Kh+NAY_hl`NrsC7Y>ErZljqN078WKaCVu$7v%0#P(Y7>a;p$7P zIo^Np09kznF@+B~T~+nii4$jnS0C1&2#J_?_qoJ0Qw9cifHMKci;HyMSb9P$LC!}< z@Bjd)r;uRUPyo>7PLbmA#uF(iEx&YSR!|EM z>5+OOMfoKx+qLxHgk=%9uxO5Ek1i6)8_CH+;-Ew?JmVKV#&E~~v9!!*WywVn23=G` zDzRKHF_`QNM-la{s$mX_qYm^p942*-jtg);E%s2x_jjni@IoL)>OpUu?-5=heqCiV z@q&bAT2%!qHjlwb6&n$ma2jn3i!(#gf6wiV*U0nz@QVzh=ti#tQVmIOTalc{fu8X{ zDa%p~mbJnjvtmzWkpoM&gsH$SNuDjh)0{#xC#Bn;KPK@QofrxF`|pq+m;ce@{6mo! z6=qEyoO*PJP=@=z^4USb1jaK8Sq;m^X^%m>0Q;L4aDt;GKI_bTz-30*!{Ws}_#iSW zve(Tlpu6;f%0z44JjfOBb1*^Xh8u_~_u=}zMmPZvv1?6UKM96xRZK4Ae-`@;)w6M4kEW&X*-l$ZA&UHS)sKr;*&XW*dAS_W3Mu0aFbl{dNgC8C z!VX~6wZ{T$T;A+ydN%zHfu73}oXM~A#=l7q!{5h>2lR#1#N%K9cjxTa2t7zy$TSq)8gmo35Y3 zK!&X?PF#3PFkroXSsoVvgUsHG9>nH_#x$tY%K-qlXW#u!+%oz86tf_|kNxs9Z03>E5}( zXab9uHPy9!?tJP^<-VEE<>jBfd^v6W_)1>X@MY_VX@2-WReBDCif@`LDLZ+M4YP7{cO|U z>%x#E*chget+{)nMk1&62A5ZnE!7IuIkYszyJT&|A(JINFx#4TJjP_rD*#Zv~8Kn zOhp{hyzKWcvD+1?kmvJ);wCU+vy3=`l*Y-?5gmY}S59T@A>H= zrie^x@X!5pF#nVA%WJ7gfad6C#$B1>AVMeA_mx=WLLB}*Ilfp+ILm8?$zNl^-mc&$Q~3C%t7riT4bZ9V|*gr03Dt&;UR1 zaM7Vi$=@I7Z6ag!(7uMdXMMtT%f{wKF#m)AcjD!lW)%xeQM_zIo`sT8)$KYdt#2kB zNZ97^pCo;|y$G)vrfRKc@nzVl)61@|zDtWiWU=4krm!by#TBWPex_QNb1uysZTM2h zK5ySdM&*gm7VmmHJ-z#4+3n+V9(?{gc-IJ2n|x{ZfYBm`Ld)h76;W7g9XfE}0Zm%9 z%eC|8&o^F(aqK5C{OHn6R|cENqj)Pv>f`b)h=QXWn8?;LUd7$X;0tk{)SrPD`YPnY9cSDZiM6aRsICV89)Zu0}0Zy;i&}*0y)FS-o0Q z1Zs8O><<%q{(ihK;tm%@$wQ1QDft&Ob|>_pq%SMWld3{S8hz7Jg zk~?^EotaC40zm+TRrjbWcEEn-ld9>PKD~c0o3X*eqZ4O|<;zRS$5yP313NBgI6Z=8cR_A`--Y0|_O)mN4gRWMni~?(=6fP>#G>{*bbY$}U8Lycs~; z{QP{oy`VNikI?T8+0)DV)hdc#qJOmZ#ORSt&O=4Iqk zKP;SYc<0_dSv9p~yPhx?LTjufZjjq-Dz6G!ylBxPqW{XJOJ^W#5}XN+jX+UGM7G1A zAZc7WX@h!_v_q zKmIz+CEnmdVveA4!Rw`>s_JoNJp!Ht_7Ru#HA<_UId-e=qT?=)sgnfW|H=xc+dZ;J zuH(06I*WbV8Sp;w-zwS(gB7WjJa*z)VEu?Q*gVQtygDUWU~&PCf+|8b?5 zQA_)>4S93a)QY*J`T2tUOrmEMvU0#K#6#6x`qx%JpYtgtz41K6^+ zvAM7OlxZSi<@wdCSK&swckgaHFIDJEx^!uKRIM=B5@q>7uB_kevsY^Q&XjNu*8G{N z%v?89O<=6(K-)iNiC5o&J}60X8XtJ;J@^DMLuLAO!}_*)3%3Ss=BVyhs6 zXTT^LuHov{7pSGF8L0^XkeI(t{&K~{5|HJ4e@I1o%rra^sZlSN9Kn*$>pva}n=nB^ zN&5!559VOqsgQnl8pq<|NP}|I?H(^9oHKUR1R%zwcvCMj%8k$;swGl->d~C?lt;6h zntVc=#Lnyi+p438MD+wi|0F956!s_N0(Z>eTKGRTfdU3=_;)VzvPMXBsZ83<&8_O% zNvd3%>6XiuWlatl{!52#;GvuPHPSgpukPD$iN2X`Xq#tCpBN}RP=r2kvX}L}%uEfJ zan2T`Es2ipNErHN60@)BuaX=#tR2_4zL|`uXLrY%Z2)5vIYBjLDT&ULGHEO-nx3vx zcdktczv~=%dR^ikX}ds``9tSPPqj5-P35{)N2Tx4M)qP_#wI4iyu9%f-S%D^^)$=O z`C=R@nW~=2-r)~19>~ctOl`dX^yx%gE3D6Qt`uE@%HsFnRMIflwlGGboUyM*3{5;9 z6{x-tnZfzlt}2VhI5>O>50~?@rjv0fw~FkpB)lMWX{k}Ka#fp4 z)z$%61K^xQqx6LVF?kndK!jh~i-=j1xEVW<=@i~N3OLpYGLkeZ%1FKqJA>XbAYFNH z&W*&FVP{^ruIEVs0xRr3O8x!qKf?c|1^5qogkloLw5>c_g4?Q>M&=kVPX-xk$=8#7jKuiv*s z!G^c>hq$*k&6<={#CRVav}*!zYv(bn@2?p8Heou=Pg`yDGs#;O_@8oviilD`Fw2N#DXE2CYLYuA_?0hJvk`ZV2o`aV; z@|j0W+bGhzw~VJ?HFeqIBkd2A<%$&o^HyC$uBTG#x=%B;)_dkJ%bSK=<>}Mox4Q)9 zDd;XbPo8$IvFfse?J$u;*<3zoH>@QQ@e}x|uO#~*o?H>&d zA~K{49_B3fwi)SD_i5tUWoBgbH`+*^so%Fn$G?r4Z$D9GO11 zPUUxjc*jFDQdObqRwhCQ4h7)Kt-M}@#d8?r1qQOr-jO*x5)6MGZNEaf_%bz8vTyMz zkV4dEV;7jd(0$5B5xivU$6NL&!_Rm}pBmTkQ!cuyMni>}>83KUJ4lQ2VW9|d1k(xA zZQH&RB4+<|u}D;?U<*y~=K~)w%jOiGO>32o8rzNC0&+h<9qo_3e(1_E2P$MMy!D`}gP~BkFEg}45cD~iFMsHw} zYp3^|?hH)S`Y>2;ksZ2*?D=D&Cw|$v3tu{HHT|s}XhB>oDB|h{U6pFB_>HoyFSh>0nUMN;9;G zF>7L?)9Tf$c~BfC+JW8et=p%aHMkt?*6**rEj_(+_M9>IUYtF0*svD(X~({Cb<6Il zJv`NFdal;_FA?wngaNtL6QU43<(@qgG*@fC@WA|@ihcM`Rv<+u3Z81qC#{)(TQziM zliQ}0FTUow_6oZWj$}G+Q`B3SsQ&uvUypzGa6`Y@=M|0`e#{zf_)O1sTD(c9Tu9x| zM5TyXX$A+59~Z1U35UW|bB2e9>T`!^V5 zZ<(a&vO`55Sog+rO;0 zw*KDyeQovp^QzLp0ehwue!t&-rrze+rAuP2fm%Sc+9J7y?HdpFp0D54K2_t(f>dP9 z`f{h}hF+DHqFzQUgOr`(4!tVouqn50wSiZvo~~6rF{DTJcHP9}u$B(`zPby=CtGehpb0k$a5)2mI2lvU!$k$D@t*j8u?P0nM798foPe)Eg z{E?FnrsUjtQkB573_Gjze$MJUU0QO=MI%*0%HNT#4J(7Lte>cBv2vqe`p{T|Dyq<6 z(D7=e!J>9e2CG)Bs;)t%?0J$;tmD$vELh=#$;Ee+8klX{d|ua9SHt4*EzbuAUVgd3WWP(`O!-|00q|0{#@@IwJ+R69%GJwrzDE~t zFp)AW^H4K^DjoX*NAo`|BBY2@#W+Zi)pCf~3n74QP#lJ0r`b zW4AEsMWQ*=(aQJk7N}skzTbk@FK{lXJm!hK`@R#IUZ~Sxw<|jY55Od8y|vx< zbk-7d&N7`R(svIyr1rD3S4Y4(`&1oqZ=J)TvNpO}wKvL)#n>KyWL=SE$@b-!%dgf> zJJWB|m4r7|vD@D~4jqx;s4Mn*bMLXk^US8y|NPKiTHIoAqsd^3)lT(}7?;SViVH<* z^+EOO-<+c88>kA_-=KiLj7H?}w%aSR1H*XLBWLeCdFVD1(Jjq_!3yBuz+BEx!IzsG z<(EVr8)&Sm^~yVl;PYUC`+<7(LTmSzo5~9o9NQgS`Z0CVBGrgw{yIc?PM#?gW?8>4y1sg|V5ZaTBOX07Bn8JT@Km55bJ?%($;Z!X>t zY}R~B@0cU{2jS~j)XwZ4OlZ?Xiu<6ShFQp4rLU()yIwG3DMNKQ@Fw9{3wnu2*GK1m zV-k}GAObpnR#3x`23`|nI1kJg4;w!Guy!cP#I2*HQleIkTQ!3nDx*h_A5XxBpBPQ~ zi&uNP0h9X2z-=JsUOwF{pS?b1~sRoxbhxqf~%GMc&{YPd&HrNr2~$Cw7J0Lc142Y^o>CIMdee95r<$=dlUG|U>tt??*XI> zZi~OzIFZ&X25i~ zjSk9qXdc0@y{!!;O4a-KbS9_4CXj$JD{cC6P68D+PzgF&c&T?lMbwqFAy(K}?5ad+ zd>czHf+}2+P_y$VIh1;ah8bJmX~A7!R%_1rmne9I-t4ywlSi`r-E%C* ze^6jiYxAu6E`D<^tcjFtT5)Qv#XSP)x<@5NBOf^q=-oSo9vd>6%H32UorliJfE_PT zzXENYm)B?G(CfGKBix#_lUt&V?Hn7hZA8?Ci3nikxwB{Gewk#VWsN+%_=0`T>7U8S z`V7x1%P1?qfLeL@aKX6K5s{Hddw24=?3<3OhD7DhFdL&i?liJ@XU6DFTW%R{4+{IJ6R1i}k~|JAkFtiZkN*H)J+y0qjUYHr@?nljNc=}?E2 zecHI-ZNtMCOf*xRG6lrVi4#NC{>IE+rm|ormSscQ(F<0w_r(xP_}beFO9ZmKVTwmD zfIClX$>cYq|IjRu(a0SaHuaB+j+Nhk{9vH1X8NGt;HG9`^S8=%iLovSVs3XUk`?nN zU&B>-bhK>fK6Uixlfky9mjvu}JZPWr?R&vJ&z--2&YcH8n?vpqvfsQ3KEzEAc6sZF zFDH5=Y@`=2?%#Fz!0nGY(DI8kQ}%}ndVMTYw*7UNs|@XS{pL+cMZ@3UsX+WzQ>8IK zt`Z1?>+%~#OIRw{hk>?@5kaFhuG^io?5wP6?xEYy#z~pVw7H46B_=2P=|xbm3rfS2 z8o$5akGs1YLhula7Vfy|(xp4$rUBgF;E*wh=QC|sm)F*Cl)-x%mw{VxU^u>XxVV>J z+f+KOrR#^^-{$|>|EHmX5aTXP>Wt*LtMtvM0(bcqz&@-G=wLFTP1a1qk?%Fyu*V>j}9I@c;vyF2R7W@U@Q~T(snb-;*GmOJxnU`;MtQW zv4Qmk3-q2{4=#H`>KDjaI8-O%ouebOVIGWr;TLT0zN^rGJtB0BF(h_N()2N+e!ycm z*??IIZ(Gg*{e9r@$jH3h%zO8MHkYXj_IZGF*Pobp@d4H}foHWXRla%Kq`^XH2g zY~p5QjolW~4ze?X2!cqT?p?{Xa08%fwC(D<8gxo4qmTIx-=e{4F`vf z8z-K4G)6Ys)N;j&jhiXW)<_SWXX0<-tia}mAn@(uEHr38Q@EWO1kvM zH{$#BO(o&4)sw%cWTlsx*;l4mSRYRbw=dWhxA5E6FIE;V9X~pc|K?3U<4+=)sVvd| z&#=}%f0$6OND5$I%zBq6i>PgN2t219T@0$#B~uxqn{_3tZ{U33V||uujF2wc#kuz9)ala&S|U>! zB{zbluM0rhU;;33c;K7S(W;oK%yVD6Dsr?e^H?POmvTla!I9I9?ZetXuB;7Yf}1xh zOmGhvB?QnZeYuL~h6hPE<;A@?HCCV403N8Qe--BzLLJ9Txo_C(FN0&G1KY#b&r(vA z&@YL4P$IwR_|J{3lHyad>gXkeSmW&FNwx_L@ixvDpg@P@u+JhW&2@EjR!a(ML|LCh z0nU}4}Tb&mP0Cf||KzWQZh$9K=!b#VDN`)_T@F+pzZ4kL4K z=;mW^6;%F1+H~*mze7T&tz8M(Xk)V?!@Zmrd2z*RZSC%v%6e;8BmBrwehRo2a&sGU z_P&`L%Wf`7@3!p?jFx<+a>YR_MFFN|AT(1MX@GHA?S~JU$_5J-^aQ2->}o$|-R0z9 z3IDWEN;ZdZhx2Rg%ym#d^16PFLxD^C=|}ijCnOMoWrwQ(UgvCzpK*pQ=`y z52HW|&>PXC>mR#EnDn{a?~#c0&k(Yx-}6Q!f~!YeE}Z>2sWoNUrj%HpYqNhiMwdk8 zKiq$~XVET;-IeiX{l5QmzNUt@c3gv1`&?B8?Z%9SX<#1Q1jFFe^S9{12}Ms_dR;}| zn?SilUx_#W7iv4?T^#9()59bs1>XrOU5HKo&TP;eZSDJvhe_nS{V25fx^?yrb)wMP z2WGOd%>glkHcl@?)+%VUc=I%+!$yqY%C`r5bWqcjaApeA8mWV+ve2jsKZ#@`lxQ3% z3?$+}DShkc`TAzUQc#kzMxh?yj33Tu+P1A*qXHFGregE%^oc49{>z7E3;306URRm5 zvVaGwgs~Uzj6;VWKIU-#TS=MW?CE-}eALZ!yXz>ozyIKLpBQEFp~PEWO%1OA)Hew6 z$g|_ejl)g|^^o9gF10+S<}9Xy2=u0@sNnpwb)Mb%SLd24KG+lobCnirGfQS_kfmPx znTfe|EvvR46nV4hYn2X#haJiS&`MI@xSZ&mV1v`!Iw$D!XxwiR< z)rgyC)*tZdFNLp&G=&(czRm+PXGUG0(W^k@PFsBYbC`p&|8PA!M+-_V2@tbD4)A_?aJq$30hBg)wx)I;RY~6vJ-_(-^{^-2LpBV z$4B$d-am*bv9p(_`0m(lvTTpj9&OoFtJf7M%Tm8{w5n6hTT7Y{W=@VQ+7~AUA<~l-JK%x z(&9iU;FB0e+1XX&l?`4;{DykA{c#v}Ac=pbpw^4XBaSeoJY%Ndx3~=#%u#z9mI&&t zgB;v;au#I_F_AyM3H%3F%s_^R+b*V5o?35(&I`f->!uVu%MbJx6#m+P3^6OyLK)aw zpsfuCek(4{K*8_2ePMP{(e$ZPlh6$eS<}Il<2Pi2Zw_tAe%tey((7q6BImPZo3r!B z`W9^PppEs;EYN09oDcHs@nfTE`Ld>Jd<-^jbR%HUwH_Zf1;4K_6m8TIvzQj(lFNyC zuNnV@&dc)p#_FmnYw#fBeykJWpE&JF`VPIsPp_mmJy^FsDL}t)fk>MFJeY*M z9oHAWe)Z}m?W5jl?fCNS`}5%ZqQ0A(ruA$8_kh$(;_F8- zfYbPy#n7@Sy2PcWl}=}A%jS|l!P**O3O(ww#@9_H^GH)SEnfT_Z9ao7#am3+KSU{2 zz%xYPIMJ~B-Md^)5D!G~HSDN(k)4hIl;QUf$c8mPpOq%rF|4+PBT@fA-%LVU`ZgMW zq=~5K*h#bJ`-7q6x$|fgn8JlMuPARVi*H9>jE@0|LSMIvhh(sJG;+MK3#cv0?S6~j zauBSTRA;e-te+k~LLA2y&>TDsaW3bG{k4BTMD1BWzaO~^M^TQQIK=r9?9ibfASg|~ z&nBpB2E{sib_R(PZLWH-2B(FW>8CNZX+>Tfl9R@{^VjY1__|-T<X?g6HM)@?M%JQn?z(WVVXJA^I4>D@8=0N z5=buJu^1{VCx>SDFU`?a1O-qNw0pve!Ufm2Y8Y+`kLzLPn>H=VG(ZoCwmjBia z`m#^T{jplFBnl{tVv?*nLhU*G5=_-TGhby?Hd& zZQC|{3X#a%ph#wCG8JXWQ05|I<}qbTvpJ$chLl;9Ov#ifiHui+LPccGPzo6`h7jKE zSJ!nv_w&5(`>gkmZ>?`wpLMNkHRyDn$L~1yW8b%Z+qQjX=<=7!FzLVRjO1rogO#4> z&VrM%=yBDI9Sy2!Nbid(^We>n&$1&G7OVgX613j~2EY^}mWT?*gdlt*6mHNsekgJ{ zQnN0!L(>y)BmXuhMgV7IQbd)-JF z$p)cx2JsSB03AE+4(6bs%I1n*Qs|5=K>1;1Wrai$IXg1H1hmCWM9{-e%cg*udj zjqL~6p_7x7z_W)KqV8@AUk+$3)=CGHQ+evo+A<2gwuSeRzAM{t)E4wF!2* zki-bwY)>ZN^aC0Jb~^5q$ADK*Q{90m0Uzn=fzLR3>_f#LoS(L9I<=twA!3j@6>o9l5bQ3~k@p_2- z&I;duGMq^!x>jS10CKWr2av0=U#bLFj4WQ$R-TwcgdYqj^sDdthv*qUIbw0vQrEpJ z6^i-H`Ta%Gg;A;yOe3k%NY|H6>o|Q&KtRCRLXLSI)f&VYgoksWSrM@WAd-y@H*+!C z(7)CZ{>)Rx(jOr&E%sK$-hg?r80JCTx@G*U4;SSjB)KX)USgLJ*n_{}bBFYK9+xy- zhzQOej9iNzAUivIC)_oLhYS0{(22Z=QE<|~pff~Y4(<^s*$vCw`H?;w-!)SSesWAf zOzc(=Zkx^x8)hHpY>>Nk-5Y*MFdD77hq~w{Drhf$sIsXeNt3mUF`3AcjTSDTg zL+AvwUOCq%xI`5B-~nG+9L|6;n2nvCP~U;kr1EuiG$*85P6t4__nwH}0Co<%A3AO<$%XCb=>rO$8cmCd~h_FsJnA9Kr#p73Wcc z0y2dWjeGnz;6NE&Sub3YE|x8z@|s?_IG4yW_mTJGXzoZc3}=ZZ=kov`cE8c19L8@H z^mHedWaf9;h0daU)Z&NS1gFRV>Ms~qU>c5B{q$!&zfi5G_f6p~DkTMfikhY-sQPk< z%f8MYNH9(tXk=rfY|{agxFR%LX9S$&1_yRJd>b9zIrqACJHr2QL6Q^dR-{c;Kl?+m z+kEj1rx#2*js{us?ty12Iyq2Jj@b#Gw8vz!9B#?!0U&>o|H4`KyD@?G6v5r6qCew#bS?tQVep;10uRgo$mdG(r#dTv9we3SK|Jt ztHZdKv{;?;kThcrk&Up28pR*$SG8L$#e&ysfU;ZD&l1*I*0cZ-_n*<0ZVkkC9J z31ZkR{)s?@w=3+~y&KdBFnOT2)kIA=pq_s%^TWxfF5(ADhx8eY{?pTKS^X!^4Rq!* zEI5`BRQvMf{gM3r1FQ87Q?52Pf30)@a(H5`boRg`T{TCc^?C?7cjGb!+XROGNGzGh zUSZ;7y})4J&S|N&_LA}ajXx$9?~*Oiqxv-vXN~F`ffN1U+Yu2{(*uWii0R0EmsWQ% z(UyLdXNF^at`)QSLw21n4W}@l(eSWv(kG3)UmIh1RKB36H3lFWg)Gqu81Gra<0X)U z#UJoZF)~ZOyi~l?HuOIRE)=T8;yzjoMgiTWVny|X4l!*JHVc$$Xg+)0nz3dK0f z3$|#!ZBu83JeG!raEl^R$67ZZ+e;chQYN(#tI8R5rNY6vyij4WJTF&VSn(DMMMmbg}9F|y{)aq5lPTXeEp>` zZDOGEBzj%Bi^(T9-H2^4*edK|uyC$@?Ra`t?Z=Pi+)OYIuMS~v%3axH&I^Aa*d96m ztcZWpv@_=?gqbTW=B}|m-=iOJd^dr-9&YR?`C2w_=Y~of;nVbF;sla_5aCYMPe@~t z_jc|Y3uQwu0y9wb4@2rX8AYg8Mk;k_7`w_V%L-!lY{YF3ln+?By`EoF( z`n@fNYERo;ow8Mg*gz6^ z*V(xMfFJc(*2&b1K-5N>4aE+81ul*fP*o*_b+F}3Qt{Ey)=(y?`pV}P{RGljVt)Fj z$I95!gL4SkSqqh6YbXBnIZ%JBC{xRwEkK*Bc$pT|1BdERz}G0ISeh`$xZ|3Au+ZMU z9@xcTQvf(dff>D;2Imm?iV&dtKFOUh5XZ#S!s^~<&jKvVlAp5&pFe*dkKo;xDKtEd z7|+Kq649hN2lLG%BL`A{X z$cy-?P(r}N3OB7gJKdk^b2{f*4%?2d@ZU0j+!46+@9R4OG{@)*hyzmD+>m4Nn}vB1 zQx-mZ?=!fFiIGT8VWRpnR_xa6qJo0kh#xqR!QasAGQ3zGl&Iy0J9@fzFZK%~BV+vG zC&0+*=KFASAs#bw$(KH-9x>`d>ynR|Xaa#jc&r4d(W3z_xL9nzatWw1s$;xSxWg|L z(Sa|E+MGqt-F*%U()uJdB<|ooDniAcp0{0!IJBcrx?6Q!pPm=deu|_KLvoOtKUs9q zDjsN<`i44&;93D2|M+&=Qy%?M00HQ96C@b0<=aqQcsZTJ;Vpt*7fEuW;w3-41*1lC z8nO`)ZBd1vpRT~L8qWcXApHrY(Q${sZj_2wTq;{b!=2w)etCLdpFp6AeF2zdy1W=? z;n5MeuN=omJGWS3>k<<0$k)gc0>TalfDhLfl91k`O;5sX?kbeOFGs4p`^s^3FsK;;y@iD9Uvy8BORz^aG{7?akotDgKg#EZkRl)74-z$P91`Fbe$Z^ zMb^{Nfj55jzz*h^*jOl+0kj`N+B=k9cQy$*1_G@h4|XI7;pzL)ysJLgxY-8oU1i#Mmss zNXK?E(PE204UQD3uD2}H`?Nvh-+@XgC;O7_j;J~$V}u|7zyOGH+4Yg%dn=b<_6d#( zs4|{7muR%16Q0qM&hvjjgpeOV%!Q`7r@NcLf{@R=uQFUnOq9h>3 zMy4xVLqzfP_HKyZ{Tjll-d=Fyk+|b0PorNs6e(T1B!VUA+zxCTT2P)vk1SWIyhOR& zSMqWV5_nr&Pn5yDghBL~^XEZ_#e{}%_#K`=%O za@WTg>Hv4{`nkppXW?;}YsW4B+PB{R5n2=wW$R$Z7*0!fw>%oi@VvyaVh^ne5k|*E zJ&c1LA7yZ`4aPU|hv!glB3}hDKb`%F(R09%zuN>=8X}>Xx?^2v=fh&O%VRuUQw76> zPWGj1tv7X{i%Wy)GiYYzWo2e2CU_f@o)t*f`2_@Uy&v6YUU}k8Wy2wZR^LZo24bSE zO)K-9X_zgm{r2PYqI@JI1{!?yFQ9rQBmIX6Z^x6~Y&F`~=ZsM(17Mr|X6aff)4R}^ zC^wJf9p0hgO5FQ^bP_`1XEfUhGd*bO?^!x`e{320yVlC|f!PHp+CVu^(4S+uk3r_%ZUKD|FA@M-Z9_STQ|o#aMO&QVfj4kfK|!JNW-dc)v-V9lDsc5 zfIMKTd-T!?wLDHF*@%ZJzl`G#2L@#+A^@;wTq(uOjOqIafI1*+1w7%$nBUXd8q+wW zuBwW512;==(!c+Jp^>lu_5*&SP$ZqlduLgH|FWX-XYHS)@6X@-U-y6Q&#hmR@b)k9i@y}CH;THfAC89?>H4D&Ap`=h95z*M?}$7L2GVdynG^h#rc0pM~G7- zP#y4{-%Q9u4%E|ADUgABc^|eLpgtUZM0g;hfc#p`>enjciK2c%Ta~aLO2ykgVQ&wG zf+(EXxZL19i(AFvk|>!B<}}a};PfqME`py`x{Uxx@MECyCrne2f6j@%{@?R6Hmj0b zc}1C1E6Yq>x3VxXF=1Fx4Hv;ByLT_|dzq;q=D%PIi>?~+L?A(ih2Ap2Tm+eDt@H`^ z@9*{H^3%&qOJ>3H`TeD|A6iRrVRf{$vKJNc)<6S@PD&E%gQ*FePlGaXPzp!0A3rwC zX$Q`G3o~Ny!cs4L7k005x6VJr^I*f9>pie(?x%!8vTUnVc_ogZB8i>#iV2^!KFh2 z!{d!~Rf+oy>Zr4430W_0I_Dk7uh_V_lq0Eu){RvUnIAw2B7UP80s=o`DY7LYfmjVb zAoplAAQ1sqavN9*U_}rhFq*{9jBu{Hzyp0<#*-ymP`yDnlPW(FGs66uwn@L|YjYrM zg2>VytHmj@vWOBeDM0*gaqoF(uAlV-xUdS}Zy%9nbXXzUAZ!lMEC4xZ;>`E=YQ%zP zb$Yj;JO=hL3rZbN1mHJt84pbHVifA1$1n)SveR$dAVW7XTzTA;F;cfY(-@zUMf4q0kps z`F_M`haO1?$}oGow{rCB*Zbe28PHkz36{&|Ks3XZ(7@^l!`LYh1%SP8_xqV5e;js} zFT|%mzeQ|^FESD;`WNWx7xnZT6+C)W246yF=WOV?pNM7SED>D@44uc8C4kC6TcY#y z!uMRhdN;4iR<|ght@l61ElghYR!ljW3pp=|r01DnkIG(Jk;h_rozA}b z9mPW1G&LpznDQbOXiwu%(_k1qq20x_e}5$?51^9ZMO@q8$r_?)3w3>oG@&o4D<#6+ z>X4g3(X1nQ;2{C~@lXP7q1U`o&inUgeZSW4_$A_5v;b?ywY~+z@rV?Pav0S(=deBj;c*DeCcp`1PWK zwaO=9E_-wIL-?17pZ;lx}_NkRRJehe&X ziCIn9d8#TZp#JE6e>3Fyg8*DGIEuaDvOY4aSv}}biLwh=ywKc7G6b67274pbN83W* z_A<)DK-+ETX^DuSL3Iy_1?IMF+5|h}uL=Wz5d=bbg$T(fTW)j77UnM-o^6LCh76E%f>Wyizvas_((+N9M&Q!=*g3Qj85DslEMln0JoI|7zqs_r{XZc( zA}x&<-rpL0xS@2r{RZmUc|bU^_t{Y#tBq)abxAqHnrYJ!|V~fMO^EP@&ir5`%qWI4}FuJEIII zMqcCgKop8ew>2I`+c~S*ec`Y!3MdQ)G5i&Ek;fB-GGHm#r63tbpw2uSh`pymh}Yby zZD5ro8UkwrOm%a_7j{QPL`r%zd1`W!V9w*Q6I_HwCP;`eJ=aye2geveSc8w4lLMp! zAbr*d7ni&zcT}CHycLnV|C+~*3b}Kg{Uh`Yj4E?LnE)Nvp<$c*Cn#)Yf3rtSeO(;0 zGu55dgVaVIbh*-+Z-K!%e~cC?@STT~pW#ZEu#ShMhp7o1@J^nC-#>LUxLkQP+JwYu z4jp|I>GSBIVlRMWbP=Z?8ji2mXXfY8i~EU@q;zz2XT<)oMUgUmcSZv?tT5eyOj6pV zZ48%Xb5j$3fcLVD58Bf_Bf!gluflDBMF{)Y)&Se^q_!YkTd}TMS#$}sMI!`}Rsmot zM3qO5G=jm@&d0y$k8=0*7h!GgysL>9U*HDgTqCj<#KAej1AXBY?onuv&{$4E@0J(} zhwcXYYl3Wa1Oua&igRpf8&U-U9yH6qr711l=U&4+w@wQusyI%ce~cP+iCh|ekn-{+0_S68#YurPrSZy?{&5tkun*0g?hFBfgp&%YZH@ThVj~-f zZt$g_Wk`;Jl>sr?a;#xA*m*R9gE{qCS(e&Xo*_`^zJM^IxC${vx}sCXsW#WQJHh@d zeDVZioa$ie4Xq>?^w+vlka`jOABi47#ctqfn8ERNeB5uWLjpuepo6$AFf9{@17Xh{ z#krAu-5>V>xE1FudTm8jh@V6NN5LwOU!I~tL`O6?lRp5=RQL>+28*Nbs>U+kE*|o(T`>-&vHICy`Cku$>0ipvuMgS~zK-?33?^z2*b=~D0353- z=(+KUa0YIkzgc+x`DPA|*i{VR>IDw0qGDmi+yT|zwNQ$d?X7LRQambG+zi5`uktg| zVv5F*n72#!r^-x+B#x*c107@2L{*Mg$E<^XtT%!LfWqcvm+KD{Zk3kIbR~y*6C?mI zhf`610>Z&Gtt*)Ngy>v}E&zZ*Sj0LamqW;yom~Ds*bd>}F$nO3E#~VE4q|Id@PM6V zh&_ZT!Eg)rSQ#-eQO>;^Adxh$Y2!W0IRq!4>x zoq(1*;1sdy^W(xft)O>cI3gf)2khxqD-dbUV5}Jk+8_!N18b4wf%O97cafMm+WE#) z69{y81(o}cWkuCbu5yTNXj|7RC@8TZjLe}y(xAMZG$ue+c0ShWO*VWFhpD;1d zNrcnlk()iq98(0{ADvd*&%|V5NP^L1K{|jX0)kyX(AQ~ILr3F*X$qKZC@3gsUFlui zwM=MZ@9mZN_osnD3DrnDF`O~*i=mFx!#lG(2GjMOut1n|gZw-Qd>KV-n&+KYIgWmR z-!jBJ?{2mAvGFZj-ksESSiY@XTsUzP>^qMNy;J(9R?$7!s+fr;RTEKnTk{=7#n^{v;6jFT)p#Em-!=o{6&eXJ2{o_LE6sTL7KBdR{`k|U zZwC?~R74_%Pywwxuv+AaC-HBfVH)ce9{bi#z7M&i9x%dIPXpz%f#JYHki+{RHeHcd z>G>BI;IVYbOo*d!x)wk=VhB5zyikl5Y;g;1o?Qk|OH6=-u&H=U0FDf@QAm2W13Lio zGsqDqp!Mc>`-cz5;y6SVyYDm((NI$}!-95S9INNT^znmEF610_so?O0cJ1Pi;V{l% zYCXIio;#;C<+$kW>v82ueSWn=-8; z3&}bQ1&%WA$%Qv{q;jq$?K^up2o6CpZC5ij>!&-EiQb+<+F$S9g$2QImvF+{vL)-b z0x$0|L}8@O0O!78Y9s0)*n5HXr%CV#vldOJjaztGH zRTiLObuVt4m}(g5je~iH+PDS2c}-!pY3(E<^HMc_CSmiW!SbaoE$%&HRx8f z^7{7!uQa^LE564nct4=R@3 z*iY7e3+d8*!+$3={vIYqjAW$HC|RF?A#(Tbwo8{*L6{^2bh9I41N%+2(k^((bIEOh zc)nz0f32Kpwq!NnS~BT+WhkTb`|l!q^7dW?68P#Sr_ahS^h97gQ;J7prlPfz*V^LI69;cB z0QH6U5)i!nsztiugS_7_YCEQJft`UWc|SZKm)&rSA{|3I*AFfPx;RVVA0WCC6A4Qr z{>F~{6LLnBYjA790uT1GnAJFg*CR$b4A5}qWPrv23;w?i6RV-0)37yO$+ zOF&ikt>b&7!zFM><2_yP*}Jlm7r`F63>}vZmV2?JuYVU)+o**@x>*9Oc>vg zzv`;qD$!WUb@mq1?8ph?mkfG;nu0nhYCR8!kl5k*u@aROtn)ce5dky8`+LQMn|a^8 zRa6jDG4F~1^_E6)acQea$-Dk@j*q4@lHo26z2z5V)SBspB`7iYM@GifK>d4bPCNXx z2#2K>b9DCxOO4fV09t2_tMUR*~x#u=Gb5EP>ZWiwAAP+9(7nSHxLh3$Qk_ zYy5y45OE((82~}KV?A-s=ZW!(Dp2Y3c_k≤E>|!vY^cF@IuEze-$Lb3;UzYWWl% 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 KKbLh*2~7Yr7F^5# literal 0 HcmV?d00001 diff --git a/docs/images/blinken.gif b/docs/images/blinken.gif new file mode 100644 index 0000000000000000000000000000000000000000..b3b7eea1633a1fa25f1760757610c2fa501a5503 GIT binary patch literal 125759 zcmb5!RZtyG*C^oGu<;FS++BmaLpBoJ1B76~LJ}Z&a0n74xCM6z?hxGF-QC?S_&)FZ zegFBd&duqbTGcbv)i-Ops%M@hrywgNY?6%V2Ko#9w}F6v`#%lgKOH~>5D^joxkUny zKmZsS{Lcsh2@D31z{mgy13(1gn@*C zi3)&_2oMMm5QqtI5CMF25Do;591$Q!1&GlA5_EtR6(B_ih|$5Mm;f;ZOo4?!frUhX zNk)u^OhG{mpfCgILa4Z}Pzm{|P^s}S`Jt3N=&Z=tbONtX00u%tCQ_&{1|Ud?LW@hy z$AZSe&jg@K0_d^;1O{NjK$tK9A`M_k0a!8sh7y3K2w*D%*lK9lY5>|>Btlsbz79S% zj2KrIf%Fp&`&$TygCMaQy_hbY=x0hDR2H?*Fib%5-wPxK0i>}287x2n5s=3Ml(7L- zY(Nbgki^DQBt%rhCDlU$>EMHu2wy2NV80Q1WkH5$&xZbynpu%tQC*iv(Zm>oHwFOU zMDo!DnpG604a{nh9ERCK4zc_$zu9dEwSgjS0B&fXByT?p6aj2Mkc30QB2i$G7$lKc zWRW-|@hGrx5;E&AZjlP!|IGboF>TpEM5RO|?IOS?$=YO#NgJ*$_@jz9cou*c0dTlp zWSL(2qBgw9VPq?r1-l5)4p$)y)n-om%3G>QkYmVIZ$epO!<}OLVHg000njiA82NXA zFpTg|L>@r~hQUa~Ajk*=7)Bx-CPEp-BOb=&9b<>ga=p61LOG=;I(o%8f-5s_#yVqU zF``~jkjPM)^f`2srQ$$iBawF?UTYx9b|g<{y3}qvRcbe1>8RS~X`SVG>}yPZZe-nb-#3Tax!UR zq-=5{t?#hG5=88@|8)`1106 zbOryXT|NFA{|${e!vB5g{~KxG7^-iy-bzcVzv1EFLi~3Z(AiODe_j)PSag63bE^MZYV~gb5&_HJR)^#Uyo$Py^ z;%pk+n|)DqN;)2WSb>4RIC+P|qHLwch$ZRhae^@YeL+ss(T;k?mK;(LW35u(bD2fd zuSC--(SOZ>i0|K>$URN$HyCN0qGi=G#7xDuc~)Hw%zapgt7AIPH`K)T3i%4)1jXNs z|DIiTA_`#}i9KGQw$vqf*KP!fOCfVLXMBI{{fzR{BFcvVM|T`0>$($JlAriWnH(Ho zr@##}&;qOx?{>!np1FT(X;G;X&^jP%%hAeqtDr>xWb&(#;VZg}^2JOSdMl;KR#rf( zsN0@{4uu-vj1apQ(8;0b`j~}SZ*j}QqU9LB41&~Y3+aE!>Ir}RrOD%_8G-nIA^?^5 zX#!DFXTDN0(F_45!|3U{sX%0vy|9~NKia^kt`BSuP>Vx+@>87}(U`Knc}E4MI)|i zdIMA%zJ7>b8f<5e$`I?stX@(*>uy!n#K^!_dH*W3xU6PTuCS(v5D~Smd_X(5 zu7tQ^luD_2;;D$;O@(?uM3|1VZ5$V zkzx~x)dYjbjTr5{Pdaz@{d5UpHU0D{2GB8{1bzxP^8uyu77HYw0Dgyk6sJDTNNdgz z_Hu~9kc6$_hP!w36y-D`g<%b{H8$9B%5cu3T}}UvLy6od>W{`T|@2adihO z>#*IrUk*+k%#oAVapkr#jZX@WFhx3&nV7v6JDzJvuuzSw>MxLm>eMIvTGyz`K5S(C ze$O4|*mX=f=iRi|uoNM>pZ&JmY2g96MQH0#2<%IF&%Qn%O;-839hftq(bBK(IF+qf0vl7eUQC*M=&gNg4<|^5LFi^ zR4*4Ks9NcV?PMa)?3Y8{vn4~%f`h;xlEajk1rn8rmfd+}jyq)B$7;cC?7p2t6%;oh z@nR1#H8IfHr9B8h|63u6tijhBoiZlt&!XFny5#GMmR?Li6E{$4MA< zVx*;Nkgs2H2pM|H8qM~)9@1jam_Z=uHSx!XswF6Sz&@q}3 zN4A0O{MR5rUF4cqdADL;hSh!IJF-(|U@pDTC!|!0S0T`}eZQwgu+OWz4h^6{8UJLx z*|#1)&sGHQ%`_?QVkdJ<2i7mmic`s z1)av=ftsU?yUG%yWc#BT^3zfaft495P}68b{ppH+sj_Cy`sRKs{VE<`vdsc*MCUFC zV1)WBEUZYxouxrg&UAdiz8WADLZBq zE0k^D+B!o!&qi`c-{y|6ez4}$*54z_iD9mG)ic>zkd@Q1#)P`znGN=R-Ebt?znbXF znY9&$poIY&B83778(OZAcg)xGoZF4I&T-rB%~!Z z*VMd(_@GS_=(fe6YvW?RlNHqqrm-B%Mshe%5!5Rs?EY1tM*Zct{5mv@Qoe8^029o_ zYm~5pBMC6o^V&tEP$DMv_aa#JnQZl*29)ayuql2^^#!EdgTyBhw<@mLEypZK{8Ww! z-XR+IPI+_It~6Ea;Qg5I7j>>mNJEEEi;@mn20vbW)(O;myGOS)^qvQW3n|S8)xG1__ zmWNg*_nBaC&hAMv_S3E`EGzTa`pR)=3H3(#Obt)K5*jA zRg&UoBKH)z+dSsng+Yn$vWc_|W(qGyi%hQZX(J+hi$IS$^2H)kH0V$3XvMnSvB~Ei z0nMn8Cg*yQ->)K8FgLwkPW(mZ=8Rvd3Y4%%fsZ9A)?qzgt`kFuiGj?_pg(WgSfN4K zZr5n{Fsf)3(#dJQU`fcIj{d%X;GxlF;QZskuaK^s|ziC&He9{(&BevoBLYgI|D zzn#zXc#{+i_w@blxtychw)VRJGP-qrqk3+8yj*Sd78%!8Z|(6wj#E$%{;>BT3h(@D zh{FUuLh-Tz&?k`8QTjze>Y^QV-YkCK!6tIU#)@1zK8QpPmHwib`=XEnQF2s2Dq<04 zT;JU~zS%nHc(r7%1IiC-@WT`pYrXKnTkXv)?g?rCIWzxbI+riC{ztqL;kZA}@en0D zQ9kbZOGdwboC02Af4niFhCxikPM0v4{}J0TjWacIjWD*%-)5|B3i>>BS9XwvW3 z>?bL_?aC=5ZOa@ekPC=FWq#MkAo=hi zt-+z_F2+WHF?bz@P7Vuh*(J#BR?WZW&CH~%jtdSIqUY2^;T)$pFxF?PR{D`kdz32= z+L8~!S11b(4(SgzQxD0%Ln$l>Ehq?4>Iz9k3>gL6T1WzJTv!CAa@oP5hy_Zi^C6!) z!s>+RJNT3n=F!&qDO&qOm*fe{<>^3IVPK6AaW4C%I3)^hBEC%eWXbTHd1ZUapYig+ zgWD1C8Q6I4oAcT5!eD4#9mIAVpd#cl$fkEe^qu+~dQl*94q!j*M7GZxsyamJ$1rI6 zF=#k25GF;1YlM!V%1Z}ELO?Es{NFUrwO;4M7$z_URKG24$0*usu$q|H1dVdkQ_ceHyTyYGCFt zIFNkEM0FDdd7-5`kH*OnPA$)tgA=F4G$d0F*oIeAo)=P{fimGFRQr-)OlF$bub>-A z1T6g|n3g2q4U%!hENi*AG#VE^5Mq;$OMwZ?Sd@}i0s?*(Da$7udeV5hY$)j@?FEAs zCx{Va!vJp}`(`hj&pk&dIfqt*(NH($`*;q49#o9fg^-k*SOgD3ji87K5J3^S_@RUV z!aOYw`6P~ZNVYLC@hf-Q?CIY|55J8`^GrqZvbFp+P;eAs5L&N9Ac)Dye;zr+zWz}3xf1e!{CbrVMPT|q=hjeg>ia? z@ga6csQIILPQW}g;;pZEagAPatzKAi zQIyq0ksS+?Kn^xb4s!WJL7iSnuX{;tBgtD4-dx|QU6Sb>}l zD^=Y`8$2%udsH!}RI!8>NX=?KUvonE5;UZZ0VZ=9IH4YAFHgodlhZxrA#Kkw7No^? zp*`x7uEr|)lp4jRn&=6q-3_#MKppi~NF%|P@12Jllg)m-!?~;j65%(arXsjh>Fgnd zaj52VO08v6EhmlDR~gi=q-L|9{xBwb?rwOV-&Rk3(yz_0s}4w@#IQd7R%@+aALvmZ ztdCPAfQZv2iarsMmr3H(6h)I1I$<2rhH6W^lvjlqQuR3!g}W5SRW%N)|LM_KkkS~; zYSod8<*X0I5J_hwN?rPrY#5a>B;2(7KCKc>gU^`vCbJxk25CV9d&9V=1#WdPL8KeF7hrOu`WJ? zE&Aq%;c@bjNwJY>gOOR!k@?h-#paRa<&o8=k#+LX zO|j8!gV9~j(f!oX!{*WB<#OR=$QgRxuBvAfiFvVGsYCNrsA{ahO-u4v!ByuEn8-- zS7vRVXYDBF9K`3G4CkD^=3LX}+*{^6SLVE*=X@yU{lw>g7|sWJ%?GE=hqTOxt;|O} z&qq-##E37%87{HE%&D_54J21uPl!~FOO5KOp32e z8?MZHt<0ybEVisHudJ-XpI6o?RyW00w+&Z!y;k?rRu5ZNk5^VtpI6T*)-J`@t_|02 zz1Hs1)*f2co>tagp4R}%bp(lZu+ciQ_d06&I(q9mWOW_uWgSYnfh)0rZ?r+^y+NG5 zLE5@OzPdsAvO!I`Nh`5QZ?wtiy~&)u$R{N9&Fa)}A$7V(+8TUa0q8c=}#s>t6Kgo_>+rds1AvD9lqXtS_VcY2Hx3RcKNx z4}aGIiX=uG6zg;yYa0p)HrhAz-Z$bna`1+t87r@}9~NasAmSWiZ5-inBP|#onn@f$ zqYsK(54&w}+E<|P%Fhm%TPxjNM{#PKfH z;ero;^+(JhRcHegtKANx8hX|bU{p{Z*|zS~QlHsGV08LGu!YPMmJl)35&dBh#Eeta zj&pPk-Xe9(3{@N~TPOkNVJ{ZWJ7dh>b`kH4(Y8E}NiwDuyj|b35HEHjg+ag2Wh3@% z0wQ*(y>JA;J^f(l1>`@Dc>;sf{^C4>Vd21$`dg3t(~t8`uS6rcAovIg%l!$%3jAMN`dSUC~U9J z#;!*B)p%B>VboDKRCccx>$BR4k}b+)Svt0TO20Z%xY6HFWe2iwr0-OB#9Vg)87-OV z7h!5w5#`5Gob*1gPh!L_F$E@#_QE)2Hzdh-vFA$!F;T z@XT;?Rg@VR$}|jxq;0318Zzi}rq_0+uZqzLKq{gzwgJc=V~n;a$e+xcemjg>YRC`( zt?6gNX_Qc2lA6v`)`X^dU%UoH{deeiT zH>NO&r}w>G8zU*;ORcw#=6by$M7{z)*7M^~<5=D|{&*Fer;@6{UgkK*p+|v98nDXd z&UY{v&sp7*`kvQPi*({x-cc@sSSyscf%Hu>F56V4>PY&|(%jjHc3&iHMuYitlkT4c zoEFRT7iRs@3}m~SpJb3Y;#Aqy=M&@WTYnm`bB^cDH ztl}rFuI>|x$tcu4gk?W?(FW(rHc93t)H4+dFY~u45WNB$tAV}H5k=r!r@Bn|^iNLm zqWV8b_L|N+$;z9LjqVx9{rf?^zwRxgBPC9xMn=oQ4*TY>Q$2Cx)Dg;p16hE!;6JhN z2S@^Zw?e85bXq0Ob9+L_dkTw7fO`HpLl6G=idQjV=nDbyhH~$Wp2Ma%;}pTvdXPwd zmiJbb-)03^r?g!RotM7-HFVvgIDrxnCL8kuolm%Yo*><7EVPksBBgor$5_r~ur+hT z`W+jgVMe!c@Wu{0e{I)SBE5iLAG+T&wkmdn;!i2|<&0_lmB%EhAPc8cZ$PxC`;CN% zUxSGtK`8dF;==)$Fh537VU{NK+wAQ^M|(KSe%8;y``*`P~v6#lu?tubvp|QmBDW1zkN7Uqdkpz%RjQ zeihVQ&2(zp?(;Np&W5Od%0idS#X{V&q&^pmG!0t78;aoO+o~Z365~TWizGdD*#^y; z;KHo{Er#sRcBO1*xo_eQ``hJ2KiX~}N~~@+Rv6VG_#6ZTFsv!ec|rg; zLc@z>wlK*<(oJGeB;jHeKh?rE@t5X^m_`q&rP0v|y#k}ToTEd!7Yr(%GP~=*(L(P_ zrU6L9K8dwXn~Q9a-F-M^vcjq4BljGXiuV5_hRY$jV4paV7XYXSyBU z86&?tLedX4_ z5R|UmP_;9Z$ZNGa+)%wYmMI-dDGv2Hn95TqHX3RCbJSI!(Uz{#RC}_dVV2Q5(o}c0 z)-HFbO3_?@vDF`XMV#K;aJ4s1BVlh4=yrWHUrm>>TL0tQ$$D34v&)F#g&nXpRs5l? z!PE4tabYM!?RBL`_srJZ+E`oL(qj;Ne#Un&;b&uQfYf=_7b38q>5d`6yzv8{lE@^0 z*rLG1ipXYvBZ!((t3Q}Nf_U>Mb6&9SPllrX%}}n!9@?MWv!A!Zbw)nZMu^?+o2iQs zUT#In5DsWY$ubsh$B@$uY{#n0CPl?+>U`OW(K0IBi5IgN*hw(X+y9to9{+PA(IT%f z&-ZiL!EVasAGEzxCkv)eX|B6&2C)Cg9{9hqM+KN7zWpdV6( zvGf*CmU1$`@ak}(BV&IWjql6#Ky|@Hj(jYO;Xes|8^?u)UMhU^s;fvx!}R2*6|HD!mW@`sAl8m>Y5fU`Q;6UuANMa z$iWK(ayN^4SOTYo?9l%t)B@F*5kP%c!(S|gzc~u#7n9uT(53~6A8tFfBaFs@0;$A= zJ^UB(SdbyMd#fvtbw{(bE`%A+PL6Mm@GB!%M_Zcyo~`r8!)2wRp~50-$oFrU`r2b{ zCpH&;vpa<}@bHTCG}V<{dfYrz{9QuvZgxyWhL_G6N%Q)c4g!zM$SP@n^X=EBs8myOj)X2jK9f9ES)8R`&>)XeQ3EE zc*UA4&~x^O8%fMzEg{Maso2ZHeYXbg3&mW9z74`AX0yKA2xjuG(4%NVdN*Tl_Dq`l zUZ!-E1hz5_9=3V60xcrW9w`b=zxy4*rN|3zEGl^kK9P_}2@AxF&*;bwWy)XdSE3?rRl@L4DI&OH$(kq2JZEJ*`ve(8m-bb(}?`SK(3l&M36+8jJ3LV}vsz zdJE{(Zywj3y*!$MNb$DKtqd}RZOpfxleFtp#8vGNpg#}*T6D=DZ{tmafxnXNPvcNbo-$gCtnRFI>&uOqSO#90XnYy-3WyG$tc<*iOl-a)w|gP5K`dT$%? zE~=}|x-5&Sv5&SrwupU~-z*&W(pREjOXplE8#kf9JGNhqC5j9mT6>c`pX!g2tQ!cW zu0Zk&S2xJ0l4L3%>u2TLRtzx8HhUE(mjq8}6%hIsLr{D5&iki1?dZ^#|sw%uOqg*My{>>F$s2u5_379MW=4A-Q4ZaAZ zW#Gz(VHT+eg+wQ>YNgA3R7}*;O3D?vDO3+H(#ltyDbl+s(j6(%sT-UrbH6Dz#w^zD zQ=F|zzA5>vQmi*OI9uCzQ)(ApY_O*|*SL67<~mYr^mlNs_2H)62eZT&MQOg1^tK{c zrNoqIXuenEwlXTb#GFZKVNmb3DsiO5LU3qd)cv+P)5{VZxS{zYthPp0xKv(C3CD`0 zLO`D^tAlhg^FI>^-C_~_Cy1Nll- zrN7QMM?RJ&|Bb?L=^$boac<=FYfC7}<>*_6-K-Py^!0F=-!Y3U(q~`ez+A^%r0#t(FzSEqc^i$ z;w>V_V?4ZxKJfIHcx9MiyS=es(Rm7Irv(RR2Wb&h3K?|^@+#vmoi>_k+8KGcn9}x` zFJ+CLuD=*tyEFj|7EWz@HAPdW4T?owkqq|G1EHD__rZD`zp0~WVM}8X6Mrp^2saS z@X&vz_c~0XIdV=9)K^$>=RH_`@WpKbfi)3G3f43iqd~cQwPF5Fh31EISxhnK&U6D3 zAK}cOlPOPggZGxUFskZ*8MoR)86$Te0k>xu$`=b@)CXGtSKp^!1&1t7@5aTowe3USPQVq3@xx z$H)relZo{S<#Ab#6@{XC@k}hZeKLjeV_6OYDrNiQmuHUEKs$08m`>T64wDEyqgu$) zAz?))gQ7#YV#uY)j$3Q=C<wnDv4w@&1m{5Si6o$R5Y8VBx*3A^LSPLe5^{OeQpP z{qjf5oWv>tr^?B~O*#;`O%wx&<3F%ef6_YX2zXrBm(a?6@HF4 z>G?^E=H@~7IqmZ~rpfYK%7Jl;!Jnr(rk{xB?nk~!1wSLIM@pe`!z6O}#h>=RH3~q$ zy4JB=;?&gE1@Yg%0+x_{AGG=JS$R-ISLl@p0SQW1Xta+s=`goY=@T}I+t}009*O6c0OBvz-2sxM?3^=cXP5PLK zZs`0Kqn~l4SC}=Kp>@r(<^1Ev(poQ@W&aSEcu_WxzMtF5RlbaEK8GK9-G0C@vw1M4VOMzqA9*FYw4`nBBIe++@>0YC1HqwK z9Wj|O>B^wMicKkeJ}JR3;X*Tx++ZQmX=ziuKpaqIe+X+_`C;l>~qF~eIs*A{O00m(1Q(GTL zKweF>UTOI9nOgjfOtR+PYNhsgc|i&_*9cf$%vR>60y&;DsNoZR3O?5gS$rH7!csQo zD1NfS+GgOw8bvQB&!B5)?t4iuU&s}*TH&kI^zAog3vPYy`3K+)sY8>gV?Py$2(agW zLhF)J$SY93#q-uCs1AIJaB$PXEU*S2xYW#AzNQ9B0o*B)Cm(XvkZu~_*{%x&;yw$h%kFaF`*uti z42+l`rK^SUcD1nOI#nnF)R4`QF3weVYB00Zv-J~9RudJFd$Lh)lsQRN;=0wJb@=&j ziCiw;-~_0dPN@lPHr4)15a_lb0qXqM`QGTf^qvxa}{+^-#ZP;#hss6jn?e`^@ z#)eRyXGP98MVfgn?16DDmvO76ManQJaMfu1a@(z@g^sqSj+O#o21coRMXNxvDl*!Md!XfHk3krWjHtD4s-)$QCh z@F6Rt!y0F zXlwyBth>a>yWS{w!%Co{C0+k0jcjT+T4lHNa=V0er&L&%nplsHb+@)>7cNbYmtJ$9%yJJk1jk~z z_giZ3d$F=i_&Efsj}~AYS{qaOWQle@UcYaNw!2;yXFU*Mg_V+8miRP~ASPle&l)Yp znqAAXA3d1UHz;v0oTog94jL#O7AYPUDK8PJJdUmL9LnArs*}g586K*H3>P&Ib%qTW zSP%7-3@h#rqfHEr91jjCj|@ExO@$53l#I+N`}Z0Q{i++;SRUyc9$ZTu+GZP?NFABC z9vM3xJ+dA>CLjI{8AGKRxwIa`<{oZL9UCqg!|E72cpBMzstXsZ3{*#DL*+BPYmYS; zh%<(y4G$bX4B&d6d)a4W|-P$$u7CIt|9*OM(J`eU?#|~mCG|Wl zi7Be)HO-GX&8{*dLouU^IU`^=!_hUP;x%JIF=M(iV^TWH<27p~j-y-(RXv$8r6{+soJ-4;%|F1#J|DzB7w|l1;)OCI1VK0%e`FajJn7q^rb3%@R&ge_i|ES8Tfu97bvLU5wlm$FwDi@z>q zctP)u7xN)Y9pOvsVoLz#5b(UzNU_wMwp?R4eq*qhc``O6zC6Od()zsILNUHny4(v7 zUs*m`o_=0wY8hK8U70pOO$9Im0Q4@dGR}jZpP?0$sU4?!YZPo9R3#k`Djn*neUu2X zZb%Igreb+52>M=_YsFbuP7-GzMp5_28m-Hm0l0afisk=XcKyFvW2PJRl#5bvkp z*r4*>gd1)WjCL~`ty8OR(w=Ux8EtSxY<^_hLVVfePv2Da+cHt!qHNs~wAtkK-r(oh zMx)%M^WK(d?UhO2Vv5*SZQWEK-BwHA=4#!RjMzFE>BkW*d-8yW4Am#Hc0{sbr#3ev z74OC??WR7qo{FYX#k3)%Ha-@&IGnb98*Q!*-Mf%cJ0@#JSl*jS+4J>ox`iw4+rR8N zs=d;G1{+GJ=j}1C~P|PFghsnKBzT1EIB<&H9}qDf+WEzcMt!N4pnB7V}C9y zV@fHT;;0%ut@UQzBV4M}*RPvSs$TT2)h@0x#5fIWJ@HIA^-ifJCOdUGI&oV%-Qzf2 zQ9WI=sW}}zF%hl9xv07?JG~h_+le^4dO5wNJX$zWyiEOdxeHgsVgGRTgnlIy z`JU$kDy!|4#9Em!)wR^?Yncz%vfr=eGp-dgbX>_UAe`05s~65>m**0f>ezLR@av4j z3zV~K+O-9@`mb-^N8VcL-x__mF;@F)3cn>Ozx}jU z%SCo;{o&Ro@*MQ~)bIOW&kujUM&6mV)dj-u!ZUD9R`*-dkN&(I#Zetra~wuT99F3w zr@{}ju=n$@9z3y*6XEyquMcY|+kR#27py%XK0OqiHl#-$Rb(7Cls`1sJ{Cqk{;_ST zT5D^Myl;K|l>7R)cWtPrxsh>0OgHfIC;P86LhJ3bLI65 zo7j%`)7JLd`VQ5G^YF86>I>H>_1fB2(9_FN`G#ln`Z;I680E_~HWcv|XB_banfWaO z8mUY{h4D`>90t`~p`4BOAbdJK<=|+Oz5oYD<{|4fDYS%6G{nuJ#+l zKiQ=h*@&+gRpQ08 zf2EaVI#U*&xUEnAQ)f8f)d8W6OoP*EFMs~uQ;7zrxy2pUL|T((ufL49`ibnO?T$wr z5zC3(JMEF&O0%fKXBI=zn4ver_gtT+zw=_Ris_jijAv-4Fv3^dFOUBzy=y|&Oa3b{ zUr15C>Xl-9y4_jR*N?R7p!i}S^3XPW-IK{1b;lb^o?iBybk?e{HP)a0;R z#q{s^(mbtryKp04?GDGKet>&>w>)*Jaf7awY)fB`$T{Z9d12@W%hIP&DIS{iaxYHZvCv?_;{Z|IB79Td<*#V!?S*^grsIl6`?hgivKF8g?-pC*;L z)5!6Luv30q3=3^l$BJJJPnC+Fx+~dcuP^JXw7mW>RaOO;dhW(jlR^4j+9cyEdfhCR zQyizENOlsNcLSt3jVYBc@u^El2RLb{QK+c$YGG>gPwvn%@HhWO{raa=?cJZ6SuLfR zX{|SMBLq6)-&1bh8J*Qs{{~`6i*qa{LIm!Zr(AVeeE)b`rEB?k&83 z8HQ4S+ujE-?3B5UGnm>nP{M;wuu%F^Q71t9C9EJ`@3?M7;L);S=CtvoY5-aPtY|E7$+~Po*5kPCes=M!@u$&} zRRborsD0y5t^Ro@Ymh-{2R3%`Nf!EpNH+(6!MDG1n>9etDzoEFNaYD+b7#% z?tH_GCT*AHi$%jW@ta9BTf@RR`+QI5?bngyH$8Gh>ECv~wO;wZ-kgw2*WwG7zxFDq zqx3jwX_I(7EvEY5ao3IV{c(2)p6+?GA^83IdXH(%_2(w`zqKcF8SKxMXEvvzRG>Mc z_J?~lA8X#sC;VU-aB1s%A8QQI)Dwao#{3B4jNu3v!EM3T=e`tSnFx~e?Jor9ewG8q zk1_>PxTY2Uq}%JLD!QH6ljDJ0%~=qY0%=O*Xm6ohDk!#Y7dGyNx2#hp^o&WISym(D zZLTRnsp}gy3yo0yZBwF_yEj~c8ewMiW+Wr7vV3_O;a28mWGi>FLcJOhj=5$OC$4g0 zyBd-Il|4BavSyS&y*Ul0o4gF8W^|~zIUU8lyu7StOl+<>1G}4ovV~^se`N1oK|OGi zo;9@}BXVAvus4b}&w3N;l_(Ql!4puX%)p}bv-@plReWRf4wj>WQe{+(U#s;F-UqZ{ z6A}CP-em?J>EI#Lm$Ss-nAFqZaCnG*I@uz6lw&3WRALMn|&aG#_m z3-Sw9l5TL!Uz0BjYFHri)1}FqP%Dc#tw2ujVJsTGCYQdn;I*5JNPN1J3-w5W0#%4+ zL~k79EqQ^`mqyKBwOYBtm@Mk9A~>PWTKW3NA?*g2ziR5)7;q7lv~!B$^W3eOENDWt z%8RN>^=_DqD2jE~2xiMCt&1L{tn?jwb-cNE*gm-t>u#ZI*QK)=81l2~UFyC28qibd z_Pe-$(w(4;(Ye&TrPwrsMHgOjIaB5>ex#4Gq+6+@Q*ID#ZOp5*Fk(~rw(L?n51E0* zSm98+ZZobVuxC-X@>Vy`ORGXtR9~H8sJ2o2>la6l#kXiye^Nq9?YxRv^+^(RYAD8B z;zHSWs}37#-f&pDg)SW;|E(=&u&KQML2fGS($uALLYUvp$oIQWRa!ANe4j7>_VTj| z-a?G~$K1R-(t{RZcRY_rrL|{d1%($wPM;<48k}XP8M&78J4VU+edFJDtX9q+SNb0W z#dTYWu(;y-PHp`VmlrFV4wOd>ah>Q4meBWHYEUFDygVuc5Ivbh z<%Fe)q^?pg3{3m$(9{^RBY*B)yDA8bwnxOieZ~2XY3L&sezMh6GJ7OLzc7bGoZ8zR z#RV-&qDnp<9#MIBLyra}ZJ)(~bBi_z6 z`GzM}pFEaUUYzUu3{M^NJyuS>h{Vq{GW}ZZ>729Z_G`$??O&(ov9Z^h zdt=_NZ`=MNwqM4!JuB6>k>r#lmoyx*8MCq?ce4&1vYOGcq14p$92O)0DA8?#HN>=! z`xWaWc66GS;5e1E@vxy<^I>ea8|rtw^(YzR;a47NN0sS7OD#d4F2UXABQuwzY=-GP zb`V=j(ss9Z6-km9ND<3)GCsk`e*|(bR`TSOi6Zh+SMzemR-HY>G5c2wyyd0O;br5i zW-XDTb*yFwcftAayEwb6x#Ft1$*Tq8x&+y}gr<2JbGlwtOCJnM@ft`Al}L-1@JJj> z$u&sx9d}8^g>vh0p;=WBK62sWaijZlV^ni1rOF6pRg%SalhbjjEqAMTbE?QxKE=e~ z)AiuL?IA$?ryjQw6k18r)ctnPbn}?n+&7tkI7it++Sr6K`<0dJL|6Ou)V7 z__F9pQgfGGh6B><%e)-9(rneT0_NS8%UxE@Je*HmVn?#lPqG5TvbM{zGS}>Ha;5mE zdnLDfVL#-==w%#ItEIbp?NVhuQv003`o8g1IfwPi@}VH%^S>MVV|>*Sp!~<=k)K|< zmiVb!%1j>qQ%fqOgx`d>HjKVD{P@og>wd!~c?0QMa%;XA$iV9y$@h-(AMg~6l?LAN zlElmOCxQ7w#cCpc^wa!M2-D(EXA_8O9!R$SlOmSL9a6uw(FX9f+s6u7V!VE=7AkNO zD8z5~)>i}{zup@fJhG}i?ouq8Rt(!3j5QT{vQqqeH5k~{dMK^*P~BQ6CR7zRcM^r6knYZD?Az=ei|NusLYeAEH||ji;gVPjV$n~ z%RCbzFhH_O_u9a7{Mt+g0Zd;2Tq^cg4s2(+|9uKRYEUTU#tDZfn zo!kx= zpOJ6b7}NmdcCb2ja> z%z2;Jb;kENt_t+c@ByfjGRb?DyYi|R_aySrGt-$<`rD0iw%1Tx@vxCH`|fqC3Qwsn zQ}iXWkehkh6OT)g$Q~etxJdUK$TFJRHdTSq@!@p7yI}U!Cu9oX1l1arvaS1 zV4pg4mspe+^?^S!)YRnF)uX}=^%S4K4&lLJI$;=8FIU%{8f1nI>rSZ~0C05FumO&S zF_nf1hlZ)JhMByExxR*lorWb;!zxO{I#a`@T;uV34cl%FyD1I(Ee(es8jd)cPE?xC z9GXvrHC^O2UG+8H>@?k>njTS_o|&3n<(l5_HGR4@p^QS9kRU?ZJOHMS!x-f#Rlap+ z^b|76a#JhENK28899l;aMwN2`Eg$7^9_3aUWgQr;p4H;~q@}R06;rJhhejWp`8>WL z>{%jpGzYoWtiNyWD0#3Jv{*YLkvddmIjAzT$Y7&U*p?pvA*BR^3+g^qSQUSS?oKa4@ur&~=mQOcqF3T;Ag7Zw%^t1Aeq@78Um@(;s< zRaQ^D*4L{r*NgWUv0h4whtp! zl?ReaO@6}B1&^X5RT#)L88qkzy;=&`zX$q^W7uv!z5L_rVL3{B!NeBv1oTw50!J^W zeLR0k$68%)vmC@piuC44^CHUsU1kst%Ph(?tfgG1!47p(e-=86e2Qk&#es>&k4onV zLOaz(qSiwWnZ*aq3a(CrvvjXTBVEgx-Chci?n6Qc`GZ7^3s}f4LC6%2ia1h=Rvv+( znMxGk09;T|vOEaFk1j9^yS0jX8)Kf%R~xSYr8NfyS6z{ES?w-M8o_&XXNYqAY{1(n z465S5T%@s#V518ZH)Alh7CzJ5b|Q#VYC#aziwvGUiduM=X^IJGxB8G|Ru`o4`~hH{ zpwKv+)QrS;_5zr40W5o#aay0@&+3tv90uW}vxLQqVZ|^pyjk%IGll4xj30~g+lzD6 zW_Wc*QKSZ(eMoo}ocbF^W1#da z6=sH+PNSnB_21hK^0W$JYXb9)f{jM>!!bbA!yzZ^;oKG=60oV*%cUj8B`C(ys(z4m zVPCkCr95xA(zYf^3P`eeMS;mcZ)C}Nc}cIZ&m$T`!)*ywcszS*M%s78SHeF$%p9+0 zF_d%VLQ4ddI4lg)(I_g`xax-~isNd;bSUPk*%Jl5FRlg=)LN08;e7HcC{DhI?O;@in** zqu9*=xpAG8WerPYVYzj(gm=1QY4!WCPN2f-&sHl;epHes^EOv2e{~zN>so#q=4mV- zDvZS{gAJ(ElAML5UZJ@dwZV-X41>b3SJ8{-kzq#E%eGP~K5tK1MFZOy!Xqt zKdo=Z>(LdXa1|Oh$geerY#1DGyuZFUM~sY13cEHt(YHM@WUw_7Vl`f9DMq?!Ac-16 zvU)Mmv(=c1@fN(<#rU|Bcf-0>OCx&jRlycv$m3GziD@49#?XJdDMj*228A*jc+MYSnQDb>6FdYoWvD=?|tVAzqa=pS|fy<&jTIb8l zd5WI7Jb@wcVA!qQ<&iSO>h~L5;IYH#jf6(C9nLvPUWau_dz_lxnns)P>khaiRyZ%I zaI=jggH4Cq?A@uif8ZD_NNU8Q%}CE8H@a>tb6S@~uO24u%_N2GWKO>gcA`o)XqK3K zMq+iNa1zP_s{n6dMz44M2(fA(GKYN}>p1eW-c_u97$q9}Norh`U zwo@~XDe+ef5(g*f4@=H#*7y0|z2L|X|IwG+s4X?4$*(cD)AM{sVz%w=p~3^#hu;h& zl?*vd3?(&%R3Eu&XS?dWa@GCds@LnPKjUh!<7#-}YJ}@%Oyg#9+s*WWo0+1UxuKhd zgPWzFn^laPb+(($E4RlV+-!T@>}K5TcibE<+#GS;ooL*hZ@WKv;O?U6?rP}n=HTw` z=k5{X?wRfG^~&A*gS$_!J9Ng~cgNlD!X1X|;ZNfcaN8sBfk%*{N3fwsh=WI{pT|X5 zj7NC3N5m_Sryo2bdp)9NJfe3zVlF&laXp{Wc*foKjDO&npy-)s=$Yi;ne6A8662Yg z?V0w4z5=&jo0(rUU?6^@)f-b47~~+yo&t1ietP=vb{=Q zdA<1HRo3fOKI8Rr$LrOFR|T&3YZ~u2x4kPLcvmTUR~vfQIC$6kdDq2w*Jpb-yz+kg z!TVjW_xl;|#vShu7v4>{J|Ag(KHc_de&Exh2yA+M+8lg7`}wrT_;h6ZbiVTW^1-L8 z*Qa~Nr)S3p*cJ5QLi=f;1Gk}r51>Pe&|yR9hy(PiA9OSZI+hI`e+8ZR0G;fGUQEqE zr+1(;7tmQ;U-*Rnw(r6N-$g~=B}3n32j3Mx-_;o3wQS$@SH2q`d^dZ2w`P2|cYJp) ze0OpE_GtX}Z~GlQ@HtugxUycJ~`Og`C$7OJXaUMbEz+R>p$>ZBCRXq9T%yR z-^M%i1;PJkKK_5$Fo7dkU#YhO;i_4vX$#{8&PgxWmn;p)w(X;Bj)S`nlo#hJ#o^-p-b?{K+6 ztXbG!bqhnbn1fn{d__nb=2xKwteMv{YpXZDR+xT)^VVJd?#Dm+T(HGvpUak&5X5~9 zVYXxp>j9y$pYG0oc7QzuFZw%KJ-)w1A=#nw&XltH$;HWa1D}(fgs%A7?#B1$C%X$@ zvS|pK8Yi))a>R8V0Zx|?KL;>b;a{YonyC_v^_%2w#ql7v{@fo(A}wVcbgtxV>~-i0 z=dD9n=EFG0(3{Y+Ks>D?*M7QbAbZjB;p2>_m_Wq11W^K+hwd3=+_BL{Rt%gnFMbv) zuJZ;rj)b+tOhP7(Whp@o;H?uKQd3Y3eHoYMmGKUdVkik2L1B?-!2-+X<97>t1v0r? z^SLDmJo_^w2zVG6UD!&P-JvF|t2sd=HmkWu!bmDCh9vg_nb+H$^2Akr#4JQ(ByB(u zL)z|ovEC1@%_J2`v`gc2Ny1l4%WBrwU%Vc>u(aUe*(8h*Jx?WWA|B_@11qyo=OKv# zB8*!gd4{?i0XL@V+#m|!^xP`)hwXG_qZTi=Y9{y1Xd>9&gl$M$i0(cTYG=^wGg;43 z?xR{};g79fEyZeb76zOr+*U+aGlj%UL$jKe_TC!Q0(kE3r)v|B0?X?eEeu~ua*T|c zerqR02W>Xo@5^iAg2c8)-z$7g6uhOX$nR8ItrNco^6CFl=x@>g1$(9Jphw7*m8kVQ znjMj@IN{VmzZ7%1i`|3xTZe<<#2oijWi{=NhP6zGtE83OsrrYEyc3Q_&64eo$E=FV zdF6wvw%jHjf95!ubb0mdc*1q1{AAkai=}(*1Qea~bT;Vo5#MAm^UKru$Q2h4x~NB- zXNw7;haO8QmM`mPlO49tR&vs(JXiCR?HhXXvR{5%FRM)R+Nk)wv*ZzE~?yH9P z_Pdkqw{xKv0BYW%TkXBq&iv|~+~?aD8h^??B#i(1Yj8=}-((a6ztrF8&nW4zE1d&+#Jb#M;#rW7-!>-D8(~CSg8VUmqCo=jB>7b_ zv1P2j29j75&q*1p)fwxN`U1H}k~&8hZWbIiAHmKvWbDTgU0@P6Xb}qz2Tn};TBvpiQ>3ZyZ~3GEW`)~_fvro#~_9PQZ$c) znPV_>x7waO1&`LJW}@>H<5s>rQb!Z0*sxVsQs&q-c?0NqWzs;!cr@OWD z?r?;ns7^#)c0;79^I)zj5U1*fRWiZkOeUjx(lvT(aaW9p;{fE3cUft zQ4CZKCq1_|dL18yCUQ*bIb$Z1NYgy^Td>tVyh`x6G{}s9vrJr)eOC?1-;t4=YN%AZ zd3SLW$4;@h#ciA~GOobmv&{9|zCvft3+=4J4hjwMw16LCKW4v1sCFe@d_4U~$OWco z9y}uny_p0xsb@Ulf0zF~=4fXy^p&C432=z9%mU}l>VP{TW6q<;r2V^5T+^%cigzAp z<>&CISFBL`$O1Ki6SelITq)`Rt4R??Uj~y1I&!#RtBqwL(wYuKlxDa6*)zh~!=#VI zS$FA?CHR)LvUnce1?3Ae-AsJW&CFXeADRGRy~(YnBdwLhJg^D~wnA8EsZ4qfr?9h% z=|hpoIQF8V3AJDMk`m^YFs0x1Ac!&i6q!QwlsOJ{HA1b1-zqhXsdV*uqmzGVCtljL*-pM>f7i`^h$WhytQQVUO`E%^vN7j)WpCuO|=0 zQ1SVW0}B}gX4SY%KSBSyXQDWPi!78*eckRELQD!bGOK$;W%bJ@-8QyrXYUNDzx=jZ zsaU^XmAvti2)Y{zfq(t=%g-s$X}m`Wdni!2mQy13l(1ARXz=C6KFl!z1q60-_-wOe z6yxg_98KH3R#HoJAT8uK_f*J2+4li6zSg}CQfS$}G-K`N{ueGpT70!V=;lWh5dV}G zcdfzf@U*z0{}7%QuHD9Z@U$?rl|-b)3+)(q0ny9vibxALFyPV(_pd}`!AIb7a0KE0 z`^zC1xE$_U!Tl@o`64L8@c{oyLJ35T+Xw{?^@RBj^Y`X9S+Kz(hinm7E70n9vV^$~ z#oH&um0^cV10nK>f{_ggAKU&o%h4_2g{-Kim39U3y;Je>rufWboIEquY3>N8m67-9 z$uIR~peLOStnl&`Jsm(N`nafS(I-fRJGShb>xgdMZz((57m>+C+QAYdg&+$X;`;82 z*NAGGt2d1ey2ntm;-D~oK(of2o(xG6k=^`}zAZ9;J&+!~u@~DI13KH0S`t;%_{0Vb zKJAm3;KoLFrTFqAY%*nU9`_3c^KyFJ+3e;@d%yc=V7!eAWf?UIX6t!HZ9{&;-FxGd`#CI>0D!8Pn^$XZ}hy?Gfv$ zD2&8L`SS8d#P{sKk1wqcvO;zv6!0EJ0`B22&=T>!s&F3W&VPun+9KjCT3st^`N%x# zM(};MV~herQz+L2;s|CjV*b^!5G@TZF&HHMs( z$(Z=bUETy)?n<$qEWVvQBEemePsoI;Yu|`7grO(x&jy-CUqjlB!2^93oiJ(H-BsSI zyE-===Uh*y+@*ZZb_av#4*i)ejbXU@RaEpFa#Dd+>s|Td;FfRhjxud=^nP_h!cHbD zQ_Ydt8NjLnh;JXp85WM>-XcSn$ODYKt$f4WsBup^io{(9Go@zQ zAxxK00XuA>uX=vg7Rf^GnBbH|sHniGq}Y^MyioKK3&~ZG6Cf6_qo9K}5NHw|TbCtE zocPI}(pg_7BHZrJnN}rPh;>$T!Z^)VpVKm~0|ru(e+f4?(tnuF&9*^6jrJaT9<~QL zMa>X_p4*pV9n9f&K}+sdxL~R>bt*o1sx5v^)mp6ZJVPa*^e^d5Y6(MbgY=3BP*uQq zg4J(nzWO)e#==|`I1wu=K>`u&py-ZL>c{@eaGy2AhVZI_<9wd-^mU3PqNv~J0n&M? zA&>F+r=9hTV`ys(AA2YoQtT=}ze9FE^NZ z4WM1e08zc0uMq7YtlB7nPMcl>nW*d5%to5Wzo94IiIi;Qyujg}CWWMz8^vQ|1bjZ9 zt!QISW6_K>F!2?%=){;3+s4G1qLXe~r=>Cxp!&50Vwkf(kX0W zp^tLvK1#N5>o`a^f^m-}KMS-|8>mg9RU-2a_m_C`Sf=3m z8EnfIAT2THwz5-0Q>*3%pz^H3@;W99h(xZK>(Cw!=7AuYLbx`ygwGM8;h~P-wRWBJ z+IZ{zT297BuP?Eb{H+;A!KjBB=b~JD^A&|)kkq>!2EMmAb1LsPm3UUV5+7Vlog|te z3me>kr!DB?aCtmDvNGneNwXbxR%>o8lW$kHQMN1ioTksgD6l^Ctc+kxPyscmaCN+c zorTssHzTM0R6-vfv71XVTWO>~jyZFzTe9Evk z|4>T*j&P#Ok$@!dnM8t7CHQk8vCQuy(lz=S8io+Aph|5eI( zk$0|%r|Q_&n-duM9x*C93T-cC8ssghYxek|k7%6!Sl z=aSl6d;`eDSsuKf)a-gbkStFjV!rNZql!Y2EUvmlABdyaBSYyEU}q_MRV$`ZD7C|c zPOoj56+xR>v?JUj=cnFzpZ^^Gk~}cmWFmbn!{lYx=o_>L9>{GlBOnUkJ3Ux&x-%i} ztX^)UUUyfa333$EHzb_`sIqc8?n(`2HF_^fS?BD@r|?1d<9@_a)_&A9XX5y{PozAbYU$n4JZT*4&zh3tU(ymMd>bDn2qKY&hD0+xdl1KF0^R*Ad&E## zNW=l3@*Jj`sk`oZob~0Xa1awS2C^-$O0h_gujM6JFsv2Os+U?nOmX|mDLwW-i$3pg z@Y(LlQllCi%Fpwh9ej_SWWr8&f$fp$5F zn4s^;_9HD-6cGEgI|`9mhqr{L0yA9`&_bFfMoi=^~(IPHoWXQ{3By4@Hd*_A7S3clUj-~M{ z7W(0cxRhh`s@vg7&0PT`^*X4}A{|n<#!CyTOC6$gCgf(X%KC zi9j(DBoOUHecqc+G?~j|;%rGH&#g0ZHPj^$E$uO;c)Xw$9^w|ho2qN`+MGnkrd6HV z@KY;&uTuaLiMgTdG$VmL?_7S2P|en89^^DiqEG2I5~G@;xs4H#$`^t9X|?Mg*ND>l zhJmBnW8Qit3u|eOS;`0O)t4Rg>!?Q5T=>ZU;U)Ypj_P2tFtiN8yQtnj#n0B38cj%^ zHj0lcttp2pJR={iYe{3HBeV5z)<9msR%#-}$VXyJ703aGS;Ax1Gh8xB^Hh1G$99wT zu5+CQ2`BcNamYm}w<=>{Sx+9I`t~idWARo!rFbCHVQ1f5^G8j6wmpH#?@#>_%i4

l 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

(+T|>K)<#RSe=|un=+oTXF+aB1=h4KGpmz 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∨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

(HD90`1T&Oq; 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

cge~naK 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ʀ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

xsk~>hoYJR2i#MKyHr@f%>LZ(H%4%`mh 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%0Ax=Y5KkRHy)?N8pZ9K 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 z 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 z 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�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%0AkFErC!&URl%FkneZaVXuK42Sn9Ck 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

)WIuJ6dVb&3 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

O0z2{e05z;yccBLMHg@PKSO3+1J z+DoY-lbGNtaU$DEyIO63du_-0APKG7s%kDa_M10vW<0}ek)Bh_qA12ZMLq_*bGpLR zKOc512^r>mYFQa?oG&@YOpt^gc8EzcftiHnB*1KXiFCiEiaLm|G7-7!KX z>A}rWlp#TAXii!t+a$oz;Ui)MQ6W{H2t^U_Bg8o{r;k}2BlfgTSd4^Z03=Q$9Ahdf zNOfmDTR?tkc8rJ*{Dg7=-ni@Kht1wwKfK>A_uwAPX0sdjVSi)U+r$RnK|pvKp(hV< zPmmbHSZacK!F;PuZc!dOBT0uM%zX@bcKm|<;?Bz`5hGh|ZHc+KlG*TyN=NHlIdG(7 z%LY&hU|a+^@4A#%wqsjTtBz{gjcv7jbT@C`xqZQXvbBr<8>0*{=irx0j)>!kQ2bBR zZGSF=q?q~g^>_g}ovypTmLYhxT0`*4DMJ3es_Nx#IMl0Tc`Z!<7qVYMb97f!o{=00 z3b_CpE=QdqL)9#!g1+<@vk1e?erQ%O zFrDU1nmy3SMt}rTj(Yqn#d;?Lh80mmvl%FLh74vr53&4*(wETDzX}X=%uCefR1TB2 z)ym`;I$%*DJ4rSxY$;eQ`itj)$zO04iES!pSY(X0$~vBZCE~1KJ%>82j=6(6Qm3rm zi}eGAQqfMrFcf|7SKJ35U?J>Xi5dZw1PuWnc`4o9*4VX6+lj>Z@7|VziW-+Jx4Y-; z+;h)1x@zl|u{>jt!BZ_Alu{v3ZMpSeO7Jzp;(l?1TGZ4O&%8n+2YyfVVZyaU1#?7# zM@4+Prs7rT425Yf6Z_WKC{}h#fiUgP%BuFjbpc8W)Q-mL(%6Q3Y4iYEg`9dX61VZa z`D{FWTup;i^yq8uVapv9(s?U4oyP*U($|ELe`h)?kOmbv+&jn_)4V}POVCPn8&_H{ zu{G9TFxI&*-HB=F7F~myuy4==kH0AI4QST zNHq3)pZ7KIJ?H48*c77?ff-^2Oc1!<6laJen}-`zYDj5PF5kVC4 zoX*svBma^wQTlMnE437QCW$@Jmi zK>Y^?;8%)xNeO_I!j~h)DJJV^=OM$-XgQx7h7WbAtCA?S0F!Fo<#%wAqM)HO7$@#` z1QvOof-xpZS}xI<(M#yLf+<7cRO_K+X#3++_4>sn%h8TgK&MwI-a?Klx774_yHkWSobZN2LWSG zZTYwQv5Q}>F40Z;egCYFLYGCzllX!G^4TH*KI|6L7DfK&fMMOn3F~2G^5sP1D z!2CYijg#Gf|Gv+gV{91&R=1@_R*yHG{Y{L@m_3*;>zX*k6X5Z#RSQgkOdyZ5H2xC#`*C^;Em+O9zLM|bGHJ!Gb#9UY3L5#;}ki-<=L{tHeZt|x_ zvYJ8?h+K9D5%AFHn93HJLHf-+tD2?wbv*V~i6T~}rYE#=AOaJ8J_&pAe(W88V?c+4 z=?`Q*lQ}JsNY~-m4#c$`Q&ZccS{>HiV$ZO2gamhl;>{pkadoqgKeE26+1lORsh(a3 zhKP=NYG`n9Mx9iJs_rBW2F3+NEBq`6J?pjZ8EUulIiO}OG78^6&T7WFpW~&_NXU}M z9m9x2Rd4Ez(#Q6EtzA=G#jDR1;};}sODy9z^r8}b3)8Z*bSkCpneZXut6=goi`CyG zvd)P>9sPIoqRNNB4Igy}5x_HVWLs0H6z8VKR>TWPVKzKXi|m!NvSIs=!+0}$stH-~ z8d;vikRv4X$5gTE2Ei@(-%QiecRKp>W;1!V|k|Qaqq>dZtiaL znJjGkjphA%qGDU0?3?ZRN>Bxj!d(MVm)c>Jb4k{MF&rLEIwf0{HOritUk)y6{{;QKg- za}M8m|4Ghe6me35mLvysJ(p*|^LcgyQYe#s0}hH9p-!Tx(twJJ=Y|T7h*D(nCl}?E zayY5?0{6?4RG_tmWvAL?XF@O_{8HQx057PPj4V#|F!U=E#0S9n1~P zY&g^+I~Tm5vyI*L%5X7Og)N?boHytv!w9_#GA2{jSh-!4h2YM^QVMQEm|aPC6MX|( zum@1xxvk%v5v|cxWn^W>piQ^|qNQX|+EX;zyjJuaAv+HC_v6DwmT@eL*bo_3JS~#f*f&U488kT@DLzQkdOP)W!bWBE6{F5(%Ui=s!YeiWXYrC z1VNGiUdncY*$H-;F0{xe@;#Ei`A}p<6itT_4B;tL4pOBN*cO63*c5z*@c8ZV6B?PZ zQ~nlPWN^U8asZ!&QK;b_k;6Ahbz89UI(R11=JN*q#Zr|lEwu(|ivy``=0TVQw328X zmo=%STzF;82nvmYeLIEw>fQa_^7i}sHl(UJ`b>D(3kQjEp43a}F@e4E8Aq7Evn5I7 zLJ3Z42Gb}qB1gv}SW;G0k~S+WEcwk>G)Tf*nnh9R{)#_v0=-$6%Hz^%jWV#CwJ|H) z1`dbK(wbClFQV-Vcg7>7OyWAOE?8gObx3U4wfhESt+QT zr+hs#z~u|C6>poVCI-*XlGS@E zrH@-BI_U=0>zZ0Py|7K=c?$#YFE|9~vC9Nr7s_zYluxWY{*#`pHm4+vJ9C1btsR4R zu(K-Z(rcpQxkJCgKcyl};-CFyKK);V*>&tkt?eF@r$d1*KEp@Q7ewOFAN;?hW}z_d z{5;T;=kF2JzfAB2%Oac2@8~4-C7G+!7|&0Ynx}EAK$H2hCCBI(y#b|FTW{Jh6n@XI z@B)8Zab{}E3(kP4Z%dR3ktbDFHyS{!Fz!8N;Qo_Pt)1-arJUPn?Kx6zoXA+s96R zP#YpICk=!y73$=Xk?@?K6P zs5{7TNtFy2wcjiWG#iclSxiudsf4(OE~9Zb?UYi!gY|r0fDF6HVzd~(a!!XwivQ`v zo0Amy!$O0!{v9KCKaI_Nb#JOmw26`%f%r4a9&u;QU&F^sX5q5Irs_t7!9`OvFoF3&@OrbW(_j}bUieR z@E%Z?iM7~}QT#EH&F3tU%etRxIqq~F;_7ceCnCeDokyGHtgTVhrqIXR*I3Kjh-}Vn z(o#xWOkJbz?865V6MXjNdPDCm~jIs7YOt%n6{u&uo=R)Pv737 zj59LEzt{>X*u{r&1UF1_6!005+~Q<~pnp|qWUM2VVNrN==~ngjpzccIZ3YH5CD?40aB zm?-fBiz#?Jel_@k`jV?VYM~YPg-l}pP;!gcN(hV{;4)a1uly?5La7@fS8YR}K+xxMTnpZ;w39G06 zfo>j9uDMn+CnP2g)llCc+qnn~a?p;|c0YP&CS)XJWgur*lUW zI*g*h%ZSulCm7jZOY>BGVrzttMyXc0w}ds}PL##?$h-?)-qphN=i`S}Rp(x9uC!*` z(4XF*R$cUry?efLjkeVCN%(cX-!!#Z*($#;WsbF5w{v_z+|alP>t{Br2gD+R3uxP5 z)G;(VAP&b!6R6>-;hHaDzuOO+UGNv>8QX5#IQHFN!3HTT73$7DyGdF%HM+s3Y2bJU z3pj?M$h6I^E`~@u$#&9j-@}V2lA;v5>0lpLK;VcxH=a9>PTp_Un_e%EGfX#eg3CTzw_;V!;f?PKq#Yf z+aP5@6DF7Ete6W-xvwP|J#0!$DS+M%DZ9NY7PuSYhLt2=b;FHzak{OA@q#=gc*6`} z_;*y6-6+++mvO$@#;Xnqu0cpRxdZS8q)u;dwxE>rB2BSSkpnZvE6{5Bcza&tOR{S0 z+J?K}T$W&4Kb&MJU!I4cT_BVAE^Xm5DdU_ji!yVMrZ==EXnA{CtoYvwyTXsRIU^a? zmXWJBkJ+-wxjM2i}*eigfPjA@bXa9O<{2wuH|>nCYUDXOifwTno$NpS{LC~B&_ z_q~VUNq`tb*TeDf%Y1S%xg4QW)MtdHxc{yJ@M&~&KEAk~TztLaU<*u>k_dr3z~^5t zZ#Yy^q|~!L`q%Y%baMlw1Qgct`+z?VZ$_6FS0fkeE~YqzAfI=m4?lk%k3J74;&Ab5 zRpM35die zA^NW2j@g>fw`WpLa4YQv4uF=H!x+|f>8KN&8dh~cq#zukGMWQmoQFeM*5F`I4J&{D zK=L&%3HwwePqs}R!zH3VY)c$C84Vp5av>a`T|pKC+fWl1AZ8OtwIGuw7f?}HK_g&$ zQ?2|dG250oqCyeN;N;|_Kl(c9_ZEb1Qs@R^I_IfdI?!txAr#RC|_aN19`0@Me3xr&7M?* z2qh^MzCELueZtFln=%?qEeepUmV<$n_|H(jP1C3q%u4^y0n7CN9F&#M&(L(%4(hJ~ z-8e?#2f#k%!8>R1svkj$=S@Hsm*3{1g7T$G^StNe?~t=doU8XP(PY`^mZe!b&d@SW zDXvjwVVe<$XCo;6eQA6&NUwoaxhC2JCqyf(?4 zquQ(x@rE@p@^MSrl{YRZ8Y}FiApZ1uH1bgBSIJL>XxNXMz&d74lsb@koOW!c9HFBhXoJfHuEg^N zSuO(st}l@qAwpg{5o)pb9)TLDTvhS%L~DqCAP^BbGPWT`s$F!$-=CbGcUnJp1?C^ZsjxH8 z(O+_fTFIFX4xBrw5MKQsAvuWSYy1~3Rcc!1-m7X8eTg^Xx+m^{rM#An%-x_OW|*&X zkWnX@k3Oi1Tx2QJk|(qpW5$PhHErWw3f{wYF)75nFR&D$xJLg_@kSv(<1 z#IhSB8fXDpp3V%Nf&1Y&StF$$p45-a;@-_L$LpA`xgTVklvkMk+kWNs83aOeoB^>= z)oTwq{_wQ+%KfSlaKuQ8;6vgMTf;8O@qJ@-9aNJNFCKsT=W(7FtXAp(iV;p1=;$>) zLW=_2kZ%iNBJ?c>Mq-~11cawTX$=X%wSJBwbkw&KgE*5H-;9Rpak4glD*pyTQ_gPY zjI|$3g+*szQp0$L#Ug2PljNe+iiAS#3!2nhO=vf2mIEF0gwp8jT$Y4Z zX-I;1!JDcR&`o7)6*iUbP?$;*Ge3d1*3s)2#k(bmBpMCgbXw=1sV~zKP1F-p%^P zE8C#v?WEy-b1mnO7h}jhPO`R$h%lYDkM#!wXglZs7~a%P!u0Pm^xiUmeVWEO&_JJ6 z^!mqqfFSNIzEL%QSozrEc=eH{g3PQg_WDbJ)k_$k6x6R2HEa(sl*Ekd#rd!dyxYRT zClxO%^|$VzKN^qw9&Q5N{2Bq$b8dT7pt=u20tE%o>}|7L6d3TH*LwRz)Z6jx@JNGXsw9eFabG+EHIMG~&~8$HhxfSGx%X|dyB)RLLF=oC zw-#tQ+8S?-zhCDK9w0L8|3L??HG{%IfUNq&W_8u4FozY&z) zo{68plQrw8b1yh`OlcSqSKv85m2Pv90fngmWajd+iJMm)Bd)vdIvzNc20Hk7kft-` zQk~tjf!CUUeF;+jJd+8%^Rz~R_9j7Nh>A|V#l##o8iS!sOyXLyriTolv(BtU>T6Zi z{Sl%ikV*2-KSlXMr+mKJ*4&B>-XL0#yf|`Rs#k7{b>%gNBBtV{{f7LaNWUsj%?jNU zwnn0P7tu~2Bz7@LRUxbXH8A$bupId3Qmi>q|Ji<4hyczpRmCop%H+cgbHP@%$2Qz~*K1;R)Sk(;1V1{Y)=ZcQrdg30Pv^zUGjy_0O;E@YP+7AxB$C9~zmy?|(D< zR@s~r`vUhB$*akax+0m<`l9J@BUg7pz0eeGUzDn_{barXs95R)+Y#UdnSt?vg@_{s#w1g;V3`Ybf3b9$*nOHP6; zT~ylNGHJlpWs*RUUn~tSWps_QkkcRONV{?DWTMhf74c^}C zj-F5vJv?bc&C6Roh&$`GVF?e30?St4XHv4Q12LV!CgY(&Xw#)Ff{gZ&-&F-#6`gap zJiAJyMaq`F3pZ`6^!E3u4|TFq<>embvGuQZh2L9La1b=#M=-2fBkOn@uViIa=$1PL z!rHat%4tnCs%}F%9!*2q^PnO2%9g(M-zT2apAOpG1*^?I0J=xHp&ujPasDu=btg|f zT-vlKmEXd1nsFuqq+ia#%IH?a391gJCKxry3E=;bZJbE`e*Phqh&GkwICE?Qnm=}W zr{*v1!gR$2`Xj7-PFe0Z;rTWrXL4uN8tf59qAI2MmqG3kxZAJJAXNFzW|-z`>yPu? z&E8!-@M+(IyEU81w9%@o-#?@VZ$V)#vdd zMHzJs+f6dMLaG!U<~N^pr)B-GlD&WPegmymO>f&c5WVYH3<4LDvvzz>YBy+-#TMwc zf!muM1dKGYh?Pizr0fQT|9gj|6kD|1#R98?C6V*--n`M!??2Q<9YwRt2rl7!!8PQ( zM4)@klmVH8DG(n2di;nYEod?Qhi#CIt$mV?;hqVOC45Gt?f02`yQP~c?aEaut3_-5 zr&KOj-jpSzvVK*3Qy5?(gOaBxG^G`}R24H^iV4&uGFsbm+&RC`pY9&Me0#8=97arAUb~yz&TUQ6yM}nn)0(qIQ(lORS|bbQ2jWR5wx7 z=%ek2AGJcQ3DNpdabc`Q&tQogoBwrvCq>RTC%Z2C--M!UU-a)xzO$DShYdP}pDc)+ zIC!&VBMh(b;YW(KEu3Hd_FCmqSWs0h1qx%~X<6d>Yh&uhw6`GEsn=H*Q!kNxx2VGR z8{0ZgU5`#XrIZ5(Yp)o?;K`WXRTE}OkB z;daXu1T@;cnY-7DZ<#?DNoPb@ha$A94tv&}70UKygAGdXaj@R-CDe%vfKL;TE2Ep31i#os@>>Q47e!gl(a1h zBd?*JfNyIUrS3kyhOOjTz=6J6Wc2+fo!fNbGV5e)yI|yA#zziEynJHU_lY;0yd^sn zVSmiER?=W35kj@pU*q@Ra_vW4Dr(m4I5(h zWOZ>Fr;AjUO(nkAQIqs24AjfH9BQB86v2lE9z&^!rab@5it%ws-o zuJ$4E&Nn^ZeRPgCJq@xPX-bjA_f1vPj60m5*8MsTzRNS-Va_w7^yt|5dCNtC|hi()m!QTNoy!^tMY1{5j7aGky=Bz2HIaWP7M@?zR*u~mh^gGxSvU`I5NCtcD zkhZ&af**Ff0_>xG^b5^a$!_B~5WVXwcyL2h;KcVy;z=(aq=8HlB)P>6gON%W(KIQL zls!hn|2{=hvL$)x1$}5A%9-uF?M}?Um(~5F}OQTJw2f z36mKSJOydctjv{OFw2!3l2QP}y(Nd0m%ID?adQ7WajLwJwqTa5m?0TAR`a*YLPl2H z7I=dG8&&BP&`<^!!Glp0NwxrkB#=^3dP>tLD3!K2$P6^-%P6W$>v-~CslgcZX!_pR z={>5uJc@YKTw15219*bD^Kd==sASIPd#B$1zo=N$3v&wc!rqWP(r%9Z#&;#2)ao#% zA^zEUo?~bN?0LP&m2^HYN+m&B=ewjX>?~btx_usseAN##n@5Zc_m0==tqikpw-G?7QKGC+{MvJxZWGoT@#6qv!ByW zlv)fr66a$B_Svo-KG=SZy-PFWHsdcR1#f{NJwuyVr%F@{dGFqInw*rx*UvN_zExag z^@hLlJlx_s!Eft)FN9h__OdQ9&>c-f@*2C3FFD^{-8MVJQ@CZf3;Usz zL}pBDw(e2WU@I-d(QLs=x>ZM;uNt4H{jxnF=f2>`K;r+QTCWAWZ)ZKBL8@1rL+=14 zBM8w=!_BTMxKi4lZK6%|KXsDLZrd;n$M1Ry9(qWMAbo%o+pr|X0<>F^?XW{Gv`pJV z^#y$_4Tin@DB12hY&Bp=A}RjKA9efQ4_=6yYXR5rZBP+9r3rZT7!tS+(m?R|>G303 zIj~VaVNV=f;umrWJG6>4+!L|;wymcRyd+&LYw66pDf-TZnykxOgLM89R6is@+k#e- zY-Cy6xnM9UXIJ1gA=`c^I@f#mhwc9J*L_Je}t&Kdq?He}iNm+MG<5xktX&UUJQ%R6nb z(R8qhLdUBA#1`q(1DB|$L)?^MR9*+e1d6^35dztkMt-|54=|SoJy{A^I&Ut3^RbY2 zOu!U;F{1`Ut;%KsF%H^P{0#vyGtS+rK$1tElg|kjwC>j>OOce$MyF{7+z_Erwg-dZ zDf=aiQdmbar{6zgsU6ho4qoKGN1cwf9SSShj;a3`D9Ji2HA92h_kt1VIk7Fm{}_v( z0{HW};aA0v&C2qeCV-2HGF*kasro!bxRP~UZp1I~8--NMPUA2X-TN!NV3AZs-Iq>) z5gQhaM%C;9RmR*T7LIM%PCE$l@8d^O+>ms#iQ;>Y&*R$JT&r3kBuE2B5eoQst$u=_ z_0txV)b{B?z5>5Os5O9ZwI=WP7BsO^UxlUN>$a*iRK$X7T`d*}FYpa(6o&?cHQ(Dd zf5;1mtvnR2b%jbM2*;EYV{oY8b71s=uzLcJe;n7IXgqBB_}nkA!dq#!cOZPB<-s_4 zQ&3|y6V<0^IiJQ_W;slN715+gm?O*N9%U=2Wm4eXj!NMeTWT}nBaNUXzK$|_wmhJt zRsUSrnwiVOx8fgzn^7?UeYMx?OWmC6z`bF@en#G9T!6Z9~S_1bHSfv#kDQ?;bCP>2)2h=U%w_*^!$b|5**i(QsaTJk71-hAXXCSw#B_2g zv*eKEv*Yj4(aQL?j8ss`+eZBI{|E0sWIqy9N)3e8I3>r#oSTp@T^4Wf?lw(^g2o0P zpZ{lK?$VEJcujNqPp9{FuR7GBf0a|oZo?oDz4H|ua0cIimLp3F<@%bs7ko7%zN`5!?1i5xgdnI3Z!5O(6?MX15fhZJBS+9?zR4eazRKV zfo>4l`9%RTRpY`Ob-PWLA!JN}hR(-hLdW!}s6+`Ba3%l3mIYT6E3ww#IY|r7N}7~R zN@~v~{VxIeCR7ij)efz9V(6&by*r$nSzvL}6goniSbGe)f+4Fs+as^0NgJN1qUQ8) zNNO$>Hi?Q@TN}@d4Z&J$$4KRcoUUDzu6kD5@WmPu&9N*JDiu?D@-ivbm30=`sbj|v zR6>~sS_rB^PC0y;XtZ_ceSDvo?9^tnnZMUfiXMH7AXGw~qZ_Ys{SH1?1(-q9way26hT(Hfd?tSs|L-FM)irDswn9&L@Sg(Fp+LV?U4jpGEFo<4z=vuvvMsK;-Jhm3 zVcm_}z55dFNNWU^=8Y~|LA0p>z}S7F&4w=2$t z`~bC6%Wi`(5WM><_K*WXt#a=xRqvIms<$MH9LEGp##pvVRki$k{QznZp=qT0zzTME zW_G;XEoCkd61bpRVnX31mrqoXeEmvgsm%Jh+*5vwP{su2KseSKLJEB%uxF8`K0*qQ zN31{_#l+!DRGc^87d}eGj@VF`8D==Oh!-{QsV0g^!%A@i;tlUOU2RTqyQCiJlvv@S z?QQEuF|tY!M-SUADTNhW5tCr0@JHKr1uk@_-h{ttPW-cx{C|p9fnf6o$(>f;kXW8Z#W+>G7n5)i@42AotWVQ1 z`ri!)3d>^2{k}}k6Rm%ZYV-|-lEF{HFcik`{VRIlun`P*K?I`*4;n+fIYUZ)+ZwyJ zX*-aR{qJ_|wu}kb!eCgh~T= zr8PM`ThPQxJ@Z@b*DA|2WW++)?kG<`N`o;>iezPM^6F>~HXV-<9^)k|6o&$YHGe_# zx_9B)rYL2Ca7;Nd2EUUnhqc_}Yg%+f*eyS%UT)NmXiRGVb7M6V*`&ey^3=bTW;-Xw zBMO`wXLHCt5Y}RLjEOP>a`cAKzjFJROYCU`s8qfW5DV{!Kwt)WM?qV0a|lGwvyS zYG0dmm1+_O#wS*L=^d9$dRR8{aCsk} zS8aU5!9*@VpU!z3AvoW7dH#^=TmAv}X)UZmzok~&ZqzUkec!JzsFjnFZ3_}FSz0b_ zA1Wb0&^NYh-RxwODr+0tX<4E2?M&{DlU^v1R@r!F&Wz8DPx9uiOyp$3*&4KD5s)v5 z`~W;k=ErainUHJXW;U7R8c3Y;$fSak_2ohvl>whQ>tGVGT)?kX8(rBfI0<}-e>bQ6 z3QGU^$u9$3S7LNKWXpvom!Zerp z%3VcxCA7q>LOmA9c}5DNjA>1d1dr3zalV$ICch^HuL%7NIXgZ3ctQ>ctvqtY3IK|M zh0os3PI)Igqy(Gd{!+gBeqNjQ%DYtQH8ViAprJ>PfJmm;+JUh{`jGi$mPQ?~ zi$z(p$|K`3rP#)f^vjTHYsIx?l2lI*I|tlkueQSJb~2i1i9C6JNU91Q;4~)Qv);^j z4(nl9ZZIHp1?H5;g8IG%P+SU3u6V;bKE|MM%7CnZL)8OZ{F$c;bklZHry)%q^5gaH zKAp9|^=``D>uOkwU4QRo){wxcXvt3qby%S z7#7pjjnh;rDAsqCHg41i3dUlaj1EKBh`(?N(j*~yHD=31#Iiw@^?W|-IEsY=7A3?R zjtbM(tQ-~1bQ`!O`aMUUIouFu5i+$U!r~^$~(^mCbWF4hFpoteX7M<5VBlbS*Qn1n>ErKM%U(eqT&#A#Byet*25I zxAlR`!$_I;FlBGy4&mC;iBRSfzB7?4onNBUkQ*=M#z%{iLJzHUKBqo;L0(!kjl#BT z0oiv!EgNm87$tHxP_Kb_S+VzPS1-x*A?ecCGj3w9E2nxzh2-L5TVGtzU?2xB-~$Tt zm;+U+PUSdIo0^bv%-d>FDi=MSU2JO5M=lN502Dj1~v@l2H_M|C) z*IdYEWTbp|IZWUASj5`iT2MZGMzhug(lua>LYH{0Yklk_8Sl;^Sd@%y3~mc={xF?A zEoWJ(cqA2^-iN(%fPH$@ftqFKTe~>!23G$qo~T;7B@@NN>;BT2y)E zSO{fhO0a_-k+g5bmjM3isw$NYTyJEAyw$A-s4g}1E6xHZMThl{q-A@@v-rjxIyyjH zd2jd9Oj;73S{>l+jxfz(_DaP6GlSA!SJLn-2`=FEeJsAk4}FqNPs1<_hVS_mKJ2hf zXuGp%Ah^J-0*+j=)L9~0r*g813Gv@?(~rT1R#lDS)UO}E&ibx#jZ&m*jEy!3g=-#= zjC+4UTcJrl&`K#!3E8&ieK_#*5K><06yXeX zS^8F0S62u%W0uW3={yc{(pPeV(lKV3MSr%;YqQ^0-hkax76$HWOk-kCJD9YHv)f;# zpFFZ=UlLOy`S3*~ z<>QZOn6_Gjw=MFP+=V?<-jQBj0f=}j>`Q(|)5LN(xmdu66K#|Y-8o{8| zis_7HU<8;Hrply;8x};&LzPFZ);vRev3ey`2>RlJF3H4^S~8esiDVC#6!wO=3LaEO z=F-4au!IP;jd&5wGM4of2K7R;fND3Nfw?ml%d8SHia~JzU|T<11RkXVpSR@Gf(lY- z&xr%|n+m?5ptBuS*y_yrzWD^aUK#7UUJ`>J=bm)*MtBMhl{B@~B|pn8ZFKhDNwH3b zre@r*wDp65axTEVxrZ^ysvw*x_i%thV~=IZjTrqYU$9sOz7KiI3RwXcnOtSh6pwZ_ z*d-~CAvt(#i6RcXi&EGOz)ToP;s`*+tV^ixbCgP>4G5`VO00T=uw{ifb$HZ?LW~Mb zKVRDz3ZYdwnAP!=v=M3d&_M8&GeLzHz9@Dh7D~s(_ZXv~<1%GZ;k|;yzYxjn0%SQ_ z;!(vH76O4WhU3Wr6-`Ah1$u z?O>JCBBTcsRsF4>fv2)V)z)oUz z&dD|W7!PExY}YZ`k)3C^66OZa7Lh6HjzWywHf5|yZY89i8zz^T13PKoG0m!No8y}& zZ=PBAYg|6A$6GJeTqpaqD*!S()WdrCo*8DY{VeP1Z?G@%xzzao!WK33b!35{A z85K`upBJlr-fFw#lM+UBgS{V90Zk-)0*1EMy1`MbGDNTfQENAoxOqr&LH9dAP03i|g zahfen%^I9n^ii0~y}8IB>LZ|1oS^exr@nlOu_)K-QRo^O%kwQRP1ESb*a$Y1`QSVh zl1RH?fOszw?%VCZO5tI(7s}3D2&gZP-6?R&dc`w0^Jqj(oL%zf4a~R@b(7fCD`Y5= zXYHI(th{+DrgN-j=RZ7_q5)0@SB6*y&I+>%ZJFyBj5*Y%*&K#hn36TBKKYVJ^7@bL zb*I-T0PrW1M;vEO{2%ccxt=Z=2K^A}@+D;^bwzHsyRJelA?1xQ)tYZnbZX(=q44O= zrutI=r-0DLl5$6%u-M>+P|EahFo4$MJtYg6YNF&BFT#WG;6HSEHu(GCuMhkC`vb6c zPs!gy`FqH|hteCbbY=-!J($*Q;iG=HD^8F??gdH8Vka+H9nmqp)Gix&ZE~8-gFxkN zr(@h3I1u&?=I2mds$OX!c0yu42P_FdbK01#6PS#i?9kR;p(flg>Jvy{4%e;-$~|25 z5^xb)VCQ~fFVBhavBJcj`i@}FUNCndmjX`o-{T>9U%b3~*En+J=J&yOQ>^)D!Nx8r z6G#VikyQ0gW=&gbQ09kLPTo&y>+e7_4Jp zm_wn-(RGp7Y%s#fcip%7_^Y;;{VI3>x_q+}pdG(bX67>ldjld^q@XlX3;1N`HuY=i#9P&DVr-&@j~% z&yp~RSj~x#+Jg|&3XMTN>TCnla5!oz8&_7T%T@oa4ffgP>Fw?R9B9i)m2w@rtzjz5 zRcQc#d9TB>-Jj(B@9=2z+p_+>V6 zZgqO8Ux`a_`T|wlEqf$?rDBSA^_r9}ey+f;e3FB`4ooeFx)d8B>ea057GYSn{sn!J zO-{ow5QX6PkvI*;w*>Z~V>pdaoNz zh{Bdzjf}%%qaV0p&C3(7C1*}_YDY*d6iQ$ct%Z|t@0<~pcbt3^2(``g;e;ISKG1*- zvO0`c+u_u{l7hh|J{bD0rCM?1Z_lr*F*FCe64Pj~or1}=7O%Y;xS=drrvja4jwZ-N zbO~q-ex(lC5KWWC+PFs8tJ~;i3hOLQwxw^@S$QdUXW}@_(#Jp3o3tp3*&?!2xX4vn8DtXVTSilp)3@#P==#sgRu}^=9t(7TQae> z^!9|>p{gV&LMSIwf?sG+y~VcxKC#^Lo^T=QsFvUgUf(AjGDLzEY-QF62F`s8(Sf?$WwpY^FbWM51ms>kJB&^zWY~oKaRy#ZCcmi zF_n)~)O0$=V|Y5}xXOIy5hv6}cU>)-QJrKeXRXQ#l9GSum@xkw9AOG)~j6ub~OPVCcjz zwwOo|oJWO$`+-6G7q-rSS7BF+r^LCr4TRd*lkI0bI>It^(jsZ zwdqj%Y%&@fK$gN~X3K{er2wBDmv80p8|_s~Yui8&zUx=a!C(nol4Gfp6gMfvho&Zh zUR)#A>yfpfS9e!UQ{(^MeMqa-!xVb14z@J&&Fh=l*_)45S_MHtaugMz2%EI}gau8P zK~M{XXG}7rl9k0uO3wDR#N|M7pAjL3!Y?S(@}Qi|_GKxBzX}}T)JC_lh0#FYnSr)4Vc%0M)4fT&s0>TJ%S`!xf;rOFw9OJT} z=tGtUaaRB5An1d1CXuenWctXE6He1Nm8TKJ72#M&5n8=M63)q6To)o`i3|s33vjp~ zDLE3F!Vm*Qw5TTa8CNW;hy}jF_dy%Fzy_J4BnKjiLYqWFSNq)H=5Tx0cwclXHw;#- z!@avZqI_!Q`X!7!3$oYDQRy2nv1+6}Xl@3>Mz%TmT2@Hpi3)KEK+8N=3=cAUDCxab zgpt8&M)iNks>9mJ6lydyvo;;gqCXk4CXC|ELyST#JA;<#tt)W19hzF&cH?IJr6H~< zRy*l+fbP-Md<-B$63OV;B@-`3GxAB=1z@w}D9Y?7#@06Ow4qOG=c7GV7aOy7y>EKs zhGEp6j%{yXc3Vwt-^ydC7vA&Wm8I?5vg~>GtI~;){w9+(FJQat71>Db3d%|4?Zt_;4l|eF zlsj0BQg~*6thSHj5#d9I;@fz6JB^-JNWwG_qrJzvFlNIdC7yJx> zmS^D>m0vIowF=Y6>h#n)?2`d)4t?;4F%2%B7&jcEMzXZPky;4hW8zB(B@2{9c4@qR%KOj+GX`)z)TC48S7Ywrt3JVB^ zxK`Ew-hq9{+D1KgFD%2%_kz0*L5HT<+%#|lKeS2E8jHYd5Rt&QKpJ89wEK)sYA(tb z(IGdN{wsM6UxYJg;Te&S?^NA=WL&7w%#4N<}Im1B@0hg$((uLGnNDJ`%z zbq)E*uZtg@ljT`W60a8b2=4}@YrfD$n%}WRg_j14mNCCIY};ixb0`-%O#Le`^nYS_ z+kYI~{KNn6<`2D)O;5ux42JLd6+Y~+Rie8yJ|;8}yCHF{G=!F@Ws6r^AqjPg_&*lbi~oadnY^!O}D)AXx#)^~n_JDYe-?&9X4 z!u1iOlUp%dala&^k;$(r)t+uSSVslsIBYsZGWx*Z^63V%zjF<~N9ZNg73BkTFP$B@ z!)ENrPC7fUmIdYi{#M2}GWp1386A!?Tk!#}lC$+^aRhTNaxCByk<9NB_w7A>n8L0c z#-XElLo26AzKrGj_%2$~IE)pp67=@-|MJiRvO)kY4)&+7}d&u$8#a%AW&OcqB z+pS!VCgTPkxrQXyM&A+(yOZtzi5`{VGnm*F*#qzq!4W7xOz}TkV^SMTq^CJJpZ$3wx zk@}&}x!CK|Npv9$s+5p;uj{%@rKp!@vRq3+i7r17N4nMq%i#6IO6}D6{P7aB-w_hG zW5W_vfn{w2Mgvy}I^!xKdhctfk*hJ3QV`KryM))I8%VONMiYwX!fu6WYIk|Sq9#44 zo=C^iX(THHS)C}-TC9LmMKqHk`)-j|Zsm@Z}Qyo)z3r8H> z-MMU36&vP`97C&Zc%oZx4hat&Ln~Z(9$^yHLyEgEb_t6`{}a9h^*I2Fl;L^I)+o*=wj6aPBQ~JO&=CG zPi8ALZZpjypdm5T_Dd9mIS?DRJQ#|(q6Oxi-S^unPgoQ+8gHIF1(x!H8gDwC?ou(C z;S&ud&5~T%k>mA`qWC5+v*_RdqlFY9@F2weTIoFTl{T7 z5CZoSa=IsUGSmEFlVWQQvQua(C0xOFHti4p1KKGy)8~tqFn=6V1TY8NO z$&($8x+SyI_LEtHz5r;8@We*YApF;L4Rkq%dnt?lX!}_@tk|~Hd|NuiJ)N8MUGx5^ zU|;U(kjP&Go!gODGlIO_x#TaraY5G`rk_l+E|R|&!FCiKhc`rT7B`pLHn$DK zn^vo63cfghE$L(^P%!TZuZVq(MS6T3T*WUa(N2b*5JuJgX`R?4eYG#=^nR1md~A}8 zAj2oBS*ELdTJfoj;q4fHk2+mOKTeFjEtcEHhT+b|@L$O__PrBf^hO^Ae}^Q&Xxs(T z3eh!C{)B=t+BEWTsRZ3mVmA6RilU$QF8`Nzvg7UO+pcWO<$3Tgtypbu6FCt6o?l@Q zr);XUp?tUxw9Ro`K~w^SNYNp+6>@i-tYO!i^Lq1gDF6Lt?0xZiUz%%(3QawaAJ2@( zGh?6su?&}k!Q^N_j>vV$Q!?ifBk+4kr6S3ksF0CQ?>@a|!Vh84|3McFz=-$!5xJy- zvxvNB48-Sw{_7R|QW1`v_(?p?(Qgx}$>v!U5kFaOC0~S!P!W)b`%I*;te7V?YJ`s>Zd*&C$NI;xN=vZg5sc&a47&lC&DnyU~bpnsZV(r2(xz)p+? zlfgjHn5AF@m=rFxNw+hVNHFR4Qt}m(w_oGvY%s`D3vrpm@Xya;Ce&G7G-Hy}i2rLQ zUNT%wx|n={cc5k>w{L`!+kVKEVv@p_D*<2TpzRs%kaptN5ATijp@9;fV{;qL9)j0n z^y`{~s||vY1kBTpOfwh)OcXI6T@852D0=UHs{ybGQl&;K5@^qyXb<`kO;h6NNq7vK zh3;vHN)rY@2LwJ#;6i1dZ7g@y1WT$yu(vk0WmKjPE>46B-NArv-67CQa^{Y5!Ijy$ zpja%shcJZb6xu1|+czIvqh#hQPXq8%ZL)2_xRh({^keyJ#kSh*x|q%*?VD;VX(9?xoig3L1odIfb1Z2 zNegfftwpWlzlXVCufreUZm5nSXZ~I=b;%`U2kRs|`y( zEqMF%t?RmLhQj}hggpLg3hK09D(5IsDX=^EaQ$FgJX=^2#iIXomBTOxluaQ!3N`m? zsttcjkBgt-xa}C_0ioAz1r%G*jWCYwmX`-wLrO$j)&j)g2}-gx7r}lhs@)p}LqlFT zqZ43!(J3xnQAZqNZ645vm4}?9rcF_8%asgSt5>aKX4Pi*keS6Rr|cRP!kP>r4dt6T zMFP90Z)^i?N?I(4b+)TW=>6O;)o2<^H^L%l@_JbX^tQKuN=aK&gJs=hk~eA4?dHpu ztsnI4(1S6m`}3-44-lP(R0c&N$celc&rg4!JU_*s^N^CHN0@;yscp(quE#Y?p%6z#Jc)#jG>SFB%KrTX@zTw?Ey*}sJS z($dvuM83jfOz5p}kEw2bdR9aascI=~Ww_5K6G5C6u~o!qc{|FN$YZXY`8Oc8fxZ<{ zxFO{S#tC%tGI@cVzD*ET7f<8v zn>#Bfg0(tzc=eo4Z=1+P82^g-?*X@o}Jj$jWb7nn>k2%;ReNJ$=pGo{r6ZbNiyb za1~N>?jeA@(eczgd3eJwVDkCJG{>BFVJ!7p%5uv|aJA_RIN(hnISqflqDQlH7lX(^ZfxBwdD8Z8gs(G82x)-5H@4Yo;MLmwHGJw#-cp0eHlm_u|>9mJmRE zJdF|>c+Z@c<5~5nQw4qX&;VINfANsQo~BLH$*3aG@i)@~uYm=qWGRCqVW!#uCRSI~ zJDz6MpuAAjg!McA%1>WQitmhIeHKMd=f+dLqQiePglWUOo8i5lI=pMvEt~hj>Fv#B z5z#ecW@gZcdxepb(JcTFB#U_ahKR+fKKr>T{NVnxs6#`T|MvP{VdbLp0<$N zFq1~iUiAV$vsDsh4NBDH?L-Dl zKHQVEv2FR1WQo3>!_7NzDd}k}3>!8UfNSq7K!9xpEOlsCQ7|Ry048nFO~^Ko zFmp=qCN@2Jlc1Z5V!Ub|T9d~QX}SmG+_PloP(!pE{0B8pK~4iP47}$RK5%F&)ZS@D z5l}>>3RM(tT+*zwiO43A6(0u9^MO^hWGlxeVJq9=+3cYE@-<=E&6{Gmk98e2(@i>fohyBCu<+Q6-V@7tGVbBE{Uzq&4XKY~b?1Yj0 zCG})b4mG%(60DSpy2BzRq>{^HrH>OQl1gi(d}JhB$@ktjuUJyJAnpHA^{qf&?#~ie zT0~QG0!_ME%@A9-BD6^2jPo1VPT*Sos9%j#!A|2a5WVLs=CFqrvE@!#x?*7k5+KyV zo{*61OdIbePGmb}E5yI=*iMKYAlV)o<<0Z+`1wsv7FAXy$3WwouT9zf&35KB9SeB!l!V~q`(}m5vhEVy0`E2Fu6r$yev)|_Rmr|u}z)l zz{_f{#5Oa)WD2?9C^c;>H>E0=5v4qYDn~}xPT;d!cYXEa;&ydmrCg6DV+Nj?hE!;y z#JV<^!m}_LRj7Ym)`}x-l;X^HaFisH6{zVDbfm0uM|xbDQqhs-NmAcea=oh8zmXd~?-#BND(Ce3(h_%N zUN^P4SO|G9iher<`}+}!>_alaOUzg19C!U=m4@Yhi7(V^iUuV{a@!3&J#p&EsG6IJ zBiNt!?C|aV@tm}*hYfFq`8B)CYjuK`xq%^nTI8kDIr1pMVVw~a7*!I?(yi)q>ZUX134_$&f|Yb#-lqB{C0RpG~W&RYw{08P)$z)F%Z4?SIkj~fgd1I zqppF37!o{kDeWws*ljoMEbGSj-<=lFQ`>oO-ba^@z3Y`)%oNPv!%>EoJc69PP7JXH zb_m8*>YZ!uM|cJLld?59LW z$Do4+PX-e&#MyziPKRWq{F45Q9qDRZL3z-VD4c7jO$ z9EW63j4aO22^LBP-C>pzQpxqT(tgJ=Nu~Sp2{q@MF=-h}8o9NFJUWtg-H;g$lCqO% z9fsm}m>{18z$Q9j$`>kDlmQuoNe~`xA3mW7 z6EcQ>*bK?p`ukx3Uzp&S!W|+VUqI%8FR3IWt^}Q^-4z-1^P+10XRZTD(kaMe)Ty004nsDiU^6(eTQQW&G6c4E+heqpSa zO^QL;LX8O8y&0M@ss)MI58A!b-w(EayV;5a1WOO*WR8qgmcb+t3YCULoJHEG!%y~`BNru628 ziLWWGFtLC1`rAL$LBHMiA1~S;pHG5*YklNkbULHKx52OJ!_8ze7~Kb_l`8bx68v^rS-)c`}x`LO^!}F-3c)n3mC(r zHUS!A5jb~}BKQWPM!37W`;AU%E~+;hqI#AvXksN!RW zO1|c*!I`~#n=lPjF2Bp!tV}jPNprBV(YS=uZ=Kb5Cc>D=6^<=#fa&{r*~a0O_yny~ z?@!w>5dEIN;y|5}KnvZ+QCh~J)2dYrwV-JNRE6ZySWE55c3KePf8W{pQBnmyEnl42 z@7}$8ch2cqmSm1|eBi(V+$2mv%u)nevp{MPG3W&0`p5M*EvR3H6I%)t5GL`hE1R#VoFQ)m}`t+!*oI?q+f|#hDeDbJSr3%I}Q(4 zsHg}kDamX}_j4^Im2`ibvuK7&q-&G|$H~p?x%eiJN z96yDanffS2m?4Q6{@wts3Pt@KeS8Ii4+^)bisDV(^E7%T*qD` zlY1fZGy*Pk)kIoj*#D&VOPMv?_nR|NPGUU-JC5y+7nMSH%1%_oCIg2SDp71&|=~et5XsfsZ*G$Qr=$NxyRqEB7pGn|H6yKeMqf$SC0A z%1g{mEiOn*PE`oVC~!*6OU{T1F3B%SOizt*tw>HSD9Oyvv*zL|ElyPkD$Og&%uPiW zPR>azE>;LhO!vz#amz2wOMy!&q*j!q=A{&4=;Es8s^tOzWy`?|KtK?O@%uc*z6Yh0 zgPU-4nbtqM#hA>Jl6QBLa{YdNd7(jxgcq2yhiihC5Fdsvts74ms?$SLTqPsg4X)iK z#RcE&XEt-S96ZOd=jkZRkOXz502Sew zi`SlATR8U09|CS#KpjANf8i26qYsu5XMaKE)H@Sc5vXvXtgU6V> z1r8)BWG*MAsRu#1WF*v>>(UwCqnv{4z9d?0s~5t+8#+NqLJ$n~2EriiBJ;Q{%HE}# zT1K6;g=7iA*#&jD5$E&mTX@J#vMrVHq5@wB9hq6k2=Ed%wR0+tPZp~6#0;titQ&*! z(F^zgmLAyPEU*R=kf0!7Tm%vTd}px;3K>4AuOPN)2+0uWRk>x^r);0BzR=>J1!0G&}8M*M0_dHw)P_KW=nZICf*!!Qtqcm0YRJS2wT zJ#FI>x?~G2rb{8H>Nz@4WpR>iO38n(q}W4J-*Sk1_r0fktetj3Sow|KNriCKZjV;! zkGIgVJmZ59Rapus;hm*($e@h$9@>r}XoRLYya1!`%n)Z}19F=*PYpP!6nblZsueF)O9A!&KL0qWl&9P0x=N1=U2=@P>bkY#Y)vuL5kEK1&P_& zO=wJ(WL7K1|K8d7fwj3LnR)Z(y*GR=bty#eNx&0)Y7-$h7J=_l1_GZ0X@uq5@&%pJ zTvT7OKnBKu}%-&bCnks1|EeQ@XLS`Zbv#kg-I2h90Vg1opK_M^7 zuzOa}6T-w-*t6qSJK^w~&}9*>i-r#qqa>*(W!kKKvE4_RY;!4Qe#{v zgECZr)hIbOC_JRP>-C4#m+dOHN*qHg101A>TKV8q*$1rQphC+T=I>4KD&#>mPDch8 zLXhmxvj~>d+9c^_MT2sNI;3#3ab{38x`hzEe*v?8;K0=k9=E~3fFhi@2q zM@f=4vIaSzV^4wGLQm>9_@fWldB|r4%!_2uY!00wR>&4{!$E34nI=hZdw(E|T!@9}DqE0598@Y&qWqRZ$0D_d9DjB1?_=`F`T#;2b~3RCkcza0S7GnqQ_*5kEi#sKn#cMhDqrcf>-6-`=`~`(kOG^VW z5WeSE%&iY(_il^NRxA`L)uXVa+1VzTO_pS~RiyvjNov)DVlH7O-+YhB^s>{P5an3F z7@o9=&=`xrcP9gZZ-6wy>V9>DPHAqc7r8|SC;U*2U?!bG3rj?Pp4Pd#;)`tR6w0?3 zpXfUuGFj7G3(9wI!E7~wbTwF`&_$MYjSsCP}7=7L<(lp6V|Xdr1^#YN8bm9EL3B0bWjT6WQ&nQa3tO3NY8V(Gr_wS9X+pt-n0lpHtI>Pma4NFC9Y(3#gRJ;q|)Abu( z2XD!#!~x0jaC8nSquXZ45tvRm>JIo52b0`Ah+kO<+s;b*m#L~|i*;2&0fX{1Q!;yT zD!yb6;sb?K!EW0y487|sc<8|n+UA~gS-UmE0t7>_?HHh-WjYonOM)aP2!{UqC?`(b zEZuw&L_WUvNYM`;UG0Pzj|7b1XRQKMN)zzu&_}Qp#F}9Bb@hpitT`xu;f5HT@R`iv z8I2+hOCnA`l-=!-kEm1RUfO1MM1QfqBdc1iL0Y%>YEwr*Q-W5KOki14S>Iq(*4%>A zgdE!{+I4ly#pn6AAM-@jd5kqi*kOQDh3Hk$Mk-;aV$B)$A8hL-u~13(2M1#z3^o+F z32v!&T}$h^*(z^MLnf|Q-fmS%ekw#8-oY%+9;ff4dZvYmk4D|eb9&sOhhF0t9CxSlYKxKJMxPyY z@wVW{;5RZdbqFsn(i(Qs%9!;;=c2r;Imo8(CQbfs)~DgakUs%ludAr=qdr_F9S+pD|*VpOX1ohsRM5 zEglLcfZpT&x~q#>-pw5DwZ$?!CL-}FFmIOUJt})EeA+1Id4**9Jx(4D{Y)VJIc78c zf5BPqT=Xjb0EJM?P6Htn-SZW9*GJ6E-Znm4W0OXk>e6IFnLCBZ0D(KL#`NC>rd2nM zY;HLBa2`yropg+qV+Lb*l`24^G(wz?dji`4Nrcto>JE*N5ftxyi;^`zXeH2uXloklTTaTF3ve2F#C8hz z`Q37HKYx0eC#jr~Bq!MO0JRF_)utn?VXsKWP0Sx`=LJTi8jC(b$(Z3ShFBt2k}g+z zo12~T*0gBoZRPDw)#xXTb>R$W@tbjyJ}!4#BX!=SfBM^@$fiQ`5a8JfE@BJ(AT)J= z+f5A31+B5QgePP6#ULK%BPLRxrZXZ*K}tRxT|%mAwwcv))A7Ci!1gACN!}dTIWfZ4 zX-@wNR@H2=t|};CP@dkDN0Z_N{J0(1H>FfvZ`v>vefO`p4@e~yUwd6U#=0>LsjJYs zw^CJ(b0Jn8JF*=Rs`&4{HUyGv+ROZamFvT~=bY>0_H&t+jE%1uT*L2NI#^4EKzqrp z2eSsBBP@R`?@)`JisF@TkiY@o7YTghTB3q`L^{6B;?s;auR>)cOffy7zZ)CL)|FBq zOu4smlY8Jg10@A&M{V`m*n)d$^Z-hQoN8N%yZFER#n<`6<2+DBjXvidcHBWGowqWr zJZ7+yKBp7X?@VO{QlktUaN!Lx7MU3lLr5&W)Ndbiv)(q2HgriLO zC}wg;2`Q=ZqzAl#42i0s8YNI*V2Fr7DROSPpkoS53Rh3J(xgD`rQU$sJCDV&0v??o z#oUDA&cVq$$glThY*_K;q$96gz$Gx<2%RNG`s55PsQS(&MWIw^v zbA48&bYtiuX%X^;mlt6)-GNH@e!J6|bpYI2zCY6?k?1HktX{Y?!|4f4%`S2}kc*z0 z>j*ipuJgB<)H#LiGb0Xd&m9BOeJM+-1iF6m5mIARN1#PtSq)?o|G(b`edB(Hj*Z7| zG8SE>Xx5DZghhDd^v?G?xzG#Q6I`j3J{1r2x+M*+Cf`#F^&IkS&0sinag_ZQZU7%9 zlSy)!%7Jww()7J(x@&d6I+EI}!Cyt4i%A{wfxQEDQ_F7KFc7@+EB0VOQeZguDv!Qg z#6XZ5O>%OKf|gbm5sDP}kQx;J_bw^NQPqYQLOa9VnZfGfx~*Hr&d(T}!9y)9R6-)q z-E!kVSKw-d_09S-s-mW1@yz!~fjye25=Z%V%zTb~6L)r^f>fXjoyb`~%MU-7gZY?|p@gN$? zC5*7}&kXjP0iD+GC{W5-6|t?bpW2oxd?zu@KEO_E8Hocu9COrUJP8ej1N(2QJ-U(q z&}{TM={fUJ@Fi@vIXhK}jht2`-JD9{B-$o$c?F40=6?gVwr+sb6fziBWI)Xn?a)|K zHK5%TZAgSr$G6Q0-2$!D>=|DIGHYmkvXQAbW1GO)l0Eu?e#S~Vu=5l7z@`B!+U0S8 zNqm^hk3F;3@DJ<{olre*!Y~ls{VQ$@L`Vom=(pN|jiFm%aNJ8{$vInR8!Dmv_u8Rp zBNdju<>Y(s?w#Mwy=$BhBwO@ODuiw0o{&_td!d77OD$5^ZP_05JMvnH=&xutR9kLk z(3aNMRl}wY1>`yzrj^P_@8K1F7+BD)GnfZF{Fx9Z0SWW6>?@RTdeGD$>+oz<_uZWL z)y2wa1$88q4+HHVol6~A8JtX4P+4nw;dy5UV~9);IHucjqZ9x4`@k^DB|DCumyHRJ zW6`7tDVSmgZ$H9}0zYNWXi>oJ9V})S$z@M2f64uH_);gn0DVu(4gw(zyz>>@wj_J- z?(0L0A-L4tCI|#rgZuSXSM+92)0uXr-5ui;g}`A7GGij+XhAp$$<~ZO-_#&}8z|B+a-vUCH5h=4ZR!aEmTj z1kU~&e^y)ms8WFqqbFrgUkidT5P#pNxIl1&9(uRcM=0nm5m|cXU~J+hNYZz2&LvX! za=Y8_-yP3g+Yy3QYrtYJg_19%Pjsbq!dc2#MsSpl9{htegRY!38a4nj5%86S5jf;w zoGZoAp|&1hDkEyFQ{X4~UDEW9x`xZO5><;S+i&<_z&Vcr z@_*BU!L(}j=!RZLa2D3@MJ+L1NN|gH#IgLII`~_o@oE+G6&}6>+08+=LXq)f>n%T} zR&7t?MiBn)Uoo7**+zj}zlIBN<#Iiw(-lG0FHMV_^*CPD-nDkuNf6?{cXsV0YbSvZ zwLli*d3olUH$Goic@+esvjEQEkDP1Bc!5CgDp3Yx1|~p7>)CXTtTxHKz;9eQ6mRRd?@L=g$M6H1S_z*;NanIOj=lvqfIDm# zju2KPTElPjiI>E;{h(nl!{8+VdMd@2i9s0PdW*0x|49VzOYakt6HF_1Q0;RDH_JqU zEuC1jqEXk9tw4R=r6l7X66Ez;3_7M6UdSGD!f?995#YuvYSP5en zqRG)d1KU66SH7X=<$4T)I_S&VX z#|hV8s){5QQxuP3E@jcnjAsy84q!5wY#=PqQ2lL%qB!<+?W3B8%928R*jtb;4V(L# zQqWX1VX`PZ)uRhb>d;Fuz6?4L?D#(6n#NW~jfZ2AsltZ??<4y^)DZ{XoLcf`2j|&S z>i1053?odu>a!@+&*fZFtaF7Ke4l>#8urQf1Shr_t-Y(qbj{5z?X4(=k00HH&~MaL zQA(=cYg5%GiiZ$x+}q79B=vNk(7{|Kl+LE_qgvQqm_j3tLyu)ZBDH}wIS#EKpi;Pa zB5qh7g)jdQ9zDu2N%bhYIEx3bwjq2+JM05zgxVXReZU)~zkd0B!HSF73v&|B2kOz(25>9WB=Oh zR!unI2FA?mH=zK`=ORg1{4U`x~SK5`rkYD5(Nap(#rBXAMZ1>=ikE+lv>Ob%;5Jx z33{>!at9q5d=ESzY<_L7(3wF*^H*4uO9ovMTi5$IY9{aI5G-oZc7*#{ce7ruZXZ^qRb}KsGwgMO zjuJ;|Gh+vP;z1(9pL~u6#i+w(B3LNp^oU7@kdcN^M|xVh9Yya(bbMA8QCZicxEIOC zG%Z0ol4Fd0DGt%^sC&l?y7ie$t%elPP)`PW)SM$8)`&-|IbgGG;Lq}54tMe)o2DWj zO*WHBTptwmqCieXEN2GyDD53^%)&BxkV~wcly}HzI0=rJK*Zn^$vDr4T5>htfl4fU{Xf_GS?A9XwcPH7!)?AXz zeDgi#=rrgc#Oy%80X%7wpf(nP?;sHOIX3$kme5iPri)`S*XHk?_ef`lMN;g!I5+*kzNE1>qwD4U@&gqCdEjJi z%yw}DlBb8sk}0vDJh>}ToGL3*f@hU(w;jMQLRTf&sflj^d_ueA9r9|OD#-VDH&l7f52e0y<>wWVY4JWJ#E{XwrykD?z?TNelL=*RCN<(>bu?t4G$*wJgM(lYM_@*n+KQI7BTRTH72=G19VEn)=c?a6U1BqLL6T-u%TOPOfYz^Cb5Ii!cBf zhdkE*B*)pta$O&LRBfxs1F}1)i2+Nvws6$7nEca(f+K{H;$r{kO!T-ii#_9qnEgsV z4N1}*O5?c=f%$O92lPOQBOgSDG(}75_=9kjqiuDuuZ$pzR~Xb2+l>U{q+`%X1B@)3 zlT{Z%BUd(~9WdT^^)xS8pIK8-lP+^9N4+kxOaRtEH*Jr~m4DsxB~s;*egR*UZl+Oq z)M5-|8;W^V5(4Ur!_Q*yg~h1Wh}zWVvXt!0*qXtKaI8wh`@`z9*|M@gOU%jyDW%Fi z^bO^pniDtn*Xm2>yoj9FE2H)30dxzE$-OO%34z6fW|fh_M@^hp#NFHE{czB2%TzaiqvyKJ4`L zsfq|^r@G>iX+${O4C8X*OFZjujg08~E`!9EP0TfdUBaOjg5?mo?rh>QW3Xz$m(V3JYE+lh6drK62P+r;}v!Ndip?& zntYZ2LgG5yMaUUv@I(_a0Fn*8>{hsIJ{&;bGa|KWGcD**4cArELiuC06uW(d7bq`h z8a8aFmY_x%w6ZLfODT7|p?yGb%u~h7c2clrQC}={a3T;o`mdN0b6=6!3uTO9}s=(W%jP4Yj%!Zjycl?VBC{g zUzB2C{k`-b-oGr8~OUm78<-E0qE4LMiW%XqLxJ||BKKc)N?(FC^`8=~FlQXE`h?eE>>V@@O`r=_G8#AYU_YE~j>rY9z)YxXNmDNTU>GLo5q>gWII zf~ETHZVJ7><>HkKE?n1DmguxLAJneZu12@+(?TcF_rrjVNik}i3jvcqA&D^2-o~Fc z$#J*63$=NwBN;>KM)gLMpY&B_WL9TL34XWl1kD|v{tOD->2O^254c_M{z(om8dWfc z3BD&`l)MF^W_aps&kw0!n_!S(5}{$Lu4tZ7w8sG)Q0<)6)hT5f7So5j7Q-ARCn%UW zg0*bhR4ZWs2&YWv%Gqy7RP2F3eC^ z4h=%wT>FNF)uj|)J-@$IHH%;kGcWgJ9{!G=zYIFQ&qP*JY9`){XQ0MaI|S(zJoy8E zAP{R=ZJk_?4y>b=9*qlF<(c(Ir}E;{>Sx66Fq@FD3q2nj;CmH@Rj&lz^x`(&%~`8j z%2X`KBX=#}nYRxoL*nGOxZ`*jgOKqH2^XVmSq-GBB*6PO#NL*N_DmqQ!3GPrP9|H5UB@gK#B=u+>4o?YrcM1+Us=?}W0&tttNj9E z<8eoZFvEps7#I7*Pny`Rzurzm^pY3`pOMX&wMl%IH#{12jEkcJFbk)hQ45w`G4UWeFE=>*jxL+7 zY~-zfQ0K$RgT#kY1Ict%{BGbwd1>(r1!x~rz=0(aR5J4m-lU#KW0WxjSZY9xF{^^+ zqw?0T?sxQTQa?4%Cgw;~4qkWnCao{4(9~Ul|L<mG`*=l%fOm@8;TPTHw7PW7`ba+ETN8{*y1oo9xrol^dGzsD`&qjyC2;^~E}g2E1lJL(TJes!~ldkWEg;&>NwxBVK~7AD zK`d(j`$s3{{|jXVtGh{LUoLjSYH%)Fu$-6zQkC?7LfO|dBTfakVQ=?&Y0`57hesuk zh~xz6NPsxcPwD`NK~+wZh^gAA$DqM&Hw5B;8F9_Q+VP)Q7D?RW9A_H1#lBev_8r5W z)zxD(Z_N}v4b*ea_$4igPjuDtD?zmxYGP_u)C15L#{pY{NeABMrCvzmM#&4OOtq{g-e{uzYu=(q15r=H;RQCo; zsI)4KyI789ZTwQ!UoQ?&Wc4UZLHwCi7Rr=2BwXB%!iO<7nt%F!NoZB6r|4eflTTS^ zG5vc(m5z+I=IIWF0;PT3O_G{qYEpW1RwAhL zHWDSE^Z6(nP5y8O;pnELAZp{~x@S(NF$2fx45H98zmCx9PAc8LoI1vCUkPNC3(joY z0_k2f(uT$id9Aw`;v5WguPu)*BMWO~!5%r~Q3%~IFI&NE_^o2($Oa`Qa9bdv{D;LP zdN0fO#Ey@1mWigRH7tMA;n9#CNu1&2tvwHI5|Vjzto5$_rQMS-`Zqgbm}w4 zcrMdRlP}`QNc^$kX5!=nHGpnsakn|8aQ)Ads- zMe?KXdz^ZL@7Sgbb{2RJs{y%Sc@c`%mljNX-dO7PRSd;EB;wPG*|ec?1Gr?>49?kQ zkUSrSBZg5SHb?1-D)|@+v7e4e$0~Xvyr2K)GykE29uPu?df+n+%RA#K{Jbj^|@8opP?5XM?OMWzq7wp=D2WC$b# zeV)8<3)h=1D|a0CXHQ5zM8N-pqnKB8zr>qef8GheCrd*$33HpCo>#p}jiM|Lmr`G7 zB0cb3T=Q(QB#?Sv$g%a6w;isb-@f$)=LY((Oz(##yHTM&75INLy}4~g2`mMqP&2V+ z|I%;kDnj04My|=pYv(s*^?tuz5&P%r7&HN4Tc$H z>UNdwHTHMwA!*0K3F9!s3^S2rDGueR5!GeW7@y7;M*rMC{V2tcu)A&$oWEsx``KFi zaINH}Wq>9kG%IA z2*5NJ#Zt=bZLhr**Wo3#P>4KBZEF99d?88RnoVel!Dg#yl2{U?) zbtxT>wldm=udL$dIz5=$RSfLFM|HXri$28uKcD^lS9!6Un4YYYc%GpiAD08Dj8&J9 zPX{l=O$e6^M@o>)-M3X}EpIb#`%m2g=l@f8JX58Q0*S%6c;@HhF=Oo&2UBMVi|MY_ zMxY<<9OaqrYZhNQ7URwcQU78eOGOD}#Q~3WU^M{ zQ@cc;>>+2sC;)DNYi3pk9iYB6a!Oz%f~UpbS_y%z%Pjdn370-ucQI z#nO0z9FNYQeh~aiHhICcJKfbowC7@>~5-Zy!s)G{@edHe%${ZzyI@Z@!#wHzn=fUUG9JXXSq)@K>dh9gU)J|-0}d( znGM;}IxvpNSc&UnEg=%Oy9}Wny#_qES*YR)fvdw1^_S5DA3QiQ*CmqtO1b(si;{p{ zj(OTb>c-WZteUidj1A zO-HQ_n$wB!re5B{K7PqYVWE7*@Nfgm*Bg)MLPL%E1Qe!qRk{%mH`nLQIg=A%z@z)Y z03PoAick`>N*S>ydcW%Ui6OFhj?edp`Z;;uz^OVlaiCf^KJ01KtVHpnfJ7&6#MTy- z=UQS;WIneCX5r4!Fhz!nnS{J{Wu!WBa$q!tQrsfRH_fCkXi6d7(QD)AiI)Y_r%0 zSmHF4_F$sH)5CGffXOj1S_lcKL}40fF=Zc)Rr+1D5p&*=b3z?3Jx*(C__MX(tl{Ok zMLLb1c8v^W2SpQYvkg~?1&U4!Y&fy(OU3#$xaq^zf=7%#(2wb}MAF(?A|@>17%|A; zlsAG$AUFEo@UI`Y%`PXyU1qz^)>NSD8B?`mm9-V5{rJF>C%#N~Y}IV@h7*I3tOJnW zsm>uhk2W`baIK%(Zm%Q!aM6Y3hS>)j7G})kJYXbL9fXjyz+?sdD}?E0oK;FJAe%I5 zkBPbXiIY?jngn1)H+94oXeo)g!nvCjTh0>*uA|NH<<_XEHXtDBS>aBB=iVf7`8@hH;a zx}VL9g1h&da9U*gl(=I`@^~LtYLc!KzZ#iSpMmRZg zi^j}TsTJLpG+$=!FX)3+kKC@H2dwF;Xbj(HFD9jRo>O+t%T^8;i?&nBwzBxL;0{Dj z4sQyMGI<&=x~{Q6aJg6Q>ha*H8oLFTi2#mn9Lw@2VZNe?iY%H=d-Pa-D!RY%ZP(<6RkOSbTW8ZHkGy2O6?f}-} zgUz6SsbLrQ=L||~mMt0wkuYW5Xf1iHO%|y$vgc}2=c_KEWjk+S@cBFmet$QGhSOaE z{`blePByI*`$`@KmiqTal7}|vSR94>I+OY1a*Z}NL~6C~pY^|FSKWLp2z;`Ck}X{I zRes{eX_^GBzm0h7ke`~oxSZe3%rH-j%0)>N7Wf#)l`Kdnctr+m+IL6QNET#PJPz@q zyTTZ*r;`TDn-2(M&0KAl$g4;737e8!*cz-=qNmA>IeVi6PI=dI%P@4*^>l4M30P4} z_6`CKb+5m*3Ko>+8a?Uy`L^+JsO5&5$ENZ&dS*-ezw~V7$m7aKotUhC4Fj-}R7gZ} zWG38CqN$n8s+bF1L5^YWr_y6u64YbK>zY#|N9>{#^fAlyS;nXQZAMn7^_@I9Ie9R% zqN6emumoIi=mb;i&@rkEq%D)1BNm>g6$^u z3kuMg@e)D}##u<4H7r2digIVgR!3*DEc@GP^>Tq4v}(b9A=a3!Iu4=L+6+kc-YcIV z*+fON(xVC`)WTMr4};hi??NP>$drTgE!1yntjFj1O6Z${q(Zi;WWn%73~ zgDl8^1-dNv-R=ts81T=lli**%rY@-4I%aOLCd^=s(QmIia0{1P8-*=B!r|I*7?P+D zu`_RYNEWRY32fIuQel^AYaO z5U3s+;$MM7T?9CNImN-vrVCQ?1P@Pse;G|?r96FrEaJ|nzHmc$EM)D>VEiIG`F)}z zbH01y{teWmq9uF4j^Z^_(;Crk?myn!2?+MnTh$^~C0Jcc0U}7)WJN3c(W#U)8T8q) zATvu~o;4hN1J!@e_P7p-*(#VZ%XHuVss``4m7Qdfa`or;Q7DEPg-pfRd#kyiBkxZo z9Ux;BUblSy&oFe_ZfY+HAq~Fp^+n)U>fp2|#L;Ez>Su@|oMk)-WFVs!C2tfgJ)%JW zUzGIC@GwOt<5m!O32Kv(Q%7a6V%&B2`=D1*p3f zMg@ZS0tmiCm?s|DT1;~&f5C7C^!Y7Rrn}V#Mti4n6pid)0&^Z`WOOi8CP0=<2~^4C z?rQP4utPwZU{t@UZtOU-B#u=E!<6x$7P|Ic{O9f%Qgu@114NiAYX(CTh}EbdHr_d6 zt&k*r_>{caQ)*)5UZ6&4nnQ!Y@n|qjaRH{!@Tsd}k&cdiT?pg?R(En+z2?sEnm#u^ zp4>P}-VzcF6vTeA7jHgmYI((~AMbUiVr7w6{U@=al4yiHqQ_eV2?3CX*_X+7owj@w z)E%miUV+2O%U$FjgdXxBMFa?^ZzQ*V2o9O5?U#VGlM6)&H@6QQL(}%>IxAPOi4=s{ zA4Su@x)(AHhFTS${3I%%_{#|Xcjpg78lPOXQ*ADt)4Jl$o0|Y>RajT{1AT~`=oPnz z41x8$Swo3mR!YITul{?nO@>>TF-=KC840I%;?l*0 zWA$fsqt|)V)&!&xj?t^+97|%Z#5c#y9B$FHP^-CTEx^-R%cbxJes(Wrk?3N_4(9Yti`HEn<^4*KGQcf z*_9VwLCrdV|I<6D!r5Y$R94TTepu#O$a+9ItB#khX~FC*iOD*yWHWlctQ=oWXMSoV zq3s~oMoT-GNy5#kv@pqg@ve!ToZ5jYQky+nqm3q9z|;ZF}iGw^j$uC)*k-Z-+cq%g+3`FxDhi@Ni@($ihh%{lhng?8=W9YQ?Q{lCpKc zZ%0h$EdT8*N}x^7*oo-9O8>PKkk0T|VqEjSRG?(OQAgD=h%|W)vuvf^DCA#Gc{-u) zTsF#>{fo6@52V)E>bKbA&^vsWnuWs#E835$qTikCO5$Ih%fhYU=mtB(lDp$0@%FPz zcDx8e0!U;iT}Zk5_9>qo2cT|}H8Z|SULiz?&1arxU+Dri07#e@l|I1DI$7HGPQ&OjpnSa9r?(y9^!^9C&;Ymo?}wj0{zef-FAS zH{L}6D$DpG256;vi6KX0)jvdN5wVi6P4@ZoZn}Z-ypsBOx>f`)^VuFA9G$M;XZ}KJ z*i+Q>mYAk!kX8VOX_%N*Kq_W-uXj~F^PwEdU3NomPXkc^$BON5+9{-q`4ktwv@AK0 za)dy;+$Pkl(!@d;0E>&CYFb*nR0yFOYTsy4LmERE@AY#DJpp#YiB|{Rw+l{%oe!Sr zz=Hl3h{$tKf4AY$oRTKrx3Cyt?d1phG^3p8NRGn#*eAj!HyT4M)ca8?rkC0OBt7II$e)KTq*?Knkb(mxaigZ5b+{tEU)r6Ow!LC zkMb_0XMSB*4+p;0$A=gHPkc(=Vjm7DkQ{_Ga}7lzF(K;Ps&qa-KE#MaolgvUtS9!C zZ=imL)@W5@;}wGo9zP*Vh04&X&mifKavP0FsY8cSL!w1Ct!CgmS9SNckmpGcmtnay zmlxbBG;xX-=CFi`VW!sQ#tktl^!S^0DX#+1XlxIZU;zN-hLk`c9jsysDZe+jVKO-& zK^4^ap>NC!>W0Hb5{vFgzZA{qj$YSO5_FO zaBI=(AAw04ntl7SW0$5P!QE6dEhz$=uOL2SKeUmg-q`!8NqOnKaon9nnA^G^4^FT@eGBrqHb_$bmn`^;9?g7 zM^f@jW3rxoAdujrY;#X4O8S>$4QvwI33`&0O*T;Ju`j6aYq$Ec56Q!>VhrO9dG zkF-N**yPF(jPQlLe&*BF_&b|#mv3WJRaJ?J3y;gSwQ5pHy^&xz5{=ezHeC@f#QaaC z(7Zw8sa{jO`YJV?8al=+A%$(MlNiI#Vhd(O3mNXYEbJndCAYXPqTg)T{ z%{cGG-T#iAcDSLHxQT`|QiQU0Od_1t&E;)dgkj>%2@*L8+qoiOp6FSWs@DG63oj;Cj z!3-X%^RwIDnOTPPoT!v7iy#ShOj5L)>f(5P6!D?fBfBQ?L=1)W4ylXDKoaf{)Gix54BH;DhpmXa zW!9qf3}@xVhQ^JRN^E`ja*?r>4Q+wqyk;{k!f3_;v&!=1eWPp(_+Pp`B~nT21f0c~ zz_e}8mVJIvi(9=h`6{{en8L{ojrDLH+pWfk7i+1<%1wi6*&t&!H~0~gFFbrK_zad{ zex^8Xy0l0B)gI5Z#shhCP8kCQM3k-0O;|<(Ecl=yz&(KQmQv)R^9;eHHwJJ|+Vv>j zHU>~*%gaa^XdP_)ZNC5{)$G45t{P7FjCi>?CR_w9ZCs-@|afLdkvb z59=t<@FbAf4eIqdGl;Zu`;ijUhhvR$C?(hA69-7d=9Vrhp6+-EofeeRAR+E6CBY|W z+1RJDW=ntx!$i9Tm_(`?F4Q*D*3RAP1Oc_~A>q2%BUwG&Z;O3**98$0JEH5m5Hw%9KHLE}<)@gM7gcU98 zqa$8XP{yIXmv1N%K@K*D)1TLcbQGYrDQZqH(eE?z4zN@R(z!=>Sc;<$9iVPzN0s6) z6=1DJL6*l@5}ypE4du^>Z$P8v3+L+~R@{Mc`|V+I8`Mv*8jq#WT0= z&Qrx;Q6rth^t_qKv;R3~9feTWdPIb#-MNo{3K)b~0@1)LKvf8@t?r`_y zP7u7G_*2pOSu+y5S%@w`QA9FuCI9p<`-&}hZH>q&jgbL|Xj7@Da4(Lz;_2?=vUYz9 z75uv59UFfC{JxcP{kecVX6ANryiHejh|Xg0Qbx#uffZr5X;a~hE%ZrmT!E8+i#1&! z-u|@Ule7nfUyGWHNrhKZsyRgUr`BF~c~x7>&<*f~3(?4QM{*@=SM|oqw+!aeM=;K( zhIuE^Tg1lIQqfTkO$JvpJgc^AR>zL>7x)x_=f^DYzeb<od+OWO<#iH)4jnwZ9W?|sGZ7d8kGxZ}d&jWeL1Ig~RM_j!EtDjenbi<> z0?u+U)}6@gGJ{kSBFYdMk&**Q0(knK&|(3#`~-XUFqfDaL*PiNsL7Gkr8ZozDPMq^ zaFoWab$=8_yfa@V=SMjnaUfe%r{;V#+EL!MxK65JAZ{8;4RvuKfXDfIz;hW?Xx zZas&w9jANYIS0zhZ}wDghs zbgJk?9mz1_OdmHbRAai4ZH-;L*~S=_Za&`cW+YjillzrRoSy6cCATKjEK)}P{NHJ# zu!Rcr4E;kj=smem2I(+1gD~Q$M_hX7+YlrSe(<4>uf+8>HK#Mg+)NM)uh4X#tW#BW zW^u#_#cNu$CUro0gkpxF5(yeC(hqI)j7mJCNGQxLv8|2n_}g8)->z=c#g>tzXkf{L ze~FvMj-2%9{fn3xyz*Y0)DQntvxC$64=789ahOZ^1Hzdy=1;q1mh38&Vk;!22;aX1 zC=Kvwg{m3&jD0QPY+|p>Tzy{8h*A^%!Q)Q$r#*Zd=m#fF<;u<-UyY`7g#}lLG)4}L4yr&5uHvu`?}Qp+}ZsV zL8DYZu;uKRNt-4_fNgBj==$1zIywKO9Ki}6X#%}cpblcX%!&j%88;mJ*%x#Pd`WI7 z9=1u=bzPEX)49Btp)tul^i%J-`o1tLc*dy|t=!rr7hptdIEq)e*NB)JBBzmAsjNL~ zb-Fe=!eQ>t81AcBj_%6A<+O6~VM9#LnO8#zY^My1-5PA@dqrrao+F)5r5B%!w>8`l z9>RGuVjoih!5_8EJyvk>cshESxvl*ibZD;N_j6 zH(Dj)YW}F3JkR(o4t}*A{Q?u!Hi1P*C)xrSS}M-glOid6ONa=YMlyRs)h_3=#O65~ z*}-&ueo@}kedR^CJOs2i9qOCrDL#B|&uIh*LIg13O2&9!UGW-H>#l@H+d znin0DwBzob1A!a*u;yj4L~>od&w1G_UALLKm}@vF9moXK4E#N}3tk3^Tgt(NGSc8* z_=Sl(-mrWg}^H5Hw znvDK^Wh_z-ZZ5)(#435%Y`P)Yn+H~%@$^T00eixEyE68P)`!&t;-U(|Wp8<4&Ybyr zsdjrz7a51e>3r;xqmN=Kv)yCH=~)-&w(oAiCII(e{b{t4lxxd*>eYcr^G!$s}SV3*%fuf+WC{C-iK;O(1{N8FOG9hS(-*>dfcE+v9B!0ct+&{iRI09oqy6qfFr@Kf(yeMC0#t>qAp5t7Ne=6thi?F z1|-xE6t0!vke~~-pSM2Ebvm?I$dPhCzwl07FUjCy!0vBXhd(%_&%W}yaUEIe*b`%) zcgv@={KyxY!LTse*An}VDrP{X+I zJ<}Cb+;)9y&r(tRW4DYR zOrL@og@t)EE^EEN)~=<~^&)9{v58&{G5fb3dv=J%lC229Z>;$&c|kPXw|7W-dcs-d z3+A|Navhj=Kp50P2}EwO+aihD1w8}i4kr$=kZph?wi7yre^Kw^)&yB=BJCn<>BDT? zc)dOWdApJ;x8rST7tiL9Ck!f!V-UuOfTLBi;j|5k>h0@AmHK3407=`5~yU!9bXRonJ2Z z$OBYCF9OBGk%P%1-Pxmo>+u*xV(!>6h=-8xPlM2Zslk@+IShhmNXQJx*a>0Q0Hq+z zelTxFDuDGlAklw4ZNO@iUObjSedRFBqI_G|+S;4H;*-)wTy^M+vj1_2 z_g5nk;zS)vrK`1_)&~~AGRF$J3XccI@Wht)_me?mB0?hh*zm1#xi7e zWpGtJ{EY?i<=)kqdP$FLeN_N|;Y-)r@%8Hrx=Ak#aX=n|@y*nYU-E4;sTEIbc&=vP z%#ykLFM4U&U3xEVkvCtLaAv;g0i&8afVA9@-wCKDxIzZ$H8#{xE0^n)F_jjC6=B;J zZ|GAS-uwU%x(+ImVYmicsy+S;LWR(qn;8CDLgB2?$F!7h2>B0U2WOIu6mpx zaQgQhoF=5=I%_*4PRw+vrWlaAS7U)6T^c!l7@steSL->^Zzt7}<^}f>y&&OtuizEA zkUY51$k%)%5&k3Mu1c}rYC*_=m6f6e`AVojhsfz8EQb=ggm*7Y?W*abTHoPOVVjhd zpK_u)Iy4=$c_#<9q(`tuE%N3h;r&&gJ*%0yPO<%lju~M8jfy$_ z3|{sLe!;%>&=7=#;1-!2R92?K(4#VfD%KX$0X~Ap*y55q@lw&8uqwc;1r7&zz%XQ#d*FJwsML3>%dfgep3UD7jbD3gP@yCzHb%CfNWOGq zvqi?OD#+e9nKo~O{y_Wp%~~wq@uLuhgCN?!NsN%Z3u#z&*QB)7F76Vs_9cKLGarLn z=4ITB@}}JTDve+HqL5B-tFk@kgUY0bDB zhkuDaARAK}^ku|ca^>-r5gJ#nV@*wa6~{&>mC!1Q0eU;6fUt~m^q(tt&QwXjSV2&9 zS>J>|^17X0GmG3rJcBdv{-|q;5%sjMdre3iwq&RdA`NDPEtvX6wV3X)=Rko zC{#SeIxn7fY^m?h0ML!UwL+`fV&;-hsDeg}nP0kdl+Y7vH*bxJ&Gr3f;|vP`hsiq5 zlx`52p*k{q+#$Z`8{R11Z$Ij_R=cEjZ-j^i32^swormEmQ`XjQTv`zqMjQ5b#4kCk zDN@z#JMY+=S1_BZOc-S5I=rcNifJ9ELF<+Zr7~)Tm8sjeuLp6D1FJ!JnK~s!y9KYq z(u^^<9pR}D98$uLwQ9)Ke@4&L0vp(hyc0d_o*4>c?X7`nr-RJa%vgRzvVJ9)UbwQ~ zEbZ?wdc3Hrf@A%bnx|j3p~JK|JCesD)|!pBTWP}m>&9pdr(lDZ4Nq(O=huD7(O6O% z@z906g0a1WyGBwBnYh%GGlUHgG$+B z)%u`N_xE#EJCkG$`;o>Z4#7t9FQD;Q(ST?(5J(lDL?*b{y}aWm zmXdX}nVmn6s!_6LcLL8Bag!c4pYCr0jiuJfQqts4jHFnQKgsTPAEmbF@s8~x4ZXmA zAs}+)&bPH&%i22ceEb|9n_dxBs&SLehS)+*gmt{1t;zyR3sM0Bx+!|M(-B-fjz-Ck z^0z_>r(E~k68V*uTt(&?YEe#2D~Qj(Wxv#PB6KH0K@;T``z;T=5)SmPbU*RDnAEcH zYSohP#_(;w->6>CBq6c}xU-h^ik2Ef?+H3=sSfu%ukIa4oq$nV&|fpIM1WMKddX&pl53FI3q79y8O|eH3JYeZi}F#I7pWVUda!uz;V+RWU#=z!2uU@gcTFDGSc!)KYT~f7|xnR#V?KCC#0{w-5fRfrPx;b_{ zf)2~6*Jn;sYT#nm@QTboQ2l!fHJ{=09Y>48nlME=ftE?Y9kiTb@7)o~QB_Hn2sK~9 z^yjbsL8k3aOAa#1RTWX)Y?C|?#p@&^!r`B7YZ>CMXucD`|qY7$Jq z`0HWoXGU4o0oo*!*cbr$V^!u@tuBLEYK&v@Vu;R@dwPe>Srfl^2N0|1-`*@gq z`FM8B#u`2|3GgJn=0+k8)&KJ%LyD$;K3pvumN+R z5X`@&(@WM1#c$O1w_6X=^bA{foSIXO_6$Mn=w-Yp_3FLvn51cK(Z>jj>>1T*`~$1r z$^hyx0>pPnFX^oTx?(}%WYTlGR!2Z^fpoKU2^Jq=ezKdUKn7@IAK$Wa8cPpJSU%_g zU0&~MoF`!JqL6q3vq5LWn*$o|V|qtP-|b4jFO< z+RAA&e=5W1YO5?!0dNw%-orw)*f$@TW^@IDQQ`hP`cNV0$#&3v(6y%dmz6&+g=;Va zAz?#-!~xT_4qR8C2m0ompB;#=f>Q9p&APzo$7=+ux~n~o*6tc|}t&g2uHZT%~uW6gt~)k3UagkH!Y!KwIEE< zB=xq?Va^dPI`igtTd2WJI zN1@LUo5AOQgOm1E=PRgBn*?n($T#|qNh5Jw7sr`a);lT*);f+(eM&7#P8AthT(Gr83o$ptbe7!WKmgySPEN!efxnhiu zyl7qgBRrDLoYA(WlQ<9dR-OO6x1gBmy#NV8&lzF&K%8Jk8de=*>Wjd>4WTbUWod@p z=>&~hN@EP-XL7q7FpQH52q{yl!qK`FoimL?bPwHCy}zB+q)mZ`Ki0U2bPEWG#A++O z5gk`zCf#5aG>oVE4DVHUVwm74S`R=HY&pW}NLvursVCc`b;_ zhCcu-$pMq>6}>GVbV+zkbnH#y*I7X0NpgN=Qi;pF&x)S)7tiuGFKpY4#h-Ot7#$1S zwyGD09)mFCBfvG6Yh^4^P|bH#-D{KG_%7oj0P({r2ZqZS_f$Qt%k=jZc=HNc6%FB~ zL>-A+`-3XRV->`@iB66$ZuR{nBMVcb2>4~cd85MvJQ~Obb8Ghqo**(_Q-v$Q56mw? zdwT+2np|ij`Aq&N**K@435&4u!@Ubpkk^Y4_05pJ1l7DdmQAOkPNDwU&Ef>`A>*^; z*-W%to-+A{Q<}}D^zm<2r!VI~XI$<2PWv1&pSWuTWk!KSO|2W~?uq8iu0-NcUyVV8J>8SuF6v%e!bB(uPjh;u&_d0`2gvo+n93{3zoPaQ3q1l0ep_~F;V-chT!so2PguNt&9sa zYLDA@tI2dYZr}9KCsgMV3sCYfliv6{ zl$q7IchjFt$A7K5k7J_{X4<|<?N}Qv^yE?_WRHw5uVnfBniJ6_CJt=DwRu7yFHUT;)l%GZ zjp|_Adb5-=?-gLVfWGq}Bl9xWF9Wb_#MVsI zQ6()wJJb?dE!dzBeT-|#rT|mPjB)0mC_s>K5#-ykhLyP}S*2MC3Qg=_{}K5CU#65GpK0goN*J}igTD}Ur`DD88dR^c1jJ&@TN1yFtLrZ!DI@5QLB{3%( z_4##_N}&N|8v?_nd{*?NFrpL8fVUcX6 z8x@0RAm!x~5%>%-7= zR8sRR@Ml}^Zr42~#XpBFnwb($H)95@lI`Nrsk-grF*0fBsU3&2>1Z~E!qh#%80l1c zx-C+y*G+M1NbDx8>V{ON^>RiA=cfPKFKVaCqsbAaGrWiKqzCuAyY_uoww?L1v`3Ux zqD%!pu@>dx_k>_cv5KneAraoK!lzhoDe${accM)XCc?4Go$aP&m}kQxzkj~UbmgWR z&?D@_nLU+gZ)$2}>xjHe7+|n&Ax%MnSbL&6=^Ol;>%$=_2FuUs4TL#5<2!O0+GkkA zpBK2Q*Pd>Bvx#KaGMw|e{Pa*?HZ$NE=VlzM1VjRVWH5#!82$e6La?otG9Q;Vy z4^SjP%RG%LVyQ#bQMRD|9mMK@<1n*X?{yz%+Dc*ifvgK zh9xx$ndE@AT)x^h3kH%mq-w;^Ay?myT1#&gy}n*3w$qfB~aBg0lK42D zN#e(F9|i$4U4ISF*w+-9sh59ipq;G3cQs`nBEWO@xESH|Wi}ENa(%p1v;0FDG5^sA zFPuxS9a#D_CR^^8g}+P)dJCZVqeRPtGJOE%J#Ojr4iOOFnuvKqhm>WIHiwNj`n19` z+AO0aj*{2~8aJ>wJTVB6BJ+yx4b*)I{57+8-bBxTJt55U;Q)zC5T=Wetm{WmypT92 z%o+wbeNOy&QBraeN~VwDlK*1@Ynh$bqAXY#a6GM*rTTYP)4TvtYGkU46Jz-6D1bFX zpoV;mTEANc`$WlG;CqJ0@M2@!b9zs+(X%^#b|=Q4hDlPyt1t}{HE#~FIQDqtR9Kcp zJUN%lHnE-WgL|7XJ`5(V^Y84z9@>|Nw4sne1;ExAeqR4D%op0H$ax%3xe<+=I%BEYAGakj29CTg0?0HIT9mR zM~(4O^X_&T>;Nx9Y6(#cMaMm6cAN|mb`d3%3hFRMFuL>5>;A~Xe7R5hAEuUX3mHXz zG>a%GD-sHC?9|y6glKMwrfXby#p8jT67Mf>vx5V~s+7oXH!V1wy$CgY36y^b#0kL$ zhIgX^XH}@`Dl8$+bK!8&Xm>sfQ5@EG1)!8&yGqK(E1&MYl8-zrxAr#Q93sv;x(~YK z+(V)*9>Y{LI|WaXA;jOH820fi0Qi)0YO0hbozoMg1ZLHuqGOMU&{@(yADqvX{yAK8 z`l}9~X2KpeCU?!bjLG42jj6g)0;OD`g&{0r&Te1b-R2{xE~EC zqeq+letPN+`&pAc&|_8cE8VtEk6sC^Id=I-Te3$gsC{Sf-PS3{U!4bvI5sntXZJ6Q zehSu5*h<|*zqGMy3LtM~STE*&nB@v6=QUGIt#y)of_XI%sTl1-n6JCVrAT~s6 zsHdGWo241uG_I#ARFISz^5|=LQhau@T^HFLOj&Hwd4I-k*Go``gTQ}@&u&h$S%2WnX3RkII%B`ijJ}qP z<}~ywT1BR!1SsM&MuZu{leL^w$44kNiTj)TC3SW7M-4Z#YfP4*R?6HFtXlre>%G9M zGVRJmOtbpbhSL+rE5+*Q^v6I%i2dWyEc?~rVb-)+AKX1z8p3MpCY^qk zRekkhg!QfLfTuTE5RNlnD>r;)yi04vGX%PXW_{iOywT9SvTr?I^$MKT6yi_Y_Qu3$ z7^hgro^GN7^z%W=Hjy*dTw)D-Ub z-z$L4PM4?e;;xd7ud|8U*C?8`x_nfz+;4*GaY|Lw$YAL|Fiv-*1o>sI-1k-m(p}H7 zXJcnDbU#|g9pmMBVd&n^yVkDtKfPC5Z{j!WzeeseUF_GLbw!K?dE})*x$MN^Et;~eof=lac)05U`XV88nGU)><3g9NU;MQ?CU4lX<(4{!FF3ko%HR?u(%0y2r zQ>tf^^)eKEJxYyLY79!S7m)en-7caJDewS?>mmOOd;>r!T+@VuJY#KX%8+Z2A)1+k z#u?Yi5(YnguvX+>3&X6Pf&VOc``hRI z;}Cq(Y=I0%DsrXCQ+M2bn0Fs1L((R$pUbRTM}m&b# zxst`G(IPJ*2`oI{XY-WP*Bu4A4MxNb$g3W<94~nfxBd@;!Lc!xF3x>=ZM=~#>5JO={g1F)qQP}XLlh4bB%xt z@w-v@h@`5QMvf^VJbI6$oj@NEb*hN8S}1*pl}~x4MilcVU)%vVhmT-cEzTjaQ%GGc z1(w*DO6p@KQpAIs=!O*R$2SvFHBL}qpF6#n@(64l=BAo3lAn2!A0={ar*^+#rVR)d zbRr=YQmaciSJ7va+$HLQghpH_o%kEiQz;@0ts+cBkqL+?0(qBUv8Q6OfAHLX5$-~N zD8-U21U4R+W^kEpX>f>6)sBmKzaCCFOwhn9n+0;^Z-z(GOgVqukq}`A1hQb|e(Gy( z)f`q)5o{g_I+L7SnFw8Xe2kEWynK3FQ1durD#U-?NfKupjiLJ=jIOfoBAa)+omR{C z2JbYOV|!aRTVBnKDq&>uf-Y4`RMo{p3Y#R)L>LoHTF9eX`&6S;5)!ZKZ3OxWGsqIQ zyL9M8G*0-nZSZy+t3Nk9)9epJ&S7+cjZ?1apR$_ znxx%sZ;^Ee%%fu|R@R4l^X)0z39dtL4`Uv;Co&dbj>NkY8nekwpMFuFYB{EKz?i!? z)2PI&7PDH6tKF@)^{L9ELzhpg@z>M{=Sc8( zsK8yquJD>erX^+ny`o5n;G~vKus-OII32ZI`p0yHG-245pY&!GC)%t)Oz<8C=tQ&g zG!(vI7s>+nQZ*`DT&(F*9RVm`CN^Mkk z*g2C;o_eR_)05V*y;-;M$JkD`olUMH+jOk1E&1xh*|f9&y&x%46h%ptrF7aken?~+ z009sLK@g<>@W;dUaCYX}dmJ3>6-LwT;Rd^_?c?Bkh~2>P+@H_RqConh8wA)7;fv&H z>U%331fKtM9_ZBep@|Ut0D5%RScRm2_qP8ndHUqIMQ|e=+G{(sAG@yWg*ItrhHWE^ zzV95dFCnrI%n`%Tca~9zE8_LGwo|pOp670y7gHqu@X20Pe>R`nH9=b5@HZZsqq=9>Me9zruH+;+)mNLjZIvjRR7UVl#MPO`_`SkHJdf}g! zgWq{8duI+l!h1079`jz0tZuoRvcvKKhzfH35?-uiLi`yMkg#}%^o z@yheP|zz;p%fNsBGm%S^2?R#Nh1o+trLiohq8$pkw-OdPb;oC>Y zjDrX4FTDVpz|Qasu$yu00_wr`9or2*dH&uGv-(!y<`Fm^|1A%EhBvVTjO{J{AY(_I zt$4PIe0#aWIlMATk9T$i?=(%)Z>RR#&a>D0X^g|LphFLHA38hXh~w`9pJ*}Gq~a98 z3o|dW!gNAopT=_>a|SMPf-ER`lw*@DkTNH#@AJ@Nf#R`FR46y|1uysBKWrc1>~Krv zBM%Urt#)=0AU4ejr;i*mg%igepzKU=_Rli}uS2xlIV-dR=0Y?WKUmY* zg5}e`y0k?A8t58UQLW)VcxS32A z^Tn(?mGG7xc-hdmwSB_S2IKC{-JmZtC^w7UqlH~!=xgf`2OtF;kdBD$Ql%2xs2Q@= z>)zcFu5zeYFd6R%S4HIhI_TXmCTT|EdAI;=+Fg8>Fd$Uk765;qS~tUSclI|KWqYue zjtgG97_m1Qe;VE@h%4%zixK<7Inj+Z91j)jwG$AnSdQxyllTHwGG)#~LVpE2Iq3aJ zj5?d#&IWT?rH6RJDo3*B_oET0%DU;!2Y18q!1^?qjk*gBF)Luxp0%_C3?T)!Hgp24 z&`3G$Bb#AMNzVqO$zov5d$Zwmq1uN}XF4lb`OYDoI3X9=rK;{Wq1`=CXFjtg|F&iwvz80Us+&)k4`l%j-V9E-&U}jryw>F^k zFi)W$%auTQ2Qd`RK?$XpU%Io=y*xL(+WtN&m>A~wH-8@VsL4amjU~(=T9P#^v3KY` zS39kOT|NwZ0}%3dI42s@nB0;t`M}&+C|YQRoa94yHtdcU%2;2d{TUzbI~q)i(auNz zhDX#z4GG@wCbzeRv`CpEr&7Za?GBTAx3fkk>F$ihLEiK0lbfkglr zo@f;>9B^9+meB`4emd`2EcZ|rCn2JNo`fV{xXZ#o@KD0YoP zKf)IVz^UNt8g1-dP=qH%S!HcVfz>r~VSSkostKm9QCidlMHkOsn7~3HZv+=d@kX!^ z78t?NaNH0wiNwmP;0;b9r?F$Aja`uTV`bBU=rvRAtfPW zF(3;9%4|W74ozTrKv>eY8UdD?dMe;6WkiH{*Qj-5d5=~(cqZ<;MmZUbPNzTk)V;r3 z#Gn;uX9CB9MikimR&?QAnUeI8T$vkDLblRI)JV+`AyJMIxEz%kqND<`YB*BpWr&yw zw~X-~%q0?0l_5NjrOLoFAruU%|D%((G^U=;<0+TC(z9iExAR`{dhvF^K|5!TyZ|2w z%luu7&^eimG<((}D5ljqRK$c!h(K5syMLnVQ^;i|WkS~Wef#Y+YRL@M6rp5}sz;Ge zV6~_aADC7=BUPgZlBuwn9!;jpdXlE+(mEz9O|lJ%7|(tZwHj3N=Fo!T<>ZCQtWIr3d zZk^Q@N!c>QH0#p0_eg_cw^gZK_ZfZCv8Dhj5#E}*$auMUXELEvLB72gR+C%kU4 zi%13y%ngY&TyTzT=#F4-?{?CsG5Y{c8ep75zXIR^Lo42fy?F~lypI(~avW%QHMyXz zMy&4<4p--wXt8wykT4`=8;EVs1<3PJy!{gpY5`6}Gt*5vPtqDeUjk!^@O;Y+we9sf zKyO||gkgzK>?0ew_?7n%-n*f_6Uc?#E+<`D-SwK$iPM=P?N~aH_{yD`Bw&)w7-}6v zw@F_Vkw_(MTJr;5Ih!|-j0_L9r59Tp>wd;0=>#tdKYE*wOWR#bO(>8 zq%|c6)#@|j1B0;FgeVa!7L)Rk>c|q>Qnsq~g*X*LOzH$WEJHF1R;(~jc%9|WUVYmT zw+Vx-*x#fwU2$3(@nkq+$?$ULQjh>^CG}RKM}1NR=(FL@a#kqB-$S7(rTxNAFi4NJ zzL1_Kt=W?IN;(;V3Vpo6z}5<{FOa<>`#I1>M-2Ayp4{uxZY^%==mooEV-Va88-COu z!d#bQtg(j$ahO>XVM5tW26Qkx%sEouz{Vbc5Ym^3YHECccLxp9#vE7j%7BK+oJ&Mr zgL%=4Ca^>5=Ba*+WlhqBts!&#T-G&fIM{m8jtGa?Ch!{B8{%P|5N&NT{fog1U$vp_ zO|+w`S(B>H7m&8d>@!(NVf!7hgw8V$3@n?Mh6m5(lW5Dk#G*XFcSc~PYSvcZeU*0e-kY*(AKp-G|U=|$PX zHX{Cz*0dY#mtd_h{bXn#y+;uuOR8qd={|}b?_wm}tJL2A*e_&P#N}{Ei_4%@sOI&Swo zh43EgNo~kiQO<-F>^7*NnBsq+k}?$zRTOpaI^XB4GPuzJBdEU9u+f!ORFV7E2^*9b zsI?V45N8R2sEj&ETJIXiz34W-IsswgHtRo9?$Vv6M>Cw-ugkDqLv zO|GW%GG6aL1~ZjQRaCyfLCD`UsCSv{yNp6|grSne{BYihLVgHheDn*Il#`Q%qi}J# zJ{LX!cte{}(yy{a37TpEW33`$3(_Gm}&p2*`javR71kIR8 zJCRKzg|Nks*Y;X))fV&=6s75SxC$cD;8db!-#OYL&W6p&Qlw_k%uZz0cRP$uxszuj zBeycT+}*2Q!~i3E1whToMW0ltLQNYKu1m;g!rCv6DbU2n2U82_IiXRtl)#&KV znd9<4*K(xM79n4JL|JvbZQT)L-hk<}FXipNCKFzZ9Y$#x%xkH$;r|H=*D=(LzBPxy z8&daE=RD;0(v3b3ySp`edIjN>D_LaPw(FQ-Oi4e{q_3IK zFWgl5wCdDMIU3xYh&QuW-5fQx8+@K2mlv@pH&3)rjp7q$i~fknCQzrdx4+@;G9Y_& znpE!zlq)K!G*_5a66$CUV5p723SQ4?Qtv~QU?iN3QoC)oH;0CTFlHmJ=@qtAU)v z2|LSdfUjIxSTh5{E3Zz*ubJZz_t7=dZ!s;?PD`7N(MnFb5zrh>f5D$QzH-N(Iihsu zVFhksxZ5xC^p!Ez{%rU#m=!KwQ{VO2HG~pL67j+WFrR=2>`g|a?zqoR3#Z~-O02|- zs1+AAor!v>S$(_AIoDfmq}f~Y%sVl28J;<6=JwQvT#Qmy+88;dRa`o#ky5U~sJ1|~L{+<#%WG|~b5UJ(QJ%eS=F*S<{c*Q)%(4q7l#eR7j- zeZ?oV@~d%5_;x+l$YN4=5ZC8CMef&XGfSof6ZI>%HP`O|h2XEX3$|PhtP?2yS?)4V zQ{-P^jmM5o{({=HDCyihsUaB$scXXfL-^KaS`WT#%{=4V6%8hsAwRU`3U4MF| zjLsSfYpGA4(l*EY!z@*h9JHvJBq%9bP zPyLx%rI_#6N^iiZwi~VfPEeDgFp8#ie(Gk@srIZKHZ-a8)OTl%eFo)6lp0WKn&63L zLaJ&`7f|}Or&Fu)*A$z0!mm$?uOWd&#Q`wah8(<+uTPpn?HtoDc0%T?iJN0i;7n^i zM&6Om=SkR(~98sMV?vO{f{H#WF)oBYu@-M(X;4 znkDS!b@zp2a<^tqkSn>ppe9q&odwNM5;qhyBPh8xpeAGS4FS#Y!)^%B(W2L%Rg^7# zG~)E3tFK$Alg&yKEFZvnFIxMwM{ZeHCG}_zO;(=6_Y9pMtV_|%9Is2R!AdWFvWt6h zaXC%jJnDW(Grk^6JOrJNxa~YkkKBG|+otAfi zt4<`Iq4{sAtf&$LRW8AJkLr!nT~i)OxzmbDxK(tuqMZIe`*HR!y<1yv5*Un3+iYb?5alFpk^g>3N}@!Hlt_C|4Fs{J?;DNC z;c#Y1{`7OSi(0J!>>-YT2c79I`UC@S_t1Y$V1Q8=T(?>WT)J@+?jw*q%#Zs`7#t^2 zLNIs+ap*t8IK6o-GXioKQsy26>xcd`@DCt?*>4HmgAokYjq8_;m8NaE^db29s;IU zGja-I(hWpsBPa}#9mL2$g9T~(7(Dr~9(oh{VLSqn9+CB1DEER2@-QO(Wks0AFiDR6 zI1XbI9{-v*Fg5`XL9ji5ZA~*&u@57-4-tmzGsVmx@NGoU7bCp_e26`mzhwiQ8|DJ- zO*ywH#)&C5jQ7A4{A-8;L+A*+nj#@E7Ha-@GBOOIQy6<>?Df!Q1IZvX?wQ#<>A`11 zunqPLh!HT-V+da>9<+3ux(9=iC%}mJ+#dF%J%$J;Cy-^=Yb}CJI5EPZiL;TZfd}lB z=0nVynfH)2@ubEbhFjzT|Kyg0yMe%pQw&&38Xc@KAb)G?wn@oW6h6wW zSosLIB7H{NolpJ}6l|?u&APkC2j90O?4O{WUGULw%gRT-9WR}cNi0=N$|nl2qRpTW z67&qoRE>ajug??mWot#@UdoC^9g`J7aA`Ko8h1we7DFGZvaS4<0Xg?K=8TfJu^};c zVNNKVg*oTe53QcTU5hz|Ccs;0)4)!-C1YkU6|?C{`TqPApi3FEA%%W7(Wg zjAV0;I4q5MO@p##Xp@ZGgS5I*lcYBK2OcYs?qIKA%*etd6EDf}9=x-a&GFZdd zM?tRGE%1mfAN9n=4({PYpU&8Al$@ zNXUi$s|TYZ>C0+z%m zAMlZk&ezqh6g0K3b>bGE`=h=jAfX*VC{@n9Kl?Jc>93~4?qIx{&IaQ}e2?B?<`ekX!6YY;D&OZ07jllyA(H{47;-A+Hn z{qH}{eY5K`iWt3hyy2pFODS86>zc|TbQ^!Gs~WRdDTc^ zMHO>+>T@mdsjZk3Dqho^nmG$TUOS!Km?z7{bh%i~7rjeC%H=ix`F7>p3-_whs9E1k z#>D#i+*`!SDcQu^c4Q{%_)ns<%6wy(qQ(Uik6JzOQT&Ts98#VIvsa5fa>2Nt0WzUzR zGi8nj383{0q#y}fJ$Oj=UOUhdmUIT3Y_SP=7)`;D^g~A zwNAB?xT(hB4rqjXX-PFqsI<#snMTb=^r0Jm5)e;@Dx>i=BssYBM^{9U0}|RIwAH8mdyHu*t$@S5&KZ73)+D zB+8n8(3Dq{D&=)#uF-kUN=$%kI|OYUSxIiBSvzQJ7MHTHvVBP}u37%c+u&M|^QJag zgSND3yn2<+uxyRs=EPj}MkWbux}_;+oNi8%B8^#N;%_Q;q(xn41RUt#sZ2STwNsLwYdqH;zQZ*Uk zzD<9&AFrzcGaET6_``?d4OKEr_91s(p_ViTSdUe;OPl!qk!naEUORA-T^znJlawo| zE@tJqP1Ppb%iymdd(Lr^~`Z|1Rm0SlAwovFm z%6&M^(R!0qVT(>vKGOg*Xuawtj>asaNuslUnr{{dR@Z6k@&VolHoDeiD*u||Sfjax;^k$;GxU~M z-(9P=)U7F zAHI-G{TLC0YS8D(X+Mz^q-`&ZDRxEuB@La+0_u*Q5~GL7AlQW1<PnT2g6O8~3gC*7_e!j?GTPFc5_Aeu}*Xs?d@`Pn3dy zB9JOnsoYS7TzeB+ICf;O3A{ayQ>pA_v@_rSEbltiF*chpn83Tz0cvd!l6Kq^xEe?y zY+tqyXoX6q_~H#Fc;L^(IXrQz(ZB{V#Y>sH`y^xrXDVE~I?~V1=Vf&?288Q=y>1i% zw-SsNXhS+{Yv)@|+Sw91gFJcP8v8uAd3{{J?bgGp{39t&(DMLN2lDz52_^KJR7%qS z!9_1H9g;X5KbSG*k)pTo!5~#3@erHOmJ9C+xLqCX9N`a&NX}<}1dH+%lWsALGu36T z66RU=|ESP;>V%qTFhcW)@JzAVoyVa9WiVI!7xCwSZC*vac4j@w}VQ@2|gny>?Q*Mw_99 z4H}4rv0AJvOEGMO%{apKo1)SYaiW-(y9zFXK$ESsg1Z3to32sQ6kcC7w)dr^NTV&Z z59}ZdA71UADW<8W6zx+~dMGs+!~fC=1V5;?ZitNZuBbTPy z41L0mTsh@oNjOTBbEig<#>P~4ysKU-VoI=*_OCWVSZa8Q;Pf+R`;7>Nis@lKEb-8| z4ykEcs4%AyqwVM)5V%v^(Xb88ffszewKN)|6*+}i zr#mv=Kq+w>q6uAK))Q-?^9A&O*9R&k?0i%kA8r}`2y2uTHTC(^oSjWxEMF$Sex9{r zO_DC*gdDGu@S{8;IGG$3+*5jztIi$`&hfAhVI%jA{5FKN91FtugF0(on*VvQ(R?MUKHv12GIl@A(Sf z7OGYI0hEFYMIcqE65LP^A@MdA*<|H-yW;P0!y2klB-A|coSU1K9 zE)U;KryyXHhe!@n(X*+`+*_fXS{Nc)WoN>v{@cGi>|Wn?m#ErNf)$u-0Z&DkK1V?h z6G>1-`fnOJdWiKkwxIiPq3@8_*ExVRbFiv?!6S@}n@`|)XQV9BZ zuhU%GdPdbs3t}At=jQ7Zdu@;|C$LjO<9r`Ub4n;3XbRGjCb`y5me+Hg=UZt8(6&^v zv^TB#=GOH#CT?m1Unu7N@i-#YTQ@HK1Wi+F#9-jWawQ+FVYh{&C2U=r@w$F4$e!DK zY9>JJcpbb!<(O-vVyqvpn^`8fm)x$K3J5=*>q_~{ksX6hyC*VppE31!&!hi0 zFaG*SZc~F%?GQftEc`)UB!bRTeaK88QRO_14?W#6S4X>;2)_Pc%s zw&RYdjt+PGp)*SDB(;@YPvc`PC)=4M<3c1XaZQmd07`au^56I2BnX0%oaKy1B=GJF z56{26E>~r*H~FE5en5Atl%a<-BM5#=OgSnZ5ML2=^Us@KNgl6YF@D5L0>A=3iU;T% z=PAk1dqQCQmqdQOfghfUm2q6GPc`(rf=Xf!RhFT+D4%J%Tycc+1Z8PVat3Vkhk~v# zPm6qr%8X!G+oWVG@!nsY&93G%K}yQVSD2$MW++J+r|F{NBthGhuV4q{XGKM00*n$e zRuW8ly&SIz10_JERVh{aJSQ~8S^5{DuX??TIXmaY8vf#HO>+LKy*0~7VCNF}WuN8y z8q>$%?k!A2Dd$65PCa%O7}} zeZEWAVjFL(` zM@g|pCn$#~G!W4M3}N?aQSey?AOdo98dHKfxrN2354p7vR_p_U2R}MxgumrFaHIZ) zPzG}Jht~Ad>Brga{Nnm*AM({avH;@guQ)HKlgW0w9dCac7j!xK`|PUhX&*%@J{l^eNfG&tn&3!0Y7m)T4%SgK${Wv~8NO~j%qKRcX>B@&Q)bjyWRvMg@Z< zs zijFoot6KSBC6T*I6eU#45)#7C3m@Nigg6?7BS453V)ExwV1m-(Ez;ueC6Ys3{wO|# zCUUin@wM0d+Us0<@EX4^%tFwsqEi^1mb8coV-`26v4%F@Jbh}(9H}Vb70u;AprG|$ zNz)BvTYW0EINCSl^41=$WtgH(ktY5LWifUqhy`mLM-Fl;&&63SV#30xEEmho*sfjC z1l3JUUFhX!tJF8#pm8DtJ#5J6i2n5u6B}y!7u8g)ab4*-iEz>1nvw1pqnfHmz(RER$A$d|EvnM^W+^^eo{_62tgIfM))J_74r|mdtrfpl zp)?9htJ_v1??!#@jwOxb#*)SxItslsrVQ0~Oz9nmi<82JTR6rv#y(qXu*OLTZN}a8 z`Slboa0GgHY>W+;pjA<2(4#;boy%U`O3Dav@kO)8pxax*{i#}rYum&$uk}mHSVh?6 zr{kXkV*7q(Z~-jA(UG{E<;y|Cmvgb%=n=XJ6%{WlKH_i(8C7rz@l)nn?wczLM8|6| zg#MaNFE7q+ujjL~>#Otmi`j{eFV$^%P>-7TjRAd`R%Om4}h$SU0d9Yk)6>AEUA3I1DdA4m%hO8PwFD@q_jv!u(sw*u_WKzVhs z#1u*iCv;A9);R3Etg)~okfTwZn^d}rm8)2FMMx>H*0 z$dh`g$P5jUU{OsUTwzw2%_uGCoa6WrMECg;vB~zCRYC3Ox?Sa6eo}Rb9c;_wj_$U# zAYtCZH}XYtxP;n_m!x&~aB2D;CQmUbW!G-Iotj|X=^RQf*7@wvka_$95@NX3B=HVb zUPK~GbC1M(SZR^)`f1g@9uPc0?=5453bWl3R%PN)hKnRlvx*Y=9v79EleFT>!0d}G z19xC-?G{HyoBj;Oo8D7G)gpFDB}}Vdcg9`tj&MY!h%&U`gg!x^zU-T(L19=w{? zJNpc30e4u`!sz0wO({iCQVaeq^FjNtB`7B(5hD-=%_ot)h8p#~`_~mSb7Vz?36>~< zRz*!-)de_%3P;B+;v(|?28{<4%%zy3sIfqzv zC7&kMx-WG~(;u|#u1K`(Z7BQ4DyShs*y?a7p6x8GMGw7jh8jPT2UROLCr8R-j zuqJarpVZK%vuOg8*}q0bQc|MZ1mGjAtI#7KB9B)lhf(aFh4jpDm+ zN9%5J_Dz-yT%!`y$X$iNKr}DHmOzW&*$R%a$RQlQHaEzs|10TEbZ76=mFzV;o9pen z@-7cIPTyT)KHV{N43fpJ(+j9!m{^P&M$=%bfh*`eFHB{XTMjHV-y~;n6G2Q&5(=6? zUPPk7=5Y9liHZe%WCi6lh0wTF@|BZ%c)K#A3GwkUXl<>n7aJCP=;F35{k8W$wHj-0 ziLYIBH@isE!vRSU&=PH{D@!U#IY|%s@15a8qA1FC zwy1z0k~lLQ&U;4w>E~))jYhLKBl3patTREDSZ`j?U`V#_+uNm5qNJX@_Lp+!pa zEMbL!ZHr~eH&kY2F(FmXD7@Qd>`~!;`glISyq+T|#iLwPNgk;nX(lAk7PVw4dCcS* zR)Bv|);wXbQOb@r2eZ+rpc^LO5OAb*rH*uW&3H!h>_3dZ8;xq=tb7oHapa}FyJDiw z<-0E2MY(~$q~5S1v^39IU}auxGhPeYxXy!PPnEB@eEAr5w&H z8NX)!_8I_=Q|4Qi&YxIP%K<9a_2OSF2@|-kGs%Wm&dWRp)S+|zNb2}VYK`}~QdMoY)a8j_p}L4Moh z7Yr%z(eEPymm-VT8=@BoZ5_a+Vn`G3sK*A|{F|x&RlNWs2iq?c3`rH`*qRMFvP8!x zK0Sr6S;Ol{;Ibg6X<(xhnq`i8W%^t*hfgCK0p(FeVmsaH9{68Sqp* z+KR~!(xkpPl5`b~Cmm3vaCZS5-DI0qW7y-E4H&Y#@^t*Z*5ei^jI%~2MbAygh7-sTJr55D+e@51wTD2 zz!Wawl?R3F`fTErElF%q6EsCMVz8;CtkKs(uF#&Yho?@prFq@+W_Q0IG)U-d16X*0 zeq~nFtnX2Hy#(K?W8doSeC49tqOp@zxUM;%{<(Rsz$k-Q5|+Tch+Rc-|Edn$=c~)p z3t>5w4ZmZ2&e@74&qQ0$egRXvzZP(IsZ@Sum_Y7^1ZM=GFw{{S42Si-oR9jpWKI88 z+26eh3QoNktA&Y%4(H}0u8P)ktaf(Q4Zai2W*X6Wk$wmi4!VpwZ6SIg0|6pGTPPD% zOAqV`DmxKtNWKR< z2s;|O9Tem@_H0~9Z4=hEQseYYFbOW^SPID3wWx5d&<~^w+j|%@Xr2R>(5yAN+ZHNK z2h-P*OL4$*Iyk-^b&J(@qn7TZv&X~}_s)V=pW>h#-|pPp5toN1`*(OTC1;nT6S6I{ z)GG^GC3OVmR9LF(X=*I>6s+@gcXaA-V%20?y|_gWhjTwUdi_F|$i(4hr|=IDvZZbQ zlXefMxN7z~T)0OoUe?vl;MfSHk+{M!@>cf&X=}Q=t1>WQk@|vgb)~%dZ;DcEC=R)A zpG#(Xds_^d$6?FRvBQAH{)ph@1p6{fbRVFqR>ffK5Dg!0qC&TUj;KhZi8UGkZ^jhD zr8FLoeOF(=Ill$n6_WkUmy0haoFJH&1(Hf1NW{#W5tcaf{&#Rv=K{6J0uA zYFX71OaFfsnT&^Nl?rf*X?0oy=%Hl4a+YM64)-zZZctgILw5PBI<9?mHvqMp+7*-h z`DP7~U|m8V7%!oV-Ni6R_^GrA(ExKmjK3>P@iq346)Af%CFYQ2RL&63)PL?=FOJ@V zGu_?~H{;GB8#WrEQxJ|}#@%S3JGV_x7j$1h>mNn5?SSY19uvEW49LS4#O-FjXaf4v zI_#Q{q_WHBdAX@@$b5|}j$D1iGYh&1GB&95s~LE9k2xWGyoa-`z! zeP@zxI5FfVjtPEulJ3cn-Cb689pg|*c3kXWaW+_fCn>jaipROAdlYt3_^$0_|3Hd6o{Gt*NxgEu|P zS+SDqo&hU>o_pZsxQN|cmCX7fs8Y3N(vbQP7al`c8cwtr7$7u&UCl8&?wFdM_;u)s zs*qlXgHw~&;^bK|h!aIvWCK+;trao0oh}m4ioCDj(rRoXmfm; zrru}l*n5W74oErl-MQ-_-%EDaoEgLvYGS%7e(iH0$>;#FM-C2wUT$w5_f)bD9Gq4S z^n1Xly(Pt|GRAiyc~uHqW=7K}IIzU)yN8x|Mf0Z$QXos)W|kxE$#}i=<#D8U1Mxw53*xY{csmIRXF#AoKr4;ax;OgN zr0@SvGQBLj^IrV*2;{MCmfD|=pfJ@G7|hI(Jigi`QY-)eUACfLhGg3pEcqk(Q}DZ| zcAZ9)w}ASVPYTdj2uJ8|2 zi|eNgDrxxhuqAG~dKJbJn&%HSgt7-LP99W^5#NUMR&de|o8J%QEF5-yK!O47-$UQ+ zFnoVdjBF-V&F_u~HUs!IA`!&NfR0>sPo-O`!QWB5HwD}#dt#}1*{tc`MW`k7amp;s z!o{8Ukxu`h0!nOnupi~dUcshv90KSncu|%)qy75ba2ezJ_{g)hKso9_yo_E({{@AT z&2GaW5QOhM#U65StX8@Azm0NiRplHhqIhj15kRoGt*X3x!Pu&mItmx$U1sKE@!g|s zEn}J=&|5AL-dp>ETC}@YQ=z(LEci)ri;eW;zISV*YuUuMP~3aiqBp9;YV(C6ka6sX zL0qe{>~|>PspAf!RO)6Zt@_OTxB!uWUC(B{;GBJYm)$pODfI4VF(rDQ4V>^Zh zK}F8aIah<_C|~yK%$(MOTIsJJ(owh{(N3CKnc5>lY98l78l<;qQY>$#Gx!U1NWtl8 z?j^f?fn+$D&N;*Lgq}D}ooikPh3iYteaT%UUBFH%`!%REg8^Na$Q^eZg~3Rp0C+=!ObU(kEJlr7e85Kh+>&y znh9f}dh_J6wxKO#_&-l!mBJ4sPla8MnV~$@5M5MDB=D0YQj8kU%IO_i%u!0UE<6W+ z0s5Nt+-M$&ont9 z505+axiHr+4;@&#e6rf9&nZITg4)-A%VaF%D)>Zrf?JF&Bg5L{_<+J!{onIaVXJUN4Ig? z?>js$V)nb|@a~wF>4wF#-z>_FRNYpX-+f0+TnpJ!^iuf(nho?I5n<_)r>95?u$D(A zEUTziF%$2~6~frsa5adZ5q6pnr=>K|t`}QN3@xBUt)yzZMcg8p3lmmmKAf(Y@#U|> z@z|Mt-HOW{Gr4m8Wz9??V`BZ;4vy{6Svc&C26(y#rEq*)k8s6TIUhAXyU?7wZidaB z#_&f3ba9_oWC49WCQ@`F>jmhVq)*@QmMVZ?n3^*~Mp^CG1oIOc!A7PzYHGjoOw~Hi zCWIsI=tqNhuK_gfGxMi zTX|NL??c14%_DcZkC=sCs+New>6Sa)M|(Qt1M)nC+J&1` zK%$OGRfJ1BXuKik%fd&5&AW6L%2t(om>ssi+G}tab$4aU&M*B?z(3{4nfQY99T4!^>*ayGmV_IBEO)I^mwkDd<8z3}jmQ#!6 zy6<>8rk&wzD_2u{b{)nBlCF6%=^tT!H0HO zc&quShpw2l@aFQO)tK|ze?3(bM_80oeGbAA2ot`0%Ils*9=0qzlIhex+WL?6i+zn$ zmF-?9y-{`|c#g$Wrk|~MF|aW;ZN3Ko1I1eFZ`(K$|L(tn2V@bs>pb?W^Jvqww-;dB zEt1?GaBYy%5^d`$O9m-9$@cQU-wa8KFHv?D1VsHqgV(2-{PqU^ z)CE2{Osmzorv7`yrLcKZ6eO+IPdr~NYeLJ66nV-@0n3*2imzy$SLKAP3r6ADCT9L}eGl;;4aM=m`2542u>dDjBbspG!CsR3G1=V`!= z7<^HZzg$+UHDcMTmqU4*ub54B4amSF3laz{>H8aigb9rfyb;pvDJ{~b0FZ=qaK<*d z^_H{$uoSPiw`)DMDK=Cd8pW8l?plf@mj^p>-y=aes)a1A}?baM`~8TEE-fc^}d-wTYzp+s-(WZPr=TbH))+H zLGaN@(j~;Cdt>K-8}Eh&&~ZTY-YAUBLm3C2&t=$3iZS`#QSM;}Iyl!#0gXvmkHl6C z>Dq9UF+BBM@D3h5)sYP${lk!GnXmk)fl4 z^i=ubss!RWOgrZoEIldcftSh!jlw$MdFOKQ=hPVFVc5Z!VrNSNL@bLjhj$V(>0l?b``(zK-G9Dj%4u zSntT|*90qc%%%&BnKBwM{W?OR86CU-ECl=WXS+}Ob!-82=Im2Q+KD}8?Sg5=6Hv{O z(~l>Hn?n|j)M%Kfn1s4iG%&i(o`@O(F;Ss*bI=d>8Gf*^!-fCo>({UCzk9mM9h`$n zAEcB&r2NF4$EQ=d*o5r=c`p^%Z&Sk-=i|G|A6e#4U?G&wx@zEUGw@Qa+CEfvegrcC zbu%DY5uwqs#yb2!p_TJ{2s(kCoE2tXea+`spWC+w=SwOS9*M2r*k`je@gmZ~IdM(2 zZ98$KJzFI^NK>`S4h4p8K!pWsL&Zdq>F2RJ^B3Is_UW2!cJXDzk9uYK>V8GnNw-j< zO!gsn@E}KRX#lmZ&AgJ>?QdeyI1$+Em$93jzWKny!g*w{YW?up=Ca+_%8~&3xZ7JY z=haF+S}`clxMemQTT`yd)w~txvgd*VVKHVvU-2) zs9*D^+ZI$fd)uFIdoO zFViv^lXG(1+YgK~XXKaTV^gC739m>Ju;ZuysRwf#=!i?=0j_IH7dBQljC*B|$w5rZ zfZp#~gcX8q1LD|kb-oJ`{(m3H)Pba1W1YogE__@zs~Hp{@DT<>MtNqR<)PNTFy&&1 z1^NIx49C^3f3b8RfHLVK&|-D*Y~K4*DS8%yc2k3H;M>4ZnI9rp!CD`ASD1<{Bj zIr-b!nO`>gLJ{fd3|dMD7ewt&oYF|A=Jrn9u1g0TY{#eQ3IbLscl=B=vJ)$M#^Xr& zbt)NXSM!GLFd&(5YTJ|PU1FzQjEoLsm4~d@vf6{NV6buejD~XC{7=Ed+f@s9hubGN zrL}Cb1H+iB?G|N4cj!;(tZHg?J7R``v?>ZUI{5nEFf6#md^%OfqyGEb-{1W{g=KKF zgJ(6Mf6qbdHj}%lBH%T4yLqLoH;2A{e!v7&J;)C0Uq~ck4I^(o59{~-B`wRUMud6J zHjJo7{eTk`v>(<=kQYv@lu-jsDT}#;5kS7>+XN+|-VoN^Ff%C%Xod^*mu-0VIm)N7oOTle#j1W6iCbD>uzvH6U-$Q z7?(je%;z~MK0Hh3qAjQx^WEJ*EXP}Yb|{mn9u7YOPi=fSc@7HZWT|XV7(;!bMAp2*+g%p)%8NNB zrqfBh<vWSi^9FMi50z}4uejs7C)&mS1j!@ zO&C?AfDVVgmh!tTZ#eI%8yHCiVCc06J)>@1EWP!Wi&lH+sO1M(ADOw$C1cmSy(_qZ z`PJIV4ouzQh31QN)VG@!`&P~VC0TY+%Dyli+yb~=|0qB;NP-TIWTV9PlK|+8dT(SQq^lDnmc=iCG-})<>=TJo+D{R^LzC zFc5yvUvUqWNT&jOrqIF|FeG3I*i%~}*SR(xi4!?a0ZshxJ3CIArcH~sNYKW2-}$@W zJszfE>UoD>Joo}np~zq+A_V$OnbMHVK!*tTKkmPwy;(^n?Oq4Y?%cBT*lD;c34>d3uKqNTIjINclM8!;tMD`$!kkQ#f z;H$;mT%TXuJzf}7mZJ`thF6wBATq7QG}jowtI#1yh<}#kiX&YV;6cIQ(DNjVF(W6) zr7*QFjc-XI>U&;Jca6`KV(-@)71N38+p|j1{*Re@8Q-F20n=>!m`{Hr*X4WP1k(Ci z=|b#ez$2DrjtQKKnB0p|>I}x^eP{Ez!a37;?CcIKX$!1b+zR#nctDS(VII0_!4#MP z$+cix8hwu`r?!ZzB(`khr_JT&g54 zlYb_Ii#OysWy5dO1$4?P=)nVKNwmO97dq&^b&tw$H+^tnCW4ZB!72Hcdv9rbe=tXV zM+NS8yZtpV#Q^n{O7b-GS0V1O3&SJIVjJ|4`Z;YNi!%%S+J<{Tt=^wPg;|(HLBl{p z*vk87uG3sUi5P!Lp&LdT!cQ2vSI9+1*=)76)YxIpOplFLte_QKRmw8^Et;2hAU3XW zhsOMRr57zZniS=3-)d?Kb*^MhQmge^>z>;(x5JqQ^aoD=z$t9))s-Ic%lM+TXHltl&v|6TX)4dTEICZx&vrlT{d`r+wKRJJV z3X?@Pf>gS)>V2C>4~4dIYDu`>iobSZcA?F+VlT_{Fz0GHtUr z9`E&5?ALOSlBS47bl0=YbERlAE){l$rq~QRf!9#*u$kW}Mv0v74w&6YzOC+Z+qI{y z+Cb~X%DP$k9rEe=DFKGaiiORDOrx!+`Hjlz?n-O3#U1dj==9;{R^MF3qdIs3!y!oe zomdk1@csggR9{cqFc5$Dr#K-VQqOVsn-@83Bcf3Av3h|!6F6SymMfTcDF{H~-Ya7&;9;pW@T7qqJ2qI!}!a&zp@ z)BrA})o9=vk&nlV^lQp5GO=>1TsdjGzdE15mbEdUT($8!F9@VvfYAzVU|PF$zLcb$ z9YSSLa&4t?o#w8uE@nS&XAvrCq(Ty`Wq^eaL^0I)CVa8X*V%*o_#$DaRHiI{j4W0Mw;L_ zG5f@J$)#vk(rb|J&%uyBPU-g%39JFf+0xn<)*_KB!I?ge2(KC_%P}A@ac`Py_RM^c66rW z;a2o2%8^wHtxADEVw-aGU2Y&cB>p zUY#YVtdS~7u$2+Ci4@GTA=I!nRB?s%$GQm$nMmWfRWK34$u&kc!ImnYE#3U!274kz z!(5+2koP~AzfsXi3MK14#T}v3v0t4fMzka9krrxC7qIv@+`Xme1sMC0=j#Indw8uWQ#L$I{c z8e~x}t(s*Xgqea?5{;v@CM|3(yeiB-lo|!u778E3xyy^MXTN@*ai~zE&xD6Z;b5wq zw`x*(oWi5>8F{FFS5#IaC7NPfSCB-J5ji>{L6ovGP`bM!fWEsyS80D3MV0%9@4|Sr zDIGmzxdNjbEG;@T-h~2l%nQ2&i)CTC7AlX;NCX=lCmb~wb`DNjRr)woCADx4uEOa$ z7pR=r=OOY&_UI`BdP=Jn!Xxx73Vql$l_=&Lrc@b*1!E|lpFMawRVG8L{Irm(#+cM6 zs|hs)oTp|;YMGJT6i986f3{XE2ZBGLw-CBQH#~S%CI_iY<;SA0tN}xtG`aHsWBT5G>|Z z@|LOuzb5p6#)ahDJ|Zpk-eDdz>@t6K;p%I)KG;>JBS{D_poZh{12oxLjvL~ziySX= z@>-7X=y#LhKk_=~$=!w6nl1IH)rvFomtde^id z;re$W^{#1Qg5d>p{~-i_;*!bnyUxtEv+Gl@`s-w?=r65R$!^;)5WVXw=8!^i;MmDE ztCQA6gEkA?+~UBX<&i{$qEs$&j3EEsAtf(zjHU(3gJ}+Dd2i;?KD{WjG6?$n0qny= z#x*2dAkbbiWuQpFWC-`)?ypeB85!eemLfSj|0wRj7bZCh_<=~*r!#*V&}M|Q^x`5v zZ@j-2$|p;#5D*vTLh&>+z~l@Bk5OtWE0aRy%}8kHH>8(}%|)}0a8(x6Hx;fZUa=Q+Ysp+;%6hb_^7;W^=D&nRKK00x@N)ODF+ zJz0nt^+aJ+2PIA9Uol7b!*k; z9Si`4o%flC&JmX@5FQj@Mg${7oUDbz+b-cfNf@6TARgzAN+{V~PHLymp z2VSv>f3ftIRzmHyRk0<26wNJ~h?=m}&pYDniTnka4Il_9ujmO$n@>@>-=>WhvqaY3 zU|=#;%r%Vfthr0B3M*%N_!4930blzR8cxsPa67O8oQQ&%k64eea&)4V&Og?8EpF#q zD|@u20=d;*-g%+$Px9W`Q|Rwd@kVc72V7>TxS194+egH1kbYTX?MCXQw|l6MEsc8J z!rA#2uC|bCac+3|PPAQEmF~#n`0WaL`Y(jPR+OlqKr&VjGs+;z(79rzYh&9KKYzIUu{`Z|75(uOn5Ay@T_MPwh zx%2t_W1i+wH0nptho7mikO+xD-<%l-Isum=JbZoljEbkU$p5f8lCih%@*Z3>B~Zd0 zB5j|?VRlI$C;iHh>ukKS{-RAdEGeV}uJe@<^V9)TF-XBtSqiHXZ8GMBRs+Z-GFn>* zTn4|p+pC-JKW_X{L8D8VgC(;N3+s%S6%J!q3YXFj>9@Kt94SzY!&1R0iWJMx5(FWo zc|dx;CjQVocV@{^%*7WVW-uMyD5jaZN* zIC=TSWI?JMi!m-lkU%tkBM|w~hu2G?QZ&NFny)vDqtd&WQKi5-^5j%U#REiQboR*@ zw(2tf3nvR7(DptYDD#Ju>ijV13Zp<#eJ#qQTW|Q$O{8Y7R~`1$d6|ZPoyp*RyPB*s zsSSH2{d%9ZfmQQNr(7$#QU!PA=~WH~u$k5|dV5GGzKEye(i|?D$=@-k5AZfSwP;oO z?~2sY=jb++dce1wt%Q~Dk+0V-VdZ)(=yfIJIM#z+Sx0{X zl~Yk~+At7)&#$-#qyS>2&(N}V9n-3{TgCcTs>(PQ;?1!$`vO80|9xkNX3(Nd^8*fd z_kH($cgflNs;H7=*iWDj_l5G1D~-T+B^-gxfeM7X&vzft$by6Nr&uBvhx)GU!AD^f zYPdn<^jQ}FUGPOZL{6kFC%fuT)%Q!}lBCZXg%bAd&QVZ{4hCfgS)RnCbHC3QW zc2lCEkUzjB&k69UoLUJ&=(q@vSf&iAVZR@5_WgXJOo2{OX61Gl95(oR!9xU3JUq!G zHc~KZ)60LMoKsisdfeokKUusyKN$zt}@hnp?XMV(3(EB zCB1Qc+$Q_NT18J!(i+}ZEh+FE81B7N(Jo*#S~}$MxNmOys}}mc5T?729Kh)S(suL^ zH9=W>f(6134EzFOfCE`LO?NuO>4Z1t-DHmt&R?zKFP4@_vp=O>9xfX@KG3&~4w(OE z)%)#lkv{Zs+aHVNfYL_~us-~kB`+1O)y)xDU#G!2Mb7rp55E6%`2!VtMI4vS7FlfDSw#^7LNhl>M7f-2ZrM<{0st}kF zx2Ph|ykG`$Eg4rq(mOOUEkibNT;dQUN^RCdxXp3t%QTI!w_rZR^0RK8{7QhH2t7Ol z3UJ;iDy&E~uB>BIjDx(Y97=wR z`4k>BH`>TieAj+SJp>zc$f(0Ai)s}{%NHR!pI^F<@Einjz8@dJWD3V84<8NhAD+a+$4{O<+Z=wopSIhXHM1G? ziDSAUAEvigXx$6vDh37WGLG94x14jLc)B0{cx!vgb`&5U9Zz}zd~dsRGU)}xz0q_( z)N?~!@~z|kWt@#;N_6w4qy5AGn1bev{!W4Vs^awH+00$*j=$w-ySWGhwn*v61YLQ{doHhqidw-e%l%aT<~Kzf%i6g3~+-e?Tbpk-paaeXL7VjHHgID&d078RkP#@Qks_!kfFmS1AN^rC}Zo z$ZE;2t+N4D9cCQv7GK} z-+QUO=2OQK*#{Vrdd=_}e*Dtj1RnR8QAzjJrE&^lu=uRKqb`7>$<(+d$#*t8G(2cd zdf4{)$dVs-6!lI9|M~!m+<@6%1uQV6Sj!cr>5G}w$UC<2WHt~h@@Zh|m5&Ge51V~L z`UlB#JbWfE8=9AM#g6}nm4oEBW^4Weby7`l+b|5h`&V#93)@**-_x$^KD@0!R|HKC zI|K_9nYM(`PS#&h_{v_u6Dh*Z9*;_Z$OuR`XEoA!2({$y+&=Ae?!Te;~yRxn5} zs6zUk=`BYZRN>XAphyzUT6AQBEQu~!T74yex=xbb9VHjuSn|0le{$@CoYlSFG9{}e zYYsVTs-Uh|*>*6TRR_S|P9Lqz8hJW2{7Ysg-87MnG# zn2XF2wf1$E7T+)2d67=Rp{i%I6UF&-`#0Gi(BADcN)pR|NX2?i~ry=)M) z(U;hE{(0{^d^e^#!F1oE4I329GVam3_)XSRXpD!m3wM@I|Dp~uPm+0leYo$E7a0AH z%Uc4|W6)f0lo`-w(dF2%``?Q)!o!9U$EOdG`kjKOm$3#I{Jz#^3eF@QDeIA$&%p z;SX#3?<2i*Xe{sjXgABeZz5^IHgOn&AMIZy-v%0(SVPEt6pCoYCXzd*c_iM!K14>g zgyXY~`+R#n|GJnPC_6_TFb&U4!I~>Axfg3(!!y?bjnMp66iXk8XpQgF8C*DyU^`Tl z1f>-0ZA#z2kV80ioLE(jOb6c=nr5Oj_tn&_kWJ!FMhRad!N<+!hBnsp1 z*c-;f*i5x;@OPz9cE`@8jAYx8SrKDjTC;gZO0O{)!Q~8=t9PY&hfK6sX+sXJvI8Sw zk~87AMC^DQID0uX(q7h#t42m>Ql)C}pKM+f;gqE?d-G!-e+Hvz5Tkjvms!}%~B@J#md_LQvw#`K?@TA-^Q0Ja_xnW$1tVb6v z79sDHEMBF5o6+VuF)DCAE1T==EX%N+Cxfyq-)vD8ENP9kvecL=oqBdj$`cFi7Qxfw zvDH?k;5^Jur!waD;S??11S#729#tHg+|Fm4%-Q@-r`rc#5rrWH!$31BX#&oRwi^zW ztKs1i8vAvQ(F(#h;FduFQ`HG{ZGMHlA`{CM97(2w-#pY?nu{3Q(|l_WEQ`3xXpXPE zg3Y;S6pkFi7zgBHMSN-FR(UdE_q2{%%72vvPEaHUw^WC@NN&pufU2#N*&9v;Mh!)A z%TcuVjiQcL2OE5Kl)7}u|uG9?|$4=Gl!w3zWtb`xDUfE{rj*pEt&w6lD!^1csk+KV9d2+-SY85zSQzr!9OWG_EKNSDkGLNhXqknYCE7|V~7$;8~F zn?&Yny&Ctj&|UjiSj<(upXDLaEIdYItKyNa;SGFqF&~fwO>Ch*Ve&t-NWUzG@!Vth zD^zkQ*I(%Ym7O4_bBfP`uHvhoczpzaS5K%x__Z^w5B{z=wG8%E`ZaPCzC7o0>^J8> zl~zq}+c*%t>sQPMSwu3t&OND{WZNt@K)Wr{_LK$zEsZQ96e*CDU1QPz-r7cV0AA9l&afkzHBToS%KgsN<+)aTIrTq zuH+OtfsDph9M|6O!_~XX|Gr$hP(DZ7FbivDpyI}AzUVDhu;#X*8Oqp&#HpjeXL*gCMuXBCzbL);!t=nuaJh8cSkyj6z#mxpRTmqA z5kHll`L#;HjG=JO|D6&7OXrjQp8-9c<|}3q4nuqcbGW&sB;1@=%@ry{U62Dy^7 za80@2+>UXA_q^U;d9jqk-H zJNFzqivN0R(1zRwpW<{h`aA4c4Ee*npB_Av)9$m~|3G z-EvC>NW6B_@sMo7=LqryR*F{%Er(&bIV{oM`S%@QBOj5to8WUxPSN!A88WWX_F4kf zh;-$9P5b5>394h$Ecps$MD9eq7vhGQh}boVTt+N}bD@+-aO#i>H^fuE8&B~XjID-A z(w@?QR_o9xC3e0WqCY|B7Al1VPFb3#j!qDcE#adW_%UirmV(n&PIo3X9BGE!Z z3MH2R@ggN@X9SRv_N*rv=7!o{+B)BIaL>{8$XY1z!Jp>9mCw_$K=kT8nm%z1S!@j# zTRoVTcQ=YX>gq&SL5oCRnW!?B+=qItYFBc_1g|divTtdVO{@1>5kLC%w~IK1;WK2H z?rU+_wQxlLpoS;|KNMn{yg1Ev)<1^1i7zjf{yCU0NZ^~imuf8sioF6H&yU&lqcgZz zF)rAG&Z3ypiJ#b|k6EqRS4vSy71qqb zB-+NU5GIeYPh39?(H^%&dSqM$=NNx`QvtdqgcHZpIdQX-I7P#9+<}K+C0#-^9YKta zY({=TwCFpcU3on%79Jxyx|MNwKo2^(M;hn^e~9lG!pArNs<+UcGF=W&TA*Pi{hcf&U488kT@X&)@r0IQWvX3ss2Bbr==2#$5WI7h6NQNXg%ZmQ@ zQF3C-Mw9qrkbHW4e3b4!S7ntXx7P_=!?F|>3Lz2bUoqoA7vM^Sryoz>Q1OzI{14k8 zfjvIu8GL6-po9e?mGANpeW9O|J~QTeJ3F8sv2oOnxu>fcxyw5XgVr7$-}n=vf+mEcUu zebY)qhe6) zs|Wvl>AmNo^*fKuER5c^n@`(I23L4YW}gnXz_WB#7<9E!py&lylts6RAg4WjGuxnh zY)neCQ4d9xRj?Rln%OG+ z*sExRmuMP^XKA{tbDE|x<19^g$xhR>`#a0jdkSY93LC#*QTJ$06NMdJAnLt?_vAm7 zl}k_DFc5(6`4v-X4+(Q=n~LME9cE{+}9&LULt-#d1kkOX$C`arNf zk8d8HT^`kC9Yu$G5$wUwk{igmK%j5Uv;~!eEfH?M-<+aMOFB%SSb-$$0^aEnWw5+1?g`}dt-Sc1f;5d(Yz=vFqwhiDM~|bWv+C^ELUj}8l56J%5dHi97d636&ebH zLMm$?(%lsah(#1Ng$GO|T`cu19`q&BIInafG11z+IyKJhv&p<6ST?}Jq@(UZ8#*LOTW&^B87 zlD2~LaMJ_pR^r2GC!_^hFJ|K8GkDYqT(C7_+(veiWx!e}u*wsreZ3}>ALFnr^Ek2p z8!|y;8^yC-S);midD{keg}Oj`EsC)8fQufDXQh@`66ZVdDre)Z1> z>z$FAk9$bcAi5jL#iJr7Bh;zCoXhN#2Abd!X%zlqn$whw!xv_o_8&qy3TNAL@7w%A z;|8_C3BvaL^Bm$&otM7b4pxq-qNa^uDqQpVRf)*GoM(|FyYDQ3BBa51_WG}^G)g3nzIDe0%HEAngf7p+)rZ`(Ey z{_bDF0Rbu(PTGAcP&Y4anr;QUB51M!gA)|Abe6bElt)p?ydwX7M~WgTO7aUjp+zk6 z?s)gyi|+X5e4VW)lNZk?fsQEs9yuA8+bEQOS9c4x(d88FdSS1Bp))Er($Yh2)sDD+~A_hDt zfs;mq7n2F61ypDRnv|`zNq4^?K|Y^MYCP+$5OniVf}pY#bG_8Qy-iaADV5MHTlTkA z1wtu&_XiaW?Y0YFpsI6O1@u?^fr*kAz~$XXXYO42K*RI})&eTrt_USJHIuFSjeGE) z0%NJ5G2SVce?crsD(E^Ru>h)oSZ?&*mzr}Bt%M6!AgYBH-ZSitR{^qR^n*Z}_E!BUY^vbYaOmaU$HyLHVH zL{qJ4MAh@0eEo_iVwlviJTYtS)&)j7Er^OCo@(_+cAU-ufv51|zGs3Ve$l8|FEQ9L z2Y9SDeZ8WF5TA2TmziC?dyn?rvcdp5>ytYL5g3kX=4hyj>>hNz{UP)nk;c}D){9f} z-EFF@J3d2|kwR%s&I!lVy`^cSUZ>=>W}Jr{${5AsqQu+mnAD3JkFc+KXiSf#1BM4p zkIH zl4Xn4R)H?%$0c6coYy)wF|k!@SS7|~-_!*nv{8c1&vB4jT7Ae)NE>gcvNM*}N6eC# z&zRl9ngtZwsn(S!&omb(jbO)f^w7Ne^|#At2sE)hC?pTmSm@xl@AD4r)5QJi;qGMY z%8Hv>H!F+#U1?wl$Kk6C!5-lGHC7{Hb7LGpOQIz>Z8X0*dl1i7uLe!?b^w+w|9&4D z6Bj$Hii@8SI8DIp1LuBVI7UNoKpb~8YJfCv2LRdf?@HAxAxd#0ak3dl2OQ1+maJ?= zq7}`tk3T(i1|FYfv~ns%(;;lI!b!$!2+`=g`bu{&)H?b{%(ublN!b;WBNNc>WdA@} zUgOj=ivP+Ggy5qRa`tG8INumpsxRhGpKL>(J2;B})Q7J5Pn}yz)|aEri!N7yHhx5}~XPN*)xU zHGM(eZ~WwW5(jM=o=X-!qju?MT42Z2e0u%+fDtblcXr&gE4@Om>@{c%PEUP>ZZjZy zKB_tUyM}y2SOOU2D+ZOhh%t8-86q> zjV<@d4qzKJT6Bl<1}hw+Ir|sX%xzzy;jrwFbY@sjAVx%@h+$cGb-x);ux4rj(X&5` zv{4;~m|B-_Zu8rV9+eXr`I; zKVFu_AL~0^q?_-j^T`08v7&7XP6NZx|BNSQH|p_s&(;TCs=I;E@u6v6IUgbP4Zl{O zTUGgPK&p_mygk+-8&zxaQ)uWA?&=xtI@I$tfSy&7O6>ZwHu|F8X$SP;Dqyw-y6=$o z+Q+&;qd&9vum*00mRmGht3_3{jjSLO3~GK@^@qeLIJnoywU+{&%S_Mc<`gg92WQF1 zx8vY5f@#rD#;Aiui~7^#Kb2NnZ`(E$e%G(yLjcKvnylEH8++Mu*$fnkk)&OL*f417 z$RZ+<0!7)$iv0H-l9Daa_S)%%(&4#$-?@;w_^r&!AUHn_$SHZsctuj41HrYVN)wq9 zodKEtHT?qw%TUN(=n7ET=tpcouBqUVlg|LSeUaF&OI&nrBxg((<3{^dD(ft*^PDhQ zzA3)SG@&9PIcFd$3@cKp3aYsjF)4GPC|h&bSi8@YtDC#W8{^7i)EU)eLo1T-N-Mss zH6&!ib%r~Lzmm0LfPoS?a|q6ZK+pmzB!QH&(o&k;ylRY%5v$p^YgpDRlmM%>RG)&N zu6mkIKC_<%RWGwAD9W5_*qQvmqLoa929+X^V;1fOVO>d7ebqnIdw{CWb+xDGvvbQA z&2vxn0ahlFZ!?UT@|D|tsD8V|@J1*B$5x+9c z=DA%<#n-q;PPh=DI`ux}$uyT(9kyqiq_HaNC3Z13w_t`jSu7ax*Q#bZ^09bv<0gc4 zH?ZCuC-a<`X8thl8IaK)a!(x{86^wPh~M|9eZS-)1I2YB*?XUy$AsB-s1-zBQOSvP zg29liB~N|=IU?J7+gdjnv^I_G!?*%{4JkD(9X)UN%@Z=3=Sw_@LNXffU`7p!4g$+O z?iVYvb}~)K8Sd zbj@vHoz-&}=)68_tJKC0^-9vxc8Z5pp?(HNUjJ8SdS zx0@f#-a!g`?s2l0x%2!n{%_TvrSX3$%-dOd5B>#(kUvkuFc8Iee~KH5nxIl97XFJ; z1QMW1fW*M!I+xhOu_OCJL5T0p*(nt;fHT;>`}y8`IXvlfC&ccyfNi+fCPHm20`Hv+ z1il8+2#d?b89Jr8sGenm3@5%&C7erV(84t$w-2j)JLQdRs!Ww{kJt2t4|!PKTMNo} zZ^1O0K)MR7QRpJey4r_UlJRZ=okhvD7lW((?)qvrzrCBMQQ0GDNwAU;DibM~Wlvba z%8=#``;Wd43R$Sa>EK{j2q#;N9D*b1E=PKrzY=S+Vtjf$$7SCf2+_wM0IHsRSRa_2 zc}Mt43@R&QgyG!-87tle@ap;{M}kNkMR8!K(z9$kIk*VEUC$_f4~3B$4igyRmo+$o z@wnWFl+!g?Vh~DeltwZUJ8n{4Y#M2ew_^InlONSDjh*8LX_hkF@{FSVgStV|zWNti z$iN6;xI-G?4O{a6+FSxs9~`vRUXlHmF#VSIAaft$Lwo|2R9kP_Fcf~zuQ-!Bk)TlC zDNEZGsv=ce*RIWBDtF5h>~=L|2)tn|Eo*Mlzn z%(#Y>=Lj^HOc{_Vm<-|J?%^wnIHSY(mCccit-T)yFk*sZ4)=(3JxuD|6-_3$C+D#& zqSpFWsybL&ojBW_OmX(T;5+!)vAn1FZUqjas$%0e5ge!?Y>p^E=s{9!|ku!m_UNuR3yxya#4Lvo7G4W0Lyi(}5QYO!45e#4wrqt2MTj zoWK8w;EdaS2nVX)RF_CNN^oenov`8_3<~y&*E4!a^i%Phu)S!G=73~in{e39&?$b< zYRe9ug8%8Lsl5o_=>Mht9f}}uz&QYW(Nb}Px$rf~SD4h_v$CTzv@M}09rgPVE2RFC zMuil;I*Z+GYYm_Eu)i_ygh#Pv?oASnz)TcYDcyM^br63#?DKfmzF!7 zD>aEdo_Rbo&)6Owr*Y~yUp_nV8E#`LVL=lD_)G(#fGyIRuHfV5*iXF(JWhV zu?iIBtPg2I0_=OH4o(yl!9yY? zx{QRG3g!tprz|Qxx?R3_VorofASK|pPQt=#Ky(Bm3B|Yb<^Z--(C0uA*c0{W*TxnW z=Gp*n1w9|n=13Q05ev01VKxiVZAxUJ@{E9PwRCIP?^jaIv})Vj$ZlL~Lxv+@NM|F} zgSSc=iwAno_ib>!!)ka>GxT6ogN3TP{A7vO%-SH1ePCE5HV`{(HCLupv^}gRym$LK zG<44d@p?Wya~c_`x27FQ)Fl=f&wY(F1^qNn6t|5CCC`Wq1vQ-ThQpydp5D67cY;39 z8K4u3o53JS)>sH-79{j<0)F5 zHH1MJlck{AZDuj3&3)SRmND28U}8!F@#@75_Ne~uL!%m%+0$#LL6uZG!@6_tU=*8| z%H>{Zq*Q9T3O%Ejn-i_50lLuz6w?kyOQP&R^lT~`B@0vJv=Zb=v-a6+shce3Y3i|P zi!~FrUe7{SqSJuk=;)|a*hHn9iUof$sBnXL#`KO(#4=l9@fg2`B)1~5Br>(|T9b8i zPkEL^z_@~hhnhCu?^JaylU}$724*Kc-(F#~j8|<}@ z3CJcvaEe#Ac`(ux&I7`Qci#f!l48t-qY)HEWmXoIep^0Ipjkxo8Jl$MkPWe}%<)8L zBr6_ZoRwZ4zV{5pJWEL#%Rce0W`a6lhI{+Z}q;{)8er!Jtw70rFZ|^;|JTg z`P30HWwcq?`XxQ=a1>)`Okngpyll+I(+k^9O(5A)5!|7^K8*Hdxj%Zc2bE*};TJR9 z;7rIfhA11UyxB;%qOf3A5_owbm~qh52Y5-TcQsQ@XW=gz^NhUWZALaUvnd(`q`Zgd`=gdddd~GY zIz}rIFfZ>=#~roHIC(b{+V#14SA=wtBHkCBXxy%P*z z;&X;6TqDx21tQIJg+hs zSr+#*R8$0&l;mYe>*G;TNg>`jS6vEzm}s1p&^sMVs^rA~HBRrBE;LwSN##qd$P zZL9Y>G%s{s=n3m-Qa`{#3Nw`T@~B(GS?$;6sUEe8d5&DKJ5$$P)5r#?uM|}9O*~*3 zouZ5o6zmU(^|uA|1O%jEFuy!riVali5=vnBij_Dg8c%o#RCc}c~ICrXmMkV%^Y z8;HDkq$5lxo0r3AAR`tR5}i(>@IlsZ0~ekI3rqJZ5A!tJXO{}~1S7vlwQ?RB1M?_M zBiG$e_!8`MQnN?H`KZeMEU);rhYiAy!9`EFahi%}3|E^Rm2)`A5FTtJHaxa>ao*Dg z*Cpr~37c}7a0GK|Mb$)-#z{KpLT-Q*5@Mt&_l@3F4Tri+b)e4I!!1gMA3BY|z=vS_ z+S8b3|Ljs?Op1%rX%D^YmNPhfi*Xo#2h}_hbs;%$Qu#KX(=zKO5uG$Sld^<7Cd-&1n#`|RU4cxzGv#VuC`CRyt zuE+jdj#K-F(30yd)VKNakJ;^)_t`#5R7W!Z``GPWaQjT{OlQB*0YaSi727|f|09_t zp~p|bp=)}Z-^oXzbKh1cVfuY~b-$XI`t`7vhi(1`vrBEOG}_y0bj$RFqm!YG@r|Xu zTq>Tn+1w4O=YJKXqmE}t^E@~QdsJOjBdi%v<_56`jds3T{{UT%!A=7)3`Fnw3f~IV zs{H^;L4_iaDpUz>s6uJt-9)5GHyDi*(R}Fv1)lgTa}AF=dJM2b6nkac?_LInNm-b5T3_g=L@lhZA%IEw zhy36ebhHq>!C2&M>=T_d`xHCK0kvq~9>=z~dwJNtzHO(d){&iN7b3zI)qMhl~7 zCyw+lsgglCWO4D=V5t-#SBmhVK;G$DG?+ALohmoJBo>Js*-l$D@!xlLN&!LX zv}r64aqQgrzWeU;`QSs9S3wYO2e1uSxiFB50)fVgX$vX^n&6$NcGr)zhT1;z;9OlC2^oQ^pRqF&P5XWwV zAPxe_N;H%NrIc4brSWiKDKyHD+CXmaM5vbwMk z8Br1+ZO~XLZSP!SKUVdW2qa!*;+O;LkTf`>_8!cX;NGqQ=ANxes{|>l9&9o ztJ+5GhuarIqDvnaRpC&y#73Cegs*@a(YY4)2Ts0@z!B4WE>bJpdE~xcC@Zs#!ry%&{AWwJYzYOcT?1yWfSc>}LX}X?s6qsr zKBDmA)79xi8Yzd>dM|`-W8Z~v&rK$i!RX6m;^r}N^@#s}TK!b1jEcxNLj8kw^OW^V ze=u$!sg0#$y*$!f=*`48G8hQ`N{5875w70SN77ZfrW=QU+5PT(|8w(u@hYJ7-39QV zDL(OE+dOZBUsG1pFD)+8Ps+^GPt7Y+D99+_;n=r`3g#I`T6NZnRzJ+nZ*j} zMX8A;O4eKeeNs(t+b|5h>sN3^0NX{A-jjX|OEPRgF$72l>|kJ!B{~*jOM)af4Tk>r zQT}SSLv^7|KJvXM>h952ma*Gw2G{UhDG#Np5hz;j0+JWfR~6ZJav6f-`Cq8`)wT6X$%z)aN-`M^1-R14JhHHLPZtQ?@j9j5|PA3ui%z3 z%^UP&f-F_Gvy^XN1920h&wp*OXb<@_`rz~Z*#~S^jJ5s(eOVhqOsz@jszdJ2O=e*bAwB#%l>K2q$2t>GM1y6kMZ6#I7=GF`Tf&P` zvIF(dcvK1LCmfCat__zc)8th~#w5!wpz+M&h0Pxt?v9ane^k~+e6J9+34odxHOfVj zt)|vDHht1#A*;*fAgad5LvnL+ihR*~z%0(ua+5a1RNwEMd5= z|D26Iupk`F=ab3%t7%7u<@HsS?~NspK1`5svtd zeb25k|0f2zX7x?vHIpwjJIx6dm8cZyFrIF^ZBOdcT!V^47KgCH!~{cNwt1!xK)t5! zLnY(5cvilqG16=y#fFC{DWJW4Er<$GHy4xnG(t34NnnUTQ^qB*d1Q;zsq&&5zNU1+ zur-B^;GyNni(^)vl7e%1s%&O8EYX7Lr~&5}(C99O@R@9M_EA+q?or>-EeDU^g((Yo zYA_Wxo;wF3RwP9x`w-%YM7YD0Syrg$qAxZzi7JaVrM8=u>9p*~jTGx*a+~>}Pp1iP zoAt4tPRo+!m1?z4cM;6b_vwP%DD~`D#$wgn6I>O4KaWCLWh*G(oP!On4MVe4YoSD) z$)wi5{V!X*%W|k|-LcO5ypo4puG=5_J$HcaXfzqqKIlzc_r#s_hjJGIdx#ZmdBLZ3qGi287eSPWEb>_{lg@!3xh>@XzA;L!J+_1)b(L)Oo< z(0E@_pvtxPr;kZ+Mcri@QPQ^Lto69ES@E}RB4KE}^TXh;6s_@N>nKt9kt7;h=HUO> zIm_+1qmO9yW`PcHe!uFy6?!uaaIp7<;W2Q%=ASqd$%lxCcV>XIpJu)PV}3&YHHTWR z^o}VGqJ~PzXOZ(97OckiB88pMYq`~c6N;634{`;p+`~^%<|*)BmEv^`QYB(j6@p_; zlv%OOMfn6v#FbJ*AvQuIsg^*3cPhbaBHM74t?|j0q7ZtYCM%&!;#^4u3@WCy$hHMj zP;FHuYmV|c(w3rJDOBMbUg}S%iZ$TR_<(xNih`sPwng`f{6QwGy%JAr4QpBC9E(ei z&e*cxHz^F26-;bW$k~QH@fjtNMI|(La|gRMrxNKg{4y#DAiJ4T?~U<=H`{lsrn$NO(lk)QqAJU3kD(>5^pMGuZc7Ptv0TD; z1@gU`VPtp%jNb;w2>-#bRzCoJ!hZ;#{}<@~gxHPwb7<<%+x2hUcZgtfj%ig@J?43=B<>*- z2n3rM`;+46a`Ad8rj`2jeCN$-#X~5h_!F@__U&E|rHj2`!NN)YVipA(*>KEa4rOUwtXL9zs$BJDht%sIjl78$ zuT`xgHch0284VV8u$Cwsz}d*JA)VvT!z=b#r6czfjPR?#h9g^*Jgho^38|RN1|@GW zJV0i^q;@$JRiv^cJH-ee1Xh-PGGL+M&Rt45PL97QHlx}XvD=3c?qRkwXyOxdsZuIx zFJzEsSkNmMD@y@rOmUi8#NG1eg#G|Hhg*)$2Q+8W8?cSaj;YaM+(W*BYwh` zEj?*dI08F2Il?b+;?eywTQTrt(t3s+%tvw3|1;X#`)1;Qv<}P z=zg^LYrsT%11|L&U%H!Z=aBa#nHs>Oc;x)^GWwY&vh8Cj=V>pfU6*|$+M^wr&f9^o h85?h>)Ahb0>gX>0k+sS~2d4l6000mG002iqO+pK>za0Po literal 0 HcmV?d00001 diff --git a/phpbench.phar.pubkey b/phpbench.phar.pubkey new file mode 100644 index 000000000..dccf73f78 --- /dev/null +++ b/phpbench.phar.pubkey @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0Qvz3ytjNinP9lmFbBIk +mjNDvdu+w1hDlQhSCUVuXhxgDOkot/DnRdT4Kh27uhTIf+hnHvNrVdPOY+gbH8yL +DTVn8Frw/lvC/9FwfqbAC5Obi1vPT2ki/rviL8PGnPzHCqvbv1+k6cf3YgDvKGJD +/Yl2qmnM3tEWcVTNawwmHYwmSAZkJjFF1KJEUMw2oaMUcB+tgqMNB/Wi06rzKhfH +/PSkZe4xbWxc9nQ8N+In+KqdDsvx34phL4OELt/rK9g3sRsDeXxInIMG8eKwiWeo +C8gqT1DEVYS9g/eeZv5eTe0H94wK7xOEwO4DmghUQFYHHHBqs88nWXEn0dXBWqK/ +gLRtkP+CRE4kwRIDFQBvQcvpVnJ/5EVbkvSvPGXKMjdiB0XVZug8lD11ubG1fcJ/ +6Q6L5r6bmilJrF3I3omNYiTfwi44LMXoEoPbkMJPNIGZCMzMu29/Z69yB1Z6uXU4 +EYMVoONFyWwBIifS0pRaM4MX5LRJ/0DCeBHru3P8ippN1it7Di178L+8MMDDMbTA +wshm/qO2jemYIW9YVJ19kytXb2B16cN/ujiZEplkrhXszQU0jNOF5Ic9RJZpMLs9 +G929A+GsQ3ep0p6Nz6nOgpG7UjEesWMblJcgVNYi8+743HZu/yeLElHkhGvly2E+ +MRwTxpUPkpgeoMGhWd2yCbUCAwEAAQ== +-----END PUBLIC KEY----- diff --git a/phpbench.phar.version b/phpbench.phar.version new file mode 100644 index 000000000..7eda1e2b9 --- /dev/null +++ b/phpbench.phar.version @@ -0,0 +1 @@ +7d13058dfe0331aea1e0b0d22051d6574a139add phpbench.phar diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 000000000..07081b9d0 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,42 @@ +parameters: + ignoreErrors: + - + message: "#^Parameter \\#1 \\$suiteEl of method PhpBench\\\\Serializer\\\\XmlDecoder\\:\\:processSuite\\(\\) expects PhpBench\\\\Dom\\\\Element, DOMNode given\\.$#" + count: 1 + path: lib/Serializer/XmlDecoder.php + + - + message: "#^Call to an undefined method DOMNode\\:\\:getAttribute\\(\\)\\.$#" + count: 21 + path: lib/Serializer/XmlDecoder.php + + - + message: "#^Parameter \\#2 \\$benchmarkEl of method PhpBench\\\\Serializer\\\\XmlDecoder\\:\\:processBenchmark\\(\\) expects PhpBench\\\\Dom\\\\Element, DOMNode given\\.$#" + count: 1 + path: lib/Serializer/XmlDecoder.php + + - + message: "#^Parameter \\#2 \\$subjectEl of method PhpBench\\\\Serializer\\\\XmlDecoder\\:\\:processSubject\\(\\) expects PhpBench\\\\Dom\\\\Element, DOMNode given\\.$#" + count: 1 + path: lib/Serializer/XmlDecoder.php + + - + message: "#^Parameter \\#1 \\$element of method PhpBench\\\\Serializer\\\\XmlDecoder\\:\\:getParameters\\(\\) expects PhpBench\\\\Dom\\\\Element, DOMNode given\\.$#" + count: 2 + path: lib/Serializer/XmlDecoder.php + + - + message: "#^Call to an undefined method DOMNode\\:\\:query\\(\\)\\.$#" + count: 1 + path: lib/Serializer/XmlDecoder.php + + - + message: "#^Parameter \\#1 \\$element of method PhpBench\\\\Serializer\\\\XmlDecoder\\:\\:getComputedStats\\(\\) expects PhpBench\\\\Dom\\\\Element, DOMNode given\\.$#" + count: 1 + path: lib/Serializer/XmlDecoder.php + + - + message: "#^Parameter \\#2 \\$variantEl of method PhpBench\\\\Serializer\\\\XmlDecoder\\:\\:processVariant\\(\\) expects PhpBench\\\\Dom\\\\Element, DOMNode given\\.$#" + count: 1 + path: lib/Serializer/XmlDecoder.php + diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 000000000..9f353ad44 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,14 @@ +parameters: + level: 5 + paths: + - lib + - examples/Extension + excludePaths: + - lib/Attributes + - lib/Benchmark/Metadata/Driver/AttributeDriver.php + typeAliases: + parameters: 'array' + +includes: + - phpstan-baseline.neon + - vendor/jangregor/phpstan-prophecy/extension.neon diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 000000000..da5a907af --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,12 @@ + + + + + + ./tests + + + ./extensions/xdebug/tests + + + diff --git a/tests/Benchmark/Assertion/AssertionProcessorBench.php b/tests/Benchmark/Assertion/AssertionProcessorBench.php new file mode 100644 index 000000000..8193a570e --- /dev/null +++ b/tests/Benchmark/Assertion/AssertionProcessorBench.php @@ -0,0 +1,58 @@ +processor = $this->container()->get(AssertionProcessor::class); + + $variant = VariantBuilder::create() + ->setRevs(100); + + for ($i = 0; $i < 100; $i++) { + $variant->iteration()->setResult( + new TimeResult(100, 1), + new MemoryResult(100, 100, 100) + ); + } + + $baseline = VariantBuilder::create() + ->setRevs(100); + + for ($i = 0; $i < 100; $i++) { + $baseline->iteration()->setResult( + new TimeResult(100, 1), + new MemoryResult(100, 100, 100) + ); + } + + + $variant = $variant->build(); + $variant->attachBaseline($baseline->build()); + $this->variant = $variant; + } + + public function benchAssert(): void + { + $this->processor->assert($this->variant, 'mode(variant.time.avg) < mode(baseline.time.avg) +/- 10%'); + } +} diff --git a/tests/Benchmark/Console/Command/RunCommandBench.php b/tests/Benchmark/Console/Command/RunCommandBench.php new file mode 100644 index 000000000..011b55969 --- /dev/null +++ b/tests/Benchmark/Console/Command/RunCommandBench.php @@ -0,0 +1,74 @@ +createExample(); + $this->workspace()->reset(); + } + + public function benchDefault(): void + { + $this->runCommand([ + 'path' => '.', + ]); + } + + public function benchInBand(): void + { + $this->runCommand([ + 'path' => '.', + '--executor' => 'local', + ]); + } + + public function benchNoEnv(): void + { + $this->runCommand([ + 'path' => '.', + '--executor' => 'local', + ], [ + RunnerExtension::PARAM_ENABLED_PROVIDERS => [], + ]); + } + + private function createExample(): void + { + $this->workspace()->put('NothingBench.php', <<<'EOT' +workspace()->path()); + $command = $this->container(array_merge([ + ConsoleExtension::PARAM_DISABLE_OUTPUT => false, + ], $config))->get(RunCommand::class); + $cwd = getcwd(); + $input = new ArrayInput($args); + $output = new BufferedOutput(); + Assert::assertEquals(0, $command->run($input, $output), 'Command exited successfully'); + chdir($cwd); + } +} diff --git a/tests/Benchmark/Expression/ParserBench.php b/tests/Benchmark/Expression/ParserBench.php new file mode 100644 index 000000000..8f8c13ee6 --- /dev/null +++ b/tests/Benchmark/Expression/ParserBench.php @@ -0,0 +1,60 @@ +init(); + $this->parser = $container->get(Parser::class); + $this->lexer = $container->get(Lexer::class); + } + + /** + * @ParamProviders({"provideExpressions"}) + * + * @param array $params + */ + public function benchEvaluate(array $params): void + { + $this->parser->parse($this->lexer->lex($params['expr'])); + } + + /** + * @return Generator + */ + public function provideExpressions(): Generator + { + yield 'comp. w/tol' => [ + 'expr' => '10 seconds < 10 seconds +/- 10 seconds', + ]; + + yield 'comp.' => [ + 'expr' => '10 seconds < 10 seconds', + ]; + } +} diff --git a/tests/Benchmark/IntegrationBenchCase.php b/tests/Benchmark/IntegrationBenchCase.php new file mode 100644 index 000000000..fcf8e08f2 --- /dev/null +++ b/tests/Benchmark/IntegrationBenchCase.php @@ -0,0 +1,36 @@ +init(); + + return $container; + } +} diff --git a/tests/Benchmark/Progress/VariantSummaryFormatterBench.php b/tests/Benchmark/Progress/VariantSummaryFormatterBench.php new file mode 100644 index 000000000..85de9f59a --- /dev/null +++ b/tests/Benchmark/Progress/VariantSummaryFormatterBench.php @@ -0,0 +1,58 @@ +formatter = $this->container()->get(VariantFormatter::class); + + $variant = VariantBuilder::create() + ->setRevs(100); + + for ($i = 0; $i < 100; $i++) { + $variant->iteration()->setResult( + new TimeResult(100, 1), + new MemoryResult(100, 100, 100) + ); + } + + $baseline = VariantBuilder::create() + ->setRevs(100); + + for ($i = 0; $i < 100; $i++) { + $baseline->iteration()->setResult( + new TimeResult(100, 1), + new MemoryResult(100, 100, 100) + ); + } + + + $variant = $variant->build(); + $variant->attachBaseline($baseline->build()); + $this->variant = $variant; + } + + public function benchFormat(): void + { + $this->formatter->formatVariant($this->variant); + } +} diff --git a/tests/Benchmark/Report/Generator/ExpressionGeneratorBench.php b/tests/Benchmark/Report/Generator/ExpressionGeneratorBench.php new file mode 100644 index 000000000..30072f690 --- /dev/null +++ b/tests/Benchmark/Report/Generator/ExpressionGeneratorBench.php @@ -0,0 +1,53 @@ +generator = $this->container()->get(ExpressionGenerator::class); + $this->suite = TestUtil::createSuite([ + 'benchmarks' => ['benchOne', 'benchTwo'], + 'subjects' => [ + 'one', 'two', + ], + 'iterations' => array_fill(0, 100, 10), + ]); + $options = new OptionsResolver(); + $this->generator->configure($options); + $this->config = $options->resolve(); + } + + public function benchGenerate(): void + { + $this->generator->generate(new SuiteCollection([$this->suite]), new Config('foo', $this->config)); + } +} diff --git a/tests/Example/CommandsTest.php b/tests/Example/CommandsTest.php new file mode 100644 index 000000000..d94bbaf48 --- /dev/null +++ b/tests/Example/CommandsTest.php @@ -0,0 +1,86 @@ +workspace()->reset(); + } + + /** + * @dataProvider provideCommand + */ + public function testCommand(string $path): void + { + $this->createExample(); + + $approval = Approval::create($path, 3); + $commands = array_map(function (string $command) { + if (0 !== strpos($command, 'phpbench')) { + throw new RuntimeException(sprintf( + 'Command test command must start with `phpbench`, got "%s"', + $command + )); + } + + return substr($command, strlen('phpbench ')); + }, explode("\n", trim($approval->getSection(1)))); + + $this->workspace()->put('phpbench.json', json_encode(array_merge([ + RunnerExtension::PARAM_ENABLED_PROVIDERS => [], + ConsoleExtension::PARAM_OUTPUT_STREAM => $this->workspace()->path('output'), + ConsoleExtension::PARAM_ERROR_STREAM => 'php://temp', + ], json_decode($approval->getSection(0), true)))); + + foreach ($commands as $command) { + $input = new StringInput($command); + $container = PhpBench::loadContainer($input, $this->workspace()->path()); + $application = $container->get(Application::class); + assert($application instanceof Application); + $application->setAutoExit(false); + $application->run($input, $container->get(ConsoleExtension::SERVICE_OUTPUT_STD)); + } + + $output = $this->workspace()->getContents('output'); + // hack to ignore the suite dates + $output = preg_replace('{[0-9]{4}-[0-9]{2}-[0-9]{2}}', 'xxxx-xx-xx', $output); + $output = preg_replace('{[0-9]{2}:[0-9]{2}:[0-9]{2}}', 'xx-xx-xx', $output); + $output = preg_replace('{#[a-z0-9]{40}}', 'E3X6AeMdP7L9E7E0X0A7McP1L8E1EdXbAbMbP7La', $output); + $approval->approve($output); + } + + /** + * @return Generator + */ + public function provideCommand(): Generator + { + /** @phpstan-ignore-next-line */ + foreach (glob(__DIR__ . '/../../examples/Command/*') as $file) { + yield [ + $file, + ]; + } + } + + private function createExample(): void + { + $this->workspace()->put('NothingBench.php', <<<'EOT' +container(); + + foreach ($expressions as $expression) { + (function (Lexer $lexer, Parser $parser, Evaluator $evaluator) use ($expression, $filename): void { + $node = $parser->parse($lexer->lex($expression)); + $result = $evaluator->evaluate($node, []); + + if (!$node instanceof ComparisonNode) { + return; + } + + if (!$result instanceof PhpValue) { + return; + } + + self::assertTrue($result->value(), sprintf( + '%s: %s', basename($filename), $expression + )); + })( + $container->get(Lexer::class), + $container->get(Parser::class), + $container->get(Evaluator::class) + ); + } + $this->addToAssertionCount(1); + } + + /** + * @return Generator + */ + public function provideExpression(): Generator + { + /** @phpstan-ignore-next-line */ + foreach (glob(__DIR__ . '/../../examples/Expression/*') as $file) { + yield [ + $file, + ]; + } + } +} diff --git a/tests/Integration/ConfiguredReportsTest.php b/tests/Integration/ConfiguredReportsTest.php new file mode 100644 index 000000000..0c59c437f --- /dev/null +++ b/tests/Integration/ConfiguredReportsTest.php @@ -0,0 +1,74 @@ +workspace()->reset(); + } + + /** + * @dataProvider provideReport + */ + public function testReport(string $generator, string $renderer): void + { + $manager = $this->container([ + ConsoleExtension::PARAM_OUTPUT_STREAM => $this->workspace()->path('test') + ])->get(ReportManager::class); + assert($manager instanceof ReportManager); + $manager->renderReports(TestUtil::createCollection([ + [], + ]), [$generator], [$renderer]); + + $approval = Approval::create(sprintf( + '%s/%s/%s-%s', + __DIR__, + '/generator/', + $generator, + $renderer + ), 0); + $approval->approve($this->workspace()->getContents('test')); + } + + /** + * @return Generator + */ + public function provideReport(): Generator + { + $generators = $this->container()->get(ReportExtension::SERVICE_REGISTRY_GENERATOR); + $renderers = $this->container()->get(ReportExtension::SERVICE_REGISTRY_RENDERER); + + foreach ($generators->getConfigNames() as $generator) { + foreach ($renderers->getConfigNames() as $renderer) { + yield [ + $generator, + $renderer + ]; + } + } + + foreach ($generators->getServiceNames() as $generator) { + // composite doesn't work without configuration + if ($generator === 'composite') { + continue; + } + + foreach ($renderers->getServiceNames() as $renderer) { + yield [ + $generator, + $renderer + ]; + } + } + } +} diff --git a/tests/Integration/generator/aggregate-csv b/tests/Integration/generator/aggregate-csv new file mode 100644 index 000000000..ad1fa06c4 --- /dev/null +++ b/tests/Integration/generator/aggregate-csv @@ -0,0 +1,2 @@ +benchmark,subject,set,revs,its,mem_peak,mode,rstdev +TestBench,benchOne,0,5,2,200,5,100 diff --git a/tests/Integration/generator/bare-console b/tests/Integration/generator/bare-console new file mode 100644 index 000000000..9955d2768 --- /dev/null +++ b/tests/Integration/generator/bare-console @@ -0,0 +1,8 @@ + ++--------------+----------------+-----------------+--------------+-------------------+-------------------+------------------------+-------------------+--------------+----------------+--------------+--------------------+-----------+------------+------------+-----------------+-----------------+------------------+-----------------+-----------------+-----------------+------------------+---------------------+---------------------+-----------------------+ +| has_baseline | benchmark_name | benchmark_class | subject_name | subject_groups | subject_time_unit | subject_time_precision | subject_time_mode | variant_name | variant_params | variant_revs | variant_iterations | suite_tag | suite_date | suite_time | iteration_index | result_time_net | result_time_revs | result_time_avg | result_mem_peak | result_mem_real | result_mem_final | result_reject_count | result_comp_z_value | result_comp_deviation | ++--------------+----------------+-----------------+--------------+-------------------+-------------------+------------------------+-------------------+--------------+----------------+--------------+--------------------+-----------+------------+------------+-----------------+-----------------+------------------+-----------------+-----------------+-----------------+------------------+---------------------+---------------------+-----------------------+ +| false | TestBench | TestBench | benchOne | [one, two, three] | microseconds | 7 | time | 0 | [value1] | 5 | 2 | test | 2016-02-06 | 00:00:00 | 0 | 0 | 1 | 0 | 200 | 200 | 200 | 0 | -1 | -100 | +| false | TestBench | TestBench | benchOne | [one, two, three] | microseconds | 7 | time | 0 | [value1] | 5 | 2 | test | 2016-02-06 | 00:00:00 | 1 | 10 | 1 | 10 | 200 | 200 | 200 | 0 | 1 | 100 | ++--------------+----------------+-----------------+--------------+-------------------+-------------------+------------------------+-------------------+--------------+----------------+--------------+--------------------+-----------+------------+------------+-----------------+-----------------+------------------+-----------------+-----------------+-----------------+------------------+---------------------+---------------------+-----------------------+ + diff --git a/tests/Integration/generator/bare-csv b/tests/Integration/generator/bare-csv new file mode 100644 index 000000000..99f968a08 --- /dev/null +++ b/tests/Integration/generator/bare-csv @@ -0,0 +1,3 @@ +has_baseline,benchmark_name,benchmark_class,subject_name,subject_groups,subject_time_unit,subject_time_precision,subject_time_mode,variant_name,variant_params,variant_revs,variant_iterations,suite_tag,suite_date,suite_time,iteration_index,result_time_net,result_time_revs,result_time_avg,result_mem_peak,result_mem_real,result_mem_final,result_reject_count,result_comp_z_value,result_comp_deviation +,TestBench,TestBench,benchOne,"[one, two, three]",microseconds,7,time,0,[value1],5,2,test,2016-02-06,00:00:00,0,0,1,0,200,200,200,0,-1,-100 +,TestBench,TestBench,benchOne,"[one, two, three]",microseconds,7,time,0,[value1],5,2,test,2016-02-06,00:00:00,1,10,1,10,200,200,200,0,1,100 diff --git a/tests/Integration/generator/bare-delimited b/tests/Integration/generator/bare-delimited new file mode 100644 index 000000000..69dbd00f1 --- /dev/null +++ b/tests/Integration/generator/bare-delimited @@ -0,0 +1,3 @@ +has_baseline benchmark_name benchmark_class subject_name subject_groups subject_time_unit subject_time_precision subject_time_mode variant_name variant_params variant_revs variant_iterations suite_tag suite_date suite_time iteration_index result_time_net result_time_revs result_time_avg result_mem_peak result_mem_real result_mem_final result_reject_count result_comp_z_value result_comp_deviation + TestBench TestBench benchOne "[one, two, three]" microseconds 7 time 0 [value1] 5 2 test 2016-02-06 00:00:00 0 0 1 0 200 200 200 0 -1 -100 + TestBench TestBench benchOne "[one, two, three]" microseconds 7 time 0 [value1] 5 2 test 2016-02-06 00:00:00 1 10 1 10 200 200 200 0 1 100 diff --git a/tests/Integration/generator/composite-console b/tests/Integration/generator/composite-console new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Integration/generator/composite-delimited b/tests/Integration/generator/composite-delimited new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Integration/generator/default-csv b/tests/Integration/generator/default-csv new file mode 100644 index 000000000..759277838 --- /dev/null +++ b/tests/Integration/generator/default-csv @@ -0,0 +1,3 @@ +iter,benchmark,subject,set,revs,mem_peak,time_avg,comp_z_value,comp_deviation +0,benchOne,benchOne,0,5,200,0,-1.00σ,-100.00% +1,benchOne,benchOne,0,5,200,10,+1.00σ,+100.00% diff --git a/tests/Integration/generator/env-console b/tests/Integration/generator/env-console new file mode 100644 index 000000000..4b34ee081 --- /dev/null +++ b/tests/Integration/generator/env-console @@ -0,0 +1,2 @@ +Suite #0 2016-02-06 00:00:00 + diff --git a/tests/Integration/generator/env-delimited b/tests/Integration/generator/env-delimited new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/tests/Integration/generator/env-delimited @@ -0,0 +1 @@ + diff --git a/tests/Integration/generator/expression-console b/tests/Integration/generator/expression-console new file mode 100644 index 000000000..e0b5f4b15 --- /dev/null +++ b/tests/Integration/generator/expression-console @@ -0,0 +1,7 @@ + ++------+-----------+----------+-----+------+-----+----------+-------------+-------------+-------------+--------------+-------------+----------+ +| tag | benchmark | subject | set | revs | its | mem_peak | best | mode | mean | worst | stdev | rstdev | ++------+-----------+----------+-----+------+-----+----------+-------------+-------------+-------------+--------------+-------------+----------+ +| test | TestBench | benchOne | 0 | 5 | 2 | 200b | 0.0000000μs | 5.0000000μs | 5.0000000μs | 10.0000000μs | 5.0000000μs | ±100.00% | ++------+-----------+----------+-----+------+-----+----------+-------------+-------------+-------------+--------------+-------------+----------+ + diff --git a/tests/Integration/generator/expression-delimited b/tests/Integration/generator/expression-delimited new file mode 100644 index 000000000..85b22afd7 --- /dev/null +++ b/tests/Integration/generator/expression-delimited @@ -0,0 +1,2 @@ +tag benchmark subject set revs its mem_peak best mode mean worst stdev rstdev +test TestBench benchOne 0 5 2 200 0 5 5 10 5 100 diff --git a/tests/Integration/generator/flat-console b/tests/Integration/generator/flat-console new file mode 100644 index 000000000..5d3014bcf --- /dev/null +++ b/tests/Integration/generator/flat-console @@ -0,0 +1,8 @@ + ++----------+----------------+-----------------+--------------+-------------------+-------------------+------------------------+-------------------+--------------+----------------+--------------+--------------------+-----------+------------+------------+-----------------+-----------------+------------------+-----------------+-----------------+-----------------+------------------+---------------------+---------------------+-----------------------+ +| baseline | benchmark_name | benchmark_class | subject_name | subject_groups | subject_time_unit | subject_time_precision | subject_time_mode | variant_name | variant_params | variant_revs | variant_iterations | suite_tag | suite_date | suite_time | iteration_index | result_time_net | result_time_revs | result_time_avg | result_mem_peak | result_mem_real | result_mem_final | result_reject_count | result_comp_z_value | result_comp_deviation | ++----------+----------------+-----------------+--------------+-------------------+-------------------+------------------------+-------------------+--------------+----------------+--------------+--------------------+-----------+------------+------------+-----------------+-----------------+------------------+-----------------+-----------------+-----------------+------------------+---------------------+---------------------+-----------------------+ +| false | TestBench | TestBench | benchOne | [one, two, three] | microseconds | 7 | time | 0 | [value1] | 5 | 2 | test | 2016-02-06 | 00:00:00 | 0 | 0 | 1 | 0 | 200 | 200 | 200 | 0 | -1 | -100 | +| false | TestBench | TestBench | benchOne | [one, two, three] | microseconds | 7 | time | 0 | [value1] | 5 | 2 | test | 2016-02-06 | 00:00:00 | 1 | 10 | 1 | 10 | 200 | 200 | 200 | 0 | 1 | 100 | ++----------+----------------+-----------------+--------------+-------------------+-------------------+------------------------+-------------------+--------------+----------------+--------------+--------------------+-----------+------------+------------+-----------------+-----------------+------------------+-----------------+-----------------+-----------------+------------------+---------------------+---------------------+-----------------------+ + diff --git a/tests/Integration/generator/flat-csv b/tests/Integration/generator/flat-csv new file mode 100644 index 000000000..cb14dc7a0 --- /dev/null +++ b/tests/Integration/generator/flat-csv @@ -0,0 +1,3 @@ +baseline,benchmark_name,benchmark_class,subject_name,subject_groups,subject_time_unit,subject_time_precision,subject_time_mode,variant_name,variant_params,variant_revs,variant_iterations,suite_tag,suite_date,suite_time,iteration_index,result_time_net,result_time_revs,result_time_avg,result_mem_peak,result_mem_real,result_mem_final,result_reject_count,result_comp_z_value,result_comp_deviation +,TestBench,TestBench,benchOne,"[one, two, three]",microseconds,7,time,0,[value1],5,2,test,2016-02-06,00:00:00,0,0,1,0,200,200,200,0,-1,-100 +,TestBench,TestBench,benchOne,"[one, two, three]",microseconds,7,time,0,[value1],5,2,test,2016-02-06,00:00:00,1,10,1,10,200,200,200,0,1,100 diff --git a/tests/Integration/generator/flat-delimited b/tests/Integration/generator/flat-delimited new file mode 100644 index 000000000..08d0fbd53 --- /dev/null +++ b/tests/Integration/generator/flat-delimited @@ -0,0 +1,3 @@ +baseline benchmark_name benchmark_class subject_name subject_groups subject_time_unit subject_time_precision subject_time_mode variant_name variant_params variant_revs variant_iterations suite_tag suite_date suite_time iteration_index result_time_net result_time_revs result_time_avg result_mem_peak result_mem_real result_mem_final result_reject_count result_comp_z_value result_comp_deviation + TestBench TestBench benchOne "[one, two, three]" microseconds 7 time 0 [value1] 5 2 test 2016-02-06 00:00:00 0 0 1 0 200 200 200 0 -1 -100 + TestBench TestBench benchOne "[one, two, three]" microseconds 7 time 0 [value1] 5 2 test 2016-02-06 00:00:00 1 10 1 10 200 200 200 0 1 100 diff --git a/tests/Integration/generator/memory-csv b/tests/Integration/generator/memory-csv new file mode 100644 index 000000000..387223393 --- /dev/null +++ b/tests/Integration/generator/memory-csv @@ -0,0 +1,3 @@ +iter,benchmark,subject,set,revs,mem_final,mem_real,mem_peak +0,benchOne,benchOne,0,5,200,200,200 +1,benchOne,benchOne,0,5,200,200,200 diff --git a/tests/Integration/generator/output_test-console b/tests/Integration/generator/output_test-console new file mode 100644 index 000000000..221a29d1b --- /dev/null +++ b/tests/Integration/generator/output_test-console @@ -0,0 +1,27 @@ +Output Test Report +================== + +Output Test Report +This report demonstrates output + +Values ++--------+-----+-------+---------------+------+-----------+ +| string | int | float | bool | null | parameter | ++--------+-----+-------+---------------+------+-----------+ +| one | 123 | 12.2 | [true, false] | null | foo.bar | ++--------+-----+-------+---------------+------+-----------+ + +Percent Difference ++----------+---------+---------+---------+---------+---------+---------+---------+---------+---------+-------+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+ +| -100 | -90 | -80 | -70 | -60 | -50 | -40 | -30 | -20 | -10 | 0 | 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | 90 | 100 | ++----------+---------+---------+---------+---------+---------+---------+---------+---------+---------+-------+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+ +| -100.00% | -90.00% | -80.00% | -70.00% | -60.00% | -50.00% | -40.00% | -30.00% | -20.00% | -10.00% | 0.00% | +10.00% | +20.00% | +30.00% | +40.00% | +50.00% | +60.00% | +70.00% | +80.00% | +90.00% | +100.00% | ++----------+---------+---------+---------+---------+---------+---------+---------+---------+---------+-------+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+ + +Time ++--------+ +| time | ++--------+ +| 10.0ms | ++--------+ + diff --git a/tests/Integration/generator/output_test-delimited b/tests/Integration/generator/output_test-delimited new file mode 100644 index 000000000..592565487 --- /dev/null +++ b/tests/Integration/generator/output_test-delimited @@ -0,0 +1,6 @@ +string int float bool null parameter +one 123 12.2 "[1, ]" ?? +-100 -90 -80 -70 -60 -50 -40 -30 -20 -10 0 10 20 30 40 50 60 70 80 90 100 +-100 -90 -80 -70 -60 -50 -40 -30 -20 -10 0 10 20 30 40 50 60 70 80 90 100 +time +10000 diff --git a/tests/IntegrationTestCase.php b/tests/IntegrationTestCase.php new file mode 100644 index 000000000..3c33cf31a --- /dev/null +++ b/tests/IntegrationTestCase.php @@ -0,0 +1,38 @@ +init(); + + return $container; + })(new Container([ + ExpressionExtension::class, + CoreExtension::class, + ReportExtension::class, + StorageExtension::class, + RunnerExtension::class, + ConsoleExtension::class, + ], array_merge([ + ExpressionExtension::PARAM_SYNTAX_HIGHLIGHTING => false, + ], $config))); + } +} diff --git a/tests/PhpBenchTestCase.php b/tests/PhpBenchTestCase.php new file mode 100644 index 000000000..66cdb8a48 --- /dev/null +++ b/tests/PhpBenchTestCase.php @@ -0,0 +1,16 @@ +workspace()->path($path); + } + + protected function initWorkspace(): void + { + $this->workspace()->reset(); + } +} diff --git a/tests/ProphecyTrait.php b/tests/ProphecyTrait.php new file mode 100644 index 000000000..857e10d97 --- /dev/null +++ b/tests/ProphecyTrait.php @@ -0,0 +1,115 @@ +recordDoubledType($classOrInterface); + } + + return $this->getProphet()->prophesize($classOrInterface); + } + + /** + * @postCondition + */ + protected function verifyProphecyDoubles(): void + { + if ($this->prophet === null) { + return; + } + + try { + $this->prophet->checkPredictions(); + } catch (PredictionException $e) { + throw new AssertionFailedError($e->getMessage()); + } finally { + $this->countProphecyAssertions(); + } + } + + /** + * @after + */ + protected function tearDownProphecy(): void + { + if (null !== $this->prophet && !$this->prophecyAssertionsCounted) { + // Some Prophecy assertions may have been done in tests themselves even when a failure happened before checking mock objects. + $this->countProphecyAssertions(); + } + + $this->prophet = null; + } + + /** + * @internal + */ + private function countProphecyAssertions(): void + { + \assert($this instanceof TestCase); + $this->prophecyAssertionsCounted = true; + + foreach ($this->prophet->getProphecies() as $objectProphecy) { + foreach ($objectProphecy->getMethodProphecies() as $methodProphecies) { + foreach ($methodProphecies as $methodProphecy) { + \assert($methodProphecy instanceof MethodProphecy); + + $this->addToAssertionCount(\count($methodProphecy->getCheckedPredictions())); + } + } + } + } + + /** + * @internal + */ + private function getProphet(): Prophet + { + if ($this->prophet === null) { + $this->prophet = new Prophet; + } + + return $this->prophet; + } +} diff --git a/tests/System/ConfigReferenceTest.php b/tests/System/ConfigReferenceTest.php new file mode 100644 index 000000000..9b2b0a576 --- /dev/null +++ b/tests/System/ConfigReferenceTest.php @@ -0,0 +1,13 @@ +phpbench('config:dump-reference --extension="PhpBench\\Extension\\DevelopmentExtension"'); + $process->mustRun(); + self::assertEquals(0, $process->getExitCode()); + } +} diff --git a/tests/System/EvalCommandTest.php b/tests/System/EvalCommandTest.php new file mode 100644 index 000000000..3cf136098 --- /dev/null +++ b/tests/System/EvalCommandTest.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\System; + +use function escapeshellarg; + +class EvalCommandTest extends SystemTestCase +{ + public function testEvaluate(): void + { + $this->getBenchResult(null, ' --store'); + $process = $this->phpbench( + 'eval "2 > 1"' + ); + $output = $process->getOutput(); + self::assertEquals(0, $process->getExitCode()); + } + + public function testEvaluateWithParams(): void + { + $this->getBenchResult(null, ' --store'); + $process = $this->phpbench( + 'eval "2 > foobar" ' . escapeshellarg(json_encode(['foobar' => 3])) + ); + self::assertEquals(0, $process->getExitCode()); + } +} diff --git a/tests/System/LogTest.php b/tests/System/LogTest.php new file mode 100644 index 000000000..2a4e05314 --- /dev/null +++ b/tests/System/LogTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\System; + +class LogTest extends SystemTestCase +{ + /** + * It should show the log. + */ + public function testLog(): void + { + $this->getBenchResult(null, ' --store'); + $process = $this->phpbench( + 'log' + ); + $output = $process->getOutput(); + $this->assertEquals(0, $process->getExitCode()); + $this->assertCount(9, explode("\n", $output)); + } +} diff --git a/tests/System/ReportTest.php b/tests/System/ReportTest.php new file mode 100644 index 000000000..f872d26da --- /dev/null +++ b/tests/System/ReportTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\System; + +class ReportTest extends SystemTestCase +{ + /** + * It should generate a report. + */ + public function testGenerateReport(): void + { + $this->getBenchResult(); + $process = $this->phpbench( + 'report --file=' . $this->fname . ' --report=default' + ); + $this->assertEquals(0, $process->getExitCode()); + $output = $process->getOutput(); + $this->assertStringContainsString('benchNothing', $output); + } + + /** + * It should generate a report when given the --ref option. + */ + public function testGenerateReportFromUuid(): void + { + $document = $this->getBenchResult(null, ' --store'); + $ref = $document->evaluate('string(./suite/@uuid)'); + $process = $this->phpbench( + 'report --ref=' . $ref . ' --report=default' + ); + $this->assertEquals(0, $process->getExitCode()); + $output = $process->getOutput(); + $this->assertStringContainsString('benchNothing', $output); + } + + /** + * It should allow the mode, precision and time-unit to be specified. + */ + public function testTimeUnitOverride(): void + { + $this->getBenchResult(); + $process = $this->phpbench( + 'report --file=' . $this->fname . ' --report=default --time-unit=seconds --mode=throughput --precision=6' + ); + $output = $process->getOutput(); + $this->assertExitCode(0, $process); + $this->assertStringContainsString('100,000.000000ops/s', $output); + } + + /** + * It should throw an exception if no reports are specified. + */ + public function testNoReports(): void + { + $process = $this->phpbench('report --file=results/report1.xml'); + $this->assertExitCode(1, $process); + $this->assertStringContainsString('You must specify or con', $process->getErrorOutput()); + } + + /** + * It should throw an exception if the report file does not exist. + */ + public function testNonExistingFile(): void + { + $process = $this->phpbench('report tests/Systemist.xml'); + $this->assertExitCode(1, $process); + } +} diff --git a/tests/System/RunTest.php b/tests/System/RunTest.php new file mode 100644 index 000000000..8eff8feca --- /dev/null +++ b/tests/System/RunTest.php @@ -0,0 +1,641 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\System; + +use PhpBench\Extension\RunnerExtension; + +class RunTest extends SystemTestCase +{ + /** + * It should use a speified, valid, configuration. + */ + public function testSpecifiedConfig(): void + { + $process = $this->phpbench('run --verbose --config=env/config_valid/phpbench.json'); + $this->assertExitCode(0, $process); + $this->assertStringContainsString('Subjects', $process->getErrorOutput()); + } + + /** + * It should use phpbench.json if present + * It should prioritize phpbench.json over .phpbench.dist.json. + */ + public function testPhpBenchConfig(): void + { + $process = $this->phpbench('run', 'env/config_valid'); + $this->assertExitCode(0, $process); + $this->assertStringContainsString('Subjects', $process->getErrorOutput()); + } + + /** + * It should use phpbench.json.dist if present. + */ + public function testPhpBenchDistConfig(): void + { + $process = $this->phpbench('run', 'env/config_dist'); + $this->assertExitCode(0, $process); + $this->assertStringContainsString('Subjects', $process->getErrorOutput()); + } + + /** + * It should run when given a path. + * It should show the default (simple) report. + */ + public function testCommand(): void + { + $process = $this->phpbench('run benchmarks/set4/NothingBench.php'); + $this->assertExitCode(0, $process); + } + + /** + * It should run and generate a named report. + */ + public function testCommandWithReport(): void + { + $process = $this->phpbench('run benchmarks/set4/NothingBench.php --report=default'); + $this->assertExitCode(0, $process); + $output = $process->getOutput(); + $this->assertStringContainsString('bench', $output); + } + + /** + * It should show an error if no path is given (and no path is configured). + */ + public function testCommandWithNoPath(): void + { + $process = $this->phpbench('run'); + $this->assertExitCode(1, $process); + $this->assertStringContainsString('You must either specify', $process->getErrorOutput()); + } + + /** + * It should run and generate a report configuration. + */ + public function testCommandWithReportConfiguration(): void + { + $process = $this->phpbench( + 'run benchmarks/set4/NothingBench.php --report=\'{"extends": "default"}\'' + ); + $this->assertExitCode(0, $process); + $output = $process->getOutput(); + $this->assertStringContainsString('benchNothing', $output); + } + + /** + * It should fail if an unknown report name is given. + */ + public function testCommandWithReportConfigurationUnknown(): void + { + $process = $this->phpbench( + 'run --report=\'{"generator": "foo_table"}\' benchmarks/set4/NothingBench.php' + ); + $this->assertExitCode(1, $process); + $this->assertStringContainsString('generator service "foo_table" does not exist', $process->getErrorOutput()); + } + + /** + * It should fail if an invalid report configuration is given. + */ + public function testCommandWithReportConfigurationInvalid(): void + { + $process = $this->phpbench( + 'run --report=\'{"name": "foo_ta\' benchmarks/set4/NothingBench.php' + ); + $this->assertExitCode(1, $process); + $this->assertStringContainsString('Parse error', $process->getErrorOutput()); + } + + /** + * It should fail if an invalid report name is provided. + */ + public function testFailInvalidReportName(): void + { + $process = $this->phpbench( + 'run --report=foobar benchmarks/set4/NothingBench.php' + ); + $this->assertExitCode(1, $process); + $this->assertStringContainsString('No generator configuration or service named "foobar" exists.', $process->getErrorOutput()); + } + + /** + * It should fail if there is an assertion failure. + */ + public function testFailAssertionFailure(): void + { + $process = $this->phpbench( + 'run benchmarks/set5/AssertFailBench.php' + ); + $this->assertExitCode(2, $process); + } + + /** + * If passed the tolerate-failure option, it should return 0 exit code even when failures are encountered. + */ + public function testFailAssertionFailureTolerate(): void + { + $process = $this->phpbench( + 'run benchmarks/set5/AssertFailBench.php --tolerate-failure' + ); + $this->assertExitCode(0, $process); + } + + /** + * It should override assertions. + */ + public function testFailAssertionOverride(): void + { + $process = $this->phpbench( + 'run benchmarks/set5/AssertFailBench.php --assert="1 < 2"' + ); + $this->assertExitCode(0, $process); + } + + public function testFormat(): void + { + $process = $this->phpbench( + 'run benchmarks/set4/NothingBench.php --format=\'"HELLO WORLD"\'' + ); + $this->assertExitCode(0, $process); + $this->assertStringContainsString('HELLO WORLD', $process->getErrorOutput()); + } + + /** + * It should dump none to an XML file. + */ + public function testDumpXml(): void + { + $process = $this->phpbench( + 'run --dump-file=' . $this->fname . ' benchmarks/set4/NothingBench.php' + ); + $this->assertExitCode(0, $process); + $output = $process->getErrorOutput(); + $this->assertStringContainsString('Dumped', $output); + $this->assertFileExists($this->fname); + } + + /** + * It should dump to stdout. + */ + public function testDumpXmlStdOut(): void + { + $process = $this->phpbench( + 'run --dump --progress=none benchmarks/set1/BenchmarkBench.php' + ); + $this->assertExitCode(0, $process); + $output = $process->getOutput(); + $this->assertXPathCount(3, $output, '//subject'); + } + + /** + * It should accept explicit parameters. + */ + public function testOverrideParameters(): void + { + $process = $this->phpbench( + 'run --dump --progress=none --parameters=\'{"length": 333}\' benchmarks/set1/BenchmarkBench.php' + ); + $this->assertExitCode(0, $process); + $output = $process->getOutput(); + $this->assertXPathCount(3, $output, '//parameter[@value=333]'); + } + + /** + * It should throw an exception if an invalid JSON string is provided for parameters. + */ + public function testOverrideParametersInvalidJson(): void + { + $process = $this->phpbench( + 'run --dump --progress=none --parameters=\'{"length": 333\' benchmarks/set1/BenchmarkBench.php' + ); + + $this->assertExitCode(1, $process); + $this->assertStringContainsString('Could not decode', $process->getErrorOutput()); + } + + /** + * Its should allow the number of iterations to be specified. + */ + public function testOverrideIterations(): void + { + $process = $this->phpbench( + 'run --filter=benchDoNothing --progress=none --dump --iterations=10 benchmarks/set1/BenchmarkBench.php' + ); + + $this->assertExitCode(0, $process); + $output = $process->getOutput(); + $this->assertXPathCount(10, $output, '//subject[@name="benchDoNothing"]//iteration'); + } + + /** + * It should override revolutions. + */ + public function testOverrideRevolutions(): void + { + $process = $this->phpbench( + 'run --filter=benchDoNothing --progress=none --dump --revs=666 benchmarks/set1/BenchmarkBench.php' + ); + + $this->assertExitCode(0, $process); + $output = $process->getOutput(); + $this->assertXPathExpression(666, $output, 'number(//subject[@name="benchDoNothing"]//variant/@revs)'); + } + + /** + * Its should allow the time unit to be specified. + */ + public function testOverrideTimeUnit(): void + { + $process = $this->phpbench( + 'run --filter=benchDoNothing --time-unit=milliseconds --iterations=10 benchmarks/set1/BenchmarkBench.php' + ); + + $this->assertExitCode(0, $process); + $this->assertStringContainsString('ms', $process->getErrorOutput()); + } + + /** + * Its should allow the mode to be specified. + */ + public function testOverrideMode(): void + { + $process = $this->phpbench( + 'run --filter=benchDoNothing --mode=throughput --iterations=10 benchmarks/set1/BenchmarkBench.php' + ); + + $this->assertExitCode(0, $process); + $this->assertStringContainsString('ops/μs', $process->getErrorOutput()); + } + + /** + * It should set the bootstrap file. + */ + public function testSetBootstrap(): void + { + // The foobar_bootstrap defines a single class which is used by FoobarBench + $process = $this->phpbench( + 'run --bootstrap=bootstrap/foobar.bootstrap benchmarks/set2/FoobarBench.php' + ); + + $this->assertExitCode(0, $process); + } + + /** + * It should set the bootstrap file which contains variables that conflict with the + * script templates. + */ + public function testSetConflictBootstrap(): void + { + // The foobar_bootstrap defines a single class which is used by FoobarBench + $process = $this->phpbench( + 'run --bootstrap=bootstrap/conflicting.bootstrap benchmarks/set2/FoobarBench.php' + ); + + $this->assertExitCode(0, $process); + } + + /** + * It should set the bootstrap using the short option. + */ + public function testSetBootstrapShort(): void + { + // The foobar_bootstrap defines a single class which is used by FoobarBench + $process = $this->phpbench( + 'run -b=bootstrap/foobar.bootstrap benchmarks/set2/FoobarBench.php' + ); + + $this->assertExitCode(0, $process); + } + + /** + * It should override the bootstrap file. + */ + public function testOverrideBootstrap(): void + { + // The foobar_bootstrap defines a single class which is used by FoobarBench + $process = $this->phpbench( + 'run --bootstrap=bootstrap/foobar.bootstrap benchmarks/set2/FoobarBench.php --config=env/config_valid/phpbench.json' + ); + + $this->assertExitCode(0, $process); + $output = $process->getErrorOutput(); + $this->assertStringContainsString('Subjects', $output); + } + + /** + * It should load the configured bootstrap relative to the config file. + */ + public function testConfigBootstrapRelativity(): void + { + // The foobar.bootstrap defines a single class which is used by FoobarBench + $process = $this->phpbench( + 'run benchmarks/set2/FoobarBench.php --config=env/config_set2/phpbench.json' + ); + + $this->assertExitCode(0, $process); + } + + /** + * It can have the progress logger specified. + * TODO: Make this a separate test and assert the output. + * + * @dataProvider provideProgressLoggers + */ + public function testProgressLogger($progress): void + { + $process = $this->phpbench( + 'run --progress=' . $progress . ' benchmarks/set1/BenchmarkBench.php' + ); + $this->assertExitCode(0, $process); + } + + public function provideProgressLoggers(): array + { + return [ + ['classdots'], + ['dots'], + ['verbose'], + ['histogram'], + ['blinken'], + ['plain'], + ['none'], + ]; + } + + /** + * It should run specified groups. + */ + public function testGroups(): void + { + $process = $this->phpbench( + 'run --group=do_nothing --dump --progress=none benchmarks/set1/BenchmarkBench.php' + ); + + $this->assertExitCode(0, $process); + $output = $process->getOutput(); + $this->assertXPathCount(1, $output, '//subject'); + } + + /** + * It should set the retry threshold. + */ + public function testRetryThreshold(): void + { + // use the debug executor, providing 3 groups of 4 iterations. the + // first two are out of bounds the third is constant. + $process = $this->phpbench( + 'run benchmarks/set4/NothingBench.php ' . + '--executor=\'{"executor": "debug", "times": [10, 100, 10, 100, 50, 500, 50, 500, 1, 1, 1, 1]}\' '. + '--retry-threshold=1 --iterations=2 '. + '--dump' + ); + $this->assertExitCode(0, $process); + $output = $process->getOutput(); + $this->assertStringContainsString(' reject-count="4"', $output); + } + + /** + * It should set the sleep option. + */ + public function testSleep(): void + { + $process = $this->phpbench( + 'run benchmarks/set4/NothingBench.php --sleep=5000' + ); + + $this->assertExitCode(0, $process); + } + + public function testQuiet(): void + { + $process = $this->phpbench( + 'run benchmarks/set4/NothingBench.php --quiet' + ); + + $this->assertExitCode(0, $process); + $this->assertEmpty($process->getErrorOutput()); + } + + /** + * It should exit with code "1" if an exception is encountered while running + * the iterations. + */ + public function testIterationErrorExitCode(): void + { + $process = $this->phpbench( + 'run benchmarks/set3/ErrorBench.php' + ); + + $this->assertExitCode(1, $process); + $this->assertStringContainsString('1 subjects encountered errors', $process->getErrorOutput()); + $this->assertStringContainsString('benchNothingElse', $process->getErrorOutput()); + } + + /** + * It should stop on the first exception if an exception is encountered. + */ + public function testIterationStopOnError(): void + { + $process = $this->phpbench( + 'run benchmarks/set3/ErrorBench.php --stop-on-error' + ); + + $this->assertExitCode(1, $process); + $this->assertStringContainsString('1 subjects encountered errors', $process->getErrorOutput()); + $this->assertStringNotContainsString('benchNothingElse', $process->getErrorOutput()); + } + + /** + * It should stop on the first exception if an exception is encountered + * with many variants + */ + public function testIterationStopOnErrorWithMultipleVariants(): void + { + $process = $this->phpbench( + 'run benchmarks/set3/ErrorVariantsBench.php --stop-on-error' + ); + + $this->assertExitCode(1, $process); + $this->assertStringContainsString('1 subjects encountered errors', $process->getErrorOutput()); + $this->assertStringNotContainsString('benchNothingElse', $process->getErrorOutput()); + } + /** + * It should allow the precision to be set. + */ + public function testPrecision(): void + { + $process = $this->phpbench( + 'run benchmarks/set4/NothingBench.php --precision=6' + ); + + $this->assertExitCode(0, $process); + $success = preg_match('{[0-9]\.([0-9]+)μs}', $process->getErrorOutput(), $matches); + $this->assertEquals(1, $success); + $this->assertEquals(6, strlen($matches[1])); + } + + /** + * It should request that the suite result to be stored. + */ + public function testStore(): void + { + $process = $this->phpbench( + 'run benchmarks/set4/NothingBench.php --store' + ); + + $this->assertExitCode(0, $process); + } + + public function testTaggingImplicitlyStoresResult(): void + { + $process = $this->phpbench( + 'run benchmarks/set4/NothingBench.php --tag=foobar' + ); + + $this->assertExitCode(0, $process); + + $process = $this->phpbench( + 'report --report=aggregate --ref=foobar' + ); + + $this->assertExitCode(0, $process); + } + + /** + * It should run with the memory-centric-microtime executor + */ + public function testRunsWithMemoryExecutor(): void + { + $process = $this->phpbench( + 'run benchmarks/set4/NothingBench.php --executor=memory_centric_microtime --iterations=1 --revs=1' + ); + + // because there is no storage driver by default the factory will throw an exception. + $this->assertExitCode(0, $process); + } + + /** + * It should not crash when zeros are reported as times. + */ + public function testZeroTimedIterations(): void + { + $process = $this->phpbench( + 'run benchmarks/set1 --executor=\'{"executor": "debug", "times": [0]}\'' + ); + $this->assertExitCode(0, $process); + } + + /** + * It should set the PHP binary, wrapper and config. + */ + public function testPhpEnvOptions(): void + { + $process = $this->phpbench( + 'run benchmarks/set4/NothingBench.php --php-binary=php --php-config="memory_limit: 10M" --php-wrapper="env"' + ); + $this->assertExitCode(0, $process); + } + + /** + * It should disable the PHP ini file. + */ + public function testPhpDisableIni(): void + { + $process = $this->phpbench( + 'run benchmarks/set4/NothingBench.php --php-disable-ini --report=env' + ); + $this->assertExitCode(0, $process); + $this->assertMatchesRegularExpression('{ini\s+\| false}', $process->getOutput()); + } + + public function testErrorWhenTimeoutExceeded(): void + { + $process = $this->phpbench( + 'run benchmarks/set6/TimeoutBench.php' + ); + $this->assertExitCode(1, $process); + } + + public function testWithSpecificProfile(): void + { + $this->workspace()->put('phpbench.json', '{"core.profiles": {"foobar":{"runner.path": "benchmarks/set4/NothingBench.php"}}}'); + $process = $this->phpbench( + 'run --profile=foobar' + ); + + $this->assertExitCode(0, $process); + } + + public function testErrorWhenUnknownProfileGiven(): void + { + $this->workspace()->put('phpbench.json', '{"core.profiles": {"foobar":{"runner.path": "benchmarks/set4/NothingBench.php"}}}'); + $process = $this->phpbench( + 'run --profile=barfoo' + ); + + $this->assertExitCode(255, $process); + $this->assertStringContainsString('Unknown profile', $process->getErrorOutput()); + } + + public function testSpecifyRemoteScriptPath(): void + { + $this->workspace()->put('phpbench.json', (string)json_encode([ + RunnerExtension::PARAM_REMOTE_SCRIPT_PATH => $this->workspace()->path('remote'), + RunnerExtension::PARAM_REMOTE_SCRIPT_REMOVE => false, + ])); + + $process = $this->phpbench( + 'run benchmarks/set4/NothingBench.php' + ); + $this->assertExitCode(0, $process); + $this->assertFileExists($this->workspace()->path('remote/remote.template')); + } + + public function testSpecifyMultiplePaths(): void + { + $this->workspace()->put('phpbench.json', (string)json_encode([ + RunnerExtension::PARAM_PATH => [ + 'benchmarks/set4/NothingBench.php', + 'benchmarks/set4/NothingBench.php', + ] + ])); + + $process = $this->phpbench('run'); + $this->assertExitCode(0, $process); + } + + public function testNoAnsi(): void + { + $process = $this->phpbench('run --assert="2 < 3" benchmarks/set4/NothingBench.php --no-ansi'); + $this->assertExitCode(0, $process); + } + + public function testDebug(): void + { + $process = $this->phpbench('run benchmarks/set4/NothingBench.php -vvv'); + $this->assertExitCode(0, $process); + self::assertStringContainsString('Spawning', $process->getErrorOutput()); + } + + public function testTheme(): void + { + $process = $this->phpbench('run benchmarks/set4/NothingBench.php -vvv --theme=basic'); + $this->assertExitCode(0, $process); + } + + public function testThemeNotFound(): void + { + $process = $this->phpbench('run benchmarks/set4/NothingBench.php -vvv --theme=notfound'); + $this->assertExitCode(255, $process); + self::assertStringContainsString('Unknown theme', $process->getErrorOutput()); + } +} diff --git a/tests/System/ShowTest.php b/tests/System/ShowTest.php new file mode 100644 index 000000000..4bef00145 --- /dev/null +++ b/tests/System/ShowTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\System; + +class ShowTest extends SystemTestCase +{ + /** + * It should show a report for a specific run. + */ + public function testDefaultReport(): void + { + $document = $this->getBenchResult(null, ' --store'); + $uuid = $document->evaluate('string(./suite/@uuid)'); + $process = $this->phpbench( + 'show ' . $uuid + ); + + $this->assertExitCode(0, $process); + } + + /** + * It should show a specific report. + */ + public function testSpecificReport(): void + { + $document = $this->getBenchResult(null, ' --store'); + $uuid = $document->evaluate('string(./suite/@uuid)'); + $process = $this->phpbench( + 'show ' . $uuid . ' --report=default' + ); + + $this->assertExitCode(0, $process); + } +} diff --git a/tests/System/SystemTestCase.php b/tests/System/SystemTestCase.php new file mode 100644 index 000000000..59724ee89 --- /dev/null +++ b/tests/System/SystemTestCase.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\System; + +use PhpBench\Dom\Document; +use PhpBench\Tests\IntegrationTestCase; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Process\Process; + +class SystemTestCase extends IntegrationTestCase +{ + /** + * @var string + */ + protected $fname; + + protected function setUp(): void + { + $this->fname = $this->workspace()->path('testfile'); + + $this->workspace()->reset(); + + $filesystem = new Filesystem(); + $filesystem->mirror(__DIR__ . '/env', $this->workspace()->path('/env')); + $filesystem->mirror(__DIR__ . '/benchmarks', $this->workspace()->path('/benchmarks')); + $filesystem->mirror(__DIR__ . '/bootstrap', $this->workspace()->path('/bootstrap')); + } + + public function getBenchResult($benchmark = null, $extraCmd = '') + { + $benchmark = $benchmark ?: 'benchmarks/set4'; + + $process = $this->phpbench( + 'run ' . $benchmark . ' --executor=debug --dump-file=' . $this->fname . ' ' . $extraCmd + ); + + if ($process->getExitCode() !== 0) { + throw new \Exception('Could not generate test data:' . $process->getErrorOutput() . $process->getOutput()); + } + + $document = new Document(); + $document->load($this->fname); + + return $document; + } + + public function phpbench(string $command, ?string $cwd = null): Process + { + $cwd = $this->workspace()->path($cwd); + + $bin = __DIR__ . '/../../bin/phpbench --verbose'; + $process = Process::fromShellCommandline($bin . ' ' . $command, $cwd); + $process->run(); + + return $process; + } + + protected function assertExitCode($expected, Process $process): void + { + $exitCode = $process->getExitCode(); + + if ($exitCode !== $expected) { + $this->fail(sprintf( + 'Expected exit code "%s" from "%s" but got "%s": STDOUT: %s ERR: %s', + $expected, + $process->getCommandLine(), + $exitCode, + $process->getOutput(), + $process->getErrorOutput() + )); + } + + $this->assertEquals($expected, $exitCode); + } + + protected function assertXPathCount($count, $xmlString, $query): void + { + $this->assertXPathExpression($count, $xmlString, sprintf('count(%s)', $query)); + } + + protected function assertXPathExpression($expected, $xmlString, $expression): void + { + $dom = new \DOMDocument(); + $result = @$dom->loadXML($xmlString); + + if (false === $result) { + throw new \RuntimeException(sprintf( + 'Could not load XML "%s"', $xmlString + )); + } + + $xpath = new \DOMXPath($dom); + $dom->formatOutput = true; + $result = $xpath->evaluate($expression); + $this->assertEquals($expected, $result); + } +} diff --git a/tests/System/benchmarks/set1/BenchmarkBench.php b/tests/System/benchmarks/set1/BenchmarkBench.php new file mode 100644 index 000000000..d713e9427 --- /dev/null +++ b/tests/System/benchmarks/set1/BenchmarkBench.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +class BenchmarkBench +{ + public function benchRandom(): void + { + usleep(rand(0, 100)); + } + + /** + * @Iterations(1) + * @Revs(1000) + * @Groups({"do_nothing"}) + */ + public function benchDoNothing(): void + { + } + + /** + * @ParamProviders({"provideParamsOne", "provideParamsTwo"}) + * @Groups({"parameterized"}) + * @Iterations(1) + */ + public function benchParameterized($params): void + { + } + + public function provideParamsOne() + { + return [ + ['length' => '1'], + ['length' => '2'], + ]; + } + + public function provideParamsTwo() + { + return [ + ['strategy' => 'left'], + ['strategy' => 'right'], + ]; + } +} diff --git a/tests/System/benchmarks/set1/IsolatedBench.php b/tests/System/benchmarks/set1/IsolatedBench.php new file mode 100644 index 000000000..e720ccad4 --- /dev/null +++ b/tests/System/benchmarks/set1/IsolatedBench.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +class IsolatedBench +{ + /** + * @Iterations(1) + */ + public function benchIterationIsolation(): void + { + $handle = fopen(sys_get_temp_dir() . '/phpbench_isolationtest', 'a'); + fwrite($handle, getmypid() . PHP_EOL); + fclose($handle); + } + + /** + * @Iterations(1) + */ + public function benchIterationsIsolation(): void + { + $handle = fopen(sys_get_temp_dir() . '/phpbench_isolationtest', 'a'); + fwrite($handle, getmypid() . PHP_EOL); + fclose($handle); + } +} diff --git a/tests/System/benchmarks/set1/IsolatedParametersBench.php b/tests/System/benchmarks/set1/IsolatedParametersBench.php new file mode 100644 index 000000000..806b44a69 --- /dev/null +++ b/tests/System/benchmarks/set1/IsolatedParametersBench.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +class IsolatedParameterBench +{ + /** + * @Groups({"process"}) + * @Iterations(1) + * @ParamProviders({"provideParams"}) + */ + public function benchIterationIsolation(): void + { + } + + public function provideParams() + { + return [ + [ + 'hello' => 'Look "I am using double quotes"', + 'goodbye' => 'Look \'I am using single quotes\'"', + 'goodbye' => 'Look \'I am use $dollars"', + ], + ]; + } +} diff --git a/tests/System/benchmarks/set1/IsolatedRevsBench.php b/tests/System/benchmarks/set1/IsolatedRevsBench.php new file mode 100644 index 000000000..f5b83a026 --- /dev/null +++ b/tests/System/benchmarks/set1/IsolatedRevsBench.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +class IsolatedRevsBench +{ + /** + * @Revs(1) + * @Revs(2) + */ + public function benchIterationIsolation(): void + { + $handle = fopen(sys_get_temp_dir() . '/phpbench_isolationtest', 'a'); + fwrite($handle, getmypid() . PHP_EOL); + fclose($handle); + } +} diff --git a/tests/System/benchmarks/set1/ParameterProviderCallableBench.php b/tests/System/benchmarks/set1/ParameterProviderCallableBench.php new file mode 100644 index 000000000..3588633ad --- /dev/null +++ b/tests/System/benchmarks/set1/ParameterProviderCallableBench.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +function param_provider() +{ + return [ + [ + 'hello' => 'goodbye', + ], + ]; +} + +class ParameterProviderCallableBench +{ + /** + * @Groups({"process"}) + * @Iterations(1) + * @ParamProviders({"param_provider"}) + */ + public function benchParamProviderCallable($params): void + { + } +} diff --git a/tests/System/benchmarks/set2/FoobarBench.php b/tests/System/benchmarks/set2/FoobarBench.php new file mode 100644 index 000000000..c46db3592 --- /dev/null +++ b/tests/System/benchmarks/set2/FoobarBench.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\System\benchmarks\foobar; + +/** + * This benchmark requires the bootstrap/foobar_bootstrap.php to be loaded in + * order that Foobar be defined. + */ +class FoobarBench +{ + public function benchFoobar(): void + { + new \Test\Foobar(); + } +} diff --git a/tests/System/benchmarks/set3/ErrorBench.php b/tests/System/benchmarks/set3/ErrorBench.php new file mode 100644 index 000000000..036303618 --- /dev/null +++ b/tests/System/benchmarks/set3/ErrorBench.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\System\benchmarks\set3; + +/** + * This benchmark throws exceptions. + */ +class ErrorBench +{ + public function benchNothing(): void + { + } + + public function benchException(): void + { + throw new \Exception('Arff'); + } + + public function benchNothingElse(): void + { + } +} diff --git a/tests/System/benchmarks/set3/ErrorVariantsBench.php b/tests/System/benchmarks/set3/ErrorVariantsBench.php new file mode 100644 index 000000000..7797c560e --- /dev/null +++ b/tests/System/benchmarks/set3/ErrorVariantsBench.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\System\benchmarks\set3; + +class ErrorVariantsBench +{ + public function provideFoos() + { + yield [ 'one' => 'two' ]; + + yield [ 'two' => 'three' ]; + } + /** + * @ParamProviders({"provideFoos"}) + */ + public function benchException(): void + { + throw new \Exception('Foobar'); + } +} diff --git a/tests/System/benchmarks/set4/NothingBench.php b/tests/System/benchmarks/set4/NothingBench.php new file mode 100644 index 000000000..35e54ed0d --- /dev/null +++ b/tests/System/benchmarks/set4/NothingBench.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\System\benchmarks\set4; + +class NothingBench +{ + public function benchNothing(): void + { + } +} diff --git a/tests/System/benchmarks/set5/AssertFailBench.php b/tests/System/benchmarks/set5/AssertFailBench.php new file mode 100644 index 000000000..0af2537fe --- /dev/null +++ b/tests/System/benchmarks/set5/AssertFailBench.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\System\benchmarks\set5; + +class AssertFailBench +{ + /** + * @Assert("1 > 2") + */ + public function benchFail(): void + { + usleep(10); + } +} diff --git a/tests/System/benchmarks/set5/AssertWarnBench.php b/tests/System/benchmarks/set5/AssertWarnBench.php new file mode 100644 index 000000000..38127d600 --- /dev/null +++ b/tests/System/benchmarks/set5/AssertWarnBench.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\System\benchmarks\set5; + +/** + * @Assert("variant.mean < 1 microseconds +/- 1000 microseconds") + */ +class AssertWarnBench +{ + public function benchFail(): void + { + } +} diff --git a/tests/System/benchmarks/set6/TimeoutBench.php b/tests/System/benchmarks/set6/TimeoutBench.php new file mode 100644 index 000000000..6012822f2 --- /dev/null +++ b/tests/System/benchmarks/set6/TimeoutBench.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +throw new \Exception('This configuration file should not have been called'); diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 000000000..b30a3fd14 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,32 @@ +type(), + $this->createProcessor()->assert($variant, $assertion)->type() + ); + } + + /** + * @return Generator + */ + public function provideAssert(): Generator + { + yield [ + VariantBuilder::create() + ->setRevs(2) + ->iteration() + ->setResult(new TimeResult(10, 1)) + ->end() + ->build(), + 'mode(variant.time.avg) < 5', + AssertionResult::fail() + ]; + + yield [ + VariantBuilder::create() + ->setRevs(2) + ->iteration() + ->setResult(new TimeResult(10, 1)) + ->end() + ->build(), + 'mode(variant.time.avg) < 5 +/- 5', + AssertionResult::tolerated() + ]; + + yield [ + VariantBuilder::create() + ->setRevs(2) + ->iteration() + ->setResult(new TimeResult(10, 1)) + ->end() + ->build(), + 'mode(variant.time.avg) > 5', + AssertionResult::ok() + ]; + } + + private function createProcessor(): AssertionProcessor + { + return new AssertionProcessor( + $this->container()->get(ExpressionLanguage::class), + $this->container()->get(Evaluator::class), + $this->container()->get(Printer::class), + $this->container()->get(EvaluatingPrinter::class), + new ParameterProvider() + ); + } +} diff --git a/tests/Unit/Benchmark/BenchmarkFinderTest.php b/tests/Unit/Benchmark/BenchmarkFinderTest.php new file mode 100644 index 000000000..6c7d65f2b --- /dev/null +++ b/tests/Unit/Benchmark/BenchmarkFinderTest.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Benchmark; + +use PhpBench\Benchmark\BenchmarkFinder; +use PhpBench\Benchmark\Metadata\BenchmarkMetadata; +use PhpBench\Benchmark\Metadata\MetadataFactory; +use PhpBench\Model\Subject; +use PhpBench\Tests\TestCase; + +class BenchmarkFinderTest extends TestCase +{ + private $finder; + private $factory; + private $benchmark1; + private $benchmark2; + + protected function setUp(): void + { + $this->factory = $this->prophesize(MetadataFactory::class); + $this->benchmark1 = $this->prophesize(BenchmarkMetadata::class); + $this->benchmark2 = $this->prophesize(BenchmarkMetadata::class); + $this->subject = $this->prophesize(Subject::class); + $this->finder = new BenchmarkFinder($this->factory->reveal(), __DIR__ . '/test'); + } + + /** + * It should return a collection of all found bench benchmarks. + * It should not instantiate abstract classes. + */ + public function testBuildCollection(): void + { + $this->factory->getMetadataForFile(__DIR__ . '/findertest/FooCaseBench.php')->willReturn($this->benchmark1->reveal()); + $this->factory->getMetadataForFile(__DIR__ . '/findertest/FooCase2Bench.php')->willReturn($this->benchmark2->reveal()); + $this->factory->getMetadataForFile(__DIR__ . '/findertest/AbstractBench.php')->willReturn(null); + $this->benchmark1->hasSubjects()->willReturn(true); + $this->benchmark2->hasSubjects()->willReturn(true); + $benchmarks = $this->finder->findBenchmarks([__DIR__ . '/findertest']); + + $this->assertCount(2, $benchmarks); + } + + /** + * It should return a collection of all found bench benchmarks. + * It should not instantiate abstract classes. + */ + public function testFromMultiplePaths(): void + { + $this->factory->getMetadataForFile( + __DIR__ . '/findertest/FooCaseBench.php' + )->willReturn($this->benchmark1->reveal()); + $this->factory->getMetadataForFile( + __DIR__ . '/findertest/FooCase2Bench.php' + )->willReturn($this->benchmark2->reveal()); + + $this->benchmark1->hasSubjects()->willReturn(true); + $this->benchmark2->hasSubjects()->willReturn(true); + + $benchmarks = $this->finder->findBenchmarks([ + __DIR__ . '/findertest/FooCaseBench.php', + __DIR__ . '/findertest/FooCase2Bench.php' + ]); + + $this->assertCount(2, iterator_to_array($benchmarks)); + } + + /** + * It should run a specified benchmark. + * It should not run other benchmarks. + */ + public function testSpecificBenchmark(): void + { + $this->factory->getMetadataForFile(__DIR__ . '/findertestnested/MyBench.php')->willReturn($this->benchmark1->reveal()); + $this->benchmark1->hasSubjects()->willReturn(true); + + $benchmarks = $this->finder->findBenchmarks([__DIR__ . '/findertestnested/MyBench.php']); + + $this->assertCount(1, $benchmarks); + } + + /** + * It should skip benchmarks that have no subjects. + */ + public function testNoSubjects(): void + { + $this->factory->getMetadataForFile(__DIR__ . '/findertestnested/MyBench.php')->willReturn($this->benchmark1->reveal()); + $this->benchmark1->hasSubjects()->willReturn(false); + + $benchmarks = $this->finder->findBenchmarks([__DIR__ . '/findertestnested/MyBench.php']); + + $this->assertCount(0, $benchmarks); + } +} diff --git a/tests/Unit/Benchmark/CartesianIteratorTest.php b/tests/Unit/Benchmark/CartesianIteratorTest.php new file mode 100644 index 000000000..fc2024dfa --- /dev/null +++ b/tests/Unit/Benchmark/CartesianIteratorTest.php @@ -0,0 +1,236 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests; + +use PhpBench\Benchmark\CartesianParameterIterator; + +class CartesianIteratorTest extends TestCase +{ + /** + * It should generate the cartestian product of all sets for each iteration. + * + * @dataProvider provideIterate + */ + public function testIterate($parameterSets, $expected): void + { + $iterator = new CartesianParameterIterator($parameterSets); + $result = []; + + foreach ($iterator as $name => $parameters) { + $result[$name] = $parameters->getArrayCopy(); + } + + $this->assertEquals($expected, $result); + } + + public function provideIterate() + { + yield '0 x 0' => [ + [ + ], + [ + ], + ]; + + yield '1 x 0' => [ + [ + [ + ], + ], + [ + '' => [ + ], + ], + ]; + + yield '1 x 1' => [ + [ + [ + ['one' => 1], + ], + ], + [ + '0' => [ + 'one' => 1, + ], + ], + ]; + + yield '2 x 2' => [ + [ + [ + ['optimized' => false], + ['optimized' => true], + ], + [ + ['nb_foos' => 4], + ['nb_foos' => 5], + ], + ], + [ + '0,0' => [ + 'optimized' => false, + 'nb_foos' => 4, + ], + '1,0' => [ + 'optimized' => true, + 'nb_foos' => 4, + ], + '0,1' => [ + 'optimized' => false, + 'nb_foos' => 5, + ], + '1,1' => [ + 'optimized' => true, + 'nb_foos' => 5, + ], + ], + ]; + + yield '3 x 2' => [ + [ + [ + ['one' => 1], + ['two' => 2], + ], + [ + ['three' => 3], + ['four' => 4], + ], + [ + ['five' => 5], + ['six' => 6], + ], + ], + [ + '0,0,0' => [ + 'one' => 1, + 'three' => 3, + 'five' => 5, + ], + '1,0,0' => [ + 'three' => 3, + 'five' => 5, + 'two' => 2, + ], + '0,1,0' => [ + 'one' => 1, + 'four' => 4, + 'five' => 5, + ], + '0,0,1' => [ + 'one' => 1, + 'three' => 3, + 'six' => 6, + ], + '1,1,0' => [ + 'two' => 2, + 'four' => 4, + 'five' => 5, + ], + '1,0,1' => [ + 'two' => 2, + 'three' => 3, + 'six' => 6, + ], + '0,1,1' => [ + 'one' => 1, + 'four' => 4, + 'six' => 6, + ], + '1,1,1' => [ + 'two' => 2, + 'four' => 4, + 'six' => 6, + ], + ], + ]; + + yield 'named' => [ + [ + [ + 'opt false' => ['optimized' => false], + 'opt true' => ['optimized' => true], + ], + [ + '4 foos' => ['nb_foos' => 4], + '5 foos' => ['nb_foos' => 5], + ], + ], + [ + 'opt false,4 foos' => [ + 'optimized' => false, + 'nb_foos' => 4, + ], + 'opt true,4 foos' => [ + 'optimized' => true, + 'nb_foos' => 4, + ], + 'opt false,5 foos' => [ + 'optimized' => false, + 'nb_foos' => 5, + ], + 'opt true,5 foos' => [ + 'optimized' => true, + 'nb_foos' => 5, + ], + ], + ]; + + yield 'uneven first set' => [ + [ + [ + [ + 'one' => 1, + 'two' => 2, + ], + ], + [ + [ + 'three' => 3, + ], + ], + ], + [ + '0,0' => [ + 'one' => 1, + 'two' => 2, + 'three' => 3, + ], + ], + ]; + + yield 'uneven second set' => [ + [ + [ + [ + 'one' => 1, + ], + ], + [ + [ + 'two' => 2, + 'three' => 3, + ], + ], + ], + [ + '0,0' => [ + 'one' => 1, + 'two' => 2, + 'three' => 3, + ], + ], + ]; + } +} diff --git a/tests/Unit/Benchmark/Metadata/AnnotationReaderTest.php b/tests/Unit/Benchmark/Metadata/AnnotationReaderTest.php new file mode 100644 index 000000000..8d352be3f --- /dev/null +++ b/tests/Unit/Benchmark/Metadata/AnnotationReaderTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Benchmark\Metadata; + +use PhpBench\Benchmark\Metadata\AnnotationReader; +use PhpBench\Benchmark\Metadata\Annotations; +use PhpBench\Reflection\ReflectionClass; +use PhpBench\Reflection\ReflectionMethod; +use PhpBench\Tests\TestCase; + +class AnnotationReaderTest extends TestCase +{ + /** + * It should read class annotations. + */ + public function testLoadClassMetadata(): void + { + $reflection = new ReflectionClass(); + $reflection->class = 'Test'; + $reflection->comment = <<<'EOT' +/** + * @BeforeClassMethods({"beforeClass"}) + * @AfterClassMethods({"afterClass"}) + */ +EOT; + + $annotations = $this->createReader()->getClassAnnotations($reflection); + $this->assertCount(2, $annotations); + } + + /** + * It should read method annotations. + */ + public function testLoadMethodMetadata(): void + { + $reflection = new ReflectionClass(); + $reflection->class = 'Test'; + $reflectionMethod = new ReflectionMethod(); + $reflectionMethod->reflectionClass = $reflection; + $reflectionMethod->comment = <<<'EOT' +/** + * @Subject() + * @Iterations(10) + */ +EOT; + + $annotations = $this->createReader()->getMethodAnnotations($reflectionMethod); + $this->assertCount(2, $annotations); + } + + /** + * It should use imported annotations when configured to do so. + */ + public function testImportedUse(): void + { + $reflection = new ReflectionClass(); + $reflection->class = 'Test'; + $reflection->path = __DIR__ . '/classes/Test.php'; + $reflectionMethod = new ReflectionMethod(); + $reflectionMethod->reflectionClass = $reflection; + $reflectionMethod->comment = <<<'EOT' +/** + * @PhpBench\Subject() + * @PhpBench\Iterations(10) + */ +EOT; + + $annotations = $this->createReader(true)->getMethodAnnotations($reflectionMethod); + $this->assertCount(2, $annotations); + } + + private function createReader($useImports = false) + { + return new AnnotationReader($useImports); + } +} diff --git a/tests/Unit/Benchmark/Metadata/BenchmarkMetadataTest.php b/tests/Unit/Benchmark/Metadata/BenchmarkMetadataTest.php new file mode 100644 index 000000000..24ae0f8fa --- /dev/null +++ b/tests/Unit/Benchmark/Metadata/BenchmarkMetadataTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Benchmark\Metadata; + +use PhpBench\Benchmark\Metadata\BenchmarkMetadata; +use PhpBench\Tests\TestCase; + +class BenchmarkMetadataTest extends TestCase +{ + private $metadata; + + protected function setUp(): void + { + $this->metadata = new BenchmarkMetadata('/path/to', 'Class'); + $this->subject1 = $this->metadata->getOrCreateSubject('subjectOne'); + $this->subject2 = $this->metadata->getOrCreateSubject('subjectTwo'); + } + + /** + * It should filter subjects based on a given filter. + */ + public function testFilter(): void + { + $this->assertCount(2, $this->metadata->getSubjects()); + $this->metadata->filterSubjectNames(['subjectOne']); + $this->assertCount(1, $this->metadata->getSubjects()); + $this->metadata->filterSubjectNames(['subjectSeventySeven']); + $this->assertCount(0, $this->metadata->getSubjects()); + } + + /** + * It should filter on the class name. + */ + public function testFilterClassName(): void + { + $this->metadata->filterSubjectNames(['Class::subjectOne*']); + $this->assertCount(1, $this->metadata->getSubjects()); + } + + /** + * It should filter using a regex. + */ + public function testFilterRegex(): void + { + $this->metadata->filterSubjectNames(['.*One*']); + $this->assertCount(1, $this->metadata->getSubjects()); + } + + /** + * It should say if it has subjects or not. + * + * @depends testFilter + */ + public function testHasSubjects(): void + { + $this->assertTrue($this->metadata->hasSubjects()); + $this->metadata->filterSubjectNames(['subjectSeventySeven']); + $this->assertFalse($this->metadata->hasSubjects()); + } + + public function testMerge(): void + { + $benchmark1 = new BenchmarkMetadata(__FILE__, __CLASS__); + $benchmark2 = new BenchmarkMetadata(__FILE__, __CLASS__); + + $benchmark1->setBeforeClassMethods(['one', 'two']); + $benchmark2->setBeforeClassMethods(['three', 'four']); + $benchmark1->setAfterClassMethods(['1', '3']); + $benchmark2->setAfterClassMethods(['2', '4']); + + $benchmark1->merge($benchmark2); + + self::assertEquals(['one', 'two', 'three', 'four'], $benchmark1->getBeforeClassMethods()); + self::assertEquals(['1', '3', '2', '4'], $benchmark1->getAfterClassMethods()); + } +} diff --git a/tests/Unit/Benchmark/Metadata/Driver/AnnotationDriverTest.php b/tests/Unit/Benchmark/Metadata/Driver/AnnotationDriverTest.php new file mode 100644 index 000000000..23e6f8535 --- /dev/null +++ b/tests/Unit/Benchmark/Metadata/Driver/AnnotationDriverTest.php @@ -0,0 +1,515 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Benchmark\Metadata\Driver; + +use InvalidArgumentException; +use PhpBench\Benchmark\Metadata\Annotations; +use PhpBench\Benchmark\Metadata\Driver\AnnotationDriver; +use PhpBench\Benchmark\Metadata\DriverInterface; +use PhpBench\Benchmark\Metadata\ExecutorMetadata; +use PhpBench\Benchmark\Metadata\SubjectMetadata; +use PhpBench\Reflection\ReflectionClass; +use PhpBench\Reflection\ReflectionHierarchy; +use PhpBench\Reflection\ReflectionMethod; +use PhpBench\Reflection\ReflectorInterface; +use PhpBench\Tests\TestCase; + +class AnnotationDriverTest extends TestCase +{ + private $driver; + private $reflector; + + protected function setUp(): void + { + $this->reflector = $this->prophesize(ReflectorInterface::class); + } + + /** + * It should return class metadata according to annotations. + */ + public function testLoadClassMetadata(): void + { + $reflection = new ReflectionClass(); + $reflection->path = 'foo'; + $reflection->class = 'Test'; + $reflection->comment = <<<'EOT' +/** + * @BeforeClassMethods({"beforeClass"}) + * @AfterClassMethods({"afterClass"}) + * @Executor("microtime", revs=100) + */ +EOT; + $hierarchy = new ReflectionHierarchy(); + $hierarchy->addReflectionClass($reflection); + + $metadata = $this->createDriver()->getMetadataForHierarchy($hierarchy); + $this->assertEquals(['beforeClass'], $metadata->getBeforeClassMethods()); + $this->assertEquals(['afterClass'], $metadata->getAfterClassMethods()); + $this->assertEquals('Test', $metadata->getClass()); + $this->assertEquals(new ExecutorMetadata('microtime', ['revs' => 100 ]), $metadata->getExecutor()); + } + + /** + * It should ignore common annotations. + */ + public function testIgnoreCommonAnnotations(): void + { + $reflection = new ReflectionClass(); + $reflection->path = 'foo'; + $reflection->class = 'Test'; + $reflection->comment = <<<'EOT' +/** + * @since Foo + * @author Daniel Leech + */ +EOT; + $hierarchy = new ReflectionHierarchy(); + $hierarchy->addReflectionClass($reflection); + + $this->createDriver()->getMetadataForHierarchy($hierarchy); + $this->addToAssertionCount(1); + } + + /** + * It should return method metadata according to annotations. + */ + public function testLoadSubject(): void + { + $reflection = new ReflectionClass(); + $reflection->class = 'Test'; + $reflection->path = 'foo'; + $hierarchy = new ReflectionHierarchy(); + $hierarchy->addReflectionClass($reflection); + + $method = new ReflectionMethod(); + $method->reflectionClass = $reflection; + $method->class = 'Test'; + $method->name = 'benchFoo'; + $method->comment = <<<'EOT' + /** + * @BeforeMethods({"beforeOne", "beforeTwo"}) + * @AfterMethods({"afterOne", "afterTwo"}) + * @Groups({"groupOne", "groupTwo"}) + * @Iterations(50) + * @ParamProviders({"ONE", "TWO"}) + * @Revs(1000) + * @Skip() + * @Sleep(500) + * @OutputTimeUnit("seconds") + * @OutputMode("throughput") + * @Warmup(501) + * @Assert("mean < 100") + * @Format("mode(variant.time.avg) as ms") + * @Executor("microtime", revs=100) + * @Timeout(0.1) + * @RetryThreshold(2.1) + */ +EOT; + $reflection->methods[$method->name] = $method; + + $metadata = $this->createDriver()->getMetadataForHierarchy($hierarchy); + $subjects = $metadata->getSubjects(); + $this->assertCount(1, $subjects); + + /** @var SubjectMetadata $metadata */ + $metadata = reset($subjects); + $this->assertEquals(['beforeOne', 'beforeTwo'], $metadata->getBeforeMethods()); + $this->assertEquals(['afterOne', 'afterTwo'], $metadata->getAfterMethods()); + $this->assertEquals(['groupOne', 'groupTwo'], $metadata->getGroups()); + $this->assertEquals([50], $metadata->getIterations()); + $this->assertEquals(['ONE', 'TWO'], $metadata->getParamProviders()); + $this->assertEquals([1000], $metadata->getRevs()); + $this->assertEquals(500, $metadata->getSleep()); + $this->assertEquals('seconds', $metadata->getOutputTimeUnit()); + $this->assertEquals('throughput', $metadata->getOutputMode()); + $this->assertEquals([501], $metadata->getWarmup()); + $this->assertEquals('mode(variant.time.avg) as ms', $metadata->getFormat()); + $this->assertEquals('mean < 100', $metadata->getAssertions()[0]); + $this->assertEquals(new ExecutorMetadata('microtime', ['revs' => 100 ]), $metadata->getExecutor()); + $this->assertTrue($metadata->getSkip()); + $this->assertEquals(0.1, $metadata->getTimeout()); + $this->assertEquals(2.1, $metadata->getRetryThreshold()); + } + + /** + * It should return method metadata for non-prefixed methods with the + * Subject annotation. + */ + public function testLoadSubjectNonPrefixed(): void + { + $reflection = new ReflectionClass(); + $reflection->class = 'Test'; + $reflection->path = 'foo'; + $hierarchy = new ReflectionHierarchy(); + $hierarchy->addReflectionClass($reflection); + + $method = new ReflectionMethod(); + $method->reflectionClass = $reflection; + $method->class = 'Test'; + $method->name = 'foo'; + $method->comment = <<<'EOT' +/** + * @Subject() + */ +EOT; + $reflection->methods[$method->name] = $method; + + $metadata = $this->createDriver()->getMetadataForHierarchy($hierarchy); + $subjects = $metadata->getSubjects(); + $this->assertCount(1, $subjects); + } + + /** + * It should ignore non-prefixed subjects without the Subject annotation. + */ + public function testLoadIgnoreNonPrefixed(): void + { + $reflection = new ReflectionClass(); + $reflection->class = 'Test'; + $reflection->path = 'foo'; + $hierarchy = new ReflectionHierarchy(); + $hierarchy->addReflectionClass($reflection); + + $method = new ReflectionMethod(); + $method->reflectionClass = $reflection; + $method->class = 'Test'; + $method->name = 'foo'; + $method->comment = <<<'EOT' +/** + * @Iterations(10) + */ +EOT; + $reflection->methods[$method->name] = $method; + + $metadata = $this->createDriver()->getMetadataForHierarchy($hierarchy); + $subjects = $metadata->getSubjects(); + $this->assertCount(0, $subjects); + } + + /** + * It should raise an exception if either before or after class methods + * are specified at the method level rather than the class level. + * + * @dataProvider provideClassMethodsOnMethodException + */ + public function testClassMethodsOnException($annotation): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('annotation can only be applied'); + $reflection = new ReflectionClass(); + $reflection->class = 'Test'; + $reflection->path = 'foo'; + $hierarchy = new ReflectionHierarchy(); + $hierarchy->addReflectionClass($reflection); + + $method = new ReflectionMethod(); + $method->reflectionClass = $reflection; + $method->class = 'Test'; + $method->name = 'benchFoo'; + $method->comment = sprintf('/** %s */', $annotation); + $reflection->methods[$method->name] = $method; + + $this->createDriver()->getMetadataForHierarchy($hierarchy); + } + + public function provideClassMethodsOnMethodException() + { + return [ + [ + '@beforeClassMethods({"foo"})', + ], + [ + '@afterClassMethods({"foo"})', + ], + ]; + } + + /** + * Test optional values. + */ + public function testSubjectOptionalValues(): void + { + $reflection = new ReflectionClass(); + $reflection->class = 'Test'; + $reflection->path = 'foo'; + $hierarchy = new ReflectionHierarchy(); + $hierarchy->addReflectionClass($reflection); + + $method = new ReflectionMethod(); + $method->reflectionClass = $reflection; + $method->class = 'Test'; + $method->name = 'benchFoo'; + $method->comment = <<<'EOT' + /** + * @OutputTimeUnit("seconds", precision=3) + */ +EOT; + $reflection->methods[$method->name] = $method; + + $metadata = $this->createDriver()->getMetadataForHierarchy($hierarchy); + $subjects = $metadata->getSubjects(); + $this->assertCount(1, $subjects); + $metadata = reset($subjects); + + $this->assertEquals(3, $metadata->getOutputTimePrecision()); + } + + /** + * Subject metadata should override class metadata. + */ + public function testLoadSubjectOverride(): void + { + $reflection = new ReflectionClass(); + $reflection->path = 'foo'; + $reflection->class = 'Test'; + $reflection->comment = <<<'EOT' + /** + * @BeforeMethods({"beforeOne", "beforeTwo"}) + */ +EOT; + $hierarchy = new ReflectionHierarchy(); + $hierarchy->addReflectionClass($reflection); + + $method = new ReflectionMethod(); + $method->reflectionClass = $reflection; + $method->class = 'Test'; + $method->name = 'benchFoo'; + $method->comment = <<<'EOT' + /** + * @BeforeMethods({"beforeFive"}) + */ +EOT; + $reflection->methods[$method->name] = $method; + + $metadata = $this->createDriver()->getMetadataForHierarchy($hierarchy); + $subjects = $metadata->getSubjects(); + $this->assertCount(1, $subjects); + $metadata = reset($subjects); + $this->assertEquals(['beforeFive'], $metadata->getBeforeMethods()); + } + + /** + * It should merge class parent classes. + */ + public function testLoadSubjectMerge(): void + { + $reflection = new ReflectionClass(); + $reflection->class = 'TestChild'; + $reflection->path = 'foo'; + $reflection->comment = <<<'EOT' + /** + * @BeforeMethods({"class2"}) + */ +EOT; + $hierarchy = new ReflectionHierarchy(); + + $method = new ReflectionMethod(); + $method->reflectionClass = $reflection; + $method->class = 'Test'; + $method->name = 'benchFoo'; + $method->comment = <<<'EOT' + /** + * @Revs(2000) + */ +EOT; + $reflection->methods[$method->name] = $method; + $method = new ReflectionMethod(); + $method->reflectionClass = $reflection; + $method->class = 'Test'; + $method->name = 'benchBar'; + $method->comment = <<<'EOT' + /** + * @Iterations(99) + */ +EOT; + $reflection->methods[$method->name] = $method; + $hierarchy->addReflectionClass($reflection); + + $reflection = new ReflectionClass(); + $reflection->class = 'Test'; + $reflection->comment = <<<'EOT' + /** + * @AfterMethods({"after"}) + * @Iterations(50) + */ +EOT; + $method = new ReflectionMethod(); + $method->reflectionClass = $reflection; + $method->class = 'Test'; + $method->name = 'benchFoo'; + $method->comment = <<<'EOT' + /** + * @Revs(1000) + */ +EOT; + $reflection->methods[$method->name] = $method; + $method = new ReflectionMethod(); + $method->reflectionClass = $reflection; + $method->class = 'Test'; + $method->name = 'benchBar'; + $method->comment = <<<'EOT' + /** + * @Revs(50) + */ +EOT; + $reflection->methods[$method->name] = $method; + + $method = new ReflectionMethod(); + $method->reflectionClass = $reflection; + $method->class = 'Test'; + $method->name = 'benchNoAnnotations'; + $method->comment = null; + $reflection->methods[$method->name] = $method; + $hierarchy->addReflectionClass($reflection); + + $metadata = $this->createDriver()->getMetadataForHierarchy($hierarchy); + + $subjects = $metadata->getSubjects(); + $this->assertCount(3, $subjects); + $subjectOne = array_shift($subjects); + $subjectTwo = array_shift($subjects); + $subjectThree = array_shift($subjects); + + $this->assertEquals('benchFoo', $subjectOne->getName()); + $this->assertEquals([2000], $subjectOne->getRevs()); + + $this->assertEquals('benchBar', $subjectTwo->getName()); + $this->assertEquals([99], $subjectTwo->getIterations()); + $this->assertEquals([50], $subjectTwo->getRevs()); + $this->assertEquals(['class2'], $subjectTwo->getBeforeMethods()); + $this->assertEquals(['after'], $subjectTwo->getAfterMethods()); + $this->assertEquals(['class2'], $subjectThree->getBeforeMethods()); + $this->assertEquals(['after'], $subjectThree->getAfterMethods()); + } + + /** + * It should extend values of previous annotations when the "extend" option is true. + */ + public function testMetadataExtend(): void + { + $reflection = new ReflectionClass(); + $reflection->class = 'TestChild'; + $reflection->path = 'foo'; + $reflection->comment = <<<'EOT' + /** + * @Groups({"group1"}) + */ +EOT; + $hierarchy = new ReflectionHierarchy(); + $hierarchy->addReflectionClass($reflection); + + $method = new ReflectionMethod(); + $method->reflectionClass = $reflection; + $method->class = 'Test'; + $method->name = 'benchFoo'; + $method->comment = <<<'EOT' + /** + * @Groups({"group2", "group3"}, extend=true) + */ +EOT; + $reflection->methods[$method->name] = $method; + $metadata = $this->createDriver()->getMetadataForHierarchy($hierarchy); + + $subjects = $metadata->getSubjects(); + $this->assertCount(1, $subjects); + $subjectOne = array_shift($subjects); + + $this->assertEquals(['group1', 'group2', 'group3'], $subjectOne->getGroups()); + } + + /** + * It should allow multiple array elements for warmup, iterations and revolutions. + */ + public function testArrayElements(): void + { + $reflection = new ReflectionClass(); + $reflection->class = 'Test'; + $reflection->path = 'foo'; + + $method = new ReflectionMethod(); + $method->reflectionClass = $reflection; + $method->class = 'Test'; + $method->name = 'benchFoo'; + $method->comment = <<<'EOT' +/** + * @Iterations({10, 20, 30}) + * @Revs({1, 2, 3}) + * @Warmup({5, 15, 115}) + */ +EOT; + $reflection->methods[$method->name] = $method; + $hierarchy = new ReflectionHierarchy(); + $hierarchy->addReflectionClass($reflection); + + $metadata = $this->createDriver()->getMetadataForHierarchy($hierarchy); + $subject = $metadata->getSubjects(); + $subject = current($subject); + $this->assertEquals([10, 20, 30], $subject->getIterations()); + $this->assertEquals([1, 2, 3], $subject->getRevs()); + $this->assertEquals([5, 15, 115], $subject->getWarmup()); + } + + /** + * It should throw a helpful exception when an annotation is not recognized. + * + */ + public function testUsefulException(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Unrecognized annotation @Foobar, valid PHPBench annotations: @BeforeMethods,'); + $hierarchy = 'test'; + + $reflection = new ReflectionClass(); + $reflection->class = 'TestChild'; + $reflection->path = 'foo'; + $reflection->comment = <<<'EOT' + /** + * @Foobar("foo") + */ +EOT; + $hierarchy = new ReflectionHierarchy(); + $hierarchy->addReflectionClass($reflection); + + $this->createDriver()->getMetadataForHierarchy($hierarchy); + } + + /** + * It should allow a custom subject pattern. + */ + public function testCustomSubjectPattern(): void + { + $reflection = new ReflectionClass(); + $reflection->path = 'foo'; + $reflection->class = 'Test'; + + $method = new ReflectionMethod(); + $method->reflectionClass = $reflection; + $method->class = 'Test'; + $method->name = 'foo_bar_Foo'; + $reflection->methods[$method->name] = $method; + $hierarchy = new ReflectionHierarchy(); + $hierarchy->addReflectionClass($reflection); + + $metadata = $this->createDriver('foo_bar_')->getMetadataForHierarchy($hierarchy); + $this->assertCount(1, $metadata->getSubjects()); + $this->assertArrayHasKey('foo_bar_Foo', $metadata->getSubjects()); + } + + private function createDriver($prefix = '^bench'): DriverInterface + { + return new AnnotationDriver( + $this->reflector->reveal(), + $prefix + ); + } +} diff --git a/tests/Unit/Benchmark/Metadata/Driver/AttributeDriverTest.php b/tests/Unit/Benchmark/Metadata/Driver/AttributeDriverTest.php new file mode 100644 index 000000000..bad631624 --- /dev/null +++ b/tests/Unit/Benchmark/Metadata/Driver/AttributeDriverTest.php @@ -0,0 +1,328 @@ +class = 'Test'; + $reflection->path = 'foo'; + $reflection->attributes = $attributes; + $hierarchy = new ReflectionHierarchy(); + $hierarchy->addReflectionClass($reflection); + + $metadata = $this->createDriver()->getMetadataForHierarchy($hierarchy); + + $assertion($metadata); + } + + /** + * @return Generator + */ + public function provideLoadBenchmark(): Generator + { + if ($this->shouldSkip()) { + $this->markTestSkipped('PHP 8 only'); + + return; + } + + yield [ + [ + new Executor('foobar'), + ], + function (BenchmarkMetadata $metadata): void { + self::assertEquals('foobar', $metadata->getExecutor()->getName()); + } + ]; + + yield [ + [ + new Executor('foobar', ['foo' => 'bar']), + ], + function (BenchmarkMetadata $metadata): void { + self::assertEquals(['foo' => 'bar'], $metadata->getExecutor()->getConfig()); + } + ]; + + yield [ + [ + new BeforeClassMethods(['foo','bar']), + ], + function (BenchmarkMetadata $metadata): void { + self::assertEquals(['foo', 'bar'], $metadata->getBeforeClassMethods()); + } + ]; + + yield [ + [ + new AfterClassMethods(['foo','bar']), + ], + function (BenchmarkMetadata $metadata): void { + self::assertEquals(['foo', 'bar'], $metadata->getAfterClassMethods()); + } + ]; + } + /** + * @dataProvider provideLoadSubject + */ + public function testLoadSubject(array $attributes, callable $assertion): void + { + $reflection = new ReflectionClass(); + $reflection->class = 'Test'; + $reflection->path = 'foo'; + $hierarchy = new ReflectionHierarchy(); + $hierarchy->addReflectionClass($reflection); + + $method = new ReflectionMethod(); + $method->reflectionClass = $reflection; + $method->class = 'Test'; + $method->name = 'benchFoo'; + $method->attributes = $attributes; + $reflection->methods[$method->name] = $method; + + $metadata = $this->createDriver()->getMetadataForHierarchy($hierarchy); + $subjects = $metadata->getSubjects(); + $this->assertCount(1, $subjects); + + /** @var SubjectMetadata $metadata */ + $metadata = reset($subjects); + + $assertion($metadata); + } + + /** + * @return Generator + */ + public function provideLoadSubject(): Generator + { + if ($this->shouldSkip()) { + $this->markTestSkipped('PHP 8 only'); + + return; + } + + yield [ + [ + new BeforeMethods(['foobar', 'barfoo']), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals(['foobar', 'barfoo'], $metadata->getBeforeMethods()); + } + ]; + + yield [ + [ + new BeforeMethods('foobar'), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals(['foobar'], $metadata->getBeforeMethods()); + } + ]; + + yield [ + [ + new AfterMethods(['foobar', 'barfoo']), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals(['foobar', 'barfoo'], $metadata->getAfterMethods()); + } + ]; + + yield [ + [ + new Groups(['foobar', 'barfoo']), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals(['foobar', 'barfoo'], $metadata->getGroups()); + } + ]; + + yield [ + [ + new Iterations(12), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals([12], $metadata->getIterations()); + } + ]; + + yield [ + [ + new ParamProviders('one'), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals(['one'], $metadata->getParamProviders()); + } + ]; + + yield [ + [ + new ParamProviders(['one', 'two']), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals(['one','two'], $metadata->getParamProviders()); + } + ]; + + yield [ + [ + new Revs(12), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals([12], $metadata->getRevs()); + } + ]; + + yield [ + [ + new Skip(), + ], + function (SubjectMetadata $metadata): void { + self::assertTrue($metadata->getSkip()); + } + ]; + + yield [ + [ + new Sleep(500), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals(500, $metadata->getSleep()); + } + ]; + + yield [ + [ + new OutputTimeUnit('seconds'), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals('seconds', $metadata->getOutputTimeUnit()); + } + ]; + + yield [ + [ + new OutputTimeUnit('seconds', 12), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals('seconds', $metadata->getOutputTimeUnit()); + self::assertEquals(12, $metadata->getOutputTimePrecision()); + } + ]; + + yield [ + [ + new OutputMode('throughput'), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals('throughput', $metadata->getOutputMode()); + } + ]; + + yield [ + [ + new Warmup(12), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals([12], $metadata->getWarmup()); + } + ]; + + yield [ + [ + new Assert('12 < 12'), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals(['12 < 12'], $metadata->getAssertions()); + } + ]; + + yield [ + [ + new Format('mode(foobar) as ms'), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals('mode(foobar) as ms', $metadata->getFormat()); + } + ]; + + yield [ + [ + new Executor('foobar'), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals('foobar', $metadata->getExecutor()->getName()); + } + ]; + + yield [ + [ + new Executor('foobar', ['foo' => 'bar']), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals(['foo' => 'bar'], $metadata->getExecutor()->getConfig()); + } + ]; + + yield [ + [ + new Timeout(12.1), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals(12.1, $metadata->getTimeout()); + } + ]; + + yield [ + [ + new RetryThreshold(10), + ], + function (SubjectMetadata $metadata): void { + self::assertEquals(10.0, $metadata->getRetryThreshold()); + } + ]; + } + + private function createDriver(): AttributeDriver + { + return new AttributeDriver(); + } + + private function shouldSkip(): bool + { + return PHP_VERSION_ID < 80000; + } +} diff --git a/tests/Unit/Benchmark/Metadata/Driver/ChainDriverTest.php b/tests/Unit/Benchmark/Metadata/Driver/ChainDriverTest.php new file mode 100644 index 000000000..6a2f8034f --- /dev/null +++ b/tests/Unit/Benchmark/Metadata/Driver/ChainDriverTest.php @@ -0,0 +1,43 @@ +prophesize(DriverInterface::class); + $driver2 = $this->prophesize(DriverInterface::class); + + $benchmark1 = new BenchmarkMetadata(__FILE__, self::class); + $benchmark1->setAfterClassMethods(['one']); + $benchmark2 = new BenchmarkMetadata(__FILE__, self::class); + $benchmark2->setAfterClassMethods(['two']); + + $hierarchy = new ReflectionHierarchy([ + new ReflectionClass(__FILE__, self::class) + ]); + + $driver1->getMetadataForHierarchy($hierarchy)->willReturn($benchmark1); + $driver2->getMetadataForHierarchy($hierarchy)->willReturn($benchmark2); + + $chain = new ChainDriver([ + $driver1->reveal(), + $driver2->reveal(), + ]); + + $metadata = $chain->getMetadataForHierarchy($hierarchy); + + self::assertEquals(['one', 'two'], $metadata->getAfterClassMethods()); + } +} diff --git a/tests/Unit/Benchmark/Metadata/Driver/ConfigDriverTest.php b/tests/Unit/Benchmark/Metadata/Driver/ConfigDriverTest.php new file mode 100644 index 000000000..9cc19a779 --- /dev/null +++ b/tests/Unit/Benchmark/Metadata/Driver/ConfigDriverTest.php @@ -0,0 +1,83 @@ + + */ + private $innerDriver; + + /** + * @var ReflectionHierarchy + */ + private $hierarchy; + + protected function setUp(): void + { + $this->hierarchy = new ReflectionHierarchy([]); + $this->innerDriver = $this->prophesize(DriverInterface::class); + } + + /** + * @dataProvider provideDriver + */ + public function testDriver(array $config, callable $assertion): void + { + $driver = Invoke::new(ConfigDriver::class, array_merge([ + 'innerDriver' => $this->innerDriver->reveal(), + ], $config)); + + $metadata = new BenchmarkMetadata(__DIR__, 'Foo'); + $metadata->getOrCreateSubject(self::EXAMPLE_SUBJECT); + + $this->innerDriver->getMetadataForHierarchy($this->hierarchy)->willReturn($metadata); + + assert($driver instanceof DriverInterface); + $assertion($driver->getMetadataForHierarchy($this->hierarchy)->getOrCreateSubject(self::EXAMPLE_SUBJECT)); + } + + public function provideDriver(): Generator + { + yield [ + [ + 'assert' => ['example_assert'], + 'executor' => 'example_executor', + 'format' => 'example_format', + 'iterations' => [5], + 'mode' => 'example_mode', + 'timeUnit' => 'example_time_unit', + 'revs' => [10], + 'timeout' => 20.1, + 'warmup' => [30], + 'retryThreshold' => 5.0, + ], + function (SubjectMetadata $subject): void { + self::assertEquals(['example_assert'], $subject->getAssertions()); + self::assertEquals('example_executor', $subject->getExecutor()->getName()); + self::assertEquals('example_format', $subject->getFormat()); + self::assertEquals([5], $subject->getIterations()); + self::assertEquals('example_mode', $subject->getOutputMode()); + self::assertEquals('example_time_unit', $subject->getOutputTimeUnit()); + self::assertEquals([10], $subject->getRevs()); + self::assertEquals(20.1, $subject->getTimeout()); + self::assertEquals([30], $subject->getWarmup()); + self::assertEquals(5.0, $subject->getRetryThreshold()); + } + ]; + } +} diff --git a/tests/Unit/Benchmark/Metadata/MetadataFactoryTest.php b/tests/Unit/Benchmark/Metadata/MetadataFactoryTest.php new file mode 100644 index 000000000..25271e4e2 --- /dev/null +++ b/tests/Unit/Benchmark/Metadata/MetadataFactoryTest.php @@ -0,0 +1,255 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Benchmark\Metadata; + +use InvalidArgumentException; +use PhpBench\Benchmark\Metadata\BenchmarkMetadata; +use PhpBench\Benchmark\Metadata\DriverInterface; +use PhpBench\Benchmark\Metadata\MetadataFactory; +use PhpBench\Benchmark\Metadata\SubjectMetadata; +use PhpBench\Reflection\ReflectionClass; +use PhpBench\Reflection\ReflectionHierarchy; +use PhpBench\Reflection\ReflectorInterface; +use PhpBench\Tests\TestCase; +use PhpBench\Tests\Util\TestUtil; +use Prophecy\Prophecy\ObjectProphecy; + +class MetadataFactoryTest extends TestCase +{ + const FNAME = 'fname'; + const PATH = '/path/to'; + + private $factory; + + /** + * @var MetadataFactoryTest + */ + private $reflector; + + /** + * @var ObjectProphecy + */ + private $driver; + + /** + * @var ObjectProphecy + */ + private $hierarchy; + + /** + * @var ObjectProphecy + */ + private $reflection; + + /** + * @var ObjectProphecy + */ + private $metadata; + + /** + * @var ObjectProphecy + */ + private $subjectMetadata; + + protected function setUp(): void + { + $this->reflector = $this->prophesize(ReflectorInterface::class); + $this->driver = $this->prophesize(DriverInterface::class); + $this->factory = new MetadataFactory( + $this->reflector->reveal(), + $this->driver->reveal() + ); + + $this->hierarchy = $this->prophesize(ReflectionHierarchy::class); + $this->hierarchy->reveal()->class = 'Class'; + $this->reflection = $this->prophesize(ReflectionClass::class); + $this->metadata = $this->prophesize(BenchmarkMetadata::class); + $this->subjectMetadata = $this->prophesize(SubjectMetadata::class); + + $this->reflector->reflect(self::FNAME)->willReturn($this->hierarchy->reveal()); + $this->driver->getMetadataForHierarchy($this->hierarchy->reveal())->willReturn($this->metadata->reveal()); + $this->reflection->abstract = false; + $this->hierarchy->getTop()->willReturn($this->reflection->reveal()); + } + + /** + * It can retrieve the metadata for a file containing a class. + */ + public function testGetMetadataForFile(): void + { + $this->hierarchy->isEmpty()->willReturn(false); + $this->metadata->getSubjects()->willReturn([]); + TestUtil::configureBenchmarkMetadata($this->metadata); + $metadata = $this->factory->getMetadataForFile(self::FNAME); + $this->assertInstanceOf('PhpBench\Benchmark\Metadata\BenchmarkMetadata', $metadata); + } + + /** + * It will return a benchmark populated with subjects. + */ + public function testWithSubjects(): void + { + $this->hierarchy->isEmpty()->willReturn(false); + $this->metadata->getSubjects()->willReturn([ + $this->subjectMetadata->reveal(), + ]); + TestUtil::configureBenchmarkMetadata($this->metadata, [ + 'path' => self::PATH, + ]); + TestUtil::configureSubjectMetadata($this->subjectMetadata); + + $metadata = $this->factory->getMetadataForFile(self::FNAME); + $this->assertInstanceOf('PhpBench\Benchmark\Metadata\BenchmarkMetadata', $metadata); + $this->assertIsArray($metadata->getSubjects()); + $this->assertCount(1, $metadata->getSubjects()); + } + + /** + * It should throw an exception if a before/after method does not exist on the benchmark. + * + */ + public function testValidationBeforeMethodsBenchmark(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Unknown before'); + $this->hierarchy->isEmpty()->willReturn(false); + TestUtil::configureBenchmarkMetadata($this->metadata, [ + 'beforeClassMethods' => ['beforeMe'], + ]); + $this->hierarchy->hasMethod('beforeMe')->willReturn(false); + $this->hierarchy->hasStaticMethod('beforeMe')->willReturn(true); + + $this->factory->getMetadataForFile(self::FNAME); + } + + /** + * It should throw an exception if a before class method is not static. + * + */ + public function testValidationBeforeClassMethodsBenchmarkNotStatic(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('must be static in benchmark class "TestClass"'); + $this->hierarchy->isEmpty()->willReturn(false); + $this->reflection->class = 'TestClass'; + TestUtil::configureBenchmarkMetadata($this->metadata, [ + 'beforeClassMethods' => ['beforeMe'], + ]); + $this->hierarchy->hasMethod('beforeMe')->willReturn(true); + $this->hierarchy->hasStaticMethod('beforeMe')->willReturn(false); + + $this->factory->getMetadataForFile(self::FNAME); + } + + /** + * It should throw an exception if a before method IS static. + * + */ + public function testValidationBeforeMethodsBenchmarkIsStatic(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('before method "beforeMe" must not be static in benchmark class "TestClass"'); + $this->hierarchy->isEmpty()->willReturn(false); + $this->reflection->class = 'TestClass'; + TestUtil::configureBenchmarkMetadata($this->metadata, []); + $this->metadata->getSubjects()->willReturn([ + $this->subjectMetadata->reveal(), + ]); + TestUtil::configureSubjectMetadata($this->subjectMetadata, [ + 'beforeMethods' => ['beforeMe'], + ]); + $this->hierarchy->hasMethod('beforeMe')->willReturn(true); + $this->hierarchy->hasStaticMethod('beforeMe')->willReturn(true); + + $this->factory->getMetadataForFile(self::FNAME); + } + + /** + * It should throw an exception if a before/after method does not exist on the subject. + * + */ + public function testValidationBeforeMethodsSubject(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Unknown before method "beforeMe" in benchmark class "TestClass"'); + $this->hierarchy->isEmpty()->willReturn(false); + $this->reflection->class = 'TestClass'; + TestUtil::configureBenchmarkMetadata($this->metadata, []); + $this->metadata->getSubjects()->willReturn([ + $this->subjectMetadata->reveal(), + ]); + TestUtil::configureSubjectMetadata($this->subjectMetadata, [ + 'beforeMethods' => ['beforeMe'], + ]); + $this->hierarchy->hasMethod('beforeMe')->willReturn(false); + + $this->factory->getMetadataForFile(self::FNAME); + } + + /** + * It should throw an exception if an after method does not exist. + * + */ + public function testValidationAfterMethods(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Unknown after'); + $this->hierarchy->isEmpty()->willReturn(false); + TestUtil::configureBenchmarkMetadata($this->metadata, [ + ]); + $this->metadata->getSubjects()->willReturn([ + $this->subjectMetadata->reveal(), + ]); + TestUtil::configureSubjectMetadata($this->subjectMetadata, [ + 'afterMethods' => ['afterMe'], + ]); + $this->hierarchy->hasMethod('afterMe')->willReturn(false); + + $this->factory->getMetadataForFile(self::FNAME); + } + + /** + * It should return null if the class hierarchy is empty. + */ + public function testEmptyClassHierachy(): void + { + $this->hierarchy->isEmpty()->willReturn(true); + $metadata = $this->factory->getMetadataForFile(self::FNAME); + $this->assertNull($metadata); + } + + /** + * It should throw an exception if the parameters are not in a valid format. + * + */ + public function testInvalidParameters(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Each parameter set must be an array, got "string" for TestBench::benchTest'); + $this->hierarchy->isEmpty()->willReturn(false); + $this->metadata->getSubjects()->willReturn([ + $this->subjectMetadata->reveal(), + ]); + TestUtil::configureBenchmarkMetadata($this->metadata, [ + 'class' => 'TestBench', + 'path' => self::PATH, + ]); + TestUtil::configureSubjectMetadata($this->subjectMetadata, [ + 'name' => 'benchTest', + 'paramProviders' => ['param1'], + ]); + $this->reflector->getParameterSets(self::PATH, ['param1'])->willReturn(['asd' => 'bar']); + + $this->factory->getMetadataForFile(self::FNAME); + } +} diff --git a/tests/Unit/Benchmark/Metadata/SubjectMetadataTest.php b/tests/Unit/Benchmark/Metadata/SubjectMetadataTest.php new file mode 100644 index 000000000..4a1f6dcd5 --- /dev/null +++ b/tests/Unit/Benchmark/Metadata/SubjectMetadataTest.php @@ -0,0 +1,231 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Benchmark\Metadata; + +use Generator; +use PhpBench\Benchmark\Metadata\BenchmarkMetadata; +use PhpBench\Benchmark\Metadata\ExecutorMetadata; +use PhpBench\Benchmark\Metadata\SubjectMetadata; +use PhpBench\Tests\TestCase; + +class SubjectMetadataTest extends TestCase +{ + private $subject; + + /** + * @var BenchmarkMetadata + */ + private $benchmark; + + protected function setUp(): void + { + $this->benchmark = $this->prophesize(BenchmarkMetadata::class); + $this->subject = new SubjectMetadata($this->benchmark->reveal(), 'subjectOne', 0); + } + + /** + * It should say if it is in a set of groups. + */ + public function testInGroups(): void + { + $this->subject->setGroups(['one', 'two', 'three']); + $result = $this->subject->inGroups(['five', 'two', 'six']); + $this->assertTrue($result); + + $result = $this->subject->inGroups(['eight', 'seven']); + $this->assertFalse($result); + + $result = $this->subject->inGroups([]); + $this->assertFalse($result); + } + + /** + * @dataProvider provideMerge + */ + public function testMerge(callable $confgurator, callable $assertion): void + { + $subject1 = new SubjectMetadata( + $this->benchmark->reveal(), + 'one' + ); + $subject2 = new SubjectMetadata( + $this->benchmark->reveal(), + 'one' + ); + + $confgurator($subject1, $subject2); + $subject1->merge($subject2); + $assertion($subject1); + } + + /** + * @return Generator + */ + public function provideMerge(): Generator + { + yield [ + function (SubjectMetadata $subject1, SubjectMetadata $subject2): void { + $subject1->setGroups(['one', 'two']); + $subject2->setGroups(['three', 'four']); + }, + function (SubjectMetadata $merged): void { + self::assertEquals([ + 'one', + 'two', + 'three', + 'four' + ], $merged->getGroups()); + } + ]; + + yield [ + function (SubjectMetadata $subject1, SubjectMetadata $subject2): void { + $subject1->setBeforeMethods(['one', 'two']); + $subject2->setBeforeMethods(['three', 'four']); + }, + function (SubjectMetadata $merged): void { + self::assertEquals(['one', 'two', 'three', 'four'], $merged->getBeforeMethods()); + } + ]; + + yield [ + function (SubjectMetadata $subject1, SubjectMetadata $subject2): void { + $subject1->setAfterMethods(['one', 'two']); + $subject2->setAfterMethods(['three', 'four']); + }, + function (SubjectMetadata $merged): void { + self::assertEquals(['one', 'two', 'three', 'four'], $merged->getAfterMethods()); + } + ]; + + yield [ + function (SubjectMetadata $subject1, SubjectMetadata $subject2): void { + $subject1->setParamProviders(['one', 'two']); + $subject2->setParamProviders(['three', 'four']); + }, + function (SubjectMetadata $merged): void { + self::assertEquals(['one', 'two', 'three', 'four'], $merged->getParamProviders()); + } + ]; + + yield [ + function (SubjectMetadata $subject1, SubjectMetadata $subject2): void { + $subject1->setIterations([1, 2]); + $subject2->setIterations([3, 4]); + }, + function (SubjectMetadata $merged): void { + self::assertEquals([1, 2, 3, 4], $merged->getIterations()); + } + ]; + + yield [ + function (SubjectMetadata $subject1, SubjectMetadata $subject2): void { + $subject1->setRevs([1, 2]); + $subject2->setRevs([3, 4]); + }, + function (SubjectMetadata $merged): void { + self::assertEquals([1, 2, 3, 4], $merged->getRevs()); + } + ]; + + yield [ + function (SubjectMetadata $subject1, SubjectMetadata $subject2): void { + $subject1->setSkip(false); + $subject2->setSkip(true); + }, + function (SubjectMetadata $merged): void { + self::assertTrue($merged->getSkip()); + } + ]; + + yield [ + function (SubjectMetadata $subject1, SubjectMetadata $subject2): void { + $subject1->setSleep(10); + $subject2->setSleep(20); + }, + function (SubjectMetadata $merged): void { + self::assertEquals(20, $merged->getSleep()); + } + ]; + + yield [ + function (SubjectMetadata $subject1, SubjectMetadata $subject2): void { + $subject1->setOutputTimeUnit('ms'); + $subject2->setOutputTimeUnit('s'); + }, + function (SubjectMetadata $merged): void { + self::assertEquals('s', $merged->getOutputTimeUnit()); + } + ]; + + yield [ + function (SubjectMetadata $subject1, SubjectMetadata $subject2): void { + $subject1->setOutputTimePrecision(2); + $subject2->setOutputTimePrecision(4); + }, + function (SubjectMetadata $merged): void { + self::assertEquals(4, $merged->getOutputTimePrecision()); + } + ]; + + yield [ + function (SubjectMetadata $subject1, SubjectMetadata $subject2): void { + $subject1->setOutputMode('throughput'); + $subject2->setOutputMode('asd'); + }, + function (SubjectMetadata $merged): void { + self::assertEquals('asd', $merged->getOutputMode()); + } + ]; + + yield [ + function (SubjectMetadata $subject1, SubjectMetadata $subject2): void { + $subject1->setWarmup([12]); + $subject2->setWarmup([13]); + }, + function (SubjectMetadata $merged): void { + self::assertEquals([12,13], $merged->getWarmup()); + } + ]; + + yield [ + function (SubjectMetadata $subject1, SubjectMetadata $subject2): void { + $subject1->setAssertions(['1']); + $subject2->setAssertions(['2']); + }, + function (SubjectMetadata $merged): void { + self::assertEquals(['1','2'], $merged->getAssertions()); + } + ]; + + yield [ + function (SubjectMetadata $subject1, SubjectMetadata $subject2): void { + $subject1->setExecutor(new ExecutorMetadata('foobar', ['1'])); + $subject2->setExecutor(new ExecutorMetadata('barfoo', ['2'])); + }, + function (SubjectMetadata $merged): void { + self::assertEquals(new ExecutorMetadata('barfoo', ['2']), $merged->getExecutor()); + } + ]; + + yield [ + function (SubjectMetadata $subject1, SubjectMetadata $subject2): void { + $subject1->setTimeout(2); + $subject2->setTimeout(4); + }, + function (SubjectMetadata $merged): void { + self::assertEquals(4, $merged->getTimeout()); + } + ]; + } +} diff --git a/tests/Unit/Benchmark/Metadata/classes/Test.php b/tests/Unit/Benchmark/Metadata/classes/Test.php new file mode 100644 index 000000000..4011bfa03 --- /dev/null +++ b/tests/Unit/Benchmark/Metadata/classes/Test.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Benchmark\Metadata\classes; + +use PhpBench\Benchmark\Metadata\Annotations as PhpBench; + +/** + * Class used to test the annotation import. + */ +class Test +{ + /** + * @PhpBench\Subject() + */ + public function test(): void + { + } +} diff --git a/tests/Unit/Benchmark/RunnerConfigTest.php b/tests/Unit/Benchmark/RunnerConfigTest.php new file mode 100644 index 000000000..929abb938 --- /dev/null +++ b/tests/Unit/Benchmark/RunnerConfigTest.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Benchmark; + +use InvalidArgumentException; +use PhpBench\Benchmark\RunnerConfig; +use PhpBench\Tests\TestCase; + +class RunnerConfigTest extends TestCase +{ + const TEST_TAG_NAME = 'tag_name'; + const TEST_FILTERS = ['filter_one', 'filter_two']; + const TEST_ITERATIONS = [5]; + const TEST_REVOLUTIONS = [6]; + const TEST_PARAMETERS = ['one' => 1]; + const TEST_SLEEP = 100; + const TEST_RETRY_THRESHOLD = 10; + const TEST_WARMUP = [10]; + const TEST_GROUPS = ['group1']; + const TEST_OUTPUT_TIME_UNIT = 'milliseconds'; + const TEST_OUTPUT_TIME_PRECISION = 2; + const TEST_EXECUTOR = 'microtimre'; + const TEST_STOP_ON_ERROR = true; + const TEST_ASSERTIONS = ['x > y']; + + /** + * It should throw an exception if the retry threshold is less than zero. + * + */ + public function testRetryLessThanZetro(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('must be greater'); + RunnerConfig::create() + ->withRetryThreshold(-1); + } + + /** + * It should throw an exception if the revolutions are less than zero. + * + */ + public function testRevolutionsLessThanZero(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('greater than'); + RunnerConfig::create()->withRevolutions([-1]); + } + + public function testBuild(): void + { + $config = RunnerConfig::create() + ->withTag(self::TEST_TAG_NAME) + ->withFilters(self::TEST_FILTERS) + ->withIterations(self::TEST_ITERATIONS) + ->withRevolutions(self::TEST_REVOLUTIONS) + ->withParameters(self::TEST_PARAMETERS) + ->withSleep(self::TEST_SLEEP) + ->withWarmup(self::TEST_WARMUP) + ->withGroups(self::TEST_GROUPS) + ->withOutputTimeUnit(self::TEST_OUTPUT_TIME_UNIT) + ->withOutputTimePrecision(self::TEST_OUTPUT_TIME_PRECISION) + ->withExecutor(self::TEST_EXECUTOR) + ->withStopOnError(self::TEST_STOP_ON_ERROR) + ->withAssertions(self::TEST_ASSERTIONS) + ->withRetryThreshold(self::TEST_RETRY_THRESHOLD); + + $this->assertEquals(self::TEST_TAG_NAME, $config->getTag()); + $this->assertEquals(self::TEST_ITERATIONS, $config->getIterations()); + $this->assertEquals(self::TEST_REVOLUTIONS, $config->getRevolutions()); + $this->assertEquals([[self::TEST_PARAMETERS]], $config->getParameterSets()); + $this->assertEquals(self::TEST_SLEEP, $config->getSleep()); + $this->assertEquals(self::TEST_RETRY_THRESHOLD, $config->getRetryThreshold()); + } + + /** + * Default should be used if no explict value set in the context. + */ + public function testDefaults(): void + { + $config = RunnerConfig::create(); + $this->assertEquals([self::TEST_RETRY_THRESHOLD], $config->getIterations([self::TEST_RETRY_THRESHOLD])); + $this->assertEquals([self::TEST_RETRY_THRESHOLD], $config->getRevolutions([self::TEST_RETRY_THRESHOLD])); + $this->assertEquals(self::TEST_RETRY_THRESHOLD, $config->getRetryThreshold(self::TEST_RETRY_THRESHOLD)); + } + + /** + * Defaults should be ignored if explicit values are set in the context. + */ + public function testOverride(): void + { + $config = RunnerConfig::create() + ->withIterations(self::TEST_ITERATIONS) + ->withRevolutions(self::TEST_REVOLUTIONS) + ->withRetryThreshold(self::TEST_RETRY_THRESHOLD); + + $this->assertEquals(self::TEST_ITERATIONS, $config->getIterations([20])); + $this->assertEquals(self::TEST_REVOLUTIONS, $config->getRevolutions([30])); + $this->assertEquals(self::TEST_RETRY_THRESHOLD, $config->getRetryThreshold(40)); + } + + /** + * The overridden parameter sets should be nested in an array of an array. + */ + public function testGetParameterSets(): void + { + $config = RunnerConfig::create() + ->withParameters([ + 'nb_elements' => 10, + ]); + + $this->assertEquals( + [ + [ + [ + 'nb_elements' => 10, + ], + ], + ], + $config->getParameterSets( + [ + [ + 'nb_elements' => 5, + ], + ] + ) + ); + } +} diff --git a/tests/Unit/Benchmark/RunnerTest.php b/tests/Unit/Benchmark/RunnerTest.php new file mode 100644 index 000000000..e253223a9 --- /dev/null +++ b/tests/Unit/Benchmark/RunnerTest.php @@ -0,0 +1,442 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Benchmark; + +use Exception; +use Generator; +use InvalidArgumentException; +use PhpBench\Assertion\AssertionProcessor; +use PhpBench\Benchmark\Exception\RetryLimitReachedException; +use PhpBench\Benchmark\Metadata\BenchmarkMetadata; +use PhpBench\Benchmark\Metadata\SubjectMetadata; +use PhpBench\Benchmark\Runner; +use PhpBench\Benchmark\RunnerConfig; +use PhpBench\Environment\Information; +use PhpBench\Environment\Supplier; +use PhpBench\Executor; +use PhpBench\Executor\Benchmark\TestExecutor; +use PhpBench\Executor\ExecutionResults; +use PhpBench\Model\Result\MemoryResult; +use PhpBench\Model\Result\TimeResult; +use PhpBench\Model\Suite; +use PhpBench\Registry\Config; +use PhpBench\Registry\ConfigurableRegistry; +use PhpBench\Tests\TestCase; +use PhpBench\Tests\Util\TestUtil; +use Prophecy\Prophecy\ObjectProphecy; + +class RunnerTest extends TestCase +{ + const TEST_PATH = 'path/to/bench.php'; + + /** + * @var ObjectProphecy + */ + private $suite; + + /** + * @var ObjectProphecy + */ + private $benchmark; + + /** + * @var TestExecutor + */ + private $executor; + + /** + * @var ObjectProphecy + */ + private $executorRegistry; + + /** + * @var ObjectProphecy + */ + private $assertion; + + /** + * @var Config + */ + private $executorConfig; + + /** + * @var ObjectProphecy + */ + private $envSupplier; + + /** + * @var array + */ + private $informations; + + /** + * @var Runner + */ + private $runner; + + protected function setUp(): void + { + $this->suite = $this->prophesize(Suite::class); + $this->benchmark = $this->prophesize(BenchmarkMetadata::class); + $this->executorRegistry = $this->prophesize(ConfigurableRegistry::class); + $this->executor = new TestExecutor(); + $this->assertion = $this->prophesize(AssertionProcessor::class); + $this->executorConfig = $this->setUpExecutorConfig([]); + $this->envSupplier = $this->prophesize(Supplier::class); + $this->informations = []; + $this->envSupplier->getInformations()->willReturn($this->informations); + + $this->runner = new Runner( + $this->executorRegistry->reveal(), + $this->envSupplier->reveal(), + $this->assertion->reveal(), + null, + null + ); + + $this->executorRegistry->getService('remote')->willReturn( + $this->executor + ); + } + + /** + * @dataProvider provideRunner + */ + public function testRunner($iterations, $revs, array $parameters, $assertionCallbacks): void + { + $subject = new SubjectMetadata($this->benchmark->reveal(), 'name'); + $subject->setIterations($iterations); + $subject->setBeforeMethods(['beforeFoo']); + $subject->setParameterSets([[$parameters]]); + $subject->setRevs($revs); + + TestUtil::configureBenchmarkMetadata($this->benchmark); + + $this->benchmark->getSubjects()->willReturn([ + $subject, + ]); + + $suite = $this->runner->run([ $this->benchmark->reveal() ], RunnerConfig::create()->withTag('context')); + + $this->assertInstanceOf('PhpBench\Model\Suite', $suite); + $this->assertNoErrors($suite); + + self::assertEquals((int)(count($revs) * array_sum($iterations)), $this->executor->getExecutedContextCount()); + + foreach ($assertionCallbacks as $callback) { + $callback($this, $suite); + } + } + + /** + * @return Generator + */ + public function provideRunner(): Generator + { + yield [ + [1], + [1], + [], + function ($test, $suite): void { + $test->assertEquals(1, $suite->getSummary()->getNbIterations()); + }, + ]; + + yield [ + [1], + [3], + [], + function ($test, $suite): void { + $test->assertEquals(1, $iterations = $suite->getIterations()); + $iteration = reset($iterations); + $test->assertEquals(3, $iteration->getVariant()->getRevolutions()); + }, + ]; + + yield [ + [4], + [3], + [], + function ($test, $suite): void { + $test->assertEquals(4, $iterations = $suite->getIterations()); + $iteration = reset($iterations); + $test->assertEquals(3, $iteration->getVariant()->getRevolutions()); + }, + ]; + + yield [ + [1], + [1], + ['one' => 'two', 'three' => 'four'], + function ($test, $suite): void { + $test->assertEquals(1, $iterations = $suite->getIterations()); + $iteration = reset($iterations); + $parameters = $iteration->getVariant()->getParameterSet(); + $test->assertEquals('two', $parameters['one']); + $test->assertEquals('four', $parameters['four']); + }, + ]; + + yield [ + [1], + [1], + ['one', 'two'], + function ($test, $suite): void { + $test->assertEquals(1, $iterations = $suite->getIterations()); + $iteration = reset($iterations); + $parameters = $iteration->getVariant()->getParameterSet(); + $test->assertEquals('one', $parameters[0]); + $test->assertEquals('two', $parameters[1]); + }, + ]; + + yield [ + [1], + [1], + ['one' => ['three' => 'four']], + function ($test, $suite): void { + $test->assertEquals(1, $iterations = $suite->getIterations()); + $iteration = reset($iterations); + $parameters = $iteration->getVariant()->getParameterSet(); + $test->assertEquals(['three', 'four'], $parameters[0]); + }, + ]; + } + + /** + * It should skip subjects that should be skipped. + */ + public function testSkip(): void + { + $subject1 = new SubjectMetadata($this->benchmark->reveal(), 'one'); + $subject2 = new SubjectMetadata($this->benchmark->reveal(), 'two'); + $subject3 = new SubjectMetadata($this->benchmark->reveal(), 'three'); + + $subject2->setSkip(true); + $this->benchmark->getSubjects()->willReturn([ + $subject1, + $subject2, + $subject3, + ]); + TestUtil::configureBenchmarkMetadata($this->benchmark); + + $suite = $this->runner->run([ $this->benchmark->reveal() ], RunnerConfig::create()); + + $this->assertInstanceOf('PhpBench\Model\Suite', $suite); + $this->assertNoErrors($suite); + $this->assertEquals(2, $suite->getSummary()->getNbSubjects()); + $subjects = $suite->getSubjects(); + + $this->assertEquals('one', $subjects[0]->getName()); + $this->assertEquals('three', $subjects[1]->getName()); + } + + public function testSleep(): void + { + $subject = new SubjectMetadata($this->benchmark->reveal(), 'name'); + $subject->setSleep(10000); + $this->benchmark->getSubjects()->willReturn([ + $subject, + ]); + TestUtil::configureBenchmarkMetadata($this->benchmark); + + $start = microtime(true); + $suite = $this->runner->run([ $this->benchmark->reveal() ], RunnerConfig::create()); + $end = microtime(true); + self::assertGreaterThanOrEqual(10000, ($end - $start) * 1E6, 'Should take at least 10 milliseconds'); + } + + public function testOverrideMetadata(): void + { + $subject = new SubjectMetadata($this->benchmark->reveal(), 'name'); + $subject->setSleep(50); + $this->benchmark->getSubjects()->willReturn([ + $subject, + ]); + TestUtil::configureBenchmarkMetadata($this->benchmark); + + $suite = $this->runner->run([ $this->benchmark->reveal() ], RunnerConfig::create() + ->withSleep(100) + ->withRetryThreshold(12) + ->withWarmup([66]) + ->withRevolutions([88])); + + $this->assertNoErrors($suite); + + self::assertEquals(88, $this->executor->lastContextOrException()->getRevolutions()); + self::assertEquals(66, $this->executor->lastContextOrException()->getWarmup()); + } + + /** + * It should serialize the retry threshold. + */ + public function testRetryThresholdExceeded(): void + { + $this->expectException(RetryLimitReachedException::class); + + $subject = new SubjectMetadata($this->benchmark->reveal(), 'name'); + $subject->setIterations([3]); + $subject->setRetryLimit(10); + + $this->benchmark->getSubjects()->willReturn([ + $subject, + ]); + + TestUtil::configureBenchmarkMetadata($this->benchmark); + + $this->setUpExecutorConfig([ + 'results' => [ + new TimeResult(1), + new TimeResult(1), + new TimeResult(1000), + ], + ]); + + $this->runner->run( + [ $this->benchmark->reveal() ], + RunnerConfig::create()->withRetryThreshold(10) + ); + } + + public function testRetryThresholdMet(): void + { + $subject = new SubjectMetadata($this->benchmark->reveal(), 'name'); + $subject->setIterations([3]); + $subject->setRetryLimit(10); + + $this->benchmark->getSubjects()->willReturn([ + $subject, + ]); + + TestUtil::configureBenchmarkMetadata($this->benchmark); + + $this->setUpExecutorConfig([ + 'results' => [ + new TimeResult(10), + new TimeResult(10), + new TimeResult(10), + ], + ]); + + $this->runner->run( + [ $this->benchmark->reveal() ], + RunnerConfig::create()->withRetryThreshold(10) + ); + + $this->addToAssertionCount(1); // no exception = retry limit not exceeded + } + + /** + * It should call the before and after class methods. + */ + public function testBeforeAndAfterClass(): void + { + TestUtil::configureBenchmarkMetadata($this->benchmark, [ + 'beforeClassMethods' => ['afterClass'], + 'afterClassMethods' => ['beforeClass'], + ]); + + $this->benchmark->getSubjects()->willReturn([]); + + $this->runner->run([ $this->benchmark->reveal() ], RunnerConfig::create()); + self::assertFalse($this->executor->hasHealthBeenChecked()); + self::assertTrue($this->executor->hasMethodBeenExecuted('afterClass')); + self::assertTrue($this->executor->hasMethodBeenExecuted('beforeClass')); + } + + /** + * It should handle exceptions thrown by the executor. + * It should handle nested exceptions. + */ + public function testHandleExceptions(): void + { + $subject = new SubjectMetadata($this->benchmark->reveal(), 'name'); + $this->benchmark->getSubjects()->willReturn([ + $subject, + ]); + + TestUtil::configureBenchmarkMetadata($this->benchmark); + + $this->setUpExecutorConfig([ + 'exception' => new Exception('Foobar', 0, new InvalidArgumentException('Barfoo')) + ]); + + $suite = $this->runner->run([ $this->benchmark->reveal() ], RunnerConfig::create()); + + $errorStacks = $suite->getErrorStacks(); + $this->assertCount(1, $errorStacks); + $errorStack = iterator_to_array(reset($errorStacks)); + $this->assertCount(2, $errorStack); + $this->assertEquals('Exception', $errorStack[0]->getClass()); + $this->assertEquals('InvalidArgumentException', $errorStack[1]->getClass()); + $this->assertEquals('Foobar', $errorStack[0]->getMessage()); + $this->assertEquals('Barfoo', $errorStack[1]->getMessage()); + } + + /** + * It should add environmental information to the DOM. + */ + public function testEnvironment(): void + { + $informations = [ + 'hello' => new Information('hello', ['say' => 'goodbye']) + ]; + $this->envSupplier->getInformations()->willReturn($informations); + + $subject = new SubjectMetadata($this->benchmark->reveal(), 'name'); + $this->benchmark->getSubjects()->willReturn([ + $subject, + ]); + + TestUtil::configureBenchmarkMetadata($this->benchmark); + $suite = $this->runner->run([ $this->benchmark->reveal() ], RunnerConfig::create()); + $envInformations = $suite->getEnvInformations(); + $this->assertSame((array) $informations, (array) $envInformations); + } + + private function assertNoErrors(Suite $suite): void + { + if ($errorStacks = $suite->getErrorStacks()) { + $errorStack = reset($errorStacks); + $this->fail('Runner encountered error: ' . $errorStack->getTop()->getMessage()); + } + + if ($errorStacks = $suite->getErrorStacks()) { + $errorStack = reset($errorStacks); + $this->fail('Runner encountered error: ' . $errorStack->getTop()->getMessage()); + } + } + + private function exampleResults(): ExecutionResults + { + return ExecutionResults::fromResults( + new TimeResult(10), + new MemoryResult(10, 10, 10) + ); + } + + /** + * @param array + */ + private function setUpExecutorConfig(array $config = []): void + { + $this->executorConfig = new Config('remote', array_merge([ + 'exception' => null, + 'executor' => 'remote', + 'results' => [TimeResult::fromArray(['net' => 1])] + ], $config)); + $this->executorRegistry->getConfig('remote')->willReturn( + $this->executorConfig + ); + } +} diff --git a/tests/Unit/Benchmark/SamplerManagerTest.php b/tests/Unit/Benchmark/SamplerManagerTest.php new file mode 100644 index 000000000..610c45f90 --- /dev/null +++ b/tests/Unit/Benchmark/SamplerManagerTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Benchmark; + +use PhpBench\Benchmark\SamplerManager; +use PhpBench\Tests\TestCase; + +/** + * @\PhpBench\Benchmark\Metadata\Annotations\BeforeMethods({"setUp"}) + */ +class SamplerManagerTest extends TestCase +{ + private $manager; + + public static $callCount = false; + + protected function setUp(): void + { + $this->manager = new SamplerManager(); + } + + /** + * It should throw an exception if a sampler callable name already exists. + * + */ + public function testRegisterTwice(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Baseline callable "foo" has already been registered.'); + $this->manager->addSamplerCallable('foo', __CLASS__ . '::samplerExample'); + $this->manager->addSamplerCallable('foo', __CLASS__ . '::samplerExample'); + } + + /** + * It should measure the mean time taken to execute a callable. + * + * @\PhpBench\Benchmark\Metadata\Annotations\Subject() + * @\PhpBench\Benchmark\Metadata\Annotations\Iterations(100) + */ + public function testCallable(): void + { + static::$callCount = 0; + $this->manager->addSamplerCallable('foo', __CLASS__ . '::samplerExample'); + $this->manager->sample('foo', 100); + $this->assertEquals(100, static::$callCount); + } + + /** + * It should throw an exception if the callable is not callable (string). + * + */ + public function testCallableNotCallable(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Given sampler "foo" callable "does_not_exist" is not callable.'); + $this->manager->addSamplerCallable('foo', 'does_not_exist'); + $this->manager->sample('foo', 100); + } + + /** + * It should throw an exception if the callable is not callable (object). + * + */ + public function testCallableNotCallableObject(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Given sampler "foo" callable "object" is not callable.'); + $this->manager->addSamplerCallable('foo', new \stdClass()); + $this->manager->sample('foo', 100); + } + + public static function samplerExample($revs): void + { + self::$callCount = $revs; + } +} diff --git a/tests/Unit/Benchmark/findertest/AbstractBench.php b/tests/Unit/Benchmark/findertest/AbstractBench.php new file mode 100644 index 000000000..4e33eb297 --- /dev/null +++ b/tests/Unit/Benchmark/findertest/AbstractBench.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +abstract class AbstractBench +{ +} diff --git a/tests/Unit/Benchmark/findertest/FooCase2Bench.php b/tests/Unit/Benchmark/findertest/FooCase2Bench.php new file mode 100644 index 000000000..000edd452 --- /dev/null +++ b/tests/Unit/Benchmark/findertest/FooCase2Bench.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +class FooTestCase2Bench +{ +} diff --git a/tests/Unit/Benchmark/findertest/FooCaseBench.php b/tests/Unit/Benchmark/findertest/FooCaseBench.php new file mode 100644 index 000000000..20ec467e4 --- /dev/null +++ b/tests/Unit/Benchmark/findertest/FooCaseBench.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +use PhpBench\BenchIteration; + +class FooCaseBench +{ + public function provideNodes() + { + return [ + [ + 'nb_nodes' => 10, + 'template' => 'arg', + ], + [ + 'nb_nodes' => 100, + 'template2' => 'arg', + ], + [ + 'nb_nodes' => 1000, + 'template3' => 'arg', + ], + ]; + } + + public function provideColumns() + { + return [ + [ + 'columns' => '*', + ], + [ + 'columns' => 'title', + ], + [ + 'columns' => 'title, body', + ], + [ + 'columns' => 'title, body, foobar, title, body, foobar', + ], + ]; + } + + public function beforeSelectSql(BenchIteration $iteration): void + { + } + + /** + * @BeforeMethod({"setupSelectSql"}) + * @ParamProvider({"provideNodes"}) + * @ParamProvider({"provideColumns"}) + * @Iterations(3) + */ + public function benchSelectSql(BenchIteration $iteration): void + { + } + + /** + * @BeforeMethod({"setupSelectSql) + * @ParamProvider({"provideNodes) + * @ParamProvider({"provideColumns"}) + * @Iterations(3) + */ + public function benchTraverseSomething(BenchIteration $iteration): void + { + } +} diff --git a/tests/Unit/Benchmark/findertestnested/Directory/MyBench.php b/tests/Unit/Benchmark/findertestnested/Directory/MyBench.php new file mode 100644 index 000000000..2c67f4446 --- /dev/null +++ b/tests/Unit/Benchmark/findertestnested/Directory/MyBench.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Benchmark\findertest\Directory; + +class MyBench +{ + public function benchVoid(): void + { + } +} diff --git a/tests/Unit/Benchmark/findertestnested/MyBench.php b/tests/Unit/Benchmark/findertestnested/MyBench.php new file mode 100644 index 000000000..2c67f4446 --- /dev/null +++ b/tests/Unit/Benchmark/findertestnested/MyBench.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Benchmark\findertest\Directory; + +class MyBench +{ + public function benchVoid(): void + { + } +} diff --git a/tests/Unit/Console/Command/LogCommandTest.php b/tests/Unit/Console/Command/LogCommandTest.php new file mode 100644 index 000000000..979aa08f0 --- /dev/null +++ b/tests/Unit/Console/Command/LogCommandTest.php @@ -0,0 +1,284 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Console\Command; + +use PhpBench\Console\CharacterReader; +use PhpBench\Console\Command\Handler\TimeUnitHandler; +use PhpBench\Console\Command\LogCommand; +use PhpBench\Registry\Registry; +use PhpBench\Storage\DriverInterface; +use PhpBench\Storage\HistoryEntry; +use PhpBench\Storage\HistoryIteratorInterface; +use PhpBench\Tests\TestCase; +use PhpBench\Util\TimeUnit; +use Prophecy\Argument; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\BufferedOutput; + +class LogCommandTest extends TestCase +{ + private $storage; + private $command; + private $driver; + private $output; + private $history; + + protected function setUp(): void + { + if (!class_exists(QuestionHelper::class)) { + $this->markTestSkipped('Not testing if QuestionHelper class does not exist (< Symfony 2.7)'); + } + + $this->storage = $this->prophesize(Registry::class); + $this->timeUnit = $this->prophesize(TimeUnit::class); + $this->timeUnitHandler = $this->prophesize(TimeUnitHandler::class); + $this->characterReader = $this->prophesize(CharacterReader::class); + $this->output = new BufferedOutput(); + + $this->command = new LogCommand( + $this->storage->reveal(), + $this->timeUnit->reveal(), + $this->timeUnitHandler->reveal(), + $this->characterReader->reveal(), + $this->output + ); + + $this->application = new Application(); + $this->command->setApplication($this->application); + + $this->driver = $this->prophesize(DriverInterface::class); + $this->storage->getService()->willReturn($this->driver->reveal()); + + $this->history = $this->prophesize(HistoryIteratorInterface::class); + + $this->timeUnit->toDestUnit(Argument::cetera())->will(function ($time) { + return $time[0]; + }); + $this->timeUnit->format(Argument::cetera())->will(function ($time) { + return $time[0]; + }); + $this->timeUnit->getDestSuffix()->willReturn('s'); + } + + protected function tearDown(): void + { + $this->resetTerminalDimensions(); + } + + /** + * It should be configured. + */ + public function testConfigure(): void + { + $this->command->configure(); + $this->addToAssertionCount(1); + } + + /** + * It should show the history. + */ + public function testHistory(): void + { + $input = new ArrayInput([ + '--no-pagination' => true, + ], $this->command->getDefinition()); + + $this->setTerminalDimensions(100, 10); + + $this->characterReader->read()->shouldNotBeCalled(); + + $this->driver->history()->willReturn($this->history->reveal()); + $this->history->rewind()->shouldBeCalled(); + $this->history->valid()->willReturn(true, true, true, false); + $this->history->key()->willReturn(0, 1, 2); + $this->history->next()->shouldBeCalled(); + $this->history->current()->willReturn( + $this->createHistoryEntry(1), + $this->createHistoryEntry(2), + $this->createHistoryEntry(3) + ); + + $this->command->execute($input, $this->output); + + $output = $this->output->fetch(); + + $expected = <<<'EOT' +run 1 +Date: 2016-01-01T00:00:00+02:00 +Branch: branch1 +Tag: foo +Scale: 10 subjects, 20 iterations, 40 revolutions +Summary: (best [mean] worst) = 0.500 [1.250] 2.000 (s) + ⅀T: 100 μRSD/r: 0.750% + +run 2 +Date: 2016-01-01T00:00:00+02:00 +Branch: branch2 +Tag: foo +Scale: 10 subjects, 20 iterations, 40 revolutions +Summary: (best [mean] worst) = 0.500 [1.250] 2.000 (s) + ⅀T: 100 μRSD/r: 0.750% + +run 3 +Date: 2016-01-01T00:00:00+01:00 +Branch: branch3 +Tag: foo +Scale: 10 subjects, 20 iterations, 40 revolutions +Summary: (best [mean] worst) = 0.500 [1.250] 2.000 (s) + ⅀T: 100 μRSD/r: 0.750% + + +EOT; + + $this->assertEquals($this->replaceDate($expected), $this->replaceDate($output)); + } + + /** + * It should paginate. + */ + public function testPaginate(): void + { + $input = new ArrayInput([], $this->command->getDefinition()); + $output = $this->output; + + $this->setTerminalDimensions(100, 14); + + $this->characterReader->read()->willReturn('')->shouldBeCalledTimes(1); + + $this->driver->history()->willReturn($this->history->reveal()); + $this->history->rewind()->shouldBeCalled(); + $this->history->valid()->willReturn(true, true, true, false); + $this->history->key()->willReturn(0, 1, 2); + $this->history->next()->shouldBeCalled(); + $this->history->current()->willReturn( + $this->createHistoryEntry(1), + $this->createHistoryEntry(2), + $this->createHistoryEntry(3) + ); + + $this->command->execute($input, $this->output); + + $output = $this->output->fetch(); + + $expected = <<<'EOT' +run 1 +Date: 2016-01-01T00:00:00+01:00 +Branch: branch1 +Tag: foo +Scale: 10 subjects, 20 iterations, 40 revolutions +Summary: (best [mean] worst) = 0.500 [1.250] 2.000 (s) + ⅀T: 100 μRSD/r: 0.750% + +run 2 +Date: 2016-01-01T00:00:00+01:00 +Branch: branch2 +Tag: foo +Scale: 10 subjects, 20 iterations, 40 revolutions +lines 0-13 any key to continue, to quit +run 3 +Date: 2016-01-01T00:00:00+01:00 +Branch: branch3 +Tag: foo +Scale: 10 subjects, 20 iterations, 40 revolutions +Summary: (best [mean] worst) = 0.500 [1.250] 2.000 (s) + ⅀T: 100 μRSD/r: 0.750% + + +EOT; + + $this->assertEquals($this->replaceDate($expected), $this->replaceDate($output)); + } + + /** + * It should quit pagination. + */ + public function testQuitPagination(): void + { + $input = new ArrayInput([], $this->command->getDefinition()); + $output = $this->output; + + $this->setTerminalDimensions(100, 14); + + $this->characterReader->read()->willReturn('q')->shouldBeCalledTimes(1); + + $this->driver->history()->willReturn($this->history->reveal()); + $this->history->rewind()->shouldBeCalled(); + $this->history->valid()->willReturn(true, true); + $this->history->key()->willReturn(0, 1); + $this->history->next()->shouldBeCalled(); + $this->history->current()->willReturn( + $this->createHistoryEntry(1), + $this->createHistoryEntry(2), + $this->createHistoryEntry(3) + ); + + $this->command->execute($input, $this->output); + + $output = $this->output->fetch(); + + $expected = <<<'EOT' +run 1 +Date: 2016-01-01T00:00:00+01:00 +Branch: branch1 +Tag: foo +Scale: 10 subjects, 20 iterations, 40 revolutions +Summary: (best [mean] worst) = 0.500 [1.250] 2.000 (s) + ⅀T: 100 μRSD/r: 0.750% + +run 2 +Date: 2016-01-01T00:00:00+01:00 +Branch: branch2 +Tag: foo +Scale: 10 subjects, 20 iterations, 40 revolutions +lines 0-13 any key to continue, to quit +EOT; + + $this->assertEquals($this->replaceDate($expected), $this->replaceDate($output)); + } + + private function replaceDate($string) + { + return preg_replace('{\+[0-9]{2}:[0-9]{2}}', '00:00', $string); + } + + private function createHistoryEntry($index) + { + return new HistoryEntry( + $index, + new \DateTime('2016-01-01'), + 'foo', 'branch' . $index, + 10, + 20, + 40, + 0.5, + 2, + 1.25, + 0.75, + 100 + ); + } + + private function setTerminalDimensions(int $columns, int $lines): void + { + putenv('COLUMNS=' . $columns); + putenv('LINES=' . $lines); + } + + private function resetTerminalDimensions(): void + { + putenv('COLUMNS'); + putenv('LINES'); + } +} diff --git a/tests/Unit/Console/Command/UpdateCommandTest.php b/tests/Unit/Console/Command/UpdateCommandTest.php new file mode 100644 index 000000000..4eb4e81bb --- /dev/null +++ b/tests/Unit/Console/Command/UpdateCommandTest.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Console\Command; + +use Humbug\SelfUpdate\Updater; +use PhpBench\Console\Command\SelfUpdateCommand; +use PhpBench\Tests\TestCase; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\BufferedOutput; + +class UpdateCommandTest extends TestCase +{ + private $updater; + private $command; + private $output; + + protected function setUp(): void + { + $this->updater = $this->prophesize(Updater::class); + $this->command = new SelfUpdateCommand($this->updater->reveal()); + + $this->output = new BufferedOutput(); + } + + /** + * It should update the PHAR. + */ + public function testUpdate(): void + { + $input = new ArrayInput([], $this->command->getDefinition()); + $this->updater->update()->shouldBeCalled()->willReturn(true); + $this->updater->getOldVersion()->willReturn('10'); + $this->updater->getNewVersion()->willReturn('20'); + $this->command->execute($input, $this->output); + $this->assertStringContainsString( + 'PHPBench was updated from "10" to "20"', + $this->output->fetch() + ); + } + + /** + * It should show a message if no update is required. + */ + public function testUpdateNotRequired(): void + { + $input = new ArrayInput([], $this->command->getDefinition()); + $this->updater->update()->shouldBeCalled()->willReturn(false); + $this->command->execute($input, $this->output); + $this->assertStringContainsString( + 'No update required', + $this->output->fetch() + ); + } + + /** + * It should rollback. + */ + public function testUpdateRollback(): void + { + $input = new ArrayInput([ + '--rollback' => true, + ], $this->command->getDefinition()); + $this->updater->rollback()->shouldBeCalled()->willReturn(true); + $this->command->execute($input, $this->output); + + $this->assertStringContainsString( + 'Successfully rolled back', + $this->output->fetch() + ); + } + + /** + * It should show an error if it could not roll back. + * + */ + public function testUpdateRollbackError(): void + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Could not rollback'); + $input = new ArrayInput([ + '--rollback' => true, + ], $this->command->getDefinition()); + $this->updater->rollback()->shouldBeCalled()->willReturn(false); + $this->command->execute($input, $this->output); + } +} diff --git a/tests/Unit/Environment/InformationTest.php b/tests/Unit/Environment/InformationTest.php new file mode 100644 index 000000000..598b816ea --- /dev/null +++ b/tests/Unit/Environment/InformationTest.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Environment; + +use BadMethodCallException; +use PhpBench\Environment\Information; +use PhpBench\Tests\TestCase; + +class InformationTest extends TestCase +{ + /** + * Information acts as an array. + */ + public function testActsAsArray(): void + { + $information = new Information( + 'hello', + [ + 'one' => 'two', + 'three' => 'four', + ] + ); + + $this->assertEquals('two', $information['one']); + $this->assertEquals('four', $information['three']); + } + + /** + * It should throw an Exception if unset is called. + * + */ + public function testUnset(): void + { + $this->expectException(BadMethodCallException::class); + $information = new Information('hello', []); + unset($information['foo']); + } + + /** + * It should throw an Exception if set is called. + * + */ + public function testSet(): void + { + $this->expectException(BadMethodCallException::class); + $information = new Information('hello', []); + $information['foo'] = 'bar'; + } + + /** + * It should retrieve its name. + */ + public function testGetName(): void + { + $information = new Information('foo', []); + $this->assertEquals('foo', $information->getName()); + } + + /** + * It should be iterable. + */ + public function testIterable(): void + { + $information = new Information('foo', [ + 'bar' => 'bar', + 'boo' => 'boo', + ]); + + $result = []; + + foreach ($information as $key => $value) { + $result[$key] = $value; + } + + $this->assertEquals([ + 'bar' => 'bar', + 'boo' => 'boo', + ], $result); + } + + public function testFlattensArrays(): void + { + $information = new Information('foo', [ + 'a' => 'b', + 'c' => [ + 'd' => [ + 'e' => 'f', + ], + 'g' => 'h', + ], + 'i' => 'j', + ]); + + $this->assertEquals([ + 'a' => 'b', + 'c_d_e' => 'f', + 'c_g' => 'h', + 'i' => 'j', + ], $information->toArray()); + } +} diff --git a/tests/Unit/Environment/Provider/GitTest.php b/tests/Unit/Environment/Provider/GitTest.php new file mode 100644 index 000000000..7b9ce86d3 --- /dev/null +++ b/tests/Unit/Environment/Provider/GitTest.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Environment\Provider; + +use PhpBench\Environment\Provider; +use PhpBench\Tests\IntegrationTestCase; +use Symfony\Component\Process\ExecutableFinder; +use Symfony\Component\Process\Process; +use function sys_get_temp_dir; + +class GitTest extends IntegrationTestCase +{ + private $provider; + private $filesystem; + + protected function setUp(): void + { + $this->workspace()->reset(); + $this->workspace()->put('foobar', 'Foobar'); + $this->exec('git init'); + $this->exec('git add foobar'); + $this->exec('git config user.email "test@example.com"'); + $this->exec('git config user.name "My Name"'); + + $this->provider = new Provider\Git($this->workspace()->path()); + } + + /** + * It should return TRUE if the CWD is a git repository. + */ + public function testIsApplicable(): void + { + $result = $this->provider->isApplicable(); + $this->assertTrue($result); + } + + /** + * It should return FALSE if the CWD is not a git repository. + */ + public function testIsNotApplicable(): void + { + $this->provider = new Provider\Git(sys_get_temp_dir()); + $result = $this->provider->isApplicable(); + $this->assertFalse($result); + } + + /** + * It should return the VCS information for the current git repository. + */ + public function testGetVcsInformation(): void + { + $info = $this->provider->getInformation(); + $this->assertEquals('git', $info['system']); + $this->assertEquals('master', $info['branch']); + $this->assertNull($info['version']); // no commit has yet been made + } + + /** + * It should show the commitsh. + */ + public function testGetVcsCommitsh(): void + { + $this->exec('git commit -m "test"'); + $info = $this->provider->getInformation(); + $this->assertNotNull($info['version']); // no commit has yet been made + $this->assertEquals(40, strlen($info['version'])); + } + + /** + * It should show the branch. + */ + public function testGetVcsBranch(): void + { + $this->exec('git commit -m "test"'); + + $this->exec('git branch foobar'); + $this->exec('git checkout foobar'); + $info = $this->provider->getInformation(); + $this->assertEquals('foobar', $info['branch']); + } + + /** + * It should not be applicable if GIT is not available. + */ + public function testNotApplicableIfGitNotFound(): void + { + $exeFinder = $this->prophesize(ExecutableFinder::class); + $exeFinder->find('git', null)->willReturn(null); + + $provider = new Provider\Git(__DIR__, $exeFinder->reveal()); + + $this->assertFalse($provider->isApplicable()); + } + + private function exec(string $cmd): void + { + $proc = Process::fromShellCommandline($cmd, $this->workspace()->path()); + $proc->mustRun(); + } +} diff --git a/tests/Unit/Environment/Provider/OpcacheTest.php b/tests/Unit/Environment/Provider/OpcacheTest.php new file mode 100644 index 000000000..33a4b009f --- /dev/null +++ b/tests/Unit/Environment/Provider/OpcacheTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Environment\Provider; + +use PhpBench\Environment\Provider; +use PhpBench\Remote\Launcher; +use PhpBench\Remote\Payload; +use PhpBench\Tests\TestCase; + +class OpcacheTest extends TestCase +{ + private $launcher; + private $payload; + + protected function setUp(): void + { + $this->launcher = $this->prophesize(Launcher::class); + $this->payload = $this->prophesize(Payload::class); + + if (false === extension_loaded('Zend OPcache')) { + $this->markTestSkipped(); + + return; + } + } + + /** + * Provider is always applicable. + */ + public function testIsApplicable(): void + { + $this->assertTrue($this->createProvider(false)->isApplicable()); + } + + /** + * It should provide the PHP version. + */ + public function testExtensionLoaded(): void + { + $info = $this->createProvider(false)->getInformation(); + $this->assertEquals(true, $info['extension_loaded']); + } + + public function testExtensionDisabledOnCli(): void + { + $info = $this->createProvider(false)->getInformation(); + $this->assertEquals(false, $info['enabled']); + } + + private function createProvider(bool $opcacheEnabled) + { + $phpConfig = [ + 'opcache.enable_cli' => $opcacheEnabled, + ]; + + $launcher = new Launcher(null, null, null, null, $phpConfig); + + return new Provider\Opcache($launcher); + } +} diff --git a/tests/Unit/Environment/Provider/PhpTest.php b/tests/Unit/Environment/Provider/PhpTest.php new file mode 100644 index 000000000..ec178eb03 --- /dev/null +++ b/tests/Unit/Environment/Provider/PhpTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Environment\Provider; + +use PhpBench\Environment\Provider; +use PhpBench\Remote\Launcher; +use PhpBench\Remote\Payload; +use PhpBench\Tests\TestCase; + +class PhpTest extends TestCase +{ + private $payload; + + protected function setUp(): void + { + $this->payload = $this->prophesize(Payload::class); + } + + /** + * Provider is always applicable. + */ + public function testIsApplicable(): void + { + $this->assertTrue($this->createProvider()->isApplicable()); + } + + /** + * It should provide the PHP version. + */ + public function testPhpVersion(): void + { + $info = $this->createProvider()->getInformation(); + $this->assertEquals(PHP_VERSION, $info['version']); + } + + /** + * It should provide the path to the PHP ini file. + */ + public function testPhpIni(): void + { + $info = $this->createProvider()->getInformation(); + $this->assertEquals(php_ini_loaded_file(), $info['ini']); + } + + private function createProvider() + { + return new Provider\Php( + new Launcher() + ); + } +} diff --git a/tests/Unit/Environment/Provider/SamplerTest.php b/tests/Unit/Environment/Provider/SamplerTest.php new file mode 100644 index 000000000..aba7e3483 --- /dev/null +++ b/tests/Unit/Environment/Provider/SamplerTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Environment\Provider; + +use PhpBench\Benchmark\SamplerManager; +use PhpBench\Environment\Information; +use PhpBench\Environment\Provider\Sampler; +use PhpBench\Tests\TestCase; + +class SamplerTest extends TestCase +{ + private $manager; + private $provider; + + protected function setUp(): void + { + $this->manager = $this->prophesize(SamplerManager::class); + $this->provider = new Sampler( + $this->manager->reveal(), + ['one'] + ); + } + + /** + * Provider is always applicable. + */ + public function testIsApplicable(): void + { + $this->assertTrue($this->provider->isApplicable()); + } + + /** + * It should get the sampler measurements from the baseline manager. + */ + public function testBaselineMeasurements(): void + { + $this->manager->sample('one', 1000)->willReturn(10); + $info = $this->provider->getInformation(); + $this->assertInstanceOf(Information::class, $info); + $this->assertEquals(iterator_to_array($info), [ + 'one' => 10, + ]); + } +} diff --git a/tests/Unit/Environment/Provider/UnameTest.php b/tests/Unit/Environment/Provider/UnameTest.php new file mode 100644 index 000000000..385fdd6f3 --- /dev/null +++ b/tests/Unit/Environment/Provider/UnameTest.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Environment\Provider; + +use PhpBench\Environment\Provider; +use PhpBench\Tests\TestCase; + +class UnameTest extends TestCase +{ + private $provider; + + protected function setUp(): void + { + $this->provider = new Provider\Uname(); + } + + /** + * Provider is always applicable. + */ + public function testIsApplicable(): void + { + $this->assertTrue($this->provider->isApplicable()); + } + + /** + * It should provide the UNAME version. + */ + public function testUnameVersion(): void + { + $info = $this->provider->getInformation(); + + foreach ([ + 'os' => 's', + 'host' => 'n', + 'release' => 'r', + 'version' => 'v', + 'machine' => 'm', + ] as $key => $mode) { + $this->assertEquals(php_uname($mode), $info[$key]); + } + } +} diff --git a/tests/Unit/Environment/Provider/UnixSysloadTest.php b/tests/Unit/Environment/Provider/UnixSysloadTest.php new file mode 100644 index 000000000..7f7b4bfca --- /dev/null +++ b/tests/Unit/Environment/Provider/UnixSysloadTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Environment\Provider; + +use PhpBench\Environment\Provider; +use PhpBench\Tests\TestCase; + +class UnixSysloadTest extends TestCase +{ + private $provider; + + protected function setUp(): void + { + if (stristr(PHP_OS, 'win')) { + $this->markTestSkipped('Unix specific test'); + } + + $this->provider = new Provider\UnixSysload(); + } + + /** + * Provider is always applicable because this test only runs + * in Unix environments. + */ + public function testIsApplicable(): void + { + $this->assertTrue($this->provider->isApplicable()); + } + + /** + * It should provide the load averages. + */ + public function testUnixSysloadVersion(): void + { + $info = $this->provider->getInformation(); + $this->assertArrayHasKey('l1', $info); + $this->assertArrayHasKey('l5', $info); + $this->assertArrayHasKey('l15', $info); + } +} diff --git a/tests/Unit/Environment/SupplierTest.php b/tests/Unit/Environment/SupplierTest.php new file mode 100644 index 000000000..566719337 --- /dev/null +++ b/tests/Unit/Environment/SupplierTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Vcs\Detector; + +use PhpBench\Environment\Information; +use PhpBench\Environment\ProviderInterface; +use PhpBench\Environment\Supplier; +use PhpBench\Tests\TestCase; + +class SupplierTest extends TestCase +{ + private $supplier; + + protected function setUp(): void + { + $this->supplier = new Supplier(); + $this->provider1 = $this->prophesize(ProviderInterface::class); + $this->provider2 = $this->prophesize(ProviderInterface::class); + $this->information1 = $this->prophesize(Information::class); + $this->information2 = $this->prophesize(Information::class); + } + + /** + * It should return nothing if all the providers are not applicable. + */ + public function testNoApplicableProviders(): void + { + $this->supplier->addProvider($this->provider1->reveal()); + $this->supplier->addProvider($this->provider2->reveal()); + $this->provider1->isApplicable()->willReturn(false); + $this->provider2->isApplicable()->willReturn(false); + + $result = $this->supplier->getInformations(); + + $this->assertEquals([], $result); + } + + /** + * It should return information from both the providers. + */ + public function testApplicableProviders(): void + { + $this->supplier->addProvider($this->provider1->reveal()); + $this->supplier->addProvider($this->provider2->reveal()); + $this->provider1->isApplicable()->willReturn(true); + $this->provider2->isApplicable()->willReturn(true); + $this->provider1->getInformation()->willReturn($this->information1->reveal()); + $this->provider2->getInformation()->willReturn($this->information2->reveal()); + + $result = $this->supplier->getInformations(); + + $this->assertSame([ + $this->information1->reveal(), + $this->information2->reveal(), + ], $result); + } +} diff --git a/tests/Unit/Executor/Benchmark/AbstractExecutorTestCase.php b/tests/Unit/Executor/Benchmark/AbstractExecutorTestCase.php new file mode 100644 index 000000000..8d13cc646 --- /dev/null +++ b/tests/Unit/Executor/Benchmark/AbstractExecutorTestCase.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Executor\Benchmark; + +use DTL\Invoke\Invoke; +use PhpBench\Executor\BenchmarkExecutorInterface; +use PhpBench\Executor\ExecutionContext; +use PhpBench\Executor\ExecutionResults; +use PhpBench\Model\Benchmark; +use PhpBench\Model\ParameterSet; +use PhpBench\Registry\Config; +use PhpBench\Tests\PhpBenchTestCase; + +abstract class AbstractExecutorTestCase extends PhpBenchTestCase +{ + /** + * @var BenchmarkExecutorInterface + */ + protected $executor; + + protected function setUp(): void + { + $this->initWorkspace(); + $this->executor = $this->createExecutor(); + } + + abstract protected function createExecutor(): BenchmarkExecutorInterface; + + abstract protected function assertExecute(ExecutionResults $reuslts): void; + + /** + * It should create a script which benchmarks the code and returns + * the time taken and the memory used. + */ + public function testExecute(): void + { + $results = $this->executor->execute( + $this->buildContext([ + 'timeOut' => 0.0, + 'methodName' => 'doSomething', + 'parameterSetName' => 'one', + 'revolutions' => 10, + 'warmup' => 1, + ]), + new Config('test', []) + ); + + $this->assertExecute($results); + } + + /** + * It should execute methods before the benchmark subject. + */ + public function testExecuteBefore(): void + { + $this->executor->execute( + $this->buildContext([ + 'methodName' => 'doSomething', + 'beforeMethods' => ['beforeMethod'], + ]), + new Config('test', []) + ); + + $this->assertFileExists($this->workspace()->path('before_method.tmp')); + } + + /** + * It should execute methods after the benchmark subject. + */ + public function testExecuteAfter(): void + { + $this->executor->execute( + $this->buildContext([ + 'methodName' => 'doSomething', + 'beforeMethods' => ['afterMethod'], + ]), + new Config('test', []) + ); + + $this->assertFileExists($this->workspacePath('after_method.tmp')); + } + + /** + * It should pass parameters to the benchmark method. + */ + public function testParameters(): void + { + $this->executor->execute( + $this->buildContext([ + 'methodName' => 'parameterized', + 'parameters' => [ + 'one' => 'two', + 'three' => 'four', + ], + ]), + new Config('test', []) + ); + + $this->assertFileExists($this->workspacePath('param.tmp')); + $params = json_decode(file_get_contents($this->workspace()->path('param.tmp')), true); + $this->assertEquals([ + 'one' => 'two', + 'three' => 'four', + ], $params); + } + + /** + * It should pass parameters to the before metadata and after metadata methods. + */ + public function testParametersBeforeSubject(): void + { + $expected = new ParameterSet(0, [ + 'one' => 'two', + 'three' => 'four', + ]); + + $this->executor->execute( + $this->buildContext([ + 'methodName' => 'parameterized', + 'beforeMethods' => ['parameterizedBefore'], + 'afterMethods' => ['parameterizedAfter'], + 'parameters' => [ + 'one' => 'two', + 'three' => 'four', + ], + ]), + new Config('test', []) + ); + + + $this->assertFileExists($this->workspacePath('parambefore.tmp')); + $params = json_decode(file_get_contents($this->workspace()->path('parambefore.tmp')), true); + $this->assertEquals($expected->getArrayCopy(), $params); + + $this->assertFileExists($this->workspacePath('paramafter.tmp')); + $params = json_decode(file_get_contents($this->workspace()->path('paramafter.tmp')), true); + $this->assertEquals($expected->getArrayCopy(), $params); + } + + protected function buildContext(array $config): ExecutionContext + { + return Invoke::new(ExecutionContext::class, $this->buildConfig($config)); + } + + protected function buildConfig(array $config): array + { + return array_merge([ + 'className' => 'PhpBench\Tests\Unit\Executor\benchmarks\MicrotimeExecutorBench', + 'classPath' => __DIR__ . '/../benchmarks/MicrotimeExecutorBench.php', + ], $config); + } +} diff --git a/tests/Unit/Executor/Benchmark/DebugExecutorTest.php b/tests/Unit/Executor/Benchmark/DebugExecutorTest.php new file mode 100644 index 000000000..b4be6d270 --- /dev/null +++ b/tests/Unit/Executor/Benchmark/DebugExecutorTest.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Executor\Benchmark; + +use DTL\Invoke\Invoke; +use PhpBench\Executor\Benchmark\DebugExecutor; +use PhpBench\Executor\ExecutionContext; +use PhpBench\Model\Result\TimeResult; +use PhpBench\Registry\Config; +use PhpBench\Remote\Launcher; +use PhpBench\Tests\TestCase; + +class DebugExecutorTest extends TestCase +{ + private $executor; + + protected function setUp(): void + { + $launcher = $this->prophesize(Launcher::class); + $this->executor = new DebugExecutor($launcher->reveal()); + } + + /** + * It should return constant times. + * + * @dataProvider provideConstantTimes + */ + public function testConstantTimes(array $times, array $spread, $nbCollections, $nbIterations, $expectedTimes): void + { + $actualTimes = []; + + for ($i = 0; $i < $nbCollections; $i++) { + for ($ii = 0; $ii < $nbIterations; $ii++) { + $results = $this->executor->execute( + Invoke::new(ExecutionContext::class, [ + 'classPath' => '', + 'className' => '', + 'methodName' => '', + 'iterationIndex' => $ii, + ]), + new Config('test', [ + 'times' => $times, + 'spread' => $spread, + ]) + ); + + foreach ($results as $result) { + if ($result instanceof TimeResult) { + $actualTimes[] = $result->getNet(); + } + } + } + } + + $this->assertEquals($expectedTimes, $actualTimes); + } + + public function provideConstantTimes() + { + return [ + [ + [], + [], + 2, + 2, + [0, 0, 0, 0], + ], + [ + [10], + [], + 2, + 4, + [10, 10, 10, 10, 10, 10, 10, 10], + ], + [ + [10, 20, 30, 40], + [], + 2, + 4, + [10, 20, 30, 40, 10, 20, 30, 40], + ], + [ + [1, 2], + [], + 4, + 2, + [1, 2, 1, 2, 1, 2, 1, 2], + ], + [ + [1, 2], + [0, 1], + 4, + 2, + [1, 3, 1, 3, 1, 3, 1, 3], + ], + [ + [1, 2], + [0, 1], + 4, + 2, + [1, 3, 1, 3, 1, 3, 1, 3], + ], + ]; + } +} diff --git a/tests/Unit/Executor/Benchmark/LocalExecutorTest.php b/tests/Unit/Executor/Benchmark/LocalExecutorTest.php new file mode 100644 index 000000000..f6537cf0f --- /dev/null +++ b/tests/Unit/Executor/Benchmark/LocalExecutorTest.php @@ -0,0 +1,57 @@ +workspace()->put('FooBench.php', 'execute( + $this->buildContext([ + 'className' => 'FoobarBench', + 'classPath' => $this->workspace()->path('FooBench.php'), + 'methodName' => 'benchFoo', + ]), + new Config('test', []) + ); + $this->addToAssertionCount(1); + } + + public function testIncludesBootstrapOnce(): void + { + $this->workspace()->put('NewFooBench.php', 'workspace()->put('bootstrap.php', 'workspace()->path('bootstrap.php')); + $context = $this->buildContext([ + 'className' => 'NewFoobarBench', + 'classPath' => $this->workspace()->path('NewFooBench.php'), + 'methodName' => 'benchFoo', + 'timeOut' => 0.0, + 'parameterSetName' => 'one', + 'revolutions' => 10, + 'warmup' => 1, + ]); + $executor->execute($context, new Config('test', [])); + $executor->execute($context, new Config('test', [])); + $this->addToAssertionCount(1); + } +} diff --git a/tests/Unit/Executor/Benchmark/RemoteExecutorTest.php b/tests/Unit/Executor/Benchmark/RemoteExecutorTest.php new file mode 100644 index 000000000..f62c3261b --- /dev/null +++ b/tests/Unit/Executor/Benchmark/RemoteExecutorTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Executor\Benchmark; + +use PhpBench\Executor\Benchmark\RemoteExecutor; +use PhpBench\Executor\BenchmarkExecutorInterface; +use PhpBench\Executor\ExecutionResults; +use PhpBench\Registry\Config; +use PhpBench\Remote\Launcher; +use RuntimeException; + +class RemoteExecutorTest extends AbstractExecutorTestCase +{ + public function testRepressOutput(): void + { + $this->expectExceptionMessage('Benchmark made some noise'); + $this->expectException(RuntimeException::class); + + $this->executor->execute( + $this->buildContext([ + 'methodName' => 'benchOutput', + ]), + new Config('test', []) + ); + } + + protected function createExecutor(): BenchmarkExecutorInterface + { + return new RemoteExecutor(new Launcher(null)); + } + + protected function assertExecute(ExecutionResults $results): void + { + self::assertCount(2, $results); + + $this->assertFileDoesNotExist($this->workspacePath('before_method.tmp')); + $this->assertFileDoesNotExist($this->workspacePath('after_method.tmp')); + $this->assertFileExists($this->workspacePath('revs.tmp')); + + // 10 revolutions + 1 warmup + $this->assertStringEqualsFile($this->workspacePath('revs.tmp'), '11'); + } +} diff --git a/tests/Unit/Executor/Method/LocalMethodExecutorTest.php b/tests/Unit/Executor/Method/LocalMethodExecutorTest.php new file mode 100644 index 000000000..13df51e3c --- /dev/null +++ b/tests/Unit/Executor/Method/LocalMethodExecutorTest.php @@ -0,0 +1,70 @@ +executeMethod(LocalTestClass::class, 'method'); + + self::assertTrue(LocalTestClass::$executed); + } + + public function testExceptionWhenClassDoesntExist(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Class "NotExist" does not exist'); + + self::assertFalse(LocalTestClass::$executed); + + $this->executeMethod('NotExist', 'method'); + + self::assertTrue(LocalTestClass::$executed); + } + + public function testExceptionWhenMethodDoesntExist(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Method "foobar" on '); + + self::assertFalse(LocalTestClass::$executed); + + $this->executeMethod(LocalTestClass::class, 'foobar'); + + self::assertTrue(LocalTestClass::$executed); + } + + private function executeMethod($className, string $methodName): void + { + $context = new MethodExecutorContext(__FILE__, $className); + + (new LocalMethodExecutor())->executeMethods($context, [ $methodName ]); + } + + protected function tearDown(): void + { + LocalTestClass::$executed = false; + } +} + +class LocalTestClass +{ + /** + * @var bool + */ + public static $executed = false; + + public function method(): void + { + self::$executed = true; + } +} diff --git a/tests/Unit/Executor/Method/RemoteMethodExecutorTest.php b/tests/Unit/Executor/Method/RemoteMethodExecutorTest.php new file mode 100644 index 000000000..6f610c73e --- /dev/null +++ b/tests/Unit/Executor/Method/RemoteMethodExecutorTest.php @@ -0,0 +1,49 @@ + + */ + private $benchmarkMetadata; + + /** + * @var string + */ + private $staticMethodFile; + + protected function setUp(): void + { + $this->staticMethodFile = __DIR__ . '/../benchmarks/static_method.tmp'; + $this->executor = new RemoteMethodExecutor(new Launcher()); + $this->benchmarkMetadata = $this->prophesize(BenchmarkMetadata::class); + $this->benchmarkMetadata->getPath()->willReturn(__DIR__ . '/../benchmarks/MicrotimeExecutorBench.php'); + $this->benchmarkMetadata->getClass()->willReturn('PhpBench\Tests\Unit\Executor\benchmarks\MicrotimeExecutorBench'); + } + + /** + * It should execute arbitrary methods on the benchmark class. + */ + public function testExecuteMethods(): void + { + $this->executor->executeMethods( + MethodExecutorContext::fromBenchmarkMetadata($this->benchmarkMetadata->reveal()), + ['initDatabase'] + ); + $this->assertFileExists($this->workspacePath('static_method.tmp')); + } +} diff --git a/tests/Unit/Executor/ParameterSetExtractorTest.php b/tests/Unit/Executor/ParameterSetExtractorTest.php new file mode 100644 index 000000000..6258baebe --- /dev/null +++ b/tests/Unit/Executor/ParameterSetExtractorTest.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Executor; + +use PhpBench\Remote\Exception\ScriptErrorException; +use PhpBench\Remote\Payload; +use PhpBench\Tests\TestCase; +use PhpBench\Tests\Unit\Executor\benchmarks\ParamProviderBench; + +class ParameterSetExtractorTest extends TestCase +{ + public function testProvideParameterFromCallable(): void + { + $this->provideParams(['PhpBench\Tests\Unit\Executor\benchmarks\hello_world']); + } + + public function testProvideParameterFromBenchmark(): void + { + $this->provideParams(['provideParams']); + } + + public function testProvideParameterFromIterator(): void + { + $this->provideParams(['provideIterator']); + } + + public function testProvideParameterFromGenerator(): void + { + $this->provideParams(['provideGenerator']); + } + + public function testThrowsExceptionIfParameterDoesntExist(): void + { + $this->expectException(ScriptErrorException::class); + $this->expectExceptionMessage('Class has no method "idontexist"'); + $this->provideParams(['idontexist']); + } + + public function testThrowsExceptionIfMethodIsPrivate(): void + { + $this->expectException(ScriptErrorException::class); + $this->expectExceptionMessage('Class has no method "privateParamProvider"'); + $this->provideParams(['privateParamProvider']); + } + + private function provideParams(array $providers): void + { + $payload = new Payload(__DIR__ . '/../../../lib/Reflection/template/parameter_set_extractor.template', [ + 'bootstrap' => __DIR__ . '/benchmarks/ParamProviderBench.php', + 'file' => __DIR__ . '/benchmarks/ParamProviderBench.php', + 'class' => ParamProviderBench::class, + 'paramProviders' => json_encode($providers), + ]); + $result = $payload->launch(); + $this->assertEquals([ + [ + ['hello' => 'goodbye'], + ], + ], $result); + } +} diff --git a/tests/Unit/Executor/benchmarks/MicrotimeExecutorBench.php b/tests/Unit/Executor/benchmarks/MicrotimeExecutorBench.php new file mode 100644 index 000000000..102568046 --- /dev/null +++ b/tests/Unit/Executor/benchmarks/MicrotimeExecutorBench.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Executor\benchmarks; + +class MicrotimeExecutorBench +{ + private static $workspaceDir = __DIR__ . '/../../../Workspace'; + + public static function initDatabase(): void + { + file_put_contents(self::$workspaceDir . '/static_method.tmp', 'Static method method executed'); + } + + public function beforeMethod(): void + { + file_put_contents(self::$workspaceDir . '/before_method.tmp', 'Before method executed'); + } + + public function afterMethod(): void + { + file_put_contents(self::$workspaceDir . '/after_method.tmp', 'After method executed'); + } + + public function benchOutput(): void + { + // PHPBench should not crash if the user outputs something in their benchmark. + echo 'Hello World'; + } + + public function doSomething(): void + { + static $count = 0; + $data = []; + + for ($i = 0; $i < 10000; $i++) { + $data[] = 'hallo'; + } + $count++; + file_put_contents(self::$workspaceDir . '/revs.tmp', $count); + } + + public function parameterized($params): void + { + file_put_contents(self::$workspaceDir . '/param.tmp', json_encode($params)); + } + + public function parameterizedBefore($params): void + { + file_put_contents(self::$workspaceDir . '/parambefore.tmp', json_encode($params)); + } + + public function parameterizedAfter($params): void + { + file_put_contents(self::$workspaceDir . '/paramafter.tmp', json_encode($params)); + } +} diff --git a/tests/Unit/Executor/benchmarks/ParamProviderBench.php b/tests/Unit/Executor/benchmarks/ParamProviderBench.php new file mode 100644 index 000000000..f59526596 --- /dev/null +++ b/tests/Unit/Executor/benchmarks/ParamProviderBench.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Executor\benchmarks; + +use ArrayIterator; +use Generator; +use Iterator; + +function hello_world() +{ + return [ + [ + 'hello' => 'goodbye', + ], + ]; +} + +class ParamProviderBench +{ + private function privateParamProvider(): void + { + } + + public function provideParams() + { + return [ + [ + 'hello' => 'goodbye', + ], + ]; + } + + public function provideGenerator(): Generator + { + yield [ 'hello' => 'goodbye' ]; + } + + public function provideIterator(): Iterator + { + return new ArrayIterator( + [ + [ 'hello' => 'goodbye' ] + ] + ); + } +} diff --git a/tests/Unit/Expression/Ast/ListNodeTest.php b/tests/Unit/Expression/Ast/ListNodeTest.php new file mode 100644 index 000000000..698858ba2 --- /dev/null +++ b/tests/Unit/Expression/Ast/ListNodeTest.php @@ -0,0 +1,73 @@ +phpValues(), 'To Array'); + } + + /** + * @return Generator + */ + public function provideValues(): Generator + { + yield [ + [], + new ListNode() + ]; + + yield [ + [1], + new ListNode([new IntegerNode(1)]), + ]; + + yield [ + [1,2], + new ListNode([new IntegerNode(1), new IntegerNode(2)]) + ]; + + yield [ + [1,2,3], + new ListNode([new IntegerNode(1), new IntegerNode(2), new IntegerNode(3)]) + ]; + + yield [ + [1,2,3,4,5], + new ListNode([ + new IntegerNode(1), + new IntegerNode(2), + new IntegerNode(3), + new IntegerNode(4), + new IntegerNode(5) + ]) + ]; + + yield [ + [1,2,[3,4,5]], + new ListNode([ + new IntegerNode(1), + new IntegerNode(2), + new ListNode([ + new IntegerNode(3), + new IntegerNode(4), + new IntegerNode(5) + ]) + ]) + ]; + } +} diff --git a/tests/Unit/Expression/Ast/PhpValueFactoryTest.php b/tests/Unit/Expression/Ast/PhpValueFactoryTest.php new file mode 100644 index 000000000..d49ca6bee --- /dev/null +++ b/tests/Unit/Expression/Ast/PhpValueFactoryTest.php @@ -0,0 +1,42 @@ + + */ + public function provideFrom(): Generator + { + yield [1, new IntegerNode(1)]; + + yield [1.1, new FloatNode(1.1)]; + + yield ['one', new StringNode('one')]; + + yield [false, new BooleanNode(false)]; + + yield [null, new NullNode()]; + } +} diff --git a/tests/Unit/Expression/Evaluator/ParameterEvaluatorTest.php b/tests/Unit/Expression/Evaluator/ParameterEvaluatorTest.php new file mode 100644 index 000000000..4a451efb3 --- /dev/null +++ b/tests/Unit/Expression/Evaluator/ParameterEvaluatorTest.php @@ -0,0 +1,164 @@ +evaluateNode( + new ParameterNode($segments), + $params + )); + } + + /** + * @return Generator + */ + public function providePropertyAccess(): Generator + { + $object = new class { + public function bar(): int + { + return 2; + } + }; + + yield 'object access' => [ + ['foo', 'bar'], + [ + 'foo' => $object + ], + new IntegerNode(2) + ]; + + yield 'value access' => [ + ['foo'], + [ + 'foo' => 12 + ], + new IntegerNode(12), + ]; + + yield 'value is array' => [ + ['foo'], + [ + 'foo' => [12, 24] + ], + new ListNode([new IntegerNode(12), new IntegerNode(24)]), + ]; + + yield 'value is array 2' => [ + ['foo'], + [ + 'foo' => [12, 24, 37] + ], + new ListNode([new IntegerNode(12), new IntegerNode(24), new IntegerNode(37)]), + ]; + + yield 'array access' => [ + ['foo', 'bar'], + [ + 'foo' => [ + 'bar' => 2.1, + ], + ], + new FloatNode(2.1) + ]; + + yield 'nested array access' => [ + ['foo', 'bar', 'baz'], + [ + 'foo' => [ + 'bar' => [ + 'baz' => 4.5, + ] + ], + ], + new FloatNode(4.5) + ]; + + yield 'array access with nested object' => [ + ['foo', 'bar', 'bar'], + [ + 'foo' => [ + 'bar' => $object, + ], + ], + new IntegerNode(2) + ]; + } + + /** + * @dataProvider provideErrors + */ + public function testErrors(array $segments, array $params, string $expectedMessage): void + { + $this->expectException(EvaluationError::class); + $this->expectExceptionMessage($expectedMessage); + $this->evaluateNode(new ParameterNode($segments), $params); + } + + /** + * @return Generator + */ + public function provideErrors(): Generator + { + $object = new class { + public function bar(): string + { + return 'hello'; + } + }; + + yield [ + ['one'], + [ + ], + 'Array does not have key', + ]; + + yield [ + ['one', 'two'], + [ + 'one' => [ + 'three' => 4 + ], + ], + 'Array does not have key', + ]; + + yield [ + ['one', 'two'], + [ + 'one' => $object + ], + 'Could not access', + ]; + + yield [ + ['one', 'two', 'three'], + [ + 'one' => [ + 'two' => 12, + ], + ], + 'Could not access', + ]; + } +} diff --git a/tests/Unit/Expression/EvaluatorTestCase.php b/tests/Unit/Expression/EvaluatorTestCase.php new file mode 100644 index 000000000..305716509 --- /dev/null +++ b/tests/Unit/Expression/EvaluatorTestCase.php @@ -0,0 +1,19 @@ +container(); + + return $container->get( + Evaluator::class + )->evaluate($node, $params); + } +} diff --git a/tests/Unit/Expression/Exception/SyntaxErrorTest.php b/tests/Unit/Expression/Exception/SyntaxErrorTest.php new file mode 100644 index 000000000..aa762b337 --- /dev/null +++ b/tests/Unit/Expression/Exception/SyntaxErrorTest.php @@ -0,0 +1,53 @@ +getMessage()); + } + + public function testFromTokenTruncatesToMaxLength(): void + { + $token = new Token(Token::T_NAME, 'Barfoo', 24); + + $tokens = new Tokens([ + new Token(Token::T_NAME, 'FoobarFoobarFoobarFoobar', 0), + $token, + new Token(Token::T_NAME, 'BarfooBarfooBarBarfoo', 30), + ]); + + $error = SyntaxError::forToken($tokens, $token, 'invalid', 20); + self::assertEquals(<<<'EOT' + +invalid: + + … oobarFoobarFoobarBarfooBarfooBarfooBarBa … + -----------------^^^^^^ +EOT + , $error->getMessage()); + } +} diff --git a/tests/Unit/Expression/Func/CoalesceFunctionTest.php b/tests/Unit/Expression/Func/CoalesceFunctionTest.php new file mode 100644 index 000000000..770c6544d --- /dev/null +++ b/tests/Unit/Expression/Func/CoalesceFunctionTest.php @@ -0,0 +1,33 @@ +eval( + new CoalesceFunction(), + '10, null' + )->value()); + self::assertEquals(null, $this->eval( + new CoalesceFunction(), + 'null, null' + )->value()); + self::assertEquals(null, $this->eval( + new CoalesceFunction(), + '' + )->value()); + self::assertEquals(null, $this->eval( + new CoalesceFunction(), + 'null' + )->value()); + self::assertEquals(12, $this->eval( + new CoalesceFunction(), + 'null,12,null' + )->value()); + } +} diff --git a/tests/Unit/Expression/Func/DisplayAsTimeFunctionTest.php b/tests/Unit/Expression/Func/DisplayAsTimeFunctionTest.php new file mode 100644 index 000000000..c26d2e4b1 --- /dev/null +++ b/tests/Unit/Expression/Func/DisplayAsTimeFunctionTest.php @@ -0,0 +1,58 @@ +eval( + new DisplayAsTimeFunction(), + '7, "milliseconds", 3, "throughput"' + )); + } + + public function testDisplayTwoArgs(): void + { + self::assertEquals(new DisplayAsTimeNode( + new IntegerNode(7), + new UnitNode(new StringNode("milliseconds")) + ), $this->eval( + new DisplayAsTimeFunction(), + '7, "milliseconds"' + )); + } + + public function testDisplayWithNullUnit(): void + { + self::assertEquals(new DisplayAsTimeNode( + new IntegerNode(7), + new UnitNode(new StringNode("microseconds")) + ), $this->eval( + new DisplayAsTimeFunction(), + '7, null' + )); + } + + public function testDisplayWithInvalidType(): void + { + $this->expectException(EvaluationError::class); + $this->eval( + new DisplayAsTimeFunction(), + '7, 10' + ); + } +} diff --git a/tests/Unit/Expression/Func/FirstFunctionTest.php b/tests/Unit/Expression/Func/FirstFunctionTest.php new file mode 100644 index 000000000..9b4755494 --- /dev/null +++ b/tests/Unit/Expression/Func/FirstFunctionTest.php @@ -0,0 +1,25 @@ +eval( + new FirstFunction(), + '["one", "two"]' + )->value()); + } + + public function testFirstEmpty(): void + { + $this->expectException(EvaluationError::class); + + $this->eval(new FirstFunction(), '[]')->value(); + } +} diff --git a/tests/Unit/Expression/Func/FormatFunctionTest.php b/tests/Unit/Expression/Func/FormatFunctionTest.php new file mode 100644 index 000000000..51d380ca3 --- /dev/null +++ b/tests/Unit/Expression/Func/FormatFunctionTest.php @@ -0,0 +1,32 @@ +eval( + new FormatFunction(), + '"%s %s %s", 2, "foo", 6' + )->value()); + } + + public function testTooManyPlaceholders(): void + { + if (PHP_VERSION_ID < 80000) { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('format()'); + } else { + $this->expectException(ArgumentCountError::class); + } + $this->eval( + new FormatFunction(), '"%s %s %s %s", 2, "foo", 6' + ); + } +} diff --git a/tests/Unit/Expression/Func/JoinFunctionTest.php b/tests/Unit/Expression/Func/JoinFunctionTest.php new file mode 100644 index 000000000..6fe27a74d --- /dev/null +++ b/tests/Unit/Expression/Func/JoinFunctionTest.php @@ -0,0 +1,25 @@ +eval( + new JoinFunction(), + '",", ["one", "two"]' + )->value()); + } + + public function testJoinEmpty(): void + { + self::assertEquals('', $this->eval( + new JoinFunction(), + '",", []' + )->value()); + } +} diff --git a/tests/Unit/Expression/Func/MaxFunctionTest.php b/tests/Unit/Expression/Func/MaxFunctionTest.php new file mode 100644 index 000000000..5049fb36d --- /dev/null +++ b/tests/Unit/Expression/Func/MaxFunctionTest.php @@ -0,0 +1,18 @@ +eval( + new MaxFunction(), + "[2, 4, 6]" + )); + } +} diff --git a/tests/Unit/Expression/Func/MeanFunctionTest.php b/tests/Unit/Expression/Func/MeanFunctionTest.php new file mode 100644 index 000000000..5503960a4 --- /dev/null +++ b/tests/Unit/Expression/Func/MeanFunctionTest.php @@ -0,0 +1,18 @@ +eval( + new MeanFunction(), + "[2, 4, 6]" + )); + } +} diff --git a/tests/Unit/Expression/Func/MinFunctionTest.php b/tests/Unit/Expression/Func/MinFunctionTest.php new file mode 100644 index 000000000..aac8188f2 --- /dev/null +++ b/tests/Unit/Expression/Func/MinFunctionTest.php @@ -0,0 +1,18 @@ +eval(new MinFunction(), "[2, 4, 6]") + ); + } +} diff --git a/tests/Unit/Expression/Func/ModeFunctionTest.php b/tests/Unit/Expression/Func/ModeFunctionTest.php new file mode 100644 index 000000000..912dd8962 --- /dev/null +++ b/tests/Unit/Expression/Func/ModeFunctionTest.php @@ -0,0 +1,17 @@ +eval( + new ModeFunction(), + '[2, 4, 6]' + )->value(), 0.1); + } +} diff --git a/tests/Unit/Expression/Func/PercentDifferenceFunctionTest.php b/tests/Unit/Expression/Func/PercentDifferenceFunctionTest.php new file mode 100644 index 000000000..3d581d93f --- /dev/null +++ b/tests/Unit/Expression/Func/PercentDifferenceFunctionTest.php @@ -0,0 +1,25 @@ +eval( + new PercentDifferenceFunction(), + "2, 1" + )->value()); + } + + public function testEvalWithTolerance(): void + { + self::assertEquals(-50, $this->eval( + new PercentDifferenceFunction(), + "2, 1, 2" + )->value()); + } +} diff --git a/tests/Unit/Expression/Func/StDevFunctionTest.php b/tests/Unit/Expression/Func/StDevFunctionTest.php new file mode 100644 index 000000000..2e256f802 --- /dev/null +++ b/tests/Unit/Expression/Func/StDevFunctionTest.php @@ -0,0 +1,17 @@ +eval( + new StDevFunction(), + '[1, 2]' + )->value()); + } +} diff --git a/tests/Unit/Expression/Func/VarianceFunctionTest.php b/tests/Unit/Expression/Func/VarianceFunctionTest.php new file mode 100644 index 000000000..a516c712a --- /dev/null +++ b/tests/Unit/Expression/Func/VarianceFunctionTest.php @@ -0,0 +1,17 @@ +eval( + new VarianceFunction(), + '[1, 2]' + )->value()); + } +} diff --git a/tests/Unit/Expression/FunctionTestCase.php b/tests/Unit/Expression/FunctionTestCase.php new file mode 100644 index 000000000..f7418d9a1 --- /dev/null +++ b/tests/Unit/Expression/FunctionTestCase.php @@ -0,0 +1,15 @@ + $callable + ]))->execute('func', $this->parse(sprintf('[%s]', $argString))->value()); + } +} diff --git a/tests/Unit/Expression/LexerTest.php b/tests/Unit/Expression/LexerTest.php new file mode 100644 index 000000000..0a61ae40e --- /dev/null +++ b/tests/Unit/Expression/LexerTest.php @@ -0,0 +1,26 @@ +lex(<<<'EOT' +10 +< +20 +EOT + ); + self::assertCount(3, $tokens); + } + + private function lex(string $string): Tokens + { + return (new Lexer([], []))->lex($string); + } +} diff --git a/tests/Unit/Expression/NodeEvaluator/MemoisedNodeEvaluatorTest.php b/tests/Unit/Expression/NodeEvaluator/MemoisedNodeEvaluatorTest.php new file mode 100644 index 000000000..9989871cd --- /dev/null +++ b/tests/Unit/Expression/NodeEvaluator/MemoisedNodeEvaluatorTest.php @@ -0,0 +1,64 @@ +inner = $this->prophesize(NodeEvaluator::class); + + $this->evaluator = $this->prophesize(Evaluator::class); + $this->nodeEvaluator = new MemoisedNodeEvaluator($this->inner->reveal()); + } + + public function testEvaluateOnce(): void + { + $node = new StringNode("fo"); + $this->inner->evaluate( + $this->evaluator->reveal(), + $node, + [] + )->willReturn(new IntegerNode(1))->shouldBeCalledOnce(); + + $this->nodeEvaluator->evaluate($this->evaluator->reveal(), $node, []); + $this->nodeEvaluator->evaluate($this->evaluator->reveal(), $node, []); + $this->nodeEvaluator->evaluate($this->evaluator->reveal(), $node, []); + $this->nodeEvaluator->evaluate($this->evaluator->reveal(), $node, []); + } + + public function testVariesWithParameters(): void + { + $node = new StringNode("fo"); + + $this->inner->evaluate($this->evaluator->reveal(), $node, Argument::any())->willReturn( + new IntegerNode(1), + new IntegerNode(1) + )->shouldBeCalledTimes(2); + + $this->nodeEvaluator->evaluate($this->evaluator->reveal(), $node, []); + $this->nodeEvaluator->evaluate($this->evaluator->reveal(), $node, ['foo' => 'bar']); + $this->nodeEvaluator->evaluate($this->evaluator->reveal(), $node, []); + $this->nodeEvaluator->evaluate($this->evaluator->reveal(), $node, []); + } +} diff --git a/tests/Unit/Expression/NodeEvaluatorsTest.php b/tests/Unit/Expression/NodeEvaluatorsTest.php new file mode 100644 index 000000000..a61600935 --- /dev/null +++ b/tests/Unit/Expression/NodeEvaluatorsTest.php @@ -0,0 +1,41 @@ + + */ + private $evaluators; + + /** + * @var NodeEvaluatorsTest + */ + private $evaluator; + + + protected function setUp(): void + { + $this->evaluators = $this->container()->get(NodeEvaluator::class); + $this->evaluator = $this->container()->get(Evaluator::class); + } + + public function testEvaluate(): void + { + self::assertEquals(new IntegerNode(1), $this->evaluators->evaluate( + $this->evaluator, + new ParameterNode(['one']), + [ + 'one' => 1, + ] + )); + } +} diff --git a/tests/Unit/Expression/NodePrinter/HighlightingNodePrinterTest.php b/tests/Unit/Expression/NodePrinter/HighlightingNodePrinterTest.php new file mode 100644 index 000000000..78e9362f4 --- /dev/null +++ b/tests/Unit/Expression/NodePrinter/HighlightingNodePrinterTest.php @@ -0,0 +1,75 @@ +prophesize(Printer::class); + $printer->print(Argument::type(Node::class)); + + $nodePrinter = $this->prophesize(NodePrinter::class); + $nodePrinter->print($printer, $node)->willReturn('test'); + + self::assertEquals($expected, (new HighlightingNodePrinter( + $nodePrinter->reveal(), + new ArrayTheme($map) + ))->print($printer->reveal(), $node)); + } + + /** + * @return Generator + */ + public function providePrint(): Generator + { + yield 'decorates' => [ + new TestNode(), + [ + TestNode::class => 'fg=foo' + ], + 'test', + ]; + + yield 'does not decorate non-mapped nodes' => [ + new NotMappedNode(), + [ + TestNode::class => 'fg=foo' + ], + 'test', + ]; + + yield 'evaluates callback' => [ + new TestNode(), + [ + TestNode::class => function (Node $node) { + return 'fg=black'; + } + ], + 'test', + ]; + } +} + +class NotMappedNode implements Node +{ +} + +class TestNode implements Node +{ +} diff --git a/tests/Unit/Expression/Parselet/ArgumentListParseletTest.php b/tests/Unit/Expression/Parselet/ArgumentListParseletTest.php new file mode 100644 index 000000000..154e366f6 --- /dev/null +++ b/tests/Unit/Expression/Parselet/ArgumentListParseletTest.php @@ -0,0 +1,61 @@ + + */ + public function provideParse(): Generator + { + yield 'single value is not an argument list' => [ + '12', + new IntegerNode(12) + ]; + + yield 'two comma separated values are an argument list' => [ + '12,24', + new ArgumentListNode([ + new IntegerNode(12), + new IntegerNode(24) + ]) + ]; + + yield 'multiple values' => [ + '12, 12, 12', + new ArgumentListNode([ + new IntegerNode(12), + new IntegerNode(12), + new IntegerNode(12) + ]) + ]; + } + + /** + * {@inheritDoc} + */ + public function provideEvaluate(): Generator + { + yield [ + '12, 12', + [], + '12, 12' + ]; + } + + /** + * {@inheritDoc} + */ + public function providePrint(): Generator + { + yield ['12, 12']; + + yield ['12, 12, 12, 12']; + } +} diff --git a/tests/Unit/Expression/Parselet/ArithmeticOperatorParseletTest.php b/tests/Unit/Expression/Parselet/ArithmeticOperatorParseletTest.php new file mode 100644 index 000000000..9bd5b0af1 --- /dev/null +++ b/tests/Unit/Expression/Parselet/ArithmeticOperatorParseletTest.php @@ -0,0 +1,46 @@ + + */ + public function provideParse(): Generator + { + yield [ + '1 * 2', + new ArithmeticOperatorNode( + new IntegerNode(1), + '*', + new IntegerNode(2) + ) + ]; + } + + /** + * {@inheritDoc} + */ + public function provideEvaluate(): Generator + { + yield ['1 * 2', [], '2']; + + yield ['1 + 2', [], '3']; + + yield ['1 + 2 * 3 / 5 * 6 - 1', [], (string)(1 + 2 * 3 / 5 * 6 - 1)]; + } + + /** + * {@inheritDoc} + */ + public function providePrint(): Generator + { + yield from $this->providePrintFromEvaluate(); + } +} diff --git a/tests/Unit/Expression/Parselet/BooleanParseletTest.php b/tests/Unit/Expression/Parselet/BooleanParseletTest.php new file mode 100644 index 000000000..66511d392 --- /dev/null +++ b/tests/Unit/Expression/Parselet/BooleanParseletTest.php @@ -0,0 +1,44 @@ + + */ + public function provideParse(): Generator + { + yield [ + 'true', + new BooleanNode(true), + ]; + + yield [ + 'false', + new BooleanNode(false), + ]; + } + + /** + * {@inheritDoc} + */ + public function provideEvaluate(): Generator + { + yield ['true', [], 'true']; + + yield ['false', [], 'false']; + } + + /** + * {@inheritDoc} + */ + public function providePrint(): Generator + { + yield from $this->providePrintFromEvaluate(); + } +} diff --git a/tests/Unit/Expression/Parselet/ComparisonParseletTest.php b/tests/Unit/Expression/Parselet/ComparisonParseletTest.php new file mode 100644 index 000000000..e3ca317b0 --- /dev/null +++ b/tests/Unit/Expression/Parselet/ComparisonParseletTest.php @@ -0,0 +1,93 @@ + + */ + public function provideParse(): Generator + { + yield [ + '1 < 2', + new ComparisonNode( + new IntegerNode(1), + '<', + new IntegerNode(2) + ) + ]; + } + + /** + * {@inheritDoc} + */ + public function provideEvaluate(): Generator + { + yield ['1 < 2', [], 'true']; + + yield ['2 < 1', [], 'false']; + + yield ['2 < 2', [], 'false']; + + yield ['1 <= 2', [], 'true']; + + yield ['2 <= 2', [], 'true']; + + yield ['3 <= 2', [], 'false']; + + yield ['2 = 2', [], 'true']; + + yield ['1 = 2', [], 'false']; + + yield ['2 > 1', [], 'true']; + + yield ['1 > 2', [], 'false']; + + yield ['2 > 2', [], 'false']; + + yield ['3 >= 2', [], 'true']; + + yield ['2 >= 2', [], 'true']; + + yield ['1 >= 2', [], 'false']; + + yield ['10 < 10 + 10 * 30 ms', [], 'true']; + + yield ['10 as ms <= 10 as ms', [], 'true']; + + yield ['"10" = "10"', [], 'true']; + } + + public function testErrorOnUnsupportedOperator(): void + { + $this->expectException(EvaluationError::class); + $this->evaluate($this->parse('"foo" < 2')); + } + + /** + * {@inheritDoc} + */ + public function providePrint(): Generator + { + yield ['1 < 2', []]; + + yield ['1 <= 2', []]; + + yield ['2 = 2', []]; + + yield ['2 > 1', []]; + + yield ['2 > 2', []]; + + yield ['3 >= 2', []]; + + yield ['10 as ms <= 10 as ms', [], '0.010ms <= 0.010ms']; + } +} diff --git a/tests/Unit/Expression/Parselet/ConcatParseletTest.php b/tests/Unit/Expression/Parselet/ConcatParseletTest.php new file mode 100644 index 000000000..521c0bd30 --- /dev/null +++ b/tests/Unit/Expression/Parselet/ConcatParseletTest.php @@ -0,0 +1,52 @@ + + */ + public function provideParse(): Generator + { + yield [ + '10 ~ 19', + new ConcatNode(new IntegerNode(10), new IntegerNode(19)), + ]; + + yield [ + '10 ~ " vs " ~ 19', + new ConcatNode( + new IntegerNode(10), + new ConcatNode( + new StringNode(" vs "), + new IntegerNode(19) + ) + ), + ]; + } + + /** + * {@inheritDoc} + */ + public function provideEvaluate(): Generator + { + yield ['"foo" ~ "bar"', [], 'foobar']; + + yield ['10 ~ "bar"', [], '10bar']; + } + + /** + * {@inheritDoc} + */ + public function providePrint(): Generator + { + yield 'does not show quotes' => ['10 ~ "bar"', [], '10bar']; + } +} diff --git a/tests/Unit/Expression/Parselet/DisplayAsParseletTest.php b/tests/Unit/Expression/Parselet/DisplayAsParseletTest.php new file mode 100644 index 000000000..0a2321077 --- /dev/null +++ b/tests/Unit/Expression/Parselet/DisplayAsParseletTest.php @@ -0,0 +1,112 @@ + + */ + public function provideParse(): Generator + { + yield [ + '1 ms as microseconds', + new DisplayAsNode(new ValueWithUnitNode( + new IntegerNode(1), + new UnitNode(new StringNode('ms')) + ), new UnitNode(new StringNode('microseconds'))) + ]; + + yield [ + '1 ms as "microseconds"', + new DisplayAsNode( + new ValueWithUnitNode(new IntegerNode(1), new UnitNode(new StringNode('ms'))), + new UnitNode(new StringNode('microseconds')) + ) + ]; + + yield [ + '1 ms as parameter', + new DisplayAsNode( + new ValueWithUnitNode(new IntegerNode(1), new UnitNode(new StringNode('ms'))), + new UnitNode(new ParameterNode(['parameter'])) + ) + ]; + + yield [ + '1 ms as parameter * 2', + new ArithmeticOperatorNode( + new DisplayAsNode( + new ValueWithUnitNode(new IntegerNode(1), new UnitNode(new StringNode('ms'))), + new UnitNode(new ParameterNode(['parameter'])) + ), + '*', + new IntegerNode(2) + ) + ]; + + yield [ + '1 ms as parameter precision 5', + new DisplayAsNode( + new ValueWithUnitNode(new IntegerNode(1), new UnitNode(new StringNode('ms'))), + new UnitNode(new ParameterNode(['parameter'])), + new IntegerNode(5) + ) + ]; + + yield [ + '1 ms as parameter precision 5 * 5', + new ArithmeticOperatorNode( + new DisplayAsNode( + new ValueWithUnitNode(new IntegerNode(1), new UnitNode(new StringNode('ms'))), + new UnitNode(new ParameterNode(['parameter'])), + new IntegerNode(5) + ), + '*', + new IntegerNode(5) + ) + ]; + } + + /** + * {@inheritDoc} + */ + public function provideEvaluate(): Generator + { + yield ['1 s as milliseconds precision foobar', [ + 'foobar' => 2 + ], '1,000.00ms']; + + yield ['1 s as milliseconds', [], '1,000.000ms']; + + yield ['1 s as foobar', [ + 'foobar' => 'milliseconds', + ], '1,000.000ms']; + } + + /** + * {@inheritDoc} + */ + public function providePrint(): Generator + { + yield 'units are interpreted literally' => ['1 s as milliseconds', [], '1 s as milliseconds']; + + yield 'int to milliseconds' => ['1000 as milliseconds', [], '1.000ms']; + + yield 'int to milliseconds precision 2' => ['1000 as milliseconds precision 2', [], '1.00ms']; + + yield 'int to bytes' => ['1000 as k', [], '1.000kb']; + + yield ['100000 as seconds < 1 second', [], '0.100s < 1 second']; + } +} diff --git a/tests/Unit/Expression/Parselet/FloatParseletTest.php b/tests/Unit/Expression/Parselet/FloatParseletTest.php new file mode 100644 index 000000000..1f2507a21 --- /dev/null +++ b/tests/Unit/Expression/Parselet/FloatParseletTest.php @@ -0,0 +1,41 @@ + + */ + public function provideParse(): Generator + { + yield [ + '1.2', + new FloatNode(1.2), + ]; + } + + /** + * {@inheritDoc} + */ + public function provideEvaluate(): Generator + { + yield [ + '1.2', + [], + '1.2' + ]; + } + + /** + * {@inheritDoc} + */ + public function providePrint(): Generator + { + yield from $this->providePrintFromEvaluate(); + } +} diff --git a/tests/Unit/Expression/Parselet/FunctionParseletTest.php b/tests/Unit/Expression/Parselet/FunctionParseletTest.php new file mode 100644 index 000000000..b73d8ba76 --- /dev/null +++ b/tests/Unit/Expression/Parselet/FunctionParseletTest.php @@ -0,0 +1,71 @@ + + */ + public function provideParse(): Generator + { + yield [ + 'foobar()', + new FunctionNode('foobar'), + ]; + + yield [ + 'foobar(12)', + new FunctionNode('foobar', + new ArgumentListNode([new IntegerNode(12)]) + ), + ]; + + yield [ + 'foobar(12, 14, 12 + 2)', + new FunctionNode( + 'foobar', + new ArgumentListNode([ + new IntegerNode(12), + new IntegerNode(14), + new ArithmeticOperatorNode(new IntegerNode(12), '+', new IntegerNode(2)) + ]) + ), + ]; + } + + /** + * {@inheritDoc} + */ + public function provideEvaluate(): Generator + { + yield [ + 'mode([12, 12])', + [], + '12' + ]; + } + + public function testEvaluateInvalidArguments(): void + { + $this->expectException(EvaluationError::class); + $this->expectExceptionMessage('mode'); + $this->evaluate($this->parse('mode(12)')); + } + + /** + * {@inheritDoc} + */ + public function providePrint(): Generator + { + yield from $this->providePrintFromEvaluate(); + } +} diff --git a/tests/Unit/Expression/Parselet/IntegerParseletTest.php b/tests/Unit/Expression/Parselet/IntegerParseletTest.php new file mode 100644 index 000000000..9abc24a2b --- /dev/null +++ b/tests/Unit/Expression/Parselet/IntegerParseletTest.php @@ -0,0 +1,41 @@ + + */ + public function provideParse(): Generator + { + yield [ + '1', + new IntegerNode(1), + ]; + } + + /** + * {@inheritDoc} + */ + public function provideEvaluate(): Generator + { + yield [ + '1', + [], + '1' + ]; + } + + /** + * {@inheritDoc} + */ + public function providePrint(): Generator + { + yield from $this->providePrintFromEvaluate(); + } +} diff --git a/tests/Unit/Expression/Parselet/ListParseletTest.php b/tests/Unit/Expression/Parselet/ListParseletTest.php new file mode 100644 index 000000000..5b47445cd --- /dev/null +++ b/tests/Unit/Expression/Parselet/ListParseletTest.php @@ -0,0 +1,44 @@ + + */ + public function provideParse(): Generator + { + yield 'single value is not an argument list' => [ + '[12]', + new ListNode([new IntegerNode(12)]) + ]; + } + + /** + * {@inheritDoc} + */ + public function provideEvaluate(): Generator + { + yield [ + '[12]', + [], + '[12]' + ]; + } + + /** + * {@inheritDoc} + */ + public function providePrint(): Generator + { + yield ['12, 12']; + + yield ['12, 12, 12, 12']; + } +} diff --git a/tests/Unit/Expression/Parselet/LogicalOperatorParseletTest.php b/tests/Unit/Expression/Parselet/LogicalOperatorParseletTest.php new file mode 100644 index 000000000..9cd5b86b0 --- /dev/null +++ b/tests/Unit/Expression/Parselet/LogicalOperatorParseletTest.php @@ -0,0 +1,48 @@ + + */ + public function provideParse(): Generator + { + yield [ + '1 and 2', + new LogicalOperatorNode( + new IntegerNode(1), + 'and', + new IntegerNode(2) + ) + ]; + } + + /** + * {@inheritDoc} + */ + public function provideEvaluate(): Generator + { + yield ['3 > 2 and 4 > 2', [], 'true']; + + yield ['3 > 2 and 4 < 2', [], 'false']; + + yield ['2 > 2 or 4 < 2', [], 'false']; + + yield ['4 < 2 or 2 < 4', [], 'true']; + } + + /** + * {@inheritDoc} + */ + public function providePrint(): Generator + { + yield from $this->providePrintFromEvaluate(); + } +} diff --git a/tests/Unit/Expression/Parselet/NullParseletTest.php b/tests/Unit/Expression/Parselet/NullParseletTest.php new file mode 100644 index 000000000..9e5bfb6c9 --- /dev/null +++ b/tests/Unit/Expression/Parselet/NullParseletTest.php @@ -0,0 +1,45 @@ + + */ + public function provideParse(): Generator + { + yield [ + 'null', + new NullNode(), + ]; + } + + /** + * {@inheritDoc} + */ + public function provideEvaluate(): Generator + { + yield [ + 'null', + [], + 'null' + ]; + } + + /** + * {@inheritDoc} + */ + public function providePrint(): Generator + { + yield [ + 'null', + [], + 'null', + ]; + } +} diff --git a/tests/Unit/Expression/Parselet/ParameterParseletTest.php b/tests/Unit/Expression/Parselet/ParameterParseletTest.php new file mode 100644 index 000000000..b9687e83f --- /dev/null +++ b/tests/Unit/Expression/Parselet/ParameterParseletTest.php @@ -0,0 +1,53 @@ + + */ + public function provideParse(): Generator + { + yield 'property' => [ + 'foo.bar', + new ParameterNode(['foo', 'bar']) + ]; + } + + /** + * {@inheritDoc} + */ + public function provideEvaluate(): Generator + { + yield [ + 'foo.bar', + ['foo' => ['bar' => 12]], + '12' + ]; + + yield [ + 'foo.bar', + ['foo' => ['bar' => 'foo']], + 'foo' + ]; + + yield [ + 'foo.bar', + ['foo' => ['bar' => null]], + 'null' + ]; + } + + /** + * {@inheritDoc} + */ + public function providePrint(): Generator + { + yield from $this->providePrintFromEvaluate(); + } +} diff --git a/tests/Unit/Expression/Parselet/ParenthesisParseletTest.php b/tests/Unit/Expression/Parselet/ParenthesisParseletTest.php new file mode 100644 index 000000000..74d726824 --- /dev/null +++ b/tests/Unit/Expression/Parselet/ParenthesisParseletTest.php @@ -0,0 +1,54 @@ + + */ + public function provideParse(): Generator + { + yield [ + '(1)', + new ParenthesisNode(new IntegerNode(1)), + ]; + + yield [ + '(1 + 1)', + new ParenthesisNode( + new ArithmeticOperatorNode( + new IntegerNode(1), + '+', + new IntegerNode(1) + ) + ), + ]; + } + + /** + * {@inheritDoc} + */ + public function provideEvaluate(): Generator + { + yield ['(1 + 2) * 3', [], '9']; + + yield ['1 + 2 * 3', [], '7']; + + yield ['1 + (2 * 3)', [], '7']; + } + + /** + * {@inheritDoc} + */ + public function providePrint(): Generator + { + yield from $this->providePrintFromEvaluate(); + } +} diff --git a/tests/Unit/Expression/Parselet/StringParseletTest.php b/tests/Unit/Expression/Parselet/StringParseletTest.php new file mode 100644 index 000000000..acd121a18 --- /dev/null +++ b/tests/Unit/Expression/Parselet/StringParseletTest.php @@ -0,0 +1,45 @@ + + */ + public function provideParse(): Generator + { + yield [ + '"1.2"', + new StringNode("1.2"), + ]; + } + + /** + * {@inheritDoc} + */ + public function provideEvaluate(): Generator + { + yield [ + '"1.2"', + [], + '1.2' + ]; + } + + /** + * {@inheritDoc} + */ + public function providePrint(): Generator + { + yield [ + '"1.2"', + [], + '1.2', + ]; + } +} diff --git a/tests/Unit/Expression/Parselet/TolerableParseletTest.php b/tests/Unit/Expression/Parselet/TolerableParseletTest.php new file mode 100644 index 000000000..fcdc87a32 --- /dev/null +++ b/tests/Unit/Expression/Parselet/TolerableParseletTest.php @@ -0,0 +1,98 @@ + + */ + public function provideParse(): Generator + { + yield [ + '1 +/- 1', + new TolerableNode(new IntegerNode(1), new IntegerNode(1)) + ]; + + yield [ + '2 < 1 +/- 1', + new ComparisonNode( + new IntegerNode(2), + '<', + new TolerableNode( + new IntegerNode(1), + new IntegerNode(1) + ) + ) + ]; + + yield [ + '2 < 1 +/- 1 or 5 > 10 +/- 10%', + new LogicalOperatorNode( + new ComparisonNode( + new IntegerNode(2), + '<', + new TolerableNode( + new IntegerNode(1), + new IntegerNode(1) + ) + ), + 'or', + new ComparisonNode( + new IntegerNode(5), + '>', + new TolerableNode( + new IntegerNode(10), + new PercentageNode(new IntegerNode(10)) + ) + ) + ) + ]; + + yield [ + '1 +/- 10%', + new TolerableNode( + new IntegerNode(1), + new PercentageNode(new IntegerNode(10)) + ) + ]; + } + + /** + * {@inheritDoc} + */ + public function provideEvaluate(): Generator + { + yield ['1 +/- 1', [], '1 ± 1']; + + yield ['2 < 2 +/- 1', [], '~true']; + + yield ['11 < 10 +/- 10%', [], '~true']; + + yield ['12 < 10 +/- 10%', [], 'false']; + + yield ['12 < 10 +/- 10%', [], 'false']; + + yield ['10 +/- 10%', [], '10 ± 1']; + + yield ['1000 +/- 10%', [], '1000 ± 100']; + + yield ['1000 as ms +/- 10%', [], '1.000ms ± 0.100ms']; + } + + /** + * {@inheritDoc} + */ + public function providePrint(): Generator + { + yield ['1 +/- 2', [], '1 ± 2']; + } +} diff --git a/tests/Unit/Expression/Parselet/ValueWithUnitParseletTest.php b/tests/Unit/Expression/Parselet/ValueWithUnitParseletTest.php new file mode 100644 index 000000000..30ba48db6 --- /dev/null +++ b/tests/Unit/Expression/Parselet/ValueWithUnitParseletTest.php @@ -0,0 +1,39 @@ + + */ + public function provideParse(): Generator + { + yield ['1 ms', new ValueWithUnitNode(new IntegerNode(1), new UnitNode(new StringNode('ms')))]; + + yield ['1ms', new ValueWithUnitNode(new IntegerNode(1), new UnitNode(new StringNode('ms')))]; + } + + /** + * {@inheritDoc} + */ + public function provideEvaluate(): Generator + { + yield ['1 s', [], (string)1E6]; + } + + /** + * {@inheritDoc} + */ + public function providePrint(): Generator + { + yield from $this->providePrintFromEvaluate(); + } +} diff --git a/tests/Unit/Expression/ParseletTestCase.php b/tests/Unit/Expression/ParseletTestCase.php new file mode 100644 index 000000000..feb603a2b --- /dev/null +++ b/tests/Unit/Expression/ParseletTestCase.php @@ -0,0 +1,83 @@ +parse($expr)); + } + + /** + * @param parameters $params + */ + public function evaluate(Node $node, array $params = []): Node + { + return $this->container()->get( + Evaluator::class + )->evaluate($node, $params); + } + + /** + * @param parameters $params + */ + public function print(Node $node, array $params = []): string + { + return $this->container()->get(Printer::class)->print($node, $params); + } + + /** + * @dataProvider provideEvaluate + * + * @param parameters $params + */ + public function testEvaluate(string $expr, array $params, string $expected): void + { + self::assertEquals( + $expected, + $this->print($this->evaluate($this->parse($expr), $params)) + ); + } + + protected function providePrintFromEvaluate(): Generator + { + foreach ($this->provideEvaluate() as [$expr, $params]) { + yield [$expr, $params]; + } + } + + /** + * @dataProvider providePrint + * + * @param parameters $params + */ + public function testPrint(string $expr, array $params = [], string $expected = null): void + { + $result = $this->print($this->parse($expr), $params); + self::assertEquals($expected ?: $expr, $result); + } + + /** + * @return Generator + */ + abstract public function provideParse(): Generator; + + /** + * @return Generator + */ + abstract public function provideEvaluate(): Generator; + + /** + * @return Generator + */ + abstract public function providePrint(): Generator; +} diff --git a/tests/Unit/Expression/ParserTest.php b/tests/Unit/Expression/ParserTest.php new file mode 100644 index 000000000..03709df9b --- /dev/null +++ b/tests/Unit/Expression/ParserTest.php @@ -0,0 +1,19 @@ +expectException(SyntaxError::class); + $this->expectExceptionMessage(<<<'EOT' +Unexpected "name" at end of expression: +EOT + ); + + $this->parse('1 * 2 nasdfnuasdf'); + } +} diff --git a/tests/Unit/Expression/ParserTestCase.php b/tests/Unit/Expression/ParserTestCase.php new file mode 100644 index 000000000..57afb9a9c --- /dev/null +++ b/tests/Unit/Expression/ParserTestCase.php @@ -0,0 +1,20 @@ +container(); + + return $container->get( + Parser::class + )->parse($container->get(Lexer::class)->lex($expr)); + } +} diff --git a/tests/Unit/Expression/Printer/BareValuePrinterTest.php b/tests/Unit/Expression/Printer/BareValuePrinterTest.php new file mode 100644 index 000000000..9e741a097 --- /dev/null +++ b/tests/Unit/Expression/Printer/BareValuePrinterTest.php @@ -0,0 +1,45 @@ +create()->print(new StringNode('hello'), [])); + } + + public function testReturnsDefaultTextIfNotPrintable(): void + { + self::assertEquals('??', $this->create()->print(new FunctionNode('hello'), [])); + } + + public function testCastsToString(): void + { + self::assertEquals('', $this->create()->print(new NullNode(), [])); + } + + public function testPrintsList(): void + { + self::assertEquals('[1, 2]', $this->create()->print( + new ListNode([ + new IntegerNode(1), + new IntegerNode(2), + ]), + [] + )); + } + + private function create(): BareValuePrinter + { + return new BareValuePrinter(); + } +} diff --git a/tests/Unit/Expression/Printer/EvaluatingPrinterTest.php b/tests/Unit/Expression/Printer/EvaluatingPrinterTest.php new file mode 100644 index 000000000..160322c6d --- /dev/null +++ b/tests/Unit/Expression/Printer/EvaluatingPrinterTest.php @@ -0,0 +1,61 @@ +container()->get(NodePrinter::class), + $this->container()->get(Evaluator::class), + [ + FunctionNode::class + ] + ); + + self::assertEquals( + $expected, + $printer->print($this->parse( + $expr + ), []) + ); + } + + public function provideEvaluate(): Generator + { + yield [ + '2 > 10', + [], + '2 > 10', + ]; + + yield [ + 'mean([1, 2, 3])', + [], + '2', + ]; + + yield [ + 'mean([1, 2, 3]) / 2', + [], + '2 / 2', + ]; + + yield [ + 'mode([1, 2, 3])', + [], + '1.998043052838', + ]; + } +} diff --git a/tests/Unit/Expression/Theme/Util/GradientTest.php b/tests/Unit/Expression/Theme/Util/GradientTest.php new file mode 100644 index 000000000..e669244aa --- /dev/null +++ b/tests/Unit/Expression/Theme/Util/GradientTest.php @@ -0,0 +1,140 @@ +to( + Color::fromHex($end), + $steps + )->toArray()); + } + + /** + * @return Generator + */ + public function provideGradient(): Generator + { + yield [ + '000000', + '640000', + 4, + [ + '000000', + '190000', + '320000', + '4b0000', + '640000', + ] + ]; + + yield [ + '000000', + '640ff0', + 2, + [ + '000000', + '320778', + '640ff0', + ] + ]; + + yield [ + '000000', + '640ff0', + 1, + [ + '000000', + '640ff0', + ] + ]; + } + + /** + * @dataProvider provideInvalidStep + * + * @param int|float $step + */ + public function testInvalidStep($step): void + { + $this->expectException(RuntimeException::class); + $this->expectErrorMessage('more than zero'); + Gradient::start(Color::fromHex('00000'))->to(Color::fromHex('aa0000'), 0); + } + + /** + * @return Generator> + */ + public function provideInvalidStep(): Generator + { + yield [ 0 ]; + + yield [ 0.00000001 ]; + + yield [ -1 ]; + } + + /** + * @dataProvider provideColorAtPercentile + */ + public function testColorAtPercentile(Gradient $gradient, int $percentile, Color $expected): void + { + self::assertEquals($expected, $gradient->colorAtPercentile($percentile)); + } + + /** + * @return Generator + */ + public function provideColorAtPercentile(): Generator + { + yield [ + Gradient::start(Color::fromHex('#000000')), + 1, + Color::fromHex('#000000') + ]; + + yield [ + Gradient::start(Color::fromRgb(0, 0, 0))->to(Color::fromRgb(0, 0, 10), 10), + 10, + Color::fromRgb(0, 0, 1) + ]; + + yield [ + Gradient::start(Color::fromRgb(0, 0, 0))->to(Color::fromRgb(0, 0, 10), 9), + 50, + Color::fromRgb(0, 0, 5) + ]; + + yield 'more than 100' => [ + Gradient::start(Color::fromRgb(0, 0, 0))->to(Color::fromRgb(0, 0, 10), 9), + 150, + Color::fromRgb(0, 0, 10) + ]; + + yield 'less than 100' => [ + Gradient::start(Color::fromRgb(0, 0, 0))->to(Color::fromRgb(0, 0, 10), 9), + -150, + Color::fromRgb(0, 0, 0) + ]; + } +} diff --git a/tests/Unit/Expression/TokensTest.php b/tests/Unit/Expression/TokensTest.php new file mode 100644 index 000000000..0d9686791 --- /dev/null +++ b/tests/Unit/Expression/TokensTest.php @@ -0,0 +1,20 @@ +expectException(SyntaxError::class); + $tokens = new Tokens([ + new Token(Token::T_NAME, 'foo', 0), + ]); + $tokens->chomp(Token::T_LOGICAL_AND); + } +} diff --git a/tests/Unit/Extension/RunnerExtensionTest.php b/tests/Unit/Extension/RunnerExtensionTest.php new file mode 100644 index 000000000..3151d8d8e --- /dev/null +++ b/tests/Unit/Extension/RunnerExtensionTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Extension; + +use PhpBench\Benchmark\Metadata\Driver\ConfigDriver; +use PhpBench\Extension\CoreExtension; +use PhpBench\Extension\RunnerExtension; +use PhpBench\Tests\IntegrationTestCase; + +class RunnerExtensionTest extends IntegrationTestCase +{ + /** + * It should expand the "path" parameter to an absolute path if it is relative. + */ + public function testRelativizePath(): void + { + $container = $this->container([ + RunnerExtension::PARAM_PATH => 'hello', + CoreExtension::PARAM_CONFIG_PATH => '/path/to/phpbench.json', + ]); + $this->assertEquals(['/path/to/hello'], $container->getParameter(RunnerExtension::PARAM_PATH)); + } + + public function testConfigDriver(): void + { + $container = $this->container([ + RunnerExtension::PARAM_RUNNER_ASSERT => 'foobar', + RunnerExtension::PARAM_RUNNER_EXECUTOR => 'foobar', + RunnerExtension::PARAM_RUNNER_FORMAT => 'foobar', + RunnerExtension::PARAM_RUNNER_ITERATIONS => 12, + RunnerExtension::PARAM_RUNNER_OUTPUT_MODE => 'mode', + RunnerExtension::PARAM_RUNNER_OUTPUT_TIME_UNIT => 'foobar', + RunnerExtension::PARAM_RUNNER_REVS => 32, + RunnerExtension::PARAM_RUNNER_TIMEOUT => 12, + RunnerExtension::PARAM_RUNNER_WARMUP => 12, + ]); + $container->get(ConfigDriver::class); + $this->addToAssertionCount(1); + } +} diff --git a/tests/Unit/Json/JsonDecoderTest.php b/tests/Unit/Json/JsonDecoderTest.php new file mode 100644 index 000000000..e5708ed9a --- /dev/null +++ b/tests/Unit/Json/JsonDecoderTest.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Json; + +use PhpBench\Json\JsonDecoder; +use PhpBench\Tests\TestCase; + +class JsonDecoderTest extends TestCase +{ + private $jsonDecoder; + + protected function setUp(): void + { + $this->jsonDecoder = new JsonDecoder(); + } + + /** + * It should convert "non-strict" JSON to JSON. + * + * @dataProvider provideNormalizer + */ + public function testNormalizer($string, $expected): void + { + $result = $this->jsonDecoder->decode($string); + $this->assertEquals($expected, $result); + } + + public function provideNormalizer() + { + return [ + [ + 'iterations: false', + [ + 'iterations' => false, + ], + ], + [ + 'foo: false, bar: true, baz: [ 10, "10", true]', + [ + 'foo' => false, + 'bar' => true, + 'baz' => [10, '10', true], + ], + ], + [ + '{"extends": "aggregate", "foo": ["bar"]}', + ['extends' => 'aggregate', 'foo' => ['bar']], + ], + [ + '{extends: aggregate, foo: ["bar"]}', + ['extends' => 'aggregate', 'foo' => ['bar']], + ], + [ + '{$eq: "bar\"foo"}', + ['$eq' => 'bar"foo'], + ], + [ + '{foobar: "barfoo"}', + ['foobar' => 'barfoo'], + ], + [ + 'foobar: "barfoo"', + ['foobar' => 'barfoo'], + ], + [ + 'foobar: "barfoo"', + ['foobar' => 'barfoo'], + ], + [ + 'foobar": "barfoo"', + ['foobar' => 'barfoo'], + ], + [ + '$and: [ {$gt: {date: "2016-01-30 09:27"}}, {$eq: {subject: "benchMySubject"}}]', + [ + '$and' => [ + [ + '$gt' => [ + 'date' => '2016-01-30 09:27', + ], + ], + [ + '$eq' => [ + 'subject' => 'benchMySubject', + ], + ], + ], + ], + ], + [ + 'foo: 10', + [ + 'foo' => 10, + ], + ], + [ + 'foo.revs: 1000', + [ + 'foo.revs' => 1000, + ], + ], + [ + '10: 10', + [ + 10 => 10, + ], + ], + [ + 'foobar[barfoo]: 5', + ['foobar[barfoo]' => 5], + ], + [ + 'foo_bar: 5', + ['foo_bar' => 5], + ], + [ + 'generator: "table", compare: "subject", compare_fields:[ "mode"], break: ["revs"], cols: ["benchmark"], sort: {"subject:benchGetOptimized:mode": "asc"}', + [ + 'generator' => 'table', + 'compare' => 'subject', + 'compare_fields' => ['mode'], + 'break' => ['revs'], + 'cols' => ['benchmark'], + 'sort' => ['subject:benchGetOptimized:mode' => 'asc'], + ], + ], + ]; + } +} diff --git a/tests/Unit/Logger/ConsoleLoggerTest.php b/tests/Unit/Logger/ConsoleLoggerTest.php new file mode 100644 index 000000000..275817edf --- /dev/null +++ b/tests/Unit/Logger/ConsoleLoggerTest.php @@ -0,0 +1,42 @@ +createOutput(); + $this->createLogger($output, false)->debug('asd'); + $this->createLogger($output, false)->notice('asd'); + $this->createLogger($output, false)->info('asd'); + self::assertEmpty($output->fetch()); + } + + public function testLogInfoWhenDebug(): void + { + $output = $this->createOutput(); + $this->createLogger($output, true)->debug('asd'); + self::assertNotEmpty($output->fetch()); + $this->createLogger($output, true)->info('asd'); + self::assertNotEmpty($output->fetch()); + $this->createLogger($output, true)->notice('asd'); + self::assertNotEmpty($output->fetch()); + } + + private function createOutput(): BufferedOutput + { + return new BufferedOutput(); + } + + private function createLogger(OutputInterface $output, bool $debug): LoggerInterface + { + return new ConsoleLogger($output, $debug); + } +} diff --git a/tests/Unit/Math/DistributionTest.php b/tests/Unit/Math/DistributionTest.php new file mode 100644 index 000000000..dcce99c34 --- /dev/null +++ b/tests/Unit/Math/DistributionTest.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Math; + +use LogicException; +use PhpBench\Math\Distribution; +use PhpBench\Tests\TestCase; + +class DistributionTest extends TestCase +{ + /** + * It should return stats. + */ + public function testStats(): void + { + $distribution = new Distribution([ + -50, + 0, + 50, + 100, + ]); + + $this->assertEquals(100, $distribution->getSum()); + $this->assertEquals(25, $distribution->getMean()); + $this->assertEquals(-50, $distribution->getMin()); + $this->assertEquals(100, $distribution->getMax()); + $this->assertEquals(56, round($distribution->getStdev())); + $this->assertEquals(3125, $distribution->getVariance()); + $this->assertEquals(25, round($distribution->getMode())); + } + + /** + * It should be iterable. + */ + public function testIterator(): void + { + $distribution = new Distribution([ + 10, + 20, + 30, + ]); + $stats = iterator_to_array($distribution); + $this->assertEquals([ + 'min' => 10, + 'max' => 30, + 'sum' => 60, + 'stdev' => 8.1649658092773, + 'mean' => 20, + 'mode' => 20, + 'variance' => 66.666666666667, + 'rstdev' => 40.824829046386, + ], $stats); + } + + /** + * It should throw an exception if zero samples are given. + * + */ + public function testDistributionZero(): void + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('zero'); + new Distribution([]); + } + + /** + * It should allow "distributions" of 1 sample. + */ + public function testDistributionOne(): void + { + $distribution = new Distribution([10]); + iterator_to_array($distribution); + $this->addToAssertionCount(1); + } + + /** + * It should allow distributions with 0 values. + */ + public function testDistributionZeroValues(): void + { + $distribution = new Distribution([0, 0]); + iterator_to_array($distribution); + $this->addToAssertionCount(1); + } + + /** + * It should return all the stats. + */ + public function testReturnStats(): void + { + $distribution = new Distribution([10, 20]); + $stats = $distribution->getStats(); + + foreach ([ + 'min', 'max', 'sum', 'stdev', 'mean', 'mode', 'variance', 'rstdev', + ] as $key) { + $this->assertArrayHasKey($key, $stats); + } + $this->assertEquals(10, $stats['min']); + $this->assertEquals(20, $stats['max']); + } + + /** + * It should throw an exception if a non-recognized pre-computed statistic is passed. + * + */ + public function testNonRecognizedPreComputed(): void + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Unknown pre-computed stat(s) encountered: "bar_stat", "boo_stat"'); + new Distribution([10, 20], ['bar_stat' => 1, 'boo_stat' => 2]); + } +} diff --git a/tests/Unit/Math/KdeTest.php b/tests/Unit/Math/KdeTest.php new file mode 100644 index 000000000..d4348248e --- /dev/null +++ b/tests/Unit/Math/KdeTest.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Tests\Unit\Unit\Math; + +use InvalidArgumentException; +use OutOfBoundsException; +use PhpBench\Math\Kde; +use PhpBench\Math\Statistics; +use PhpBench\Tests\TestCase; + +class KdeTest extends TestCase +{ + /** + * It should evaluate a kernel distribution estimate over a given space. + * + * @dataProvider provideEvaluate + */ + public function testEvaluate($dataSet, $space, $bwMethod, $expected): void + { + $kde = new Kde($dataSet, $bwMethod); + $result = $kde->evaluate($space); + + // round result + $result = array_map(function ($v) { + return round($v, 8); + }, $result); + + $this->assertEquals($expected, $result); + } + + public function provideEvaluate() + { + return [ + [ + [ + 10, 20, 15, 5, + ], + Statistics::linspace(0, 9, 10), + 'silverman', + [ + 0.01537595, 0.0190706, 0.02299592, 0.02700068, 0.03092369, 0.0346125, 0.03794007, 0.0408159, 0.04318983, 0.04504829, + ], + ], + [ + [ + 10, 20, 15, 5, + ], + Statistics::linspace(0, 3, 4), + 'scott', + [ + 0.01480612, 0.01869787, 0.02286675, 0.02713209, + ], + ], + [ + [ + 10, 20, 15, 5, + ], + Statistics::linspace(0, 3, 4), + 'silverman', + [ + 0.01537595, 0.0190706, 0.02299592, 0.02700068, + ], + ], + ]; + } + + /** + * It should throw an exception if an invalid bandwidth method is given. + * + */ + public function testInvalidBandwidth(): void + { + $this->expectException(InvalidArgumentException::class); + new Kde([1, 2], 'foo'); + } + + /** + * It should throw an exception if the data set has zero elements. + * + */ + public function testNoElements(): void + { + $this->expectException(OutOfBoundsException::class); + new Kde([]); + } + + /** + * It should throw an exception if the data set has only a single element. + * + */ + public function testOneElement(): void + { + $this->expectException(OutOfBoundsException::class); + new Kde([1]); + } +} diff --git a/tests/Unit/Math/StatisticsTest.php b/tests/Unit/Math/StatisticsTest.php new file mode 100644 index 000000000..24099348b --- /dev/null +++ b/tests/Unit/Math/StatisticsTest.php @@ -0,0 +1,292 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Math; + +use Generator; +use InvalidArgumentException; +use PhpBench\Math\Statistics; +use PhpBench\Tests\TestCase; + +class StatisticsTest extends TestCase +{ + /** + * It should return the standard deviation. + */ + public function testStdev(): void + { + $this->assertEquals(1.4142, round(Statistics::stdev([1, 2, 3, 4, 5]), 4)); + $this->assertEquals(17.2116, round(Statistics::stdev([13, 23, 12, 44, 55]), 4)); + $this->assertEquals(0, round(Statistics::stdev([1]), 4)); + $this->assertEquals(0, round(Statistics::stdev([1, 1, 1]), 4)); + $this->assertEquals(2.47, round(Statistics::stdev([2, 6, 4, 1, 7, 3, 6, 1, 7, 1, 6, 5, 1, 1], true), 2)); + } + + public function testRStdev(): void + { + $this->assertEquals(47.1405, round(Statistics::rstdev([1, 2, 3, 4, 5]), 4)); + $this->assertEquals(33.3333, round(Statistics::rstdev([1, 2]), 4)); + $this->assertEquals(47.1405, round(Statistics::rstdev([1, 2], true), 4)); + } + + /** + * It should return the average. + */ + public function testMean(): void + { + $expected = 33 / 7; + $this->assertEquals($expected, Statistics::mean([2, 2, 2, 2, 2, 20, 3])); + } + + /** + * It should generate a linear space. + * + * @dataProvider provideLinearSpace + */ + public function testLinearSpace($min, $max, $steps, $endpoint, $expected): void + { + $result = Statistics::linspace($min, $max, $steps, $endpoint); + $this->assertEquals($expected, $result); + } + + public function provideLinearSpace() + { + return [ + [ + 2, 3, 5, + true, + [2, 2.25, 2.5, 2.75, 3], + ], + [ + 2, 10, 5, + true, + [2, 4, 6, 8, 10], + ], + [ + 2, 10, 5, + false, + [ + 2, + 3.6000000000000001, + 5.2000000000000002, + 6.8000000000000007, + 8.4000000000000004, + ], + ], + ]; + } + + /** + * It should throw an exception if the linspace min and max are the same number. + * + */ + public function testLinspaceMinMaxSame(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Min and max cannot be the same number: 4'); + Statistics::linspace(4, 4, 10); + } + + /** + * @dataProvider provideKdeMode + */ + public function testKdeMode($population, $space, $bandwidth, $expected): void + { + $result = Statistics::kdeMode($population, $space, $bandwidth); + $this->assertEquals($expected, round($result, 2)); + } + + public function provideKdeMode() + { + return [ + [ + [ + 10, 20, 15, 5, + ], + 10, + 'silverman', + 13.33, + ], + [ + [ + 10, 20, + ], + 10, + 'silverman', + 15.56, + ], + [ + // custom bandwidth, multimodal + [ + 10, 20, 15, 5, + ], + 10, + 0.2, + 12.5, + ], + [ + // only one element + [ + 10, + ], + 10, + 0.1, + 10, + ], + [ + // min and max the same + [ + 10, 10, 10, + ], + 10, + 0.1, + 10, + ], + ]; + } + + /** + * It should return histogram data. + * + * @dataProvider provideHistogram + */ + public function testHistogram(array $data, $steps, $lower, $upper, array $expected): void + { + $result = Statistics::histogram($data, $steps, $lower, $upper); + $this->assertEquals($expected, $result); + } + + public function provideHistogram() + { + return [ + [ + [10, 10, 2, 2], + 10, + null, null, + [ + 2 => 2, + '2.8' => 0, + '3.6' => 0, + '4.4' => 0, + '5.2' => 0, + '6' => 0, + '6.8' => 0, + '7.6' => 0, + '8.4' => 0, + '9.2' => 0, + 10 => 2, + ], + ], + [ + [1, 10, 2, 2, 2, 3, 2, 4], + 9, + null, null, + [ + 1 => 1, + 2 => 4, + 3 => 1, + 4 => 1, + 5 => 0, + 6 => 0, + 7 => 0, + 8 => 0, + 9 => 0, + 10 => 1, + ], + ], + [ + [ + -3, + -2, + -1, + 0, + 1, + 2, + 3, + ], + 10, + -3, + 3, + [ + -3 => 1, + '-2.4' => 1, + '-1.8' => 0, + '-1.2' => 1, + '-0.6' => 1, + '2.2204460492503E-16' => 0, + '0.6' => 1, + '1.2' => 0, + '1.8' => 1, + '2.4' => 1, + 3 => 0, + ], + ], + ]; + } + + /** + * @dataProvider providePercentageDifference + */ + public function testPercentageDifference(float $value1, float $value2, float $expected): void + { + $result = Statistics::percentageDifference($value1, $value2); + + if ((string)$expected == 'NAN') { + self::assertEquals('NAN', (string)$result); + + return; + } + self::assertEquals(round($expected, 2), $result); + } + + /** + * @return Generator + */ + public function providePercentageDifference(): Generator + { + yield 'zero' => [ + 0, + 0, + 0 + ]; + + yield 'zero and one' => [ + 0, + 1, + INF, + ]; + + yield 'one and zero' => [ + 1, + 0, + -100 + ]; + + yield 'one and two' => [ + 1, + 2, + 100 + ]; + + yield 'one and three' => [ + 1, + 3, + 200 + ]; + + yield 'one and zero point five' => [ + 1, + 0.5, + -50 + ]; + } +} diff --git a/tests/Unit/Model/BenchmarkTest.php b/tests/Unit/Model/BenchmarkTest.php new file mode 100644 index 000000000..c53810bb0 --- /dev/null +++ b/tests/Unit/Model/BenchmarkTest.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Model; + +use PhpBench\Benchmark\Metadata\SubjectMetadata; +use PhpBench\Model\Benchmark; +use PhpBench\Model\ResolvedExecutor; +use PhpBench\Model\Suite; +use PhpBench\Registry\Config; +use PhpBench\Tests\TestCase; + +class BenchmarkTest extends TestCase +{ + const EXAMPLE_FORMAT = 'foobar'; + + /** + * @var Benchmark + */ + private $benchmark; + private $suite; + + protected function setUp(): void + { + $this->suite = $this->prophesize(Suite::class); + $this->benchmark = new Benchmark($this->suite->reveal(), 'Class'); + } + + public function testGetSet(): void + { + $this->assertSame($this->suite->reveal(), $this->benchmark->getSuite()); + $this->assertEquals('Class', $this->benchmark->getClass()); + } + + /** + * @dataProvider provideName + */ + public function testGetName(string $class, string $expected): void + { + $benchmark = new Benchmark($this->suite->reveal(), $class); + self::assertEquals($expected, $benchmark->getName()); + } + + /** + * @return array> + */ + public function provideName(): array + { + return [ + [ '', '' ], + [ 'A', 'A' ], + [ 'A\\B', 'B' ], + [ 'A\\B\\Code', 'Code' ], + ]; + } + + /** + * It should create and add a subject from some metadata. + */ + public function testCreateSubjectFromMetadata(): void + { + $metadata = $this->prophesize(SubjectMetadata::class); + $metadata->getName()->willReturn('hello'); + $metadata->getGroups()->willReturn(['one', 'two']); + $metadata->getSleep()->willReturn(30); + $metadata->getFormat()->willReturn(self::EXAMPLE_FORMAT); + $metadata->getRetryThreshold()->willReturn(10); + $metadata->getOutputTimeUnit()->willReturn(50); + $metadata->getOutputMode()->willReturn(60); + $metadata->getOutputTimePrecision()->willReturn(3); + + $executor = ResolvedExecutor::fromNameAndConfig('foo', new Config('one', ['foo' => 'bar'])); + + $subject = $this->benchmark->createSubjectFromMetadataAndExecutor($metadata->reveal(), $executor); + $this->assertInstanceOf('PhpBench\Model\Subject', $subject); + $this->assertEquals('hello', $subject->getName()); + $this->assertEquals(['one', 'two'], $subject->getGroups()); + $this->assertEquals(30, $subject->getSleep()); + $this->assertEquals(self::EXAMPLE_FORMAT, $subject->getFormat()); + $this->assertEquals(10, $subject->getRetryThreshold()); + $this->assertEquals(50, $subject->getOutputTimeUnit()); + $this->assertEquals(60, $subject->getOutputMode()); + $this->assertEquals(3, $subject->getOutputTimePrecision()); + + $subjects = $this->benchmark->getSubjects(); + $this->assertCount(1, $subjects); + $bSubject = current($subjects); + $this->assertInstanceOf('PhpBench\Model\Subject', $bSubject); + $this->assertSame($subject, $bSubject); + } +} diff --git a/tests/Unit/Model/IterationTest.php b/tests/Unit/Model/IterationTest.php new file mode 100644 index 000000000..4b17a54f5 --- /dev/null +++ b/tests/Unit/Model/IterationTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Model; + +use PhpBench\Model\Iteration; +use PhpBench\Model\Result\TimeResult; +use PhpBench\Model\Variant; +use PhpBench\Tests\TestCase; +use PhpBench\Tests\Util\TestUtil; + +class IterationTest extends TestCase +{ + private $iteration; + + protected function setUp(): void + { + $this->variant = $this->prophesize(Variant::class); + $this->iteration = new Iteration( + 0, + $this->variant->reveal() + ); + } + + /** + * It should have getters that return its values. + */ + public function testGetters(): void + { + $this->assertEquals($this->variant->reveal(), $this->iteration->getVariant()); + } + + /** + * It should return the revolution time. + */ + public function testGetRevTime(): void + { + $iteration = new Iteration(1, $this->variant->reveal(), TestUtil::createResults(100)); + $this->variant->getRevolutions()->willReturn(100); + $this->assertEquals(1, $iteration->getResult(TimeResult::class)->getRevTime(100)); + } +} diff --git a/tests/Unit/Model/Result/RejectionCountResultTest.php b/tests/Unit/Model/Result/RejectionCountResultTest.php new file mode 100644 index 000000000..9d9c2d4d7 --- /dev/null +++ b/tests/Unit/Model/Result/RejectionCountResultTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Model\Result; + +use PhpBench\Model\Result\RejectionCountResult; +use PhpBench\Tests\TestCase; + +class RejectionCountResultTest extends TestCase +{ + public function testMemoryGreaterEqualThan(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Rejection count must be greater or equal to 0'); + new RejectionCountResult(-1); + } +} diff --git a/tests/Unit/Model/Result/TimeResultTest.php b/tests/Unit/Model/Result/TimeResultTest.php new file mode 100644 index 000000000..4bb04e9d5 --- /dev/null +++ b/tests/Unit/Model/Result/TimeResultTest.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Model\Result; + +use PhpBench\Model\Result\TimeResult; +use PhpBench\Tests\TestCase; + +class TimeResultTest extends TestCase +{ + public function testGetters(): void + { + $result = new TimeResult(10); + $this->assertEquals(10, $result->getNet()); + } + + /** + * It should allow time "0" (because windows mostly). + */ + public function testTimeZero(): void + { + $result = new TimeResult(0); + $this->assertEquals(0, $result->getNet()); + } + + /** + * It should throw an exception if the time is less than 0. + * + */ + public function testLessThanZero(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Time cannot be less than 0, got -10'); + new TimeResult(-10); + } + + /** + * It should throw an exception if the revolutions is zero0 or less than zero. + * + */ + public function testRevsAreZero(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Revolutions must be more than 0, got 0'); + $result = new TimeResult(10); + $result->getRevTime(0); + } + + /** + * It should return the rev time. + */ + public function testGetRevTime(): void + { + $result = new TimeResult(10); + $revTime = $result->getRevTime(2); + + $this->assertEquals(5, $revTime); + } +} diff --git a/tests/Unit/Model/ResultCollectionTest.php b/tests/Unit/Model/ResultCollectionTest.php new file mode 100644 index 000000000..cb117154c --- /dev/null +++ b/tests/Unit/Model/ResultCollectionTest.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Model; + +use PhpBench\Model\Result\MemoryResult; +use PhpBench\Model\Result\TimeResult; +use PhpBench\Model\ResultCollection; +use PhpBench\Tests\TestCase; + +class ResultCollectionTest extends TestCase +{ + private $collection; + + protected function setUp(): void + { + $this->timeResult = new TimeResult(1); + $this->memoryResult = new MemoryResult(1, 0, 0); + + $this->collection = new ResultCollection([]); + } + + /** + * It can have results added in the constructor. + */ + public function testAddConstructor(): void + { + $collection = new ResultCollection([ + $expected = new TimeResult(10), + ]); + + $result = $collection->getResult(TimeResult::class); + $this->assertSame($expected, $result); + } + + /** + * It should be able to have results added to it. + * It should retrive results. + */ + public function testAddResult(): void + { + $this->collection->setResult($this->timeResult); + $this->assertEquals( + $this->timeResult, + $this->collection->getResult(TimeResult::class) + ); + } + + /** + * It should throw an exception when retrieving a non-existant class. + * + */ + public function testNonExistantClass(): void + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Result of class "stdClass" has not been set'); + $this->collection->getResult(\stdClass::class); + } + + /** + * It should return a named metric. + */ + public function testGetNamedMetric(): void + { + $this->collection->setResult($this->timeResult); + $this->assertEquals(1, $this->collection->getMetric(TimeResult::class, 'net')); + } + + /** + * It should throw an exception if the named metric does not exist. + * + */ + public function testNamedMetricDoesNotExist(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Unknown metric "foobar" for result class "PhpBench\Model\Result\TimeResult". Available metrics: "net"'); + $this->collection->setResult($this->timeResult); + $this->assertEquals(1, $this->collection->getMetric(TimeResult::class, 'foobar')); + } + + /** + * It should return a default value when using getMetricOrDefault when the + * class has not been set. + */ + public function testGetMetricOrDefault(): void + { + $this->collection->setResult($this->timeResult); + $this->assertEquals(100, $this->collection->getMetricOrDefault('UnknownClass', 'barbar', 100)); + } +} diff --git a/tests/Unit/Model/SubjectTest.php b/tests/Unit/Model/SubjectTest.php new file mode 100644 index 000000000..f76bb9d4a --- /dev/null +++ b/tests/Unit/Model/SubjectTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Subject; + +use PhpBench\Model\Benchmark; +use PhpBench\Model\ParameterSet; +use PhpBench\Model\Subject; +use PhpBench\Tests\TestCase; + +class SubjectTest extends TestCase +{ + private $subject; + private $benchmark; + + protected function setUp(): void + { + $this->benchmark = $this->prophesize(Benchmark::class); + $this->benchmark->getSubjects()->willReturn([]); + $this->subject = new Subject($this->benchmark->reveal(), 'subjectOne', 0); + } + + /** + * It should say if it is in a set of groups. + */ + public function testInGroups(): void + { + $this->subject->setGroups(['one', 'two', 'three']); + $result = $this->subject->inGroups(['five', 'two', 'six']); + $this->assertTrue($result); + + $result = $this->subject->inGroups(['eight', 'seven']); + $this->assertFalse($result); + + $result = $this->subject->inGroups([]); + $this->assertFalse($result); + } + + /** + * It should create variants. + */ + public function testCreateVariant(): void + { + $parameterSet = $this->prophesize(ParameterSet::class); + $parameterSet->getName()->willReturn('foo'); + $variant = $this->subject->createVariant( + $parameterSet->reveal(), + 10, + 20 + ); + + $this->assertEquals($this->subject, $variant->getSubject()); + $this->assertEquals($parameterSet->reveal(), $variant->getParameterSet()); + $this->assertEquals(0, $variant->count()); + $this->assertEquals(10, $variant->getRevolutions()); + $this->assertEquals(20, $variant->getWarmup()); + } +} diff --git a/tests/Unit/Model/SuiteCollectionTest.php b/tests/Unit/Model/SuiteCollectionTest.php new file mode 100644 index 000000000..3f2f5a3a5 --- /dev/null +++ b/tests/Unit/Model/SuiteCollectionTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Model; + +use PhpBench\Model\Suite; +use PhpBench\Model\SuiteCollection; +use PhpBench\Tests\TestCase; + +class SuiteCollectionTest extends TestCase +{ + protected function setUp(): void + { + $this->suite1 = $this->prophesize(Suite::class); + $this->suite2 = $this->prophesize(Suite::class); + $this->suite3 = $this->prophesize(Suite::class); + } + + /** + * It should add suites. + */ + public function testAddSuite(): void + { + $collection = new SuiteCollection(); + $collection->addSuite($this->suite1->reveal()); + $this->assertSame([ + $this->suite1->reveal(), + ], $collection->getSuites()); + } + + /** + * It should merge another colleciton into itself. + */ + public function testMergeCollection(): void + { + $collection = new SuiteCollection([ + $this->suite1->reveal(), + ]); + $newCollection = new SuiteCollection([ + $this->suite2->reveal(), + $this->suite3->reveal(), + ]); + + $collection->mergeCollection($newCollection); + + $this->assertEquals([ + $this->suite1->reveal(), + $this->suite2->reveal(), + $this->suite3->reveal(), + ], $collection->getSuites()); + } +} diff --git a/tests/Unit/Model/SuiteTest.php b/tests/Unit/Model/SuiteTest.php new file mode 100644 index 000000000..9b94a0308 --- /dev/null +++ b/tests/Unit/Model/SuiteTest.php @@ -0,0 +1,151 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Model; + +use PhpBench\Assertion\VariantAssertionResults; +use PhpBench\Environment\Information; +use PhpBench\Model\Benchmark; +use PhpBench\Model\ErrorStack; +use PhpBench\Model\Iteration; +use PhpBench\Model\ParameterSet; +use PhpBench\Model\Subject; +use PhpBench\Model\Suite; +use PhpBench\Model\Variant; +use PhpBench\Tests\TestCase; + +class SuiteTest extends TestCase +{ + private $env1; + private $bench1; + + protected function setUp(): void + { + $this->env1 = $this->prophesize(Information::class); + $this->bench1 = $this->prophesize(Benchmark::class); + $this->subject1 = $this->prophesize(Subject::class); + $this->variant1 = $this->prophesize(Variant::class); + $this->iteration1 = $this->prophesize(Iteration::class); + } + + /** + * It should add a benchmark. + */ + public function testCreateBenchmark(): void + { + $benchmark = $this->createSuite([])->createBenchmark('FooBench'); + $this->assertInstanceOf('PhpBench\Model\Benchmark', $benchmark); + } + + /** + * It should return all of the iterations in the suite. + * It should return all of the subjects + * It should return all of the variants. + */ + public function testGetIterations(): void + { + $this->bench1->getSubjects()->willReturn([$this->subject1->reveal()]); + $this->subject1->getVariants()->willReturn([$this->variant1->reveal()]); + $this->variant1->getIterator()->willReturn(new \ArrayIterator([$this->iteration1->reveal()])); + + $suite = $this->createSuite([ + $this->bench1->reveal(), + ], [ + $this->env1->reveal(), + ]); + + $this->assertSame([$this->iteration1->reveal()], $suite->getIterations()); + $this->assertSame([ + $this->variant1->reveal(), + ], $suite->getVariants()); + $this->assertSame([ + $this->subject1->reveal(), + ], $suite->getSubjects()); + } + + /** + * It should return all of the error stacks. + */ + public function testGetErrorStacks(): void + { + $errorStack = $this->prophesize(ErrorStack::class); + $this->bench1->getSubjects()->willReturn([$this->subject1->reveal()]); + $this->subject1->getVariants()->willReturn([$this->variant1->reveal()]); + $this->variant1->hasErrorStack()->willReturn(true); + $this->variant1->getErrorStack()->willReturn($errorStack->reveal()); + + $suite = $this->createSuite([ + $this->bench1->reveal(), + ], [ + $this->env1->reveal(), + ]); + + $this->assertSame([ + $errorStack->reveal(), + ], $suite->getErrorStacks()); + } + + /** + * It should return the summary. + */ + public function testGetSummary(): void + { + $errorStack = $this->prophesize(ErrorStack::class); + $this->bench1->getSubjects()->willReturn([$this->subject1->reveal()]); + $this->subject1->getVariants()->willReturn([$this->variant1->reveal()]); + $this->variant1->hasErrorStack()->willReturn(true); + $this->variant1->count()->willReturn(1); + $this->variant1->getSubject()->willReturn($this->subject1->reveal()); + $this->variant1->getRevolutions()->willReturn(10); + $this->variant1->getRejectCount()->willReturn(0); + $this->variant1->getRejectCount()->willReturn(0); + $this->variant1->getAssertionResults()->willReturn(new VariantAssertionResults($this->variant1->reveal(), [])); + $this->variant1->getErrorStack()->willReturn($errorStack->reveal()); + $errorStack->count()->willReturn(0); + + $suite = $this->createSuite([ + $this->bench1->reveal(), + ], [ + $this->env1->reveal(), + ]); + + $summary = $suite->getSummary(); + $this->assertInstanceOf('PhpBench\Model\Summary', $summary); + } + + public function testFindVariant(): void + { + $suite = $this->createSuite([]); + $variant = $suite->createBenchmark('Foobar')->createSubject('barfoo')->createVariant( + ParameterSet::create('one', []), + 1, + 1 + ); + + self::assertSame($variant, $suite->findVariant( + 'Foobar', + 'barfoo', + 'one' + )); + } + + private function createSuite(array $benchmarks = [], array $informations = []): Suite + { + return new Suite( + 'context', + new \DateTime('2016-01-25'), + 'path/to/config', + $benchmarks, + $informations + ); + } +} diff --git a/tests/Unit/Model/SummaryTest.php b/tests/Unit/Model/SummaryTest.php new file mode 100644 index 000000000..87168168e --- /dev/null +++ b/tests/Unit/Model/SummaryTest.php @@ -0,0 +1,151 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Model; + +use PhpBench\Assertion\AssertionResult; +use PhpBench\Assertion\VariantAssertionResults; +use PhpBench\Environment\Information; +use PhpBench\Math\Distribution; +use PhpBench\Model\Benchmark; +use PhpBench\Model\ErrorStack; +use PhpBench\Model\Subject; +use PhpBench\Model\Suite; +use PhpBench\Model\Summary; +use PhpBench\Model\Variant; +use PhpBench\Tests\TestCase; +use Prophecy\Prophecy\ObjectProphecy; + +class SummaryTest extends TestCase +{ + /** + * @var ObjectProphecy + */ + private $suite; + + /** + * @var ObjectProphecy + */ + private $bench1; + + /** + * @var ObjectProphecy + */ + private $subject1; + + /** + * @var ObjectProphecy + */ + private $variant1; + + /** + * @var ObjectProphecy + */ + private $stats; + + protected function setUp(): void + { + $this->suite = $this->prophesize(Suite::class); + $this->bench1 = $this->prophesize(Benchmark::class); + $this->subject1 = $this->prophesize(Subject::class); + $this->variant1 = $this->prophesize(Variant::class); + $this->stats = $this->prophesize(Distribution::class); + } + + /** + * It should provide a summary. + */ + public function testSummary(): void + { + $this->setUpSuite(); + $this->suite->getEnvInformations()->willReturn([]); + + $summary = new Summary($this->suite->reveal()); + + $this->assertEquals(1, $summary->getNbSubjects()); + $this->assertEquals(4, $summary->getNbIterations()); + $this->assertEquals(10, $summary->getNbRevolutions()); + $this->assertEquals(1, $summary->getMinTime()); + $this->assertEquals(2, $summary->getMaxTime()); + $this->assertEquals(5, $summary->getMeanTime()); + $this->assertEquals(6, $summary->getModeTime()); + $this->assertEquals(7, $summary->getTotalTime()); + $this->assertEquals(8, $summary->getMeanStDev()); + $this->assertEquals(9, $summary->getMeanRelStDev()); + $this->assertEquals(1, $summary->getNbFailures()); + $this->assertFalse($summary->getXdebugEnabled()); + $this->assertFalse($summary->getOpcacheEnabled()); + $this->assertNull($summary->getPhpVersion()); + } + + public function testOpCacheAndXDebugInformationEmpty(): void + { + $this->setUpSuite(); + $this->suite->getEnvInformations()->willReturn([ + 'php' => new Information('php', []), + 'opcache' => new Information('opcache', []), + ]); + $summary = new Summary($this->suite->reveal()); + $this->assertFalse($summary->getXdebugEnabled()); + $this->assertFalse($summary->getOpcacheEnabled()); + $this->assertNull($summary->getPhpVersion()); + } + + public function testOpCacheAndXDebugInformationComplete(): void + { + $this->setUpSuite(); + $this->suite->getEnvInformations()->willReturn([ + 'php' => new Information('php', [ + 'version' => '6.0', + 'xdebug' => true, + ]), + 'opcache' => new Information('opcache', [ + 'enabled' => true, + ]), + ]); + $summary = new Summary($this->suite->reveal()); + $this->assertTrue($summary->getXdebugEnabled()); + $this->assertTrue($summary->getOpcacheEnabled()); + $this->assertEquals('6.0', $summary->getPhpVersion()); + } + + private function setUpSuite(): void + { + $this->bench1->getSubjects()->willReturn([$this->subject1->reveal()]); + $this->subject1->getVariants()->willReturn([$this->variant1->reveal()]); + $this->variant1->getStats()->willReturn($this->stats->reveal()); + $this->variant1->count()->willReturn(4); + $this->variant1->getRejectCount()->willReturn(11); + $this->variant1->hasErrorStack()->willReturn(false); + $this->variant1->getRevolutions()->willReturn(10); + $this->variant1->getSubject()->willReturn($this->subject1->reveal()); + $this->variant1->getAssertionResults()->willReturn( + new VariantAssertionResults($this->variant1->reveal(), [ + AssertionResult::ok(), + AssertionResult::tolerated('foo'), + AssertionResult::fail('foo'), + ]) + ); + $this->variant1->getErrorStack()->willReturn(new ErrorStack($this->variant1->reveal(), [])); + $this->stats->getIterator()->willReturn(new \ArrayIterator([ + 'min' => '1', + 'max' => '2', + 'mean' => 5, + 'mode' => 6, + 'sum' => 7, + 'stdev' => 8, + 'rstdev' => 9, + + ])); + $this->suite->getBenchmarks()->willReturn([$this->bench1]); + } +} diff --git a/tests/Unit/Model/TagTest.php b/tests/Unit/Model/TagTest.php new file mode 100644 index 000000000..65fa6b18f --- /dev/null +++ b/tests/Unit/Model/TagTest.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Model; + +use PhpBench\Model\Tag; +use PhpBench\Storage\Exception\InvalidTagException; +use PhpBench\Tests\TestCase; + +class TagTest extends TestCase +{ + /** + * @doesNotPerformAssertions + * @dataProvider provideValidTag + */ + public function testValidTag(string $tag): void + { + new Tag($tag); + } + + public function provideValidTag() + { + yield ['foobar']; + + yield ['FooBAR']; + + yield ['42']; + + yield ['foo42']; + + yield ['foo_42']; + } + + /** + * @dataProvider provideInvalidTag + */ + public function testInvalidTag(string $tag): void + { + $this->expectException(InvalidTagException::class); + + new Tag($tag); + } + + public function provideInvalidTag() + { + yield ['']; + + yield ['foo-bar']; + + yield ['foo&#!$bar']; + + yield ['foo4.2']; + } +} diff --git a/tests/Unit/Model/VariantTest.php b/tests/Unit/Model/VariantTest.php new file mode 100644 index 000000000..30a599685 --- /dev/null +++ b/tests/Unit/Model/VariantTest.php @@ -0,0 +1,225 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Model; + +use PhpBench\Model\Iteration; +use PhpBench\Model\ParameterSet; +use PhpBench\Model\Result\ComputedResult; +use PhpBench\Model\Result\MemoryResult; +use PhpBench\Model\Result\TimeResult; +use PhpBench\Model\Subject; +use PhpBench\Model\Variant; +use PhpBench\Tests\TestCase; +use PhpBench\Tests\Util\TestUtil; +use Prophecy\Argument; +use RuntimeException; + +class VariantTest extends TestCase +{ + private $subject; + private $parameterSet; + + protected function setUp(): void + { + $this->subject = $this->prophesize(Subject::class); + $this->parameterSet = $this->prophesize(ParameterSet::class); + } + + /** + * It should spawn variant. + * It should be iterable + * It sohuld be countable. + */ + public function testIterationSpawn(): void + { + $variant = new Variant($this->subject->reveal(), $this->parameterSet->reveal(), 10, 20); + $variant->spawnIterations(4); + + $this->assertCount(4, $variant); + + foreach ($variant as $iteration) { + $this->assertInstanceOf('PhpBench\Model\Iteration', $iteration); + } + } + + /** + * It should create new iterations with the correct indexes. + */ + public function testCreateIteration(): void + { + $variant = new Variant($this->subject->reveal(), $this->parameterSet->reveal(), 10, 20); + $iteration = $variant->createIteration(TestUtil::createResults(10, 20)); + $this->assertInstanceOf('PhpBench\Model\Iteration', $iteration); + $this->assertEquals(10, $iteration->getResult(TimeResult::class)->getNet()); + $this->assertEquals(20, $iteration->getResult(MemoryResult::class)->getPeak()); + $this->assertEquals(0, $iteration->getIndex()); + + $iteration = $variant->createIteration(TestUtil::createResults(10, 20)); + $this->assertEquals(1, $iteration->getIndex()); + + $iteration = $variant->createIteration(TestUtil::createResults(10, 20)); + $this->assertEquals(2, $iteration->getIndex()); + } + + /** + * It should calculate the stats of each iteration from the mean. + */ + public function testComputeStats(): void + { + $variant = new Variant($this->subject->reveal(), $this->parameterSet->reveal(), 4, 0); + $this->subject->getRetryThreshold()->willReturn(10); + + $variant->createIteration(TestUtil::createResults(4)); + $variant->createIteration(TestUtil::createResults(8)); + $variant->createIteration(TestUtil::createResults(4)); + $variant->createIteration(TestUtil::createResults(16)); + + $variant->computeStats(); + + $this->assertEquals(-50, $variant[0]->getResult(ComputedResult::class)->getDeviation()); + $this->assertEquals(-0.81649658092772615, $variant[0]->getResult(ComputedResult::class)->getZValue()); + + $this->assertEquals(0, $variant[1]->getResult(ComputedResult::class)->getDeviation()); + $this->assertEquals(0, $variant[1]->getResult(ComputedResult::class)->getZValue()); + + $this->assertEquals(-50, $variant[2]->getResult(ComputedResult::class)->getDeviation()); + $this->assertEquals(-0.81649658092772615, $variant[2]->getResult(ComputedResult::class)->getZValue()); + + $this->assertEquals(100, $variant[3]->getResult(ComputedResult::class)->getDeviation()); + $this->assertEquals(1.6329931618554523, $variant[3]->getResult(ComputedResult::class)->getZValue()); + } + + /** + * It should not crash if compute deviations is called with zero variant in the collection. + */ + public function testComputeDeviationZeroIterations(): void + { + $variant = new Variant($this->subject->reveal(), $this->parameterSet->reveal(), 10, 20); + $variant->computeStats(); + $this->addToAssertionCount(1); + } + + /** + * It should mark variant as rejected if they deviate too far from the mean. + */ + public function testReject(): void + { + $variant = new Variant($this->subject->reveal(), $this->parameterSet->reveal(), 4, 20); + $this->subject->getRetryThreshold()->willReturn(10); + + $variant->createIteration(TestUtil::createResults(4)); + $variant->createIteration(TestUtil::createResults(8)); + $variant->createIteration(TestUtil::createResults(4)); + $variant->createIteration(TestUtil::createResults(16)); + $variant->computeStats(); + + $this->assertCount(3, $variant->getRejects()); + $this->assertContainsEquals($variant[2], $variant->getRejects()); + $this->assertContainsEquals($variant[3], $variant->getRejects()); + $this->assertNotContainsEquals($variant[1], $variant->getRejects()); + } + + private function createIteration($time, $expectedDeviation = null, $expectedZValue = null) + { + $iteration = $this->prophesize(Iteration::class); + $iteration->getRevolutions()->willReturn(1); + $iteration->getResult(TimeResult::class)->willReturn(new TimeResult($time)); + $iteration->getResult(MemoryResult::class)->willReturn(new MemoryResult($time)); + + if (null !== $expectedDeviation) { + $iteration->setDeviation($expectedDeviation)->shouldBeCalled(); + + if (null === $expectedZValue) { + $iteration->setZValue(Argument::that(function ($args) use ($expectedZValue) { + return round($args[0], 4) == round($expectedZValue, 4); + }))->shouldBeCalled(); + } else { + $iteration->setZValue(Argument::any())->shouldBeCalled(); + } + } + + return $iteration->reveal(); + } + + /** + * It should be aware of exceptions. + */ + public function testExceptionAwareness(): void + { + $variant = new Variant($this->subject->reveal(), $this->parameterSet->reveal(), 10, 20); + $error = new \Exception('Test'); + + $this->assertFalse($variant->hasErrorStack()); + $variant->setException($error); + $this->assertTrue($variant->hasErrorStack()); + $this->assertEquals('Test', $variant->getErrorStack()->getTop()->getMessage()); + } + + /** + * It should return a new ErrorStack if none has not been set. + */ + public function testExceptionNoneGet(): void + { + $variant = new Variant($this->subject->reveal(), $this->parameterSet->reveal(), 10, 20); + $errorStack = $variant->getErrorStack(); + $this->assertInstanceOf('PhpBench\Model\ErrorStack', $errorStack); + } + + /** + * It should throw an exception if getStats is called when no computation has taken place. + * + */ + public function testGetStatsNoComputeException(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('No statistics have yet'); + $variant = new Variant($this->subject->reveal(), $this->parameterSet->reveal(), 10, 20); + $variant->getStats(); + } + + /** + * It should throw an exception if getStats is called when an exception has been set. + * + */ + public function testGetStatsWithExceptionException(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Cannot retrieve stats when an exception'); + $variant = new Variant($this->subject->reveal(), $this->parameterSet->reveal(), 4, 20); + $this->subject->getRetryThreshold()->willReturn(10); + $variant->createIteration(TestUtil::createResults(4, 10)); + $variant->createIteration(TestUtil::createResults(4, 10)); + $variant->createIteration(TestUtil::createResults(4, 10)); + $variant->createIteration(TestUtil::createResults(4, 10)); + $variant->computeStats(); + $variant->setException(new \Exception('Test')); + $variant->getStats(); + } + + /** + * It should return times and memories. + */ + public function testGetMetricValues(): void + { + $variant = new Variant($this->subject->reveal(), $this->parameterSet->reveal(), 1, 0); + + $variant->createIteration(TestUtil::createResults(4, 100)); + $variant->createIteration(TestUtil::createResults(8, 200)); + + $times = $variant->getMetricValuesByRev(TimeResult::class, 'net'); + $memories = $variant->getMetricValues(MemoryResult::class, 'peak'); + + $this->assertEquals([4, 8], $times); + $this->assertEquals([100, 200], $memories); + } +} diff --git a/tests/Unit/Progress/Logger/BlinkenLoggerTest.php b/tests/Unit/Progress/Logger/BlinkenLoggerTest.php new file mode 100644 index 000000000..7bd2c2e9b --- /dev/null +++ b/tests/Unit/Progress/Logger/BlinkenLoggerTest.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Progress\Logger; + +use PhpBench\Model\Benchmark; +use PhpBench\Model\ParameterSet; +use PhpBench\Model\Subject; +use PhpBench\Model\Variant; +use PhpBench\Progress\Logger\BlinkenLogger; +use PhpBench\Util\TimeUnit; + +class BlinkenLoggerTest extends LoggerTestCase +{ + const ASSERTION_FAILURE_MESSAGE = 'Failure message'; + /** + * @var BlinkenLogger + */ + private $logger; + /** + * @var ObjectProphecy + */ + private $benchmark; + /** + * @var ObjectProphecy + */ + private $subject; + /** + * @var Variant + */ + private $variant; + + protected function setUp(): void + { + parent::setUp(); + $this->timeUnit = new TimeUnit(TimeUnit::MICROSECONDS, TimeUnit::MILLISECONDS); + + $this->logger = new BlinkenLogger($this->output, $this->variantFormatter, $this->timeUnit); + $this->benchmark = $this->prophesize(Benchmark::class); + $this->subject = $this->prophesize(Subject::class); + $this->variant = new Variant( + $this->subject->reveal(), + new ParameterSet('one'), + 10, + 0 + ); + $this->variant->spawnIterations(4); + $this->benchmark->getSubjects()->willReturn([ + $this->subject->reveal(), + ]); + $this->benchmark->getClass()->willReturn('BenchmarkTest'); + + $this->subject->getName()->willReturn('benchSubject'); + $this->subject->getIndex()->willReturn(1); + $this->subject->getOutputTimeUnit()->willReturn('milliseconds'); + $this->subject->getOutputTimePrecision()->willReturn(5); + $this->subject->getOutputMode()->willReturn('time'); + $this->subject->getRetryThreshold()->willReturn(10); + } + + /** + * It should show the benchmark name and list all of the subjects. + */ + public function testShowAndList(): void + { + $this->logger->benchmarkStart($this->benchmark->reveal()); + $display = $this->output->fetch(); + $this->assertStringContainsString('BenchmarkTest', $display); + $this->assertStringContainsString('#0 benchSubject', $display); + } + + /** + * It should initialize the status line. + */ + public function testIterationStart(): void + { + $this->logger->iterationStart($this->variant[0]); + $display = $this->output->fetch(); + $this->assertStringContainsString( + '0.000', + $display + ); + } + + /** + * It should show information at the start of the variant. + */ + public function testIterationsStart(): void + { + $this->logger->variantStart($this->variant); + $display = $this->output->fetch(); + $this->assertStringContainsString( + 'benchSubject', + $display + ); + $this->assertStringContainsString( + 'parameter set one', + $display + ); + } + + /** + * It should show an error if the iteration has an exception. + */ + public function testIterationException(): void + { + $this->variant->setException(new \Exception('foo')); + $this->logger->variantEnd($this->variant); + $this->assertStringContainsString('ERROR', $this->output->fetch()); + } +} diff --git a/tests/Unit/Progress/Logger/DotsLoggerTest.php b/tests/Unit/Progress/Logger/DotsLoggerTest.php new file mode 100644 index 000000000..001d4984f --- /dev/null +++ b/tests/Unit/Progress/Logger/DotsLoggerTest.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Progress\Logger; + +use PhpBench\Assertion\AssertionResult; +use PhpBench\Assertion\VariantAssertionResults; +use PhpBench\Model\Benchmark; +use PhpBench\Model\Iteration; +use PhpBench\Model\Subject; +use PhpBench\Model\Variant; +use PhpBench\Progress\Logger\DotsLogger; +use Prophecy\Prophecy\ObjectProphecy; + +class DotsLoggerTest extends LoggerTestCase +{ + /** + * @var ObjectProphecy|Benchmark + */ + private $benchmark; + /** + * @var ObjectProphecy|Subject + */ + private $subject; + /** + * @var ObjectProphecy|Iteration + */ + private $iteration; + /** + * @var ObjectProphecy|Variant + */ + private $variant; + + protected function setUp(): void + { + parent::setUp(); + + $this->benchmark = $this->prophesize(Benchmark::class); + $this->subject = $this->prophesize(Subject::class); + $this->iteration = $this->prophesize(Iteration::class); + $this->variant = $this->prophesize(Variant::class); + } + + /** + * It should reset the line and dump the buffer when NOT in CI mode. + */ + public function testIterationsEnd(): void + { + $logger = $this->createLogger(); + $this->variant->getRejectCount()->willReturn(0); + $this->variant->hasErrorStack()->willReturn(false); + $this->variant->getAssertionResults()->willReturn(new VariantAssertionResults($this->variant->reveal(), [])); + $logger->variantEnd($this->variant->reveal()); + self::assertEquals("\x0D. ", $this->output->fetch()); + } + + /** + * It should log an error. + */ + public function testIterationsEndException(): void + { + $logger = $this->createLogger(); + $this->variant->hasErrorStack()->willReturn(true); + $this->variant->getAssertionResults()->willReturn(new VariantAssertionResults($this->variant->reveal(), [])); + $this->variant->getRejectCount()->willReturn(0); + $logger->variantEnd($this->variant->reveal()); + self::assertEquals("\x0DE ", $this->output->fetch()); + } + + /** + * It should log a failure. + */ + public function testIterationsEndFailure(): void + { + $logger = $this->createLogger(); + $this->variant->hasErrorStack()->willReturn(false); + $this->variant->getRejectCount()->willReturn(0); + $this->variant->getAssertionResults()->willReturn(new VariantAssertionResults($this->variant->reveal(), [AssertionResult::fail()])); + $logger->variantEnd($this->variant->reveal()); + self::assertEquals("\x0DF ", $this->output->fetch()); + } + + /** + * It should return early if the rejection count > 0. + */ + public function testIterationsEndRejectionsReturnEarly(): void + { + $logger = $this->createLogger(); + $this->variant->getRejectCount()->willReturn(5); + $logger->variantEnd($this->variant->reveal()); + self::assertEquals('', $this->output->fetch()); + } + + /** + * It should show a spinner when not in CI mode. + */ + public function testIteration(): void + { + $logger = $this->createLogger(); + + $this->iteration->getIndex()->willReturn(0, 1, 2, 3, 4); + + $logger->iterationStart($this->iteration->reveal()); + $logger->iterationStart($this->iteration->reveal()); + $logger->iterationStart($this->iteration->reveal()); + $logger->iterationStart($this->iteration->reveal()); + $logger->iterationStart($this->iteration->reveal()); + self::assertEquals("\r|\r/\r-\r\\\r|", $this->output->fetch()); + } + + private function createLogger(): DotsLogger + { + $logger = new DotsLogger( + $this->output, + $this->variantFormatter, + $this->timeUnit + ); + + return $logger; + } +} diff --git a/tests/Unit/Progress/Logger/HistogramLoggerTest.php b/tests/Unit/Progress/Logger/HistogramLoggerTest.php new file mode 100644 index 000000000..adcd92395 --- /dev/null +++ b/tests/Unit/Progress/Logger/HistogramLoggerTest.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Progress\Logger; + +use PhpBench\Model\Benchmark; +use PhpBench\Model\Iteration; +use PhpBench\Model\ParameterSet; +use PhpBench\Model\Subject; +use PhpBench\Model\Variant; +use PhpBench\Progress\Logger\HistogramLogger; +use PhpBench\Tests\Util\TestUtil; + +class HistogramLoggerTest extends LoggerTestCase +{ + protected function setUp(): void + { + parent::setUp(); + $this->logger = new HistogramLogger($this->output, $this->variantFormatter, $this->timeUnit); + $this->benchmark = $this->prophesize(Benchmark::class); + $this->subject = $this->prophesize(Subject::class); + $this->iteration = $this->prophesize(Iteration::class); + $this->variant = new Variant( + $this->subject->reveal(), + new ParameterSet('one'), + 1, + 0 + ); + $this->variant->spawnIterations(4); + $this->benchmark->getSubjects()->willReturn([ + $this->subject->reveal(), + ]); + $this->benchmark->getClass()->willReturn('BenchmarkTest'); + + $this->subject->getName()->willReturn('benchSubject'); + $this->subject->getIndex()->willReturn(1); + $this->subject->getOutputTimeUnit()->willReturn('milliseconds'); + $this->subject->getOutputMode()->willReturn('time'); + $this->subject->getRetryThreshold()->willReturn(10); + $this->subject->getOutputTimePrecision()->willReturn(3); + } + + /** + * It should show the benchmark name and list all of the subjects. + */ + public function testBenchmarkStart(): void + { + $this->logger->benchmarkStart($this->benchmark->reveal()); + $display = $this->output->fetch(); + $this->assertStringContainsString('BenchmarkTest', $display); + $this->assertStringContainsString('#1 benchSubject', $display); + } + + /** + * Test iteration start. + */ + public function testIterationStart(): void + { + $this->iteration->getIndex()->willReturn(1); + $this->iteration->getVariant()->willReturn($this->variant); + $this->logger->iterationStart($this->iteration->reveal()); + $display = $this->output->fetch(); + $this->assertStringContainsString('it 1/4', $display); + } + + /** + * It should show information at the start of the variant. + */ + public function testIterationsStart(): void + { + $this->logger->variantStart($this->variant); + $display = $this->output->fetch(); + $this->assertStringContainsString( + '1 (σ = 0.000ms ) -2σ [ ] +2σ', + $display + ); + $this->assertStringContainsString( + 'benchSubject', + $display + ); + $this->assertStringContainsString( + 'parameter set one', + $display + ); + } + + /** + * It should show an error if the iteration has an exception. + */ + public function testIterationException(): void + { + $this->variant->setException(new \Exception('foo')); + $this->logger->variantEnd($this->variant); + $this->assertStringContainsString('ERROR', $this->output->fetch()); + } + + /** + * It should show the histogram and statistics when an iteration is + * completed (and there were no rejections). + */ + public function testIterationEnd(): void + { + foreach ($this->variant as $iteration) { + foreach (TestUtil::createResults(10, 10) as $result) { + $iteration->setResult($result); + } + } + $this->variant->computeStats(); + + $this->logger->variantEnd($this->variant); + $display = $this->output->fetch(); + $this->assertStringContainsString( + '1 (σ = 0.000ms ) -2σ [ █ ] +2σ summary', + $display + ); + } +} diff --git a/tests/Unit/Progress/Logger/LoggerTestCase.php b/tests/Unit/Progress/Logger/LoggerTestCase.php new file mode 100644 index 000000000..a3d5fbfaf --- /dev/null +++ b/tests/Unit/Progress/Logger/LoggerTestCase.php @@ -0,0 +1,41 @@ +output = new BufferedOutput(); + + $this->timeUnit = new TimeUnit(TimeUnit::MICROSECONDS, TimeUnit::MILLISECONDS); + $this->variantFormatter = new class implements VariantFormatter { + public function formatVariant(Variant $variant): string + { + return 'summary'; + } + }; + } +} diff --git a/tests/Unit/Progress/Logger/PhpBenchLoggerTest.php b/tests/Unit/Progress/Logger/PhpBenchLoggerTest.php new file mode 100644 index 000000000..0618dcce9 --- /dev/null +++ b/tests/Unit/Progress/Logger/PhpBenchLoggerTest.php @@ -0,0 +1,174 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Progress\Logger; + +use PhpBench\Assertion\AssertionResult; +use PhpBench\Assertion\VariantAssertionResults; +use PhpBench\Math\Distribution; +use PhpBench\Model\Benchmark; +use PhpBench\Model\Error; +use PhpBench\Model\ErrorStack; +use PhpBench\Model\ParameterSet; +use PhpBench\Model\Subject; +use PhpBench\Model\Suite; +use PhpBench\Model\Summary; +use PhpBench\Model\Variant; + +abstract class PhpBenchLoggerTest extends LoggerTestCase +{ + protected $logger; + protected $output; + protected $document; + protected $benchmark; + protected $variant; + protected $subject; + protected $parameterSet; + protected $stats; + + protected function setUp(): void + { + parent::setUp(); + $this->suite = $this->prophesize(Suite::class); + $this->summary = $this->prophesize(Summary::class); + $this->benchmark = $this->prophesize(Benchmark::class); + $this->variant = $this->prophesize(Variant::class); + $this->subject = $this->prophesize(Subject::class); + $this->parameterSet = $this->prophesize(ParameterSet::class); + $this->stats = $this->prophesize(Distribution::class); + + $this->logger = $this->getLogger(); + + $this->suite->getSummary()->willReturn($this->summary->reveal()); + $this->variant->getBaseline()->willReturn($this->variant->reveal()); + + $this->stats->getMin()->willReturn(1.0); + $this->stats->getMax()->willReturn(1.0); + $this->stats->getVariance()->willReturn(1.0); + $this->stats->getMean()->willReturn(1.0); + $this->stats->getMode()->willReturn(1.0); + $this->stats->getStdev()->willReturn(2.0); + $this->stats->getRstdev()->willReturn(20); + } + + abstract public function getLogger(); + + /** + * It should show the PHPBench version. + */ + public function testStart(): void + { + $this->addToAssertionCount(1); + } + + /** + * It should show a summary at the end of the suite. + */ + public function testEndSuite(): void + { + $this->setUpSummary(); + $this->suite->getFailures()->willReturn([]); + $this->suite->getErrorStacks()->willReturn([]); + $this->logger->endSuite($this->suite->reveal()); + self::assertNotEmpty($this->output->fetch()); + } + + public function testEndSuiteErrors(): void + { + $error1 = $this->prophesize(Error::class); + $error1->getMessage()->willReturn('MessageOne'); + $error1->getClass()->willReturn('ExceptionOne'); + $error1->getTrace()->willReturn('-- trace --'); + + $error2 = $this->prophesize(Error::class); + $error2->getMessage()->willReturn('MessageTwo'); + $error2->getClass()->willReturn('ExceptionTwo'); + $error2->getTrace()->willReturn('-- trace --'); + $errorStack = $this->prophesize(ErrorStack::class); + $errorStack->getVariant()->willReturn($this->variant->reveal()); + $errorStack->getIterator()->willReturn(new \ArrayIterator([$error1->reveal(), $error2->reveal()])); + + $this->setUpSummary(); + $this->suite->getFailures()->willReturn([]); + $this->suite->getErrorStacks()->willReturn([$errorStack]); + $errorStack->getVariant()->willReturn($this->variant->reveal()); + $this->variant->getSubject()->willReturn($this->subject->reveal()); + $this->subject->getBenchmark()->willReturn($this->benchmark->reveal()); + $this->subject->getName()->willReturn('bar'); + $this->benchmark->getClass()->willReturn('Namespace\Foo'); + $this->logger->endSuite($this->suite->reveal()); + + $buffer = $this->output->fetch(); + + self::assertStringContainsString('1 subjects encountered errors', $buffer); + self::assertStringContainsString('Namespace\Foo::bar', $buffer); + self::assertStringContainsString('ExceptionOne', $buffer); + self::assertStringContainsString('ExceptionTwo', $buffer); + self::assertStringContainsString('MessageOne', $buffer); + self::assertStringContainsString('Two', $buffer); + } + + public function testEndSuiteFailures(): void + { + $failure1 = AssertionResult::fail('Failed!'); + $failure2 = AssertionResult::fail('Failed!'); + $failures = new VariantAssertionResults($this->variant->reveal(), [$failure1, $failure2]); + + $this->setUpSummary(); + $this->suite->getFailures()->willReturn([$failures]); + $this->suite->getErrorStacks()->willReturn([]); + $this->variant->getSubject()->willReturn($this->subject->reveal()); + $this->variant->getParameterSet()->willReturn(new ParameterSet('one',[])); + $this->subject->getBenchmark()->willReturn($this->benchmark->reveal()); + $this->subject->getName()->willReturn('bar'); + $this->benchmark->getClass()->willReturn('Namespace\Foo'); + + + + $this->logger->endSuite($this->suite->reveal()); + $buffer = $this->output->fetch(); + self::assertStringContainsString('1 variants failed', $buffer); + } + + private function setUpSummary(): void + { + $nbSubjects = 4; + $nbIterations = 1; + $nbRevolutions = 2; + $nbRejects = 3; + $min = 10; + $max = 12; + $mean = 11; + $mode = 10; + $totalTime = 123; + $meanStDev = 321; + $meanRelStDev = 231; + $nbFailures = 0; + $nbAssertions = 0; + + $this->summary->getNbSubjects()->willReturn($nbSubjects); + $this->summary->getNbAssertions()->willReturn(0); + $this->summary->getNbErrors()->willReturn(0); + + $this->summary->getNbIterations()->willReturn($nbIterations); + $this->summary->getNbRevolutions()->willReturn($nbRevolutions); + $this->summary->getNbRejects()->willReturn($nbRejects); + $this->summary->getNbFailures()->willReturn($nbFailures); + $this->summary->getMinTime()->willReturn($min); + $this->summary->getMeanTime()->willReturn($mean); + $this->summary->getModeTime()->willReturn($mode); + $this->summary->getMaxTime()->willReturn($max); + $this->summary->getTotalTime()->willReturn($totalTime); + $this->summary->getMeanStDev()->willReturn($meanStDev); + $this->summary->getMeanRelStDev()->willReturn($meanRelStDev); + } +} diff --git a/tests/Unit/Progress/Logger/TravisLoggerTest.php b/tests/Unit/Progress/Logger/TravisLoggerTest.php new file mode 100644 index 000000000..9b0104eb6 --- /dev/null +++ b/tests/Unit/Progress/Logger/TravisLoggerTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Progress\Logger; + +use PhpBench\Assertion\VariantAssertionResults; +use PhpBench\Progress\Logger\TravisLogger; + +class TravisLoggerTest extends PhpBenchLoggerTest +{ + public function getLogger() + { + return new TravisLogger($this->output, $this->variantFormatter, $this->timeUnit); + } + + /** + * It should output when the benchmark starts. + */ + public function testBenchmarkStart(): void + { + $this->benchmark->getClass()->willReturn('Benchmark'); + $this->logger->benchmarkStart($this->benchmark->reveal()); + self::assertEquals("Benchmark\n\n", $this->output->fetch()); + } + + /** + * It should output at the end of an iteration set. + */ + public function testIterationsEnd(): void + { + $this->variant->getRejectCount()->willReturn(0); + $this->variant->hasErrorStack()->willReturn(false); + $this->variant->count()->willReturn(10); + $this->variant->getStats()->willReturn($this->stats->reveal()); + $this->variant->getSubject()->willReturn($this->subject->reveal()); + $this->variant->getParameterSet()->willReturn($this->parameterSet->reveal()); + $this->variant->getAssertionResults()->willReturn(new VariantAssertionResults($this->variant->reveal(), [])); + $this->subject->getOutputTimeUnit()->willReturn(null); + $this->subject->getOutputMode()->willReturn(null); + $this->subject->getName()->willReturn('benchFoo'); + $this->subject->getVariants()->willReturn([$this->variant->reveal()]); + $this->subject->getOutputTimePrecision()->willReturn(null); + $this->parameterSet->getIndex()->willReturn(0); + + $this->logger->variantEnd($this->variant->reveal()); + self::assertStringContainsString('summary', $this->output->fetch()); + } +} diff --git a/tests/Unit/Progress/Logger/VerboseLoggerTest.php b/tests/Unit/Progress/Logger/VerboseLoggerTest.php new file mode 100644 index 000000000..c90d5791e --- /dev/null +++ b/tests/Unit/Progress/Logger/VerboseLoggerTest.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Progress\Logger; + +use PhpBench\Assertion\VariantAssertionResults; +use PhpBench\Progress\Logger\VerboseLogger; +use PhpBench\Util\TimeUnit; + +class VerboseLoggerTest extends PhpBenchLoggerTest +{ + public function getLogger() + { + $timeUnit = new TimeUnit(TimeUnit::MICROSECONDS, TimeUnit::MILLISECONDS); + + return new VerboseLogger($this->output, $this->variantFormatter, $timeUnit); + } + + /** + * It should output when the benchmark starts. + */ + public function testBenchmarkStart(): void + { + $this->benchmark->getClass()->willReturn('Benchmark'); + $this->logger->benchmarkStart($this->benchmark->reveal()); + self::assertStringContainsString('Benchmark', $this->output->fetch()); + } + + /** + * It should output at the end of an iteration set. + */ + public function testIterationsEnd(): void + { + $this->variant->hasErrorStack()->willReturn(false); + $this->variant->getRejectCount()->willReturn(0); + $this->variant->getStats()->willReturn($this->stats->reveal()); + $this->variant->getSubject()->willReturn($this->subject->reveal()); + $this->variant->getAssertionResults()->willReturn(new VariantAssertionResults($this->variant->reveal(), [])); + $this->variant->getParameterSet()->willReturn($this->parameterSet->reveal()); + $this->subject->getOutputTimeUnit()->willReturn(null); + $this->subject->getOutputMode()->willReturn(null); + $this->subject->getOutputTimePrecision()->willReturn(null); + $this->subject->getName()->willReturn('benchFoo'); + $this->parameterSet->getIndex()->willReturn(0); + + $this->logger->variantEnd($this->variant->reveal()); + self::assertStringContainsString('summary', $this->output->fetch()); + } +} diff --git a/tests/Unit/Progress/LoggerRegistryTest.php b/tests/Unit/Progress/LoggerRegistryTest.php new file mode 100644 index 000000000..8800091a4 --- /dev/null +++ b/tests/Unit/Progress/LoggerRegistryTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit; + +use InvalidArgumentException; +use PhpBench\Progress\LoggerInterface; +use PhpBench\Progress\LoggerRegistry; +use PhpBench\Tests\TestCase; + +class LoggerRegistryTest extends TestCase +{ + private $registry; + + protected function setUp(): void + { + $this->registry = new LoggerRegistry(); + $this->progressLogger = $this->prophesize(LoggerInterface::class); + } + + /** + * It should register progress loggers. + */ + public function testRegisterProgressLogger(): void + { + $this->registry->addProgressLogger('foobar', $this->progressLogger->reveal()); + $logger = $this->registry->getProgressLogger('foobar'); + $this->assertSame($this->progressLogger->reveal(), $logger); + } + + /** + * It should throw an exception when a non-existing logger is requested. + * + */ + public function testUnknownLogger(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('No progress logger with name "barfoo" has been registered, known progress loggers: "foobar"'); + $this->registry->addProgressLogger('foobar', $this->progressLogger->reveal()); + $this->registry->getProgressLogger('barfoo'); + } +} diff --git a/tests/Unit/Progress/VariantSummaryFormatterTest.php b/tests/Unit/Progress/VariantSummaryFormatterTest.php new file mode 100644 index 000000000..bc817c0eb --- /dev/null +++ b/tests/Unit/Progress/VariantSummaryFormatterTest.php @@ -0,0 +1,60 @@ +createFormatter( + '"variant: " ~ mode(variant.time.avg)', + '"baseline: " ~ mode(variant.baseline.avg)' + )->formatVariant($variant)); + } + + public function testFormatBaseline(): void + { + $variant = TestUtil::getVariant(); + $this->createBaseline($variant); + self::assertEquals('baseline: 30', $this->createFormatter( + '"variant: " ~ mode(variant.time.avg)', + '"baseline: " ~ mode(baseline.time.avg)' + )->formatVariant($variant)); + } + + private function createFormatter(string $format, string $baselineFormat): VariantSummaryFormatter + { + return new VariantSummaryFormatter( + $this->container()->get(ExpressionLanguage::class), + $this->container()->get(EvaluatingPrinter::class), + $this->container()->get(ParameterProvider::class), + $format, + $baselineFormat + ); + } + + private function createBaseline(Variant $variant, int $time = 30): void + { + $baseline = $variant->getSubject()->createVariant( + new ParameterSet('no',[]), 10, 10, [] + ); + $baseline->spawnIterations(1); + + foreach ($baseline->getIterations() as $iteration) { + $iteration->setResult(new TimeResult($time)); + } + $baseline->computeStats(); + $variant->attachBaseline($baseline); + } +} diff --git a/tests/Unit/Reflection/ReflectionHierarchyTest.php b/tests/Unit/Reflection/ReflectionHierarchyTest.php new file mode 100644 index 000000000..6662c22cd --- /dev/null +++ b/tests/Unit/Reflection/ReflectionHierarchyTest.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Reflection; + +use PhpBench\Reflection\ReflectionClass; +use PhpBench\Reflection\ReflectionHierarchy; +use PhpBench\Reflection\ReflectionMethod; +use PhpBench\Tests\TestCase; + +class ReflectionHierarchyTest extends TestCase +{ + private $hierarchy; + private $reflection1; + private $reflection2; + + protected function setUp(): void + { + $this->hierarchy = new ReflectionHierarchy(); + $this->reflection1 = new ReflectionClass(); + $this->reflection2 = new ReflectionClass(); + } + + /** + * It can have reflection classes added to it. + * It is iterable. + * It should get the top reflection. + */ + public function testAddReflectionsAndIterate(): void + { + $this->hierarchy->addReflectionClass($this->reflection1); + $this->hierarchy->addReflectionClass($this->reflection2); + + foreach ($this->hierarchy as $index => $reflectionClass) { + $this->assertInstanceOf(ReflectionClass::class, $reflectionClass); + } + + $this->assertEquals(1, $index); + + $top = $this->hierarchy->getTop(); + $this->assertSame($this->reflection1, $top); + } + + /** + * It should throw an exception if there are no classes and the top is requested. + * + */ + public function testGetTopNoClasses(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Cannot get top'); + $this->hierarchy->getTop(); + } + + /** + * It can determine if a method exists. + */ + public function testHasMethod(): void + { + $this->reflection1->methods['foobar'] = true; + $this->hierarchy->addReflectionClass($this->reflection1); + $this->hierarchy->addReflectionClass($this->reflection2); + + $this->assertTrue($this->hierarchy->hasMethod('foobar')); + $this->assertFalse($this->hierarchy->hasMethod('barfoo')); + + $this->reflection2->methods['barfoo'] = true; + $this->assertTrue($this->hierarchy->hasMethod('barfoo')); + } + + /** + * It can determine if a method is static. + */ + public function testHasStaticMethod(): void + { + $this->reflection1->methods['foobar'] = new ReflectionMethod(); + $this->reflection1->methods['foobar']->isStatic = true; + $this->hierarchy->addReflectionClass($this->reflection1); + $this->hierarchy->addReflectionClass($this->reflection2); + + $this->assertTrue($this->hierarchy->hasStaticMethod('foobar')); + $this->assertFalse($this->hierarchy->hasStaticMethod('barfoo')); + + $this->reflection1->methods['barfoo'] = new ReflectionMethod(); + $this->reflection1->methods['barfoo']->isStatic = true; + $this->assertTrue($this->hierarchy->hasStaticMethod('barfoo')); + } +} diff --git a/tests/Unit/Reflection/RemoteReflectorTest.php b/tests/Unit/Reflection/RemoteReflectorTest.php new file mode 100644 index 000000000..d0c8c7a58 --- /dev/null +++ b/tests/Unit/Reflection/RemoteReflectorTest.php @@ -0,0 +1,263 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Reflection; + +use Generator; +use PhpBench\Attributes\Iterations; +use PhpBench\Attributes\Revs; +use PhpBench\Reflection\ReflectionClass; +use PhpBench\Reflection\ReflectionHierarchy; +use PhpBench\Reflection\ReflectionMethod; +use PhpBench\Reflection\RemoteReflector; +use PhpBench\Remote\Launcher; +use PhpBench\Tests\IntegrationTestCase; +use PhpBench\Tests\Unit\Reflection\reflector\Class1; +use PhpBench\Tests\Unit\Reflection\reflector\Class2; +use PhpBench\Tests\Unit\Reflection\reflector\Class3; + +class RemoteReflectorTest extends IntegrationTestCase +{ + private $reflector; + + protected function setUp(): void + { + $executor = new Launcher(null, null, __DIR__ . '/../../../vendor/autoload.php', null); + $this->reflector = new RemoteReflector($executor); + } + + /** + * It should return information about a class in a different application. + */ + public function testReflector(): void + { + $fname = __DIR__ . '/reflector/ExampleClass.php'; + $classHierarchy = $this->reflector->reflect($fname); + $this->assertInstanceOf(ReflectionHierarchy::class, $classHierarchy); + $reflection = $classHierarchy->getTop(); + $this->assertInstanceOf(ReflectionClass::class, $reflection); + $this->assertEquals('\PhpBench\Tests\Unit\Reflection\reflector\ExampleClass', $reflection->class); + $this->assertStringContainsString('Some doc comment', $reflection->comment); + $this->assertEquals($fname, $reflection->path); + $this->assertEquals([ + 'methodOne', + 'methodTwo', + 'provideParamsOne', + 'provideParamsTwo', + 'provideParamsNonScalar', + 'provideParamsNull', + ], array_keys($reflection->methods)); + $this->assertStringContainsString('Method One Comment', $reflection->methods['methodOne']->comment); + } + + /** + * @dataProvider provideReflectAttributes + */ + public function testReflectAttributes(string $source, callable $assertion): void + { + $this->workspace()->put('test.php', $source); + + $classHierarchy = $this->reflector->reflect($this->workspace()->path('test.php')); + $this->assertInstanceOf(ReflectionHierarchy::class, $classHierarchy); + $reflection = $classHierarchy->getTop(); + $this->assertInstanceOf(ReflectionClass::class, $reflection); + assert($reflection instanceof ReflectionClass); + $assertion($reflection); + } + + /** + * @return Generator + */ + public function provideReflectAttributes(): Generator + { + if (PHP_VERSION_ID < 80000) { + $this->markTestSkipped('PHP 8 only'); + + return; + } + + yield [ + <<<'EOT' +attributes); + $first = reset($class->attributes); + self::assertInstanceof(Iterations::class, $first); + } + ]; + + yield [ + <<<'EOT' +attributes); + self::assertInstanceof(Revs::class, $first); + assert($first instanceof Revs); + self::assertEquals([12], $first->revs); + } + ]; + + yield [ + <<<'EOT' +methods); + assert($method instanceof ReflectionMethod); + self::assertCount(1, $method->attributes); + $first = reset($method->attributes); + self::assertInstanceof(Iterations::class, $first); + assert($first instanceof Iterations); + self::assertEquals([12], $first->iterations); + } + ]; + + yield 'ignores non-existing attributes' => [ + <<<'EOT' +methods); + assert($method instanceof ReflectionMethod); + self::assertCount(0, $method->attributes); + } + ]; + } + + /** + * It should return an empty class hierarchy if no classes are found in a file. + */ + public function testReflectorNoClass(): void + { + $fname = __DIR__ . '/reflector/EmptyFile.php'; + $classHierarchy = $this->reflector->reflect($fname); + $this->assertInstanceOf(ReflectionHierarchy::class, $classHierarchy); + $this->assertTrue($classHierarchy->isEmpty()); + } + + /** + * It should parse a file whose class declaration is on the 20th line. + * See: https://github.com/phpbench/phpbench/issues/325. + */ + public function testReflector20LineFile(): void + { + $fname = __DIR__ . '/reflector/ExampleClass2.php'; + $this->reflector->reflect($fname); + $this->addToAssertionCount(1); + } + + /** + * It should inherit metadata from parent classes. + */ + public function testHierarchy(): void + { + $classHierarchy = $this->reflector->reflect(__DIR__ . '/reflector/Class3.php'); + $this->assertInstanceOf(ReflectionHierarchy::class, $classHierarchy); + $classHierarchy = iterator_to_array($classHierarchy); + $reflection = array_shift($classHierarchy); + $this->assertEquals('\\' . Class3::class, $reflection->class); + $reflection = array_shift($classHierarchy); + $this->assertEquals(Class2::class, $reflection->class); + $reflection = array_shift($classHierarchy); + $this->assertEquals(Class1::class, $reflection->class); + } + + /** + * It should return the parameter sets from a benchmark class. + */ + public function testGetParameterSets(): void + { + $parameterSets = $this->reflector->getParameterSets(__DIR__ . '/reflector/ExampleClass.php', [ + 'provideParamsOne', + 'provideParamsTwo', + 'provideParamsNull', + ]); + + $this->assertEquals([ + [ + [ + 'one' => 'two', + 'three' => 'four', + ], + ], + [ + [ + 'five' => 'six', + 'seven' => 'eight', + ], + ], + [ + [ + 'nine' => null, + 'ten' => null, + ], + ], + ], $parameterSets); + } + + /** + * It should not throw an exception if the parameter set contains non-scalar values. + */ + public function testGetParameterSetsNonScalar(): void + { + $this->reflector->getParameterSets(__DIR__ . '/reflector/ExampleClass.php', [ + 'provideParamsNonScalar', + ]); + $this->addToAssertionCount(1); + } + + /** + * It should parse a class which has multiple class keywords and return the first + * declared class. + */ + public function testMultipleClassKeywords(): void + { + $fname = __DIR__ . '/reflector/ClassWithClassKeywords.php'; + $classHierarchy = $this->reflector->reflect($fname); + $reflection = $classHierarchy->getTop(); + $this->assertEquals('\Test\ClassWithClassKeywords', $reflection->class); + } +} diff --git a/tests/Unit/Reflection/reflector/Class1.php b/tests/Unit/Reflection/reflector/Class1.php new file mode 100644 index 000000000..08acd2867 --- /dev/null +++ b/tests/Unit/Reflection/reflector/Class1.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Reflection\reflector; + +abstract class Class1 +{ + /** + * one. + */ + public function one(): void + { + } +} diff --git a/tests/Unit/Reflection/reflector/Class2.php b/tests/Unit/Reflection/reflector/Class2.php new file mode 100644 index 000000000..b642b8221 --- /dev/null +++ b/tests/Unit/Reflection/reflector/Class2.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Reflection\reflector; + +class Class2 extends Class1 +{ + /** + * two. + */ + public function two(): void + { + } +} diff --git a/tests/Unit/Reflection/reflector/Class3.php b/tests/Unit/Reflection/reflector/Class3.php new file mode 100644 index 000000000..bfd371a45 --- /dev/null +++ b/tests/Unit/Reflection/reflector/Class3.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Reflection\reflector; + +class Class3 extends Class2 +{ + /** + * foobar. + */ + public function two(): void + { + } + + /** + * three. + */ + public function three(): void + { + } +} diff --git a/tests/Unit/Reflection/reflector/ClassWithClassKeywords.php b/tests/Unit/Reflection/reflector/ClassWithClassKeywords.php new file mode 100644 index 000000000..eec1561a0 --- /dev/null +++ b/tests/Unit/Reflection/reflector/ClassWithClassKeywords.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace Test; + +/** + * @revs(10000) + * @iterations(5) + */ +class ClassWithClassKeywords +{ + protected $class = \Test\B::class; + + public function benchIsSubclassOf(): void + { + is_subclass_of($this->class, \Test\A::class); + } + + public function benchReflectionClass(): void + { + $c = new \ReflectionClass($this->class); + $c->isSubclassOf(\Test\A::class); + } +} + +class A +{ +} diff --git a/tests/Unit/Reflection/reflector/EmptyFile.php b/tests/Unit/Reflection/reflector/EmptyFile.php new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Unit/Reflection/reflector/ExampleClass.php b/tests/Unit/Reflection/reflector/ExampleClass.php new file mode 100644 index 000000000..13433fd40 --- /dev/null +++ b/tests/Unit/Reflection/reflector/ExampleClass.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Reflection\reflector; + +/** + * Some doc comment. + */ +class ExampleClass +{ + /** + * Method One Comment. + */ + public function methodOne(): void + { + } + + /** + * Method Two Comment. + */ + public function methodTwo(): void + { + } + + public function provideParamsOne() + { + return [ + [ + 'one' => 'two', + 'three' => 'four', + ], + ]; + } + + public function provideParamsTwo() + { + return [ + [ + 'five' => 'six', + 'seven' => 'eight', + ], + ]; + } + + public function provideParamsNonScalar() + { + return [ + [ + 'five' => 'six', + 'seven' => new \stdClass(), + ], + ]; + } + + public function provideParamsNull() + { + return [ + [ + 'nine' => null, + 'ten' => null, + ], + ]; + } +} diff --git a/tests/Unit/Reflection/reflector/ExampleClass2.php b/tests/Unit/Reflection/reflector/ExampleClass2.php new file mode 100644 index 000000000..13d6745de --- /dev/null +++ b/tests/Unit/Reflection/reflector/ExampleClass2.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace ZendBench\Hydrator; + +/** + * @AfterMethods({"foo"}) + * @BeforeMethods({"classSetUp"}) + * @Revs(1000) + * @Iterations(20) + * @Warmup(2) + */ +class ExampleClass2 +{ + // code +} diff --git a/tests/Unit/Registry/ConfigTest.php b/tests/Unit/Registry/ConfigTest.php new file mode 100644 index 000000000..4c8bd153f --- /dev/null +++ b/tests/Unit/Registry/ConfigTest.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Registry; + +use InvalidArgumentException; +use PhpBench\Registry\Config; +use PhpBench\Tests\TestCase; + +class ConfigTest extends TestCase +{ + private $config; + + protected function setUp(): void + { + } + + /** + * It should throw an exception if an offset does not exist. + * + */ + public function testExceptionOffsetNotExist(): void + { + $this->expectExceptionMessage('Configuration offset "offset_not_exist" does not exist.'); + $this->expectException(InvalidArgumentException::class); + $config = new Config( + 'test', + [ + 'foo' => 'bar', + 'bar' => [ + 'one' => 1, + 'two' => 2, + ], + ]); + $config['offset_not_exist']; + } + + /** + * It should throw an exception if an invalid name is given. + * + * @dataProvider provideInvalidName + */ + public function testInvalidName($name): void + { + $this->expectException(InvalidArgumentException::class); + new Config($name, []); + } + + public function provideInvalidName() + { + return [ + ['he lo'], + ['foo&'], + [':'], + [''], + ]; + } + + /** + * It should allow good names. + * + * @dataProvider provideGoodName + */ + public function testGoodName($name): void + { + $config = new Config($name, []); + $this->assertEquals($name, $config->getName()); + } + + public function provideGoodName() + { + return [ + ['helo'], + ['foo-bar'], + ['foo_bar'], + ]; + } +} diff --git a/tests/Unit/Registry/ConfigurableRegistryTest.php b/tests/Unit/Registry/ConfigurableRegistryTest.php new file mode 100644 index 000000000..0403e0033 --- /dev/null +++ b/tests/Unit/Registry/ConfigurableRegistryTest.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Registry; + +use InvalidArgumentException; +use PhpBench\Json\JsonDecoder; +use PhpBench\Registry\Config; +use PhpBench\Registry\ConfigurableRegistry; +use Prophecy\Argument; +use Seld\JsonLint\ParsingException; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class ConfigurableRegistryTest extends RegistryTest +{ + protected function setUp(): void + { + parent::setUp(); + + $this->registry = new ConfigurableRegistry( + 'test', + $this->container->reveal(), + new JsonDecoder() + ); + } + + /** + * It should set configuration for a registered service + * It should retrieve configurations. + */ + public function testGetSetConfig(): void + { + $config = [ + 'test' => 'service', + 'one' => 1, + 'two' => 2, + ]; + + $this->registry->setService('service', $this->service1->reveal()); + $this->service1->configure(Argument::type(OptionsResolver::class))->will(function ($args): void { + $args[0]->setDefaults(['one' => 'one', 'two' => 'two']); + }); + + $this->registry->setConfig('one', $config); + + $result = $this->registry->getConfig('one'); + $this->assertEquals($config, $result->getArrayCopy()); + } + + /** + * It should resolve configs that exend other configs. + */ + public function testResolveExtended(): void + { + $config1 = [ + 'test' => 'service', + 'one' => 'two', + 'three' => 'four', + ]; + $config2 = [ + 'extends' => 'config1', + 'two' => 'three', + 'four' => 'five', + ]; + + $this->registry->setService('service', $this->service1->reveal()); + $this->service1->configure(Argument::type(OptionsResolver::class))->will(function ($args): void { + $args[0]->setDefaults([ + 'test' => null, + 'one' => null, + 'two' => null, + 'three' => null, + 'four' => null, + ]); + }); + + $this->registry->setConfig('config1', $config1); + $this->registry->setConfig('config2', $config2); + + $result = $this->registry->getConfig('config2'); + $this->assertEquals([ + 'test' => 'service', + 'one' => 'two', + 'three' => 'four', + 'two' => 'three', + 'four' => 'five', + ], $result->getArrayCopy()); + } + + /** + * It should throw an exception if a config extends a config from a different service. + * + */ + public function testExtendsDifferentServiceException(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('test configuration for service "service2" cannot extend configuration for different service "service1"'); + $config1 = [ + 'test' => 'service1', + ]; + $config2 = [ + 'test' => 'service2', + 'extends' => 'config1', + ]; + + $this->registry->setService('service1', $this->service1->reveal()); + $this->registry->setService('service2', $this->service2->reveal()); + + $this->registry->setConfig('config1', $config1); + $this->registry->setConfig('config2', $config2); + + $this->registry->getConfig('config2'); + } + + /** + * If a JSON encoded string is passed to getConfig, then it should be processed. + */ + public function testGetConfigJsonString(): void + { + $this->registry->setService('service', $this->service1->reveal()); + $this->service1->configure(Argument::type(OptionsResolver::class))->shouldBeCalled(); + + $result = $this->registry->getConfig('{"test": "service"}'); + $this->assertEquals(new Config('test', [ + 'test' => 'service', + ]), $result); + } + + /** + * If a invalid JSON encoded string is passed to getConfig, then it should throw an exception. + * + */ + public function testGetConfigJsonStringInvalid(): void + { + $this->expectException(ParsingException::class); + $this->registry->setService('service', $this->service1->reveal()); + + $result = $this->registry->getConfig('{tes t: se rvice'); + $this->assertEquals(new Config('test', [ + 'test' => 'service', + ]), $result); + } + + public function testServiceDirectly(): void + { + $this->registry->setService('service', $this->service1->reveal()); + + $result = $this->registry->getConfig('service'); + $this->assertEquals(new Config('test', [ + 'test' => 'service', + ]), $result); + } +} diff --git a/tests/Unit/Registry/RegistryTest.php b/tests/Unit/Registry/RegistryTest.php new file mode 100644 index 000000000..e8e0c50d0 --- /dev/null +++ b/tests/Unit/Registry/RegistryTest.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Registry; + +use InvalidArgumentException; +use PhpBench\DependencyInjection\Container; +use PhpBench\Registry\RegistrableInterface; +use PhpBench\Registry\Registry; +use PhpBench\Tests\TestCase; +use RuntimeException; + +class RegistryTest extends TestCase +{ + protected $registry; + protected $container; + protected $service1; + protected $service2; + + protected function setUp(): void + { + $this->container = $this->prophesize(Container::class); + $this->service1 = $this->prophesize(RegistrableInterface::class); + $this->service2 = $this->prophesize(RegistrableInterface::class); + + $this->registry = new Registry( + 'test', + $this->container->reveal() + ); + } + + /** + * It should register a service + * It should retrieve a service + * It should retrieve a service from the container only once. + */ + public function testRegisterRetrieveService(): void + { + $this->container->get('foo') + ->willReturn( + $this->service1->reveal() + ) + ->shouldBeCalledTimes(1); + + $this->registry->registerService('bar', 'foo'); + $this->registry->getService('bar'); + $this->registry->getService('bar'); + } + + /** + * It should set and retrieve services. + */ + public function testSetAndRetrieve(): void + { + $this->registry->setService('one', $this->service1->reveal()); + $service = $this->registry->getService('one'); + $this->assertSame( + $this->service1->reveal(), + $service + ); + } + + /** + * It should return a default service if no argument is given to get(). + */ + public function testDefaultGet(): void + { + $registry = new Registry( + 'test', + $this->container->reveal(), + 'foo' + ); + $service = new \stdClass(); + $registry->setService('foo', $service); + $this->assertEquals($registry->getService(), $service); + } + + /** + * It should throw an exception if no argument given to get() and no default is defined. + * + */ + public function testDefaultGetNoDefault(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('You must configure a default test service, registered test services: "foo"'); + $registry = new Registry( + 'test', + $this->container->reveal() + ); + $service = new \stdClass(); + $registry->setService('foo', $service); + $this->assertEquals($registry->getService(), $service); + } + + /** + * It should throw an exception if a service does not exist. + * + */ + public function testExceptionServivceNotExist(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('test service "bar" does not exist. Registered test services: "one"'); + $this->registry->setService('one', $this->service1->reveal()); + $this->registry->getService('bar'); + } + + /** + * It should throw an exception if setting an already set service. + * + */ + public function testRegisterAlreadyRegistered(): void + { + $this->expectExceptionMessage('test service "one" already exists'); + $this->expectException(InvalidArgumentException::class); + $this->registry->setService('one', $this->service1->reveal()); + $this->registry->setService('one', $this->service1->reveal()); + } +} diff --git a/tests/Unit/Remote/IniStringBuilderTest.php b/tests/Unit/Remote/IniStringBuilderTest.php new file mode 100644 index 000000000..4c0c138e2 --- /dev/null +++ b/tests/Unit/Remote/IniStringBuilderTest.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Remote; + +use PhpBench\Remote\IniStringBuilder; +use PhpBench\Tests\TestCase; + +class IniStringBuilderTest extends TestCase +{ + /** + * @var IniStringBuilder + */ + private $builder; + + protected function setUp(): void + { + $this->builder = new IniStringBuilder(); + } + + /** + * @dataProvider provideBuild + */ + public function testBuild(array $example, string $expectedIniString): void + { + $iniString = $this->builder->build($example); + $this->assertEquals($expectedIniString, $iniString); + } + + public function provideBuild() + { + return [ + [ + [ + 'a' => 'b', + ], + '-da=b', + ], + [ + [ + 'a' => 'b', + 'b' => 'a', + ], + '-da=b -db=a', + ], + [ + [ + 'a' => ['b', 'c', 'd'], + ], + '-da=b -da=c -da=d', + ], + ]; + } +} diff --git a/tests/Unit/Remote/LauncherTest.php b/tests/Unit/Remote/LauncherTest.php new file mode 100644 index 000000000..210b35219 --- /dev/null +++ b/tests/Unit/Remote/LauncherTest.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Remote; + +use InvalidArgumentException; +use PhpBench\Remote\Launcher; +use PhpBench\Remote\Payload; +use PhpBench\Remote\PayloadFactory; +use PhpBench\Tests\TestCase; +use Symfony\Component\Process\ExecutableFinder; + +class LauncherTest extends TestCase +{ + private $factory; + private $payload; + private $finder; + + protected function setUp(): void + { + $this->factory = $this->prophesize(PayloadFactory::class); + $this->payload = $this->prophesize(Payload::class); + $this->finder = $this->prophesize(ExecutableFinder::class); + } + + /** + * It should generate a script from a given template, launch it + * and return the results. + */ + public function testLiveExecute(): void + { + $launcher = $this->createLiveLauncher(); + $result = $launcher->payload( + __DIR__ . '/template/foo.template', + [ + 'foo' => 'bar', + ] + )->launch(); + + $this->assertEquals([ + 'foo' => 'bar', + ], $result); + } + + /** + * It should pass the wrapper, ini settings and php-binary to the payload. + */ + public function testPassSettingsToPayload(): void + { + $launcher = new Launcher( + $this->factory->reveal(), + $this->finder->reveal(), + $bootstrap = __DIR__ . '/../../../vendor/autoload.php', + '/path/to/php', + $phpConfig = ['setting_1' => 'value_1', 'setting_2' => 'value_2'], + 'wrapper' + ); + + $this->factory->create( + __FILE__, + ['bootstrap' => $bootstrap], + '/path/to/php', + null + )->willReturn($this->payload->reveal()); + + $this->payload->setWrapper('wrapper')->shouldBeCalled(); + $this->payload->mergePhpConfig($phpConfig)->shouldBeCalled(); + + $launcher->payload(__FILE__, []); + } + + /** + * It should throw an exception if the bootstrap file does not exist. + * + */ + public function testInvalidBootstrap(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Bootstrap file'); + $launcher = new Launcher( + $this->factory->reveal(), + $this->finder->reveal(), + __DIR__ . '/../../../../vendor/notexisting.php' + ); + $launcher->payload( + __DIR__ . '/template/foo.template', + [ + 'foo' => 'bar', + ] + ); + } + + private function createLiveLauncher() + { + return new Launcher( + new PayloadFactory(), + new ExecutableFinder(), + __DIR__ . '/../../../vendor/autoload.php' + ); + } +} diff --git a/tests/Unit/Remote/PayloadFactoryTest.php b/tests/Unit/Remote/PayloadFactoryTest.php new file mode 100644 index 000000000..c36388d13 --- /dev/null +++ b/tests/Unit/Remote/PayloadFactoryTest.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Remote; + +use PhpBench\Remote\Payload; +use PhpBench\Remote\PayloadFactory; +use PhpBench\Tests\TestCase; + +class PayloadFactoryTest extends TestCase +{ + private $factory; + + protected function setUp(): void + { + $this->factory = new PayloadFactory(); + } + + /** + * It should create a new payload. + */ + public function testCreate(): void + { + $payload = $this->factory->create('template', ['token' => 'one'], '/path/to/php'); + $this->assertInstanceOf(Payload::class, $payload); + } +} diff --git a/tests/Unit/Remote/PayloadTest.php b/tests/Unit/Remote/PayloadTest.php new file mode 100644 index 000000000..2517cf8cd --- /dev/null +++ b/tests/Unit/Remote/PayloadTest.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Remote; + +use PhpBench\Remote\Payload; +use PhpBench\Remote\ProcessFactory; +use PhpBench\Tests\TestCase; +use Prophecy\Argument; +use RuntimeException; +use Symfony\Component\Process\Process; + +class PayloadTest extends TestCase +{ + private $process; + private $processFactory; + + protected function setUp(): void + { + $this->process = $this->prophesize(Process::class); + $this->processFactory = $this->prophesize(ProcessFactory::class); + } + + /** + * It should generate a script from a given template, launch it + * and return the results. + */ + public function testLaunch(): void + { + $payload = new Payload( + __DIR__ . '/template/foo.template', + [ + 'foo' => 'bar', + ] + ); + + $result = $payload->launch($payload); + + $this->assertEquals([ + 'foo' => 'bar', + ], $result); + } + + /** + * It should throw an exception if the script is invalid. + * + */ + public function testInvalidScript(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('syntax error'); + $payload = new Payload( + __DIR__ . '/template/invalid.template' + ); + + $payload->launch($payload); + } + + /** + * It should customize the PHP binary path. + */ + public function testBinaryPath(): void + { + $payload = $this->validPayload(); + $payload->setPhpPath('/foo/bar'); + $this->processFactory->create(Argument::containingString('/foo/bar'), null)->willReturn($this->process); + $this->process->run()->shouldBeCalled(); + $this->process->isSuccessful()->willReturn(true); + $this->process->getOutput()->willReturn(serialize(['foo' => 'bar'])); + + $payload->launch($payload); + } + + /** + * It should pass PHP ini settings to the PHP executable. + */ + public function testPhpConfig(): void + { + $payload = $this->validPayload(); + + $payload->mergePhpConfig([ + 'foo' => 'bar', + ]); + $payload->mergePhpConfig([ + 'bar' => 'foo', + ]); + + $this->processFactory->create(Argument::containingString('-dfoo=bar'), null)->willReturn($this->process)->shouldBeCalled(); + $this->processFactory->create(Argument::containingString('-dbar=foo'), null)->willReturn($this->process)->shouldBeCalled(); + $this->process->run()->shouldBeCalled(); + $this->process->run()->shouldBeCalled(); + $this->process->isSuccessful()->willReturn(true); + $this->process->getOutput()->willReturn(serialize(['foo' => 'bar'])); + + $payload->launch($payload); + } + + /** + * It should allow the PHP executable to be wrapped with a different executable. + */ + public function testWrap(): void + { + $payload = $this->validPayload(); + $payload->setWrapper('bockfire'); + $payload->setPhpPath('/boo/bar/php'); + $this->processFactory->create(Argument::containingString('bockfire \'/boo/bar/php\''), null)->willReturn($this->process)->shouldBeCalled(); + $this->process->run()->shouldBeCalled(); + $this->process->isSuccessful()->willReturn(true); + $this->process->getOutput()->willReturn(serialize(['foo' => 'bar'])); + + $payload->launch($payload); + } + + /** + * It should throw an execption if a template is not found. + * + */ + public function testTemplateNotFound(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Could not find script template'); + $processFactory = $this->prophesize(ProcessFactory::class); + $payload = new Payload( + __DIR__ . '/template/not-existing-filename.template', + [], + null, + null, + $processFactory->reveal() + ); + + $payload->launch($payload); + } + + private function validPayload(): Payload + { + return $this->validPayloadWithPhpConfig(); + } + + private function validPayloadWithPhpConfig(array $phpConfig = []): Payload + { + return new Payload( + __DIR__ . '/template/foo.template', + [], + null, + null, + $this->processFactory->reveal() + ); + } +} diff --git a/tests/Unit/Remote/launcher/autoload.php b/tests/Unit/Remote/launcher/autoload.php new file mode 100644 index 000000000..e8788914a --- /dev/null +++ b/tests/Unit/Remote/launcher/autoload.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +return; diff --git a/tests/Unit/Remote/template/foo.template b/tests/Unit/Remote/template/foo.template new file mode 100644 index 000000000..7423cafd6 --- /dev/null +++ b/tests/Unit/Remote/template/foo.template @@ -0,0 +1,5 @@ + $foo)); diff --git a/tests/Unit/Remote/template/invalid.template b/tests/Unit/Remote/template/invalid.template new file mode 100644 index 000000000..c09e53cb1 --- /dev/null +++ b/tests/Unit/Remote/template/invalid.template @@ -0,0 +1,2 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Report\Generator; + +use PhpBench\Dom\Document; +use PhpBench\Model\SuiteCollection; +use PhpBench\Registry\Config; +use PhpBench\Report\Generator\CompositeGenerator; +use PhpBench\Report\Model\Reports; +use PhpBench\Report\ReportManager; +use PhpBench\Tests\TestCase; +use Prophecy\Argument; + +class CompositeGeneratorTest extends TestCase +{ + private $generator; + private $manager; + + protected function setUp(): void + { + $this->manager = $this->prophesize(ReportManager::class); + $this->collection = $this->prophesize(SuiteCollection::class); + $this->generator = new CompositeGenerator($this->manager->reveal()); + } + + /** + * It should generate a composite report. + */ + public function testGenerateComposite(): void + { + $config = ['reports' => ['one', 'two']]; + + // for some reason prophecy doesn't like passing the suite document here, so just do a type check + $expected = $this->getReportsDocument(); + $this->manager->generateReports(Argument::type(SuiteCollection::class), ['one', 'two'])->willReturn($expected); + $reports = $this->generator->generate($this->collection->reveal(), new Config('test', $config)); + self::assertSame($expected, $reports); + } + + public function getReportsDocument(): Reports + { + return Reports::empty(); + } +} diff --git a/tests/Unit/Report/Generator/EnvGeneratorTest.php b/tests/Unit/Report/Generator/EnvGeneratorTest.php new file mode 100644 index 000000000..679b0ed03 --- /dev/null +++ b/tests/Unit/Report/Generator/EnvGeneratorTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Report\Generator; + +use PhpBench\DependencyInjection\Container; +use PhpBench\Report\Generator\EnvGenerator; +use PhpBench\Report\GeneratorInterface; + +class EnvGeneratorTest extends GeneratorTestCase +{ + protected function acceptanceSubPath(): string + { + return 'env'; + } + + protected function createGenerator(Container $container): GeneratorInterface + { + return new EnvGenerator(); + } +} diff --git a/tests/Unit/Report/Generator/ExpressionGeneratorTest.php b/tests/Unit/Report/Generator/ExpressionGeneratorTest.php new file mode 100644 index 000000000..6f7db31f0 --- /dev/null +++ b/tests/Unit/Report/Generator/ExpressionGeneratorTest.php @@ -0,0 +1,32 @@ +get(ExpressionLanguage::class), + $container->get(Evaluator::class), + $container->get(EvaluatingPrinter::class), + new SuiteCollectionTransformer(), + new ConsoleLogger(new ConsoleOutput()) + ); + } +} diff --git a/tests/Unit/Report/Generator/GeneratorTestCase.php b/tests/Unit/Report/Generator/GeneratorTestCase.php new file mode 100644 index 000000000..e107edba8 --- /dev/null +++ b/tests/Unit/Report/Generator/GeneratorTestCase.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Report\Generator; + +use Generator; +use PhpBench\DependencyInjection\Container; +use PhpBench\Extension\ExpressionExtension; +use PhpBench\Model\SuiteCollection; +use PhpBench\Registry\Config; +use PhpBench\Report\GeneratorInterface; +use PhpBench\Report\Renderer\ConsoleRenderer; +use PhpBench\Tests\IntegrationTestCase; +use PhpBench\Tests\Util\Approval; +use PhpBench\Tests\Util\TestUtil; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\OptionsResolver\OptionsResolver; +use Throwable; + +abstract class GeneratorTestCase extends IntegrationTestCase +{ + /** + * @dataProvider provideGenerate + */ + public function testGenerate(string $path): void + { + $approval = Approval::create($path,3); + + $container = $this->container(); + $generator = $this->createGenerator($container); + $options = new OptionsResolver(); + $generator->configure($options); + + try { + $document = $generator->generate( + new SuiteCollection([TestUtil::createSuite(array_merge([ + 'output_time_precision' => 3, + ], $approval->getConfig(0)))]), + new Config('asd', $options->resolve($approval->getConfig(1))) + ); + $output = new BufferedOutput(); + ( + new ConsoleRenderer($output, $container->get(ExpressionExtension::SERVICE_PLAIN_PRINTER)) + )->render($document, new Config('asd', [ + 'table_style' => 'default', + ])); + $actual = $output->fetch(); + } catch (Throwable $e) { + $actual = $e->getMessage(); + } + + $approval->approve($actual); + } + + /** + * @return Generator + */ + public function provideGenerate(): Generator + { + foreach (glob(sprintf('%s/%s/*', __DIR__, $this->acceptanceSubPath())) as $path) { + yield [ + $path + ]; + } + } + + abstract protected function acceptanceSubPath(): string; + + abstract protected function createGenerator(Container $container): GeneratorInterface; +} diff --git a/tests/Unit/Report/Generator/bare/bare-vertical b/tests/Unit/Report/Generator/bare/bare-vertical new file mode 100644 index 000000000..6827dee19 --- /dev/null +++ b/tests/Unit/Report/Generator/bare/bare-vertical @@ -0,0 +1,44 @@ +{ + "subjects": [ + "one" + ], + "iterations": [ + 1 + ] +} +--- +{ + "vertical": true +} +--- + ++------------------------+-------------------+ +| field | value | ++------------------------+-------------------+ +| has_baseline | false | +| benchmark_name | TestBench | +| benchmark_class | TestBench | +| subject_name | one | +| subject_groups | [one, two, three] | +| subject_time_unit | microseconds | +| subject_time_precision | 3 | +| subject_time_mode | time | +| variant_name | 0 | +| variant_params | [value1] | +| variant_revs | 5 | +| variant_iterations | 1 | +| suite_tag | test | +| suite_date | 2016-02-06 | +| suite_time | 00:00:00 | +| iteration_index | 0 | +| result_time_net | 1 | +| result_time_revs | 1 | +| result_time_avg | 1 | +| result_mem_peak | 200 | +| result_mem_real | 200 | +| result_mem_final | 200 | +| result_reject_count | 0 | +| result_comp_z_value | 0 | +| result_comp_deviation | 0 | ++------------------------+-------------------+ + diff --git a/tests/Unit/Report/Generator/bare/bare1 b/tests/Unit/Report/Generator/bare/bare1 new file mode 100644 index 000000000..6fdb2be7f --- /dev/null +++ b/tests/Unit/Report/Generator/bare/bare1 @@ -0,0 +1,29 @@ +{ + "subjects": [ + "one", + "two" + ], + "iterations": [ + 10, + 20, + 10, + 30 + ] +} +--- +[] +--- + ++--------------+----------------+-----------------+--------------+-------------------+-------------------+------------------------+-------------------+--------------+----------------+--------------+--------------------+-----------+------------+------------+-----------------+-----------------+------------------+-----------------+-----------------+-----------------+------------------+---------------------+---------------------+-----------------------+ +| has_baseline | benchmark_name | benchmark_class | subject_name | subject_groups | subject_time_unit | subject_time_precision | subject_time_mode | variant_name | variant_params | variant_revs | variant_iterations | suite_tag | suite_date | suite_time | iteration_index | result_time_net | result_time_revs | result_time_avg | result_mem_peak | result_mem_real | result_mem_final | result_reject_count | result_comp_z_value | result_comp_deviation | ++--------------+----------------+-----------------+--------------+-------------------+-------------------+------------------------+-------------------+--------------+----------------+--------------+--------------------+-----------+------------+------------+-----------------+-----------------+------------------+-----------------+-----------------+-----------------+------------------+---------------------+---------------------+-----------------------+ +| false | TestBench | TestBench | one | [one, two, three] | microseconds | 3 | time | 0 | [value1] | 5 | 4 | test | 2016-02-06 | 00:00:00 | 0 | 10 | 1 | 10 | 200 | 200 | 200 | 0 | -0.904534033733 | -42.857142857143 | +| false | TestBench | TestBench | one | [one, two, three] | microseconds | 3 | time | 0 | [value1] | 5 | 4 | test | 2016-02-06 | 00:00:00 | 1 | 20 | 1 | 20 | 200 | 200 | 200 | 0 | 0.301511344578 | 14.285714285714 | +| false | TestBench | TestBench | one | [one, two, three] | microseconds | 3 | time | 0 | [value1] | 5 | 4 | test | 2016-02-06 | 00:00:00 | 2 | 10 | 1 | 10 | 200 | 200 | 200 | 0 | -0.904534033733 | -42.857142857143 | +| false | TestBench | TestBench | one | [one, two, three] | microseconds | 3 | time | 0 | [value1] | 5 | 4 | test | 2016-02-06 | 00:00:00 | 3 | 30 | 1 | 30 | 200 | 200 | 200 | 0 | 1.507556722889 | 71.428571428571 | +| false | TestBench | TestBench | two | [one, two, three] | microseconds | 3 | time | 0 | [value1] | 5 | 4 | test | 2016-02-06 | 00:00:00 | 0 | 10 | 1 | 10 | 200 | 200 | 200 | 0 | -0.904534033733 | -42.857142857143 | +| false | TestBench | TestBench | two | [one, two, three] | microseconds | 3 | time | 0 | [value1] | 5 | 4 | test | 2016-02-06 | 00:00:00 | 1 | 20 | 1 | 20 | 200 | 200 | 200 | 0 | 0.301511344578 | 14.285714285714 | +| false | TestBench | TestBench | two | [one, two, three] | microseconds | 3 | time | 0 | [value1] | 5 | 4 | test | 2016-02-06 | 00:00:00 | 2 | 10 | 1 | 10 | 200 | 200 | 200 | 0 | -0.904534033733 | -42.857142857143 | +| false | TestBench | TestBench | two | [one, two, three] | microseconds | 3 | time | 0 | [value1] | 5 | 4 | test | 2016-02-06 | 00:00:00 | 3 | 30 | 1 | 30 | 200 | 200 | 200 | 0 | 1.507556722889 | 71.428571428571 | ++--------------+----------------+-----------------+--------------+-------------------+-------------------+------------------------+-------------------+--------------+----------------+--------------+--------------------+-----------+------------+------------+-----------------+-----------------+------------------+-----------------+-----------------+-----------------+------------------+---------------------+---------------------+-----------------------+ + diff --git a/tests/Unit/Report/Generator/env/env b/tests/Unit/Report/Generator/env/env new file mode 100644 index 000000000..1d1f62f9f --- /dev/null +++ b/tests/Unit/Report/Generator/env/env @@ -0,0 +1,25 @@ +{ + "env": { + "vcs": { + "branch": "my_branch", + "version": "asdasdasd" + }, + "uname": { + "os": "linux", + "version": "4.2.0" + } + } +} +--- +[] +--- +Suite # 2016-02-06 00:00:00 ++----------+---------+-----------+ +| provider | key | value | ++----------+---------+-----------+ +| vcs | branch | my_branch | +| vcs | version | asdasdasd | +| uname | os | linux | +| uname | version | 4.2.0 | ++----------+---------+-----------+ + diff --git a/tests/Unit/Report/Generator/env/env_empty b/tests/Unit/Report/Generator/env/env_empty new file mode 100644 index 000000000..7e2421ff3 --- /dev/null +++ b/tests/Unit/Report/Generator/env/env_empty @@ -0,0 +1,8 @@ +{ + "env": [] +} +--- +[] +--- +Suite # 2016-02-06 00:00:00 + diff --git a/tests/Unit/Report/Generator/expression/aggregate1 b/tests/Unit/Report/Generator/expression/aggregate1 new file mode 100644 index 000000000..1256195cd --- /dev/null +++ b/tests/Unit/Report/Generator/expression/aggregate1 @@ -0,0 +1,26 @@ +{ + "subjects": [ + "one", + "two" + ], + "iterations": [ + 10, + 20, + 10, + 30 + ] +} +--- +{ + "aggregate": [ + "benchmark_class" + ] +} +--- + ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+---------+ +| tag | benchmark | subject | set | revs | its | mem_peak | best | mode | mean | worst | stdev | rstdev | ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+---------+ +| test | TestBench | one | 0 | 5 | 4 | 200b | 10.000μs | 11.644μs | 17.500μs | 30.000μs | 8.292μs | ±47.38% | ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+---------+ + diff --git a/tests/Unit/Report/Generator/expression/aggregate2 b/tests/Unit/Report/Generator/expression/aggregate2 new file mode 100644 index 000000000..fbfd7f853 --- /dev/null +++ b/tests/Unit/Report/Generator/expression/aggregate2 @@ -0,0 +1,24 @@ +{ + "subjects": [ + "one", + "two" + ], + "iterations": [ + 10, + 20, + 10, + 30 + ] +} +--- +{ + "aggregate": [] +} +--- + ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+---------+ +| tag | benchmark | subject | set | revs | its | mem_peak | best | mode | mean | worst | stdev | rstdev | ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+---------+ +| test | TestBench | one | 0 | 5 | 4 | 200b | 10.000μs | 11.644μs | 17.500μs | 30.000μs | 8.292μs | ±47.38% | ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+---------+ + diff --git a/tests/Unit/Report/Generator/expression/aggregate3 b/tests/Unit/Report/Generator/expression/aggregate3 new file mode 100644 index 000000000..beda8c8ea --- /dev/null +++ b/tests/Unit/Report/Generator/expression/aggregate3 @@ -0,0 +1,29 @@ +{ + "subjects": [ + "one", + "two" + ], + "iterations": [ + 10, + 20, + 10, + 30 + ] +} +--- +{ + "aggregate": [ + "iteration_index" + ] +} +--- + ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ +| tag | benchmark | subject | set | revs | its | mem_peak | best | mode | mean | worst | stdev | rstdev | ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ +| test | TestBench | one | 0 | 5 | 4 | 200b | 10.000μs | 10.000μs | 10.000μs | 10.000μs | 0.000μs | ±0.00% | +| test | TestBench | one | 0 | 5 | 4 | 200b | 20.000μs | 20.000μs | 20.000μs | 20.000μs | 0.000μs | ±0.00% | +| test | TestBench | one | 0 | 5 | 4 | 200b | 10.000μs | 10.000μs | 10.000μs | 10.000μs | 0.000μs | ±0.00% | +| test | TestBench | one | 0 | 5 | 4 | 200b | 30.000μs | 30.000μs | 30.000μs | 30.000μs | 0.000μs | ±0.00% | ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ + diff --git a/tests/Unit/Report/Generator/expression/aggregate4 b/tests/Unit/Report/Generator/expression/aggregate4 new file mode 100644 index 000000000..d57257c0b --- /dev/null +++ b/tests/Unit/Report/Generator/expression/aggregate4 @@ -0,0 +1,20 @@ +{ + "subjects": [ + "one", + "two" + ], + "iterations": [ + 10, + 20, + 10, + 30 + ] +} +--- +{ + "aggregate": [ + "not-existing" + ] +} +--- +Cannot aggregate: field "not-existing" does not exist, know fields: "has_baseline", "benchmark_name", "benchmark_class", "subject_name", "subject_groups", "subject_time_unit", "subject_time_precision", "subject_time_mode", "variant_name", "variant_params", "variant_revs", "variant_iterations", "suite_tag", "suite_date", "suite_time", "iteration_index", "result_time_net", "result_time_revs", "result_time_avg", "result_mem_peak", "result_mem_real", "result_mem_final", "result_reject_count", "result_comp_z_value", "result_comp_deviation" \ No newline at end of file diff --git a/tests/Unit/Report/Generator/expression/break1 b/tests/Unit/Report/Generator/expression/break1 new file mode 100644 index 000000000..9e1c07575 --- /dev/null +++ b/tests/Unit/Report/Generator/expression/break1 @@ -0,0 +1,30 @@ +{ + "subjects": [ + "one", + "two" + ], + "iterations": [ + 10 + ] +} +--- +{ + "break": [ + "subject" + ] +} +--- +one ++------+-----------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ +| tag | benchmark | set | revs | its | mem_peak | best | mode | mean | worst | stdev | rstdev | ++------+-----------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ +| test | TestBench | 0 | 5 | 1 | 200b | 10.000μs | 10.000μs | 10.000μs | 10.000μs | 0.000μs | ±0.00% | ++------+-----------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ + +two ++------+-----------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ +| tag | benchmark | set | revs | its | mem_peak | best | mode | mean | worst | stdev | rstdev | ++------+-----------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ +| test | TestBench | 0 | 5 | 1 | 200b | 10.000μs | 10.000μs | 10.000μs | 10.000μs | 0.000μs | ±0.00% | ++------+-----------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ + diff --git a/tests/Unit/Report/Generator/expression/break2 b/tests/Unit/Report/Generator/expression/break2 new file mode 100644 index 000000000..1236976c9 --- /dev/null +++ b/tests/Unit/Report/Generator/expression/break2 @@ -0,0 +1,22 @@ +{ + "subjects": [ + "one", + "two" + ], + "iterations": [ + 10 + ] +} +--- +{ + "break": [] +} +--- + ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ +| tag | benchmark | subject | set | revs | its | mem_peak | best | mode | mean | worst | stdev | rstdev | ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ +| test | TestBench | one | 0 | 5 | 1 | 200b | 10.000μs | 10.000μs | 10.000μs | 10.000μs | 0.000μs | ±0.00% | +| test | TestBench | two | 0 | 5 | 1 | 200b | 10.000μs | 10.000μs | 10.000μs | 10.000μs | 0.000μs | ±0.00% | ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ + diff --git a/tests/Unit/Report/Generator/expression/cols-baseline-has-les-iterations b/tests/Unit/Report/Generator/expression/cols-baseline-has-les-iterations new file mode 100644 index 000000000..3e101bdee --- /dev/null +++ b/tests/Unit/Report/Generator/expression/cols-baseline-has-les-iterations @@ -0,0 +1,31 @@ +{ + "baseline": { + "subjects": [ + "one" + ], + "iterations": [ + 20 + ] + }, + "subjects": [ + "one" + ], + "iterations": [ + 10, + 20 + ] +} +--- +{ + "baseline_expressions": { + "mode": "\"baseline: \" ~ mode(baseline_time_avg)" + } +} +--- + ++------+-----------+---------+-----+------+-----+------------+----------+--------------+----------+----------+---------+----------------+ +| tag | benchmark | subject | set | revs | its | mem_peak | best | mode | mean | worst | stdev | rstdev | ++------+-----------+---------+-----+------+-----+------------+----------+--------------+----------+----------+---------+----------------+ +| test | TestBench | one | 0 | 5 | 2 | 200b 0.00% | 10.000μs | baseline: 20 | 15.000μs | 20.000μs | 5.000μs | ±33.33% +0.00% | ++------+-----------+---------+-----+------+-----+------------+----------+--------------+----------+----------+---------+----------------+ + diff --git a/tests/Unit/Report/Generator/expression/cols-baseline-has-more-iterations b/tests/Unit/Report/Generator/expression/cols-baseline-has-more-iterations new file mode 100644 index 000000000..128e91c6f --- /dev/null +++ b/tests/Unit/Report/Generator/expression/cols-baseline-has-more-iterations @@ -0,0 +1,31 @@ +{ + "baseline": { + "subjects": [ + "one" + ], + "iterations": [ + 10, + 20 + ] + }, + "subjects": [ + "one" + ], + "iterations": [ + 10 + ] +} +--- +{ + "baseline_expressions": { + "mode": "\"baseline: \" ~ mode(baseline_time_avg)" + } +} +--- + ++------+-----------+---------+-----+------+-----+------------+----------+---------------------------+----------+----------+---------+-----------------+ +| tag | benchmark | subject | set | revs | its | mem_peak | best | mode | mean | worst | stdev | rstdev | ++------+-----------+---------+-----+------+-----+------------+----------+---------------------------+----------+----------+---------+-----------------+ +| test | TestBench | one | 0 | 5 | 1 | 200b 0.00% | 10.000μs | baseline: 15.009784735812 | 10.000μs | 10.000μs | 0.000μs | ±0.00% -100.00% | ++------+-----------+---------+-----+------+-----+------------+----------+---------------------------+----------+----------+---------+-----------------+ + diff --git a/tests/Unit/Report/Generator/expression/cols-baseline-subject-not-existing b/tests/Unit/Report/Generator/expression/cols-baseline-subject-not-existing new file mode 100644 index 000000000..b6781f354 --- /dev/null +++ b/tests/Unit/Report/Generator/expression/cols-baseline-subject-not-existing @@ -0,0 +1,31 @@ +{ + "baseline": { + "subjects": [ + "two" + ], + "iterations": [ + 10, + 20 + ] + }, + "subjects": [ + "one" + ], + "iterations": [ + 10 + ] +} +--- +{ + "baseline_expressions": { + "mode": "\"baseline: \" ~ mode(baseline_time_avg)" + } +} +--- + ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ +| tag | benchmark | subject | set | revs | its | mem_peak | best | mode | mean | worst | stdev | rstdev | ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ +| test | TestBench | one | 0 | 5 | 1 | 200b | 10.000μs | 10.000μs | 10.000μs | 10.000μs | 0.000μs | ±0.00% | ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ + diff --git a/tests/Unit/Report/Generator/expression/cols-baseline1 b/tests/Unit/Report/Generator/expression/cols-baseline1 new file mode 100644 index 000000000..802e582a2 --- /dev/null +++ b/tests/Unit/Report/Generator/expression/cols-baseline1 @@ -0,0 +1,30 @@ +{ + "baseline": { + "subjects": [ + "one" + ], + "iterations": [ + 10 + ] + }, + "subjects": [ + "one" + ], + "iterations": [ + 10 + ] +} +--- +{ + "baseline_expressions": { + "mode": "\"baseline: \" ~ mode(baseline_time_avg)" + } +} +--- + ++------+-----------+---------+-----+------+-----+------------+----------+--------------+----------+----------+---------+--------------+ +| tag | benchmark | subject | set | revs | its | mem_peak | best | mode | mean | worst | stdev | rstdev | ++------+-----------+---------+-----+------+-----+------------+----------+--------------+----------+----------+---------+--------------+ +| test | TestBench | one | 0 | 5 | 1 | 200b 0.00% | 10.000μs | baseline: 10 | 10.000μs | 10.000μs | 0.000μs | ±0.00% 0.00% | ++------+-----------+---------+-----+------+-----+------------+----------+--------------+----------+----------+---------+--------------+ + diff --git a/tests/Unit/Report/Generator/expression/cols-selection b/tests/Unit/Report/Generator/expression/cols-selection new file mode 100644 index 000000000..d9e6f991f --- /dev/null +++ b/tests/Unit/Report/Generator/expression/cols-selection @@ -0,0 +1,36 @@ +{ + "baseline": { + "subjects": [ + "two" + ], + "iterations": [ + 10, + 20 + ] + }, + "subjects": [ + "one", + "two" + ], + "basetime": 0, + "iterations": [ + 10 + ] +} +--- +{ + "cols": [ + "subject", + "benchmark", + "mode" + ] +} +--- + ++---------+-----------+------------------+ +| subject | benchmark | mode | ++---------+-----------+------------------+ +| one | TestBench | 10.000μs | +| two | TestBench | 10.000μs -33.38% | ++---------+-----------+------------------+ + diff --git a/tests/Unit/Report/Generator/expression/cols-selection-baseline b/tests/Unit/Report/Generator/expression/cols-selection-baseline new file mode 100644 index 000000000..130e2916b --- /dev/null +++ b/tests/Unit/Report/Generator/expression/cols-selection-baseline @@ -0,0 +1,27 @@ +{ + "subjects": [ + "one", + "two" + ], + "basetime": 0, + "iterations": [ + 10 + ] +} +--- +{ + "cols": [ + "subject", + "benchmark", + "mode" + ] +} +--- + ++---------+-----------+----------+ +| subject | benchmark | mode | ++---------+-----------+----------+ +| one | TestBench | 10.000μs | +| two | TestBench | 10.000μs | ++---------+-----------+----------+ + diff --git a/tests/Unit/Report/Generator/expression/cols1 b/tests/Unit/Report/Generator/expression/cols1 new file mode 100644 index 000000000..1ac56173f --- /dev/null +++ b/tests/Unit/Report/Generator/expression/cols1 @@ -0,0 +1,16 @@ +{ + "subjects": [ + "one", + "two" + ], + "iterations": [ + 10 + ] +} +--- +{ + "cols": [] +} +--- + + diff --git a/tests/Unit/Report/Generator/expression/cols2 b/tests/Unit/Report/Generator/expression/cols2 new file mode 100644 index 000000000..7e68886d3 --- /dev/null +++ b/tests/Unit/Report/Generator/expression/cols2 @@ -0,0 +1,25 @@ +{ + "subjects": [ + "one", + "two" + ], + "iterations": [ + 10 + ] +} +--- +{ + "cols": { + "subject": "\"hello\"", + "benchmark": "\"hello\"" + } +} +--- + ++---------+-----------+ +| subject | benchmark | ++---------+-----------+ +| hello | hello | +| hello | hello | ++---------+-----------+ + diff --git a/tests/Unit/Report/Generator/expression/cols3 b/tests/Unit/Report/Generator/expression/cols3 new file mode 100644 index 000000000..82f02f1b8 --- /dev/null +++ b/tests/Unit/Report/Generator/expression/cols3 @@ -0,0 +1,27 @@ +{ + "subjects": [ + "one", + "two" + ], + "basetime": 0, + "iterations": [ + 10 + ] +} +--- +{ + "cols": { + "subject": "\"hello\"", + "benchmark": "\"hello\"", + "mode": "mode(result_time_avg)" + } +} +--- + ++---------+-----------+------+ +| subject | benchmark | mode | ++---------+-----------+------+ +| hello | hello | 10 | +| hello | hello | 10 | ++---------+-----------+------+ + diff --git a/tests/Unit/Report/Generator/expression/empty b/tests/Unit/Report/Generator/expression/empty new file mode 100644 index 000000000..c55a5acf1 --- /dev/null +++ b/tests/Unit/Report/Generator/expression/empty @@ -0,0 +1,23 @@ +{ + "subjects": [ + "one", + "two" + ], + "iterations": [ + 10, + 20, + 10, + 30 + ] +} +--- +[] +--- + ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+---------+ +| tag | benchmark | subject | set | revs | its | mem_peak | best | mode | mean | worst | stdev | rstdev | ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+---------+ +| test | TestBench | one | 0 | 5 | 4 | 200b | 10.000μs | 12.975μs | 17.500μs | 30.000μs | 8.292μs | ±47.38% | +| test | TestBench | two | 0 | 5 | 4 | 200b | 10.000μs | 12.975μs | 17.500μs | 30.000μs | 8.292μs | ±47.38% | ++------+-----------+---------+-----+------+-----+----------+----------+----------+----------+----------+---------+---------+ + diff --git a/tests/Unit/Report/Generator/expression/include_baseline1 b/tests/Unit/Report/Generator/expression/include_baseline1 new file mode 100644 index 000000000..381529d9a --- /dev/null +++ b/tests/Unit/Report/Generator/expression/include_baseline1 @@ -0,0 +1,30 @@ +{ + "baseline": { + "subjects": [ + "one" + ], + "iterations": [ + 20 + ] + }, + "subjects": [ + "one" + ], + "iterations": [ + 10, + 20 + ] +} +--- +{ + "include_baseline": true +} +--- + ++----------+-----------+---------+-----+------+-----+------------+--------------+------------------+--------------+--------------+-------------+----------------+ +| tag | benchmark | subject | set | revs | its | mem_peak | best | mode | mean | worst | stdev | rstdev | ++----------+-----------+---------+-----+------+-----+------------+--------------+------------------+--------------+--------------+-------------+----------------+ +| baseline | TestBench | one | 0 | 5 | 1 | 200b | 20.0000000μs | 20.0000000μs | 20.0000000μs | 20.0000000μs | 0.0000000μs | ±0.00% | +| test | TestBench | one | 0 | 5 | 2 | 200b 0.00% | 10.000μs | 15.010μs -24.95% | 15.000μs | 20.000μs | 5.000μs | ±33.33% +0.00% | ++----------+-----------+---------+-----+------+-----+------------+--------------+------------------+--------------+--------------+-------------+----------------+ + diff --git a/tests/Unit/Report/Generator/expression/subject_time_specification b/tests/Unit/Report/Generator/expression/subject_time_specification new file mode 100644 index 000000000..18f3ea3bd --- /dev/null +++ b/tests/Unit/Report/Generator/expression/subject_time_specification @@ -0,0 +1,26 @@ +{ + "subjects": [ + "one", + "two" + ], + "output_time_unit": "minutes", + "output_mode": "throughput", + "output_time_precision": 2, + "iterations": [ + 10, + 20, + 10, + 30 + ] +} +--- +[] +--- + ++------+-----------+---------+-----+------+-----+----------+-------------------+-------------------+-------------------+-------------------+-------------------+---------+ +| tag | benchmark | subject | set | revs | its | mem_peak | best | mode | mean | worst | stdev | rstdev | ++------+-----------+---------+-----+------+-----+----------+-------------------+-------------------+-------------------+-------------------+-------------------+---------+ +| test | TestBench | one | 0 | 5 | 4 | 200b | 6,000,000.00ops/m | 4,624,434.39ops/m | 3,428,571.43ops/m | 2,000,000.00ops/m | 7,236,272.27ops/m | ±47.38% | +| test | TestBench | two | 0 | 5 | 4 | 200b | 6,000,000.00ops/m | 4,624,434.39ops/m | 3,428,571.43ops/m | 2,000,000.00ops/m | 7,236,272.27ops/m | ±47.38% | ++------+-----------+---------+-----+------+-----+----------+-------------------+-------------------+-------------------+-------------------+-------------------+---------+ + diff --git a/tests/Unit/Report/Generator/expression/subject_time_specification_with_baseline b/tests/Unit/Report/Generator/expression/subject_time_specification_with_baseline new file mode 100644 index 000000000..decca6d64 --- /dev/null +++ b/tests/Unit/Report/Generator/expression/subject_time_specification_with_baseline @@ -0,0 +1,31 @@ +{ + "baseline": { + "subjects": [ + "two" + ], + "iterations": [ + 10, + 20 + ] + }, + "subjects": [ + "two" + ], + "output_time_unit": "minutes", + "output_mode": "throughput", + "output_time_precision": 2, + "iterations": [ + 10, + 20 + ] +} +--- +[] +--- + ++------+-----------+---------+-----+------+-----+------------+-------------------+-------------------------+-------------------+-------------------+--------------------+---------------+ +| tag | benchmark | subject | set | revs | its | mem_peak | best | mode | mean | worst | stdev | rstdev | ++------+-----------+---------+-----+------+-----+------------+-------------------+-------------------------+-------------------+-------------------+--------------------+---------------+ +| test | TestBench | two | 0 | 5 | 2 | 200b 0.00% | 6,000,000.00ops/m | 3,997,392.44ops/m 0.00% | 4,000,000.00ops/m | 3,000,000.00ops/m | 12,000,000.00ops/m | ±33.33% 0.00% | ++------+-----------+---------+-----+------+-----+------------+-------------------+-------------------------+-------------------+-------------------+--------------------+---------------+ + diff --git a/tests/Unit/Report/Generator/expression/subject_time_specification_with_nulls b/tests/Unit/Report/Generator/expression/subject_time_specification_with_nulls new file mode 100644 index 000000000..d7a3e8b4c --- /dev/null +++ b/tests/Unit/Report/Generator/expression/subject_time_specification_with_nulls @@ -0,0 +1,31 @@ +{ + "baseline": { + "subjects": [ + "two" + ], + "iterations": [ + 10, + 20 + ] + }, + "subjects": [ + "two" + ], + "output_time_unit": null, + "output_mode": null, + "output_time_precision": null, + "iterations": [ + 10, + 20 + ] +} +--- +[] +--- + ++------+-----------+---------+-----+------+-----+------------+----------+----------------+----------+----------+---------+---------------+ +| tag | benchmark | subject | set | revs | its | mem_peak | best | mode | mean | worst | stdev | rstdev | ++------+-----------+---------+-----+------+-----+------------+----------+----------------+----------+----------+---------+---------------+ +| test | TestBench | two | 0 | 5 | 2 | 200b 0.00% | 10.000μs | 15.010μs 0.00% | 15.000μs | 20.000μs | 5.000μs | ±33.33% 0.00% | ++------+-----------+---------+-----+------+-----+------------+----------+----------------+----------+----------+---------+---------------+ + diff --git a/tests/Unit/Report/Generator/expression/suite-empty b/tests/Unit/Report/Generator/expression/suite-empty new file mode 100644 index 000000000..a71f904a4 --- /dev/null +++ b/tests/Unit/Report/Generator/expression/suite-empty @@ -0,0 +1,7 @@ +{ + "subjects": [], + "iterations": [] +} +--- +[] +--- diff --git a/tests/Unit/Report/Renderer/AbstractRendererCase.php b/tests/Unit/Report/Renderer/AbstractRendererCase.php new file mode 100644 index 000000000..91c2c766b --- /dev/null +++ b/tests/Unit/Report/Renderer/AbstractRendererCase.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Report\Renderer; + +use PhpBench\Registry\Config; +use PhpBench\Report\Generator\OutputTestGenerator; +use PhpBench\Report\Model\Reports; +use PhpBench\Report\RendererInterface; +use PhpBench\Tests\IntegrationTestCase; +use PhpBench\Tests\Util\TestUtil; +use Symfony\Component\OptionsResolver\OptionsResolver; + +abstract class AbstractRendererCase extends IntegrationTestCase +{ + abstract protected function getRenderer(): RendererInterface; + + /** + * @param parameters $config + */ + protected function renderReport($reports, $config): void + { + $renderer = $this->getRenderer(); + $options = new OptionsResolver(); + $renderer->configure($options); + + $renderer->render($reports, new Config('test', $options->resolve($config))); + } + + public function reports(array $config = []): Reports + { + $collection = TestUtil::createCollection([]); + + return (new OutputTestGenerator())->generate($collection, new Config('foo', $config)); + } +} diff --git a/tests/Unit/Report/Renderer/ConsoleRendererTest.php b/tests/Unit/Report/Renderer/ConsoleRendererTest.php new file mode 100644 index 000000000..e416f6f8f --- /dev/null +++ b/tests/Unit/Report/Renderer/ConsoleRendererTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Report\Renderer; + +use Generator; +use PhpBench\Expression\Printer; +use PhpBench\Report\Renderer\ConsoleRenderer; +use PhpBench\Report\RendererInterface; +use PhpBench\Tests\Util\Approval; +use Symfony\Component\Console\Output\BufferedOutput; + +class ConsoleRendererTest extends AbstractRendererCase +{ + /** + * @var BufferedOutput + */ + private $output; + + protected function setUp(): void + { + $this->output = new BufferedOutput(); + } + + protected function getRenderer(): RendererInterface + { + return new ConsoleRenderer($this->output, $this->container()->get(Printer::class)); + } + + /** + * @dataProvider provideRender + */ + public function testRender(string $path): void + { + $approval = Approval::create($path, 2); + + $this->renderReport($this->reports(), $approval->getConfig(0)); + + $approval->approve($this->output->fetch()); + } + + public function provideRender(): Generator + { + foreach (glob(sprintf('%s/%s/*', __DIR__, 'console')) as $path) { + yield [$path]; + } + } +} diff --git a/tests/Unit/Report/Renderer/DelimitedRendererTest.php b/tests/Unit/Report/Renderer/DelimitedRendererTest.php new file mode 100644 index 000000000..af06b16dc --- /dev/null +++ b/tests/Unit/Report/Renderer/DelimitedRendererTest.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Report\Renderer; + +use PhpBench\Expression\Printer; +use PhpBench\Report\Renderer\DelimitedRenderer; +use PhpBench\Report\RendererInterface; +use Symfony\Component\Console\Output\BufferedOutput; + +class DelimitedRendererTest extends AbstractRendererCase +{ + /** + * @var BufferedOutput + */ + private $output; + + protected function setUp(): void + { + $this->output = new BufferedOutput(); + } + + protected function getRenderer(): RendererInterface + { + return new DelimitedRenderer($this->output, $this->container()->get(Printer::class)); + } + + public function testRender(): void + { + $this->renderReport($this->reports(), []); + + $output = $this->output->fetch(); + $this->addToAssertionCount(1); + } + + public function testRenderComma(): void + { + $this->renderReport($this->reports(), [ + 'delimiter' => ',' + ]); + $this->addToAssertionCount(1); + } +} diff --git a/tests/Unit/Report/Renderer/console/default b/tests/Unit/Report/Renderer/console/default new file mode 100644 index 000000000..40e54d1a0 --- /dev/null +++ b/tests/Unit/Report/Renderer/console/default @@ -0,0 +1,29 @@ +[] +--- +Output Test Report +================== + +Output Test Report +This report demonstrates output + +Values ++--------+-----+-------+---------------+------+-----------+ +| string | int | float | bool | null | parameter | ++--------+-----+-------+---------------+------+-----------+ +| one | 123 | 12.2 | [true, false] | null | foo.bar | ++--------+-----+-------+---------------+------+-----------+ + +Percent Difference ++----------+---------+---------+---------+---------+---------+---------+---------+---------+---------+-------+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+ +| -100 | -90 | -80 | -70 | -60 | -50 | -40 | -30 | -20 | -10 | 0 | 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | 90 | 100 | ++----------+---------+---------+---------+---------+---------+---------+---------+---------+---------+-------+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+ +| -100.00% | -90.00% | -80.00% | -70.00% | -60.00% | -50.00% | -40.00% | -30.00% | -20.00% | -10.00% | 0.00% | +10.00% | +20.00% | +30.00% | +40.00% | +50.00% | +60.00% | +70.00% | +80.00% | +90.00% | +100.00% | ++----------+---------+---------+---------+---------+---------+---------+---------+---------+---------+-------+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+ + +Time ++--------+ +| time | ++--------+ +| 10.0ms | ++--------+ + diff --git a/tests/Unit/Report/Renderer/console/title-description b/tests/Unit/Report/Renderer/console/title-description new file mode 100644 index 000000000..ef142a5f3 --- /dev/null +++ b/tests/Unit/Report/Renderer/console/title-description @@ -0,0 +1,31 @@ +{ + "table_style": "borderless" +} +--- +Output Test Report +================== + +Output Test Report +This report demonstrates output + +Values + ======== ===== ======= =============== ====== =========== + string int float bool null parameter + ======== ===== ======= =============== ====== =========== + one 123 12.2 [true, false] null foo.bar + ======== ===== ======= =============== ====== =========== + +Percent Difference + ========== ========= ========= ========= ========= ========= ========= ========= ========= ========= ======= ========= ========= ========= ========= ========= ========= ========= ========= ========= ========== + -100 -90 -80 -70 -60 -50 -40 -30 -20 -10 0 10 20 30 40 50 60 70 80 90 100 + ========== ========= ========= ========= ========= ========= ========= ========= ========= ========= ======= ========= ========= ========= ========= ========= ========= ========= ========= ========= ========== + -100.00% -90.00% -80.00% -70.00% -60.00% -50.00% -40.00% -30.00% -20.00% -10.00% 0.00% +10.00% +20.00% +30.00% +40.00% +50.00% +60.00% +70.00% +80.00% +90.00% +100.00% + ========== ========= ========= ========= ========= ========= ========= ========= ========= ========= ======= ========= ========= ========= ========= ========= ========= ========= ========= ========= ========== + +Time + ======== + time + ======== + 10.0ms + ======== + diff --git a/tests/Unit/Report/ReportManagerTest.php b/tests/Unit/Report/ReportManagerTest.php new file mode 100644 index 000000000..7e4986d82 --- /dev/null +++ b/tests/Unit/Report/ReportManagerTest.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Report; + +use PhpBench\Model\SuiteCollection; +use PhpBench\Registry\Config; +use PhpBench\Registry\ConfigurableRegistry; +use PhpBench\Report\GeneratorInterface; +use PhpBench\Report\Model\Reports; +use PhpBench\Report\RendererInterface; +use PhpBench\Report\ReportManager; +use PhpBench\Tests\TestCase; +use Prophecy\Prophecy\ObjectProphecy; + +class ReportManagerTest extends TestCase +{ + private $reportManager; + private $generator; + private $suiteCollection; + + /** + * @var ObjectProphecy + */ + private $rendererRegistry; + + /** + * @var ObjectProphecy + */ + private $generatorRegistry; + + /** + * @var ObjectProphecy + */ + private $renderer; + + /** + * @var Reports + */ + private $reports; + + protected function setUp(): void + { + $this->rendererRegistry = $this->prophesize(ConfigurableRegistry::class); + $this->generatorRegistry = $this->prophesize(ConfigurableRegistry::class); + + $this->reportManager = new ReportManager( + $this->generatorRegistry->reveal(), + $this->rendererRegistry->reveal() + ); + + $this->generator = $this->prophesize(GeneratorInterface::class); + $this->renderer = $this->prophesize(RendererInterface::class); + + $this->suiteCollection = new SuiteCollection(); + $this->reports = Reports::empty(); + } + + /** + * It should render a report. + */ + public function testRender(): void + { + $config = new Config('test', [ + 'generator' => 'service', + 'one' => 'two', + ]); + $outputConfig = new Config('test', [ + 'renderer' => 'renderer', + 'three' => 'four', + ]); + $this->generatorRegistry->getConfig('test_report')->willReturn($config); + $this->generatorRegistry->getService('service')->willReturn( + $this->generator->reveal() + ); + $this->generator->generate($this->suiteCollection, $config)->willReturn($this->reports); + + $this->rendererRegistry->getConfig('console_output')->willReturn($outputConfig); + $this->rendererRegistry->getService('renderer')->willReturn( + $this->renderer->reveal() + ); + $this->renderer->render($this->reports, $outputConfig)->shouldBeCalled(); + + $this->reportManager->renderReports( + $this->suiteCollection, + ['test_report'], + ['console_output'] + ); + } +} diff --git a/tests/Unit/Serializer/XmlDecoderTest.php b/tests/Unit/Serializer/XmlDecoderTest.php new file mode 100644 index 000000000..b7704b804 --- /dev/null +++ b/tests/Unit/Serializer/XmlDecoderTest.php @@ -0,0 +1,179 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Serializer; + +use PhpBench\Dom\Document; +use PhpBench\Environment\Information; +use PhpBench\Model\Suite; +use PhpBench\Model\SuiteCollection; +use PhpBench\Serializer\XmlDecoder; +use PhpBench\Serializer\XmlEncoder; + +class XmlDecoderTest extends XmlTestCase +{ + /** + * It should encode the suite to an XML document. + * + * @dataProvider provideEncode + */ + public function testDecoder(array $params, $expected): void + { + $collection = $this->getSuiteCollection($params); + $dom = $this->encode($collection); + + $decoder = new XmlDecoder(); + $collection = $decoder->decode($dom); + + $decodedDom = $this->encode($collection); + + $this->assertEquals( + $dom->dump(), + $decodedDom->dump() + ); + } + + /** + * It should throw an exception when encountering a non-existing result class. + * + */ + public function testDecodeUnknownResultClass(): void + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('XML file defines a non-existing result class "FooVendor\FooResult" - maybe you are missing an extension?'); + $dom = new Document(1.0); + $dom->loadXML(<< + + + + +EOT + ); + $decoder = new XmlDecoder(); + $collection = $decoder->decode($dom); + } + + /** + * It should throw an exception for a non-existing result key. + * + */ + public function testDecodeUnknownResultKey(): void + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('No result class was provided with key "foobar" for attribute "foobar-foo"'); + $dom = new Document(1.0); + $dom->loadXML(<< + + + + + + + + + + +EOT + ); + $decoder = new XmlDecoder(); + $collection = $decoder->decode($dom); + } + + /** + * It should throw an exception if an attribute name has no - prefix. + * + */ + public function testInvalidAttribute(): void + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Expected attribute name to have a result key prefix, got "foo"'); + $dom = new Document(1.0); + $dom->loadXML(<< + + + + + + + + + + +EOT + ); + $decoder = new XmlDecoder(); + $collection = $decoder->decode($dom); + } + + public function testDecodeBoolValue(): void + { + $dom = new Document(1.0); + $dom->loadXML(<< + + + + 1 + + + + +EOT + ); + $decoder = new XmlDecoder(); + $collection = $decoder->decode($dom); + $suite = $collection->getSuites()[0]; + assert($suite instanceof Suite); + $envs = $suite->getEnvInformations(); + self::assertCount(1, $envs); + $env = reset($envs); + self::assertInstanceOf(Information::class, $env); + assert($env instanceof Information); + self::assertIsBool($env->offsetGet('foo')); + } + + public function testDecodeLegacyEnv(): void + { + $dom = new Document(1.0); + $dom->loadXML(<< + + + + + + +EOT + ); + $decoder = new XmlDecoder(); + $collection = $decoder->decode($dom); + $suite = $collection->getSuites()[0]; + assert($suite instanceof Suite); + $envs = $suite->getEnvInformations(); + self::assertCount(3, $envs); + $env = reset($envs); + self::assertInstanceOf(Information::class, $env); + assert($env instanceof Information); + self::assertCount(5, $env); + } + + private function encode(SuiteCollection $collection) + { + $xmlEncoder = new XmlEncoder(); + $dom = $xmlEncoder->encode($collection); + + return $dom; + } +} diff --git a/tests/Unit/Serializer/XmlEncoderTest.php b/tests/Unit/Serializer/XmlEncoderTest.php new file mode 100644 index 000000000..cc5bc9da4 --- /dev/null +++ b/tests/Unit/Serializer/XmlEncoderTest.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Serializer; + +use PhpBench\Environment\Information; +use PhpBench\Model\Benchmark; +use PhpBench\Model\Iteration; +use PhpBench\Model\Subject; +use PhpBench\Model\Suite; +use PhpBench\Model\SuiteCollection; +use PhpBench\Model\Variant; +use PhpBench\PhpBench; +use PhpBench\Serializer\XmlEncoder; + +class XmlEncoderTest extends XmlTestCase +{ + protected function setUp(): void + { + $this->suiteCollection = $this->prophesize(SuiteCollection::class); + $this->suite = $this->prophesize(Suite::class); + $this->env1 = $this->prophesize(Information::class); + $this->bench1 = $this->prophesize(Benchmark::class); + $this->subject1 = $this->prophesize(Subject::class); + $this->variant1 = $this->prophesize(Variant::class); + $this->iteration1 = $this->prophesize(Iteration::class); + } + + /** + * It should encode the suite to an XML document. + * + * @dataProvider provideEncode + */ + public function testEncode(array $params, $expected): void + { + $expected = str_replace('PHPBENCH_VERSION', PhpBench::VERSION, $expected); + $collection = $this->getSuiteCollection($params); + $xmlEncoder = new XmlEncoder(); + $dom = $xmlEncoder->encode($collection); + $this->assertInstanceOf('PhpBench\Dom\Document', $dom); + $this->assertEquals($expected, $dom->dump()); + } +} diff --git a/tests/Unit/Serializer/XmlTestCase.php b/tests/Unit/Serializer/XmlTestCase.php new file mode 100644 index 000000000..bae233c96 --- /dev/null +++ b/tests/Unit/Serializer/XmlTestCase.php @@ -0,0 +1,249 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Serializer; + +use PhpBench\Assertion\AssertionResult; +use PhpBench\Assertion\VariantAssertionResults; +use PhpBench\Environment\Information; +use PhpBench\Math\Distribution; +use PhpBench\Model\Benchmark; +use PhpBench\Model\Error; +use PhpBench\Model\ErrorStack; +use PhpBench\Model\Iteration; +use PhpBench\Model\ParameterSet; +use PhpBench\Model\ResolvedExecutor; +use PhpBench\Model\Result\ComputedResult; +use PhpBench\Model\Result\MemoryResult; +use PhpBench\Model\Result\TimeResult; +use PhpBench\Model\Subject; +use PhpBench\Model\Suite; +use PhpBench\Model\SuiteCollection; +use PhpBench\Model\Tag; +use PhpBench\Model\Variant; +use PhpBench\Registry\Config; +use PhpBench\Tests\TestCase; + +class XmlTestCase extends TestCase +{ + protected function setUp(): void + { + $this->suiteCollection = $this->prophesize(SuiteCollection::class); + $this->suite = $this->prophesize(Suite::class); + $this->env1 = $this->prophesize(Information::class); + $this->bench1 = $this->prophesize(Benchmark::class); + $this->subject1 = $this->prophesize(Subject::class); + $this->variant1 = $this->prophesize(Variant::class); + $this->iteration1 = $this->prophesize(Iteration::class); + } + + public function getSuiteCollection($params) + { + $params = array_merge([ + 'error' => false, + 'failure' => false, + 'groups' => [], + 'params' => [], + ], $params); + + $this->suiteCollection->getSuites()->willReturn([$this->suite->reveal()]); + $this->suite->getUuid()->willReturn(1234); + $this->suite->getDate()->willReturn(new \DateTime('2015-01-01T00:00:00+00:00')); + $this->suite->getTag()->willReturn(new Tag('test')); + $this->suite->getConfigPath()->willReturn('/path/to/config.json'); + $this->suite->getEnvInformations()->willReturn([ + $this->env1, + ]); + $this->env1->getName()->willReturn('info1'); + $this->env1->getIterator()->willReturn(new \ArrayIterator([ + 'foo' => 'bar', + ])); + $this->suite->getBenchmarks()->willReturn([ + $this->bench1->reveal(), + ]); + $this->bench1->getSubjects()->willReturn([ + $this->subject1->reveal(), + ]); + $this->bench1->getClass()->willReturn('Bench1'); + $this->subject1->getVariants()->willReturn([ + $this->variant1->reveal(), + ]); + $this->subject1->getName()->willReturn('subjectName'); + $this->subject1->getGroups()->willReturn($params['groups']); + $this->subject1->getSleep()->willReturn(5); + $this->subject1->getOutputTimeUnit()->willReturn('milliseconds'); + $this->subject1->getOutputTimePrecision()->willReturn(7); + $this->subject1->getOutputMode()->willReturn('throughput'); + $this->subject1->getExecutor()->willReturn(ResolvedExecutor::fromNameAndConfig('foo', new Config('asd', []))); + $this->subject1->getRetryThreshold()->willReturn(10); + $this->variant1->getWarmup()->willReturn(50); + $this->variant1->getBaseline()->willReturn(null); + $this->variant1->getParameterSet()->willReturn(new ParameterSet('some params', $params['params'])); + $this->variant1->hasErrorStack()->willReturn($params['error']); + + if ($params['failure']) { + $this->variant1->getAssertionResults()->willReturn(new VariantAssertionResults($this->variant1->reveal(), [AssertionResult::fail()])); + } else { + $this->variant1->getAssertionResults()->willReturn(new VariantAssertionResults($this->variant1->reveal(), [])); + } + + $this->variant1->isComputed()->willReturn(true); + $this->variant1->getRevolutions()->willReturn(100); + + if ($params['error']) { + $this->variant1->getErrorStack()->willReturn( + new ErrorStack( + $this->variant1->reveal(), + [ + new Error( + 'This is an error', + 'ErrorClass', + 0, 1, 2, + '-- trace --' + ), + ] + ) + ); + } + + $results = []; + + if ($params['failure']) { + $results[] = AssertionResult::fail('Fail!'); + } + + $this->variant1->getAssertionResults()->willReturn(new VariantAssertionResults($this->variant1->reveal(), $results)); + + $this->variant1->getStats()->willReturn(new Distribution([0.1])); + $this->variant1->getIterator()->willReturn(new \ArrayIterator([ + $this->iteration1->reveal(), + ])); + $this->iteration1->getResults()->willReturn([ + new TimeResult(10, 1), + new MemoryResult(100, 110, 109), + new ComputedResult(0, 0, 5), + ]); + + return $this->suiteCollection->reveal(); + } + + public function provideEncode() + { + return [ + [ + [ + 'groups' => ['group1', 'group2'], + 'params' => [ + 'foo' => 'bar', + 'bar' => [ + 'baz' => 'bon', + ], + 'baz' => null, + ], + ], + <<<'EOT' + + + + + + bar + + + + + + + + + + + + + + + + + + + + + + + + + + +EOT + ], + [ + ['error' => true], + <<<'EOT' + + + + + + bar + + + + + + + + + This is an error + + + + + + + +EOT + ], + 'failure' => [ + ['failure' => true], + <<<'EOT' + + + + + + bar + + + + + + + + + Fail! + + + + + + + + + + + + +EOT + ], + ]; + } +} diff --git a/tests/Unit/Storage/Driver/Xml/HistoryIteratorTest.php b/tests/Unit/Storage/Driver/Xml/HistoryIteratorTest.php new file mode 100644 index 000000000..86613c580 --- /dev/null +++ b/tests/Unit/Storage/Driver/Xml/HistoryIteratorTest.php @@ -0,0 +1,147 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Storage\Driver\Xml; + +use PhpBench\Dom\Document; +use PhpBench\Model\Suite; +use PhpBench\Model\SuiteCollection; +use PhpBench\Model\Summary; +use PhpBench\Model\Tag; +use PhpBench\Serializer\XmlDecoder; +use PhpBench\Storage\Driver\Xml\HistoryIterator; +use PhpBench\Tests\IntegrationTestCase; +use Prophecy\Argument; +use Symfony\Component\Filesystem\Filesystem; + +class HistoryIteratorTest extends IntegrationTestCase +{ + private $xmlDecoder; + private $iterator; + private $filesystem; + + protected function setUp(): void + { + $this->workspace()->reset(); + $this->xmlDecoder = $this->prophesize(XmlDecoder::class); + $this->iterator = new HistoryIterator( + $this->xmlDecoder->reveal(), + $this->workspace()->path() + ); + + $this->filesystem = new Filesystem(); + } + + /** + * It should "iterate" given an empty directory. + */ + public function testIterateEmpty(): void + { + $entries = iterator_to_array($this->iterator); + $this->assertCount(0, $entries); + } + + /** + * It should "iterate" if the directory does not exist. + */ + public function testNoDirectoryExist(): void + { + $iterator = new HistoryIterator( + $this->xmlDecoder->reveal(), + 'foobar_not_exist' + ); + + $iterator->current(); + $this->addToAssertionCount(1); + } + + /** + * It should iterate over entries. + */ + public function testIterate(): void + { + $collections = []; + $collections[1] = $this->createEntry(1, new \DateTime('2014-02-03')); + $collections[2] = $this->createEntry(2, new \DateTime('2016-01-01')); + $collections[3] = $this->createEntry(3, new \DateTime('2016-02-01')); + $collections[4] = $this->createEntry(4, new \DateTime('2016-02-03')); + $collections[5] = $this->createEntry(5, new \DateTime('2016-02-08 12:00:00')); + $collections[6] = $this->createEntry(6, new \DateTime('2016-02-08 06:00:00')); + + $this->xmlDecoder->decode(Argument::type(Document::class))->will(function ($args) use (&$order, &$collections) { + $dom = $args[0]; + $uuid = $dom->evaluate('number(./@uuid)'); + + return $collections[$uuid]; + }); + + $entries = iterator_to_array($this->iterator); + $this->assertCount(6, $entries); + $first = array_shift($entries); + $this->assertEquals('2016-02-08 12:00:00', $first->getDate()->format('Y-m-d H:i:s')); + $this->assertEquals(5, $first->getNbSubjects()); + $this->assertEquals(10, $first->getNbIterations()); + $this->assertEquals(1000, $first->getNbRevolutions()); + $this->assertEquals(1, $first->getMinTime()); + $this->assertEquals(2, $first->getMaxTime()); + $this->assertEquals(3, $first->getMeanTime()); + $this->assertEquals(4, $first->getMeanRelStDev()); + $this->assertEquals(5, $first->getTotalTime()); + + $last = array_pop($entries); + $this->assertEquals('2014-02-03', $last->getDate()->format('Y-m-d')); + } + + private function createEntry($uuid, \DateTime $date) + { + $dom = new Document(); + $test = $dom->createRoot('test'); + $test->setAttribute('uuid', $uuid); + $path = $this->workspace()->path($date->format('/Y/m/d/') . '/' . $uuid . '.xml'); + + if (!$this->filesystem->exists(dirname($path))) { + $this->filesystem->mkdir(dirname($path)); + } + + $dom->save($path); + + return $this->createCollection($uuid, $date); + } + + private function createCollection($uuid, \DateTime $date) + { + $collection = $this->prophesize(SuiteCollection::class); + $suite = $this->prophesize(Suite::class); + $summary = $this->prophesize(Summary::class); + + $collection->getSuites()->willReturn([$suite->reveal()]); + $suite->getUuid()->willReturn($uuid); + $suite->getDate()->willReturn($date); + $suite->getTag()->willReturn(new Tag('foo')); + $suite->getEnvInformations()->willReturn([ + 'vcs' => [ + 'branch' => 'foo_branch', + ], + ]); + $suite->getSummary()->willReturn($summary->reveal()); + $summary->getNbSubjects()->willReturn(5); + $summary->getNbIterations()->willReturn(10); + $summary->getNbRevolutions()->willReturn(1000); + $summary->getMinTime()->willReturn(1); + $summary->getMaxTime()->willReturn(2); + $summary->getMeanTime()->willReturn(3); + $summary->getMeanRelStDev()->willReturn(4); + $summary->getTotalTime()->willReturn(5); + + return $collection; + } +} diff --git a/tests/Unit/Storage/Driver/Xml/XmlDriverTest.php b/tests/Unit/Storage/Driver/Xml/XmlDriverTest.php new file mode 100644 index 000000000..16c665d9b --- /dev/null +++ b/tests/Unit/Storage/Driver/Xml/XmlDriverTest.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Storage\Driver\Xml; + +use PhpBench\Dom\Document; +use PhpBench\Model\Suite; +use PhpBench\Model\SuiteCollection; +use PhpBench\Serializer\XmlDecoder; +use PhpBench\Serializer\XmlEncoder; +use PhpBench\Storage\Driver\Xml\HistoryIterator; +use PhpBench\Storage\Driver\Xml\XmlDriver; +use PhpBench\Tests\TestCase; +use Prophecy\Argument; +use Symfony\Component\Filesystem\Filesystem; + +class XmlDriverTest extends TestCase +{ + private $xmlEncoder; + private $xmlDecoder; + private $filesystem; + private $collection; + private $suite; + + /** + */ + private $document; + + protected function setUp(): void + { + $this->xmlEncoder = $this->prophesize(XmlEncoder::class); + $this->xmlDecoder = $this->prophesize(XmlDecoder::class); + $this->filesystem = $this->prophesize(Filesystem::class); + + $this->driver = new XmlDriver( + '/path/to', + $this->xmlEncoder->reveal(), + $this->xmlDecoder->reveal(), + $this->filesystem->reveal() + ); + + $this->collection = $this->prophesize(SuiteCollection::class); + $this->suite = $this->prophesize(Suite::class); + $this->document = $this->prophesize(Document::class); + } + + /** + * It should store a suite collection. + */ + public function testStore(): void + { + $this->collection->getSuites()->willReturn([ + $this->suite->reveal(), + ]); + $this->suite->getUuid()->willReturn($uuid = '1339f38b191b77e1185f9729eb25a2aa4e262b01'); + $this->filesystem->exists('/path/to/7e0/3/c')->willReturn(true); + $this->xmlEncoder->encode(Argument::type(SuiteCollection::class))->shouldBeCalled() + ->willReturn($this->document->reveal()); + $this->document->save('/path/to/7e0/3/c/' . $uuid . '.xml')->shouldBeCalled(); + + $this->driver->store($this->collection->reveal()); + } + + /** + * It should create a non existing directory when storing the collection. + */ + public function testStoreMkdir(): void + { + $this->collection->getSuites()->willReturn([ + $this->suite->reveal(), + ]); + $this->suite->getUuid()->willReturn($uuid = '1339f38b191b77e1185f9729eb25a2aa4e262b01'); + $this->filesystem->exists('/path/to/7e0/3/c')->willReturn(false); + $this->filesystem->mkdir('/path/to/7e0/3/c')->shouldBeCalled(); + $this->xmlEncoder->encode(Argument::type(SuiteCollection::class))->shouldBeCalled() + ->willReturn($this->document->reveal()); + $this->document->save('/path/to/7e0/3/c/' . $uuid . '.xml')->shouldBeCalled(); + + $this->driver->store($this->collection->reveal()); + } + + /** + * It should throw an exception if it cannot locate a given run by UUID. + * + */ + public function testFetch(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Cannot find run with reference'); + $uuid = '1339f38b191b77e1185f9729eb25a2aa4e262b01'; + $this->filesystem->exists('/path/to/7e0/3/c/' . $uuid . '.xml')->willReturn(false); + + $this->driver->fetch($uuid); + } + + /** + * It should return true if ->has is called and the collection exists. + */ + public function testHas(): void + { + $uuid = '1339f38b191b77e1185f9729eb25a2aa4e262b01'; + $this->filesystem->exists('/path/to/7e0/3/c/' . $uuid . '.xml')->willReturn(true); + + $this->assertTrue($this->driver->has($uuid)); + } + + /** + * It should return false for `has` given an invalid UUID. + */ + public function testHasInvalidUuid(): void + { + $uuid = '123'; + $this->assertFalse($this->driver->has($uuid)); + } + + /** + * It should return the history iterator. + */ + public function testGetHistoryIterator(): void + { + $history = $this->driver->history(); + $this->assertInstanceOf(HistoryIterator::class, $history); + } +} diff --git a/tests/Unit/Storage/UuidResolver/ChainResolverTest.php b/tests/Unit/Storage/UuidResolver/ChainResolverTest.php new file mode 100644 index 000000000..a35918067 --- /dev/null +++ b/tests/Unit/Storage/UuidResolver/ChainResolverTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Storage\UuidResolver; + +use PhpBench\Storage\UuidResolver\ChainResolver; +use PhpBench\Storage\UuidResolverInterface; +use PhpBench\Tests\TestCase; +use Prophecy\Prophecy\ObjectProphecy; + +class ChainResolverTest extends TestCase +{ + const TEST_REFERENCE = '1234'; + const TEST_UUID = 'uuid'; + + /** + * @var UuidResolverInterface|ObjectProphecy + */ + private $resolver; + + protected function setUp(): void + { + $this->resolver = $this->prophesize(UuidResolverInterface::class); + } + + public function testNullIfNoResolverIntervened(): void + { + $chainResolver = new ChainResolver([]); + $this->assertEquals(null, $chainResolver->resolve(self::TEST_REFERENCE)); + } + + public function testChainResolve(): void + { + $chainResolver = new ChainResolver([$this->resolver->reveal()]); + $this->resolver->resolve(self::TEST_REFERENCE)->willReturn(self::TEST_UUID); + + $uuid = $chainResolver->resolve(self::TEST_REFERENCE); + + $this->assertEquals(self::TEST_UUID, $uuid); + } + + public function testNullWhenChainResolveNoSupport(): void + { + $chainResolver = new ChainResolver([$this->resolver->reveal()]); + $this->resolver->resolve(self::TEST_REFERENCE)->willReturn(null); + + $uuid = $chainResolver->resolve(self::TEST_REFERENCE); + + $this->assertEquals(null, $uuid); + } +} diff --git a/tests/Unit/Storage/UuidResolver/LatestResolverTest.php b/tests/Unit/Storage/UuidResolver/LatestResolverTest.php new file mode 100644 index 000000000..301774013 --- /dev/null +++ b/tests/Unit/Storage/UuidResolver/LatestResolverTest.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Storage\UuidResolver; + +use PhpBench\Registry\Registry; +use PhpBench\Storage\DriverInterface; +use PhpBench\Storage\HistoryEntry; +use PhpBench\Storage\HistoryIteratorInterface; +use PhpBench\Storage\UuidResolver\LatestResolver; +use PhpBench\Tests\TestCase; +use RuntimeException; + +class LatestResolverTest extends TestCase +{ + private $resolver; + private $storage; + private $history; + private $historyEntry; + private $historyEntry1; + + protected function setUp(): void + { + $registry = $this->prophesize(Registry::class); + $this->storage = $this->prophesize(DriverInterface::class); + $registry->getService()->willReturn($this->storage->reveal()); + $this->history = $this->prophesize(HistoryIteratorInterface::class); + $this->historyEntry = $this->prophesize(HistoryEntry::class); + $this->historyEntry1 = $this->prophesize(HistoryEntry::class); + + $this->resolver = new LatestResolver( + $registry->reveal() + ); + } + + /** + * It should resove the "latest" token. + */ + public function testResolveLatest(): void + { + $this->storage->history()->willReturn($this->history->reveal()); + $this->history->current()->willReturn($this->historyEntry->reveal()); + $this->historyEntry->getRunId()->willReturn(1234); + + $ref = $this->resolver->resolve('latest'); + $this->assertEquals(1234, $ref); + } + + public function testNullWhenNotBeginningWithLatest(): void + { + $this->storage->history()->willReturn($this->history->reveal()); + $this->history->current()->willReturn($this->historyEntry->reveal()); + $this->historyEntry->getRunId()->willReturn(1234); + + $ref = $this->resolver->resolve('foobar'); + $this->assertEquals(null, $ref); + } + + public function testResolveCantResolve(): void + { + $this->expectException(RuntimeException::class); + $this->resolver->resolve('latest-nutbar'); + } + + /** + * It should return the nth history run using the minus operator. + */ + public function testLatestMinusNth(): void + { + $this->storage->history()->willReturn($this->history->reveal()); + $this->history->current()->willReturn( + $this->historyEntry->reveal(), + $this->historyEntry->reveal(), + $this->historyEntry1->reveal() + ); + $this->historyEntry->getRunId()->willReturn(1234); + $this->historyEntry1->getRunId()->willReturn(4321); + + $this->history->next()->shouldBeCalledTimes(3); + + $ref = $this->resolver->resolve('latest-2'); + $this->assertEquals(4321, $ref); + } + + /** + * It should throw an exception if no history is present. + * + */ + public function testNoHistory(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('No history present'); + $this->storage->history()->willReturn($this->history->reveal()); + $this->history->current()->willReturn(false); + + $this->resolver->resolve('latest'); + } +} diff --git a/tests/Unit/Storage/UuidResolver/TagResolverTest.php b/tests/Unit/Storage/UuidResolver/TagResolverTest.php new file mode 100644 index 000000000..bdc0f51d8 --- /dev/null +++ b/tests/Unit/Storage/UuidResolver/TagResolverTest.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Storage\UuidResolver; + +use PhpBench\Model\Tag; +use PhpBench\Storage\Driver\Fake\FakeHistoryIterator; +use PhpBench\Storage\DriverInterface; +use PhpBench\Storage\HistoryEntry; +use PhpBench\Storage\StorageRegistry; +use PhpBench\Storage\UuidResolver\TagResolver; +use PhpBench\Tests\TestCase; + +class TagResolverTest extends TestCase +{ + /** + * @var ObjectProphecy + */ + private $storage; + + /** + * @var ObjectProphecy + */ + private $history; + + /** + * @var ObjectProphecy + */ + private $historyEntry; + + /** + * @var ObjectProphecy + */ + private $historyEntry1; + + /** + * @var TagResolver + */ + private $resolver; + + protected function setUp(): void + { + $this->storage = $this->prophesize(DriverInterface::class); + $this->historyEntry = $this->prophesize(HistoryEntry::class); + $this->historyEntry1 = $this->prophesize(HistoryEntry::class); + $this->history = new FakeHistoryIterator( + $this->historyEntry->reveal(), + $this->historyEntry1->reveal() + ); + $registry = $this->prophesize(StorageRegistry::class); + $registry->getService()->willReturn($this->storage->reveal()); + $this->storage->history()->willReturn($this->history); + + $this->resolver = new TagResolver($registry->reveal()); + } + + public function testThrowsExceptionWhenNoTagFound(): void + { + self::assertNull($this->resolver->resolve('1asd3foobar123')); + } + + /** + * @dataProvider provideTags + */ + public function testReturnsUuidForLatestTag(string $tag): void + { + $this->historyEntry->getTag()->willReturn(new Tag($tag)); + $this->historyEntry->getRunId()->willReturn(1234); + + $uuid = $this->resolver->resolve($tag); + $this->assertEquals(1234, $uuid); + } + + public function provideTags(): array + { + return [ + [ 'foobar',], + [ '1234',], + [ 'php74',], + ]; + } + + public function testReturnsUuidForTagWithMatchingTagAtOffset(): void + { + $this->historyEntry->getTag()->willReturn(new Tag('foobar')); + $this->historyEntry1->getTag()->willReturn(new Tag('foobar')); + $this->historyEntry1->getRunId()->willReturn(1234); + + $uuid = $this->resolver->resolve('foobar-1'); + $this->assertEquals(1234, $uuid); + } +} diff --git a/tests/Unit/Util/MemoryUnitTest.php b/tests/Unit/Util/MemoryUnitTest.php new file mode 100644 index 000000000..abe78ec40 --- /dev/null +++ b/tests/Unit/Util/MemoryUnitTest.php @@ -0,0 +1,120 @@ + + */ + public function provideConvertToBytes(): Generator + { + yield [ + 1, + MemoryUnit::BYTES, + 1 + ]; + + yield [ + 1, + MemoryUnit::KILOBYTES, + 1000 + ]; + + yield [ + 1, + MemoryUnit::MEGABYTES, + 1000000 + ]; + + yield [ + 1, + MemoryUnit::GIGABYTES, + 1000000000 + ]; + + yield [ + 2, + MemoryUnit::GIGABYTES, + 2000000000 + ]; + } + + public function testInvalidUnit(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Unknown memory unit'); + MemoryUnit::convertTo(5, 'bobobytes', MemoryUnit::BYTES); + } + + + /** + * @dataProvider provideSuffix + */ + public function testSuffix(string $unit, string $expected): void + { + self::assertEquals($expected, MemoryUnit::suffixFor($unit)); + } + + /** + * @return Generator + */ + public function provideSuffix(): Generator + { + yield [ + 'megabytes', + 'mb', + ]; + + yield [ + 'mb', + 'mb', + ]; + + yield [ + 'bytes', + 'b', + ]; + } + + /** + * @dataProvider provideSuitableUnit + */ + public function testResolveSuitableUnit(float $value, string $expectedUnit): void + { + self::assertEquals($expectedUnit, MemoryUnit::resolveSuitableUnit(MemoryUnit::AUTO, $value)); + } + + /** + * @return Generator + */ + public function provideSuitableUnit(): Generator + { + yield [1, MemoryUnit::BYTES]; + + yield [100, MemoryUnit::BYTES]; + + yield [1000, MemoryUnit::KILOBYTES]; + + yield [10000, MemoryUnit::KILOBYTES]; + + yield [100000, MemoryUnit::KILOBYTES]; + + yield [1000000, MemoryUnit::MEGABYTES]; + + yield [1000000000, MemoryUnit::GIGABYTES]; + } +} diff --git a/tests/Unit/Util/TextTruncateTest.php b/tests/Unit/Util/TextTruncateTest.php new file mode 100644 index 000000000..a7837aaf1 --- /dev/null +++ b/tests/Unit/Util/TextTruncateTest.php @@ -0,0 +1,37 @@ + + */ + public function provideCenter(): Generator + { + yield [ + '12345678', + 5, + 2, + '... 4567 ...' + ]; + + yield ['1234567', 5, 2, '... 4567']; + + yield ['', 5, 2, '']; + + yield ['1234', 2, 2, '1234']; + } +} diff --git a/tests/Unit/Util/TimeUnitTest.php b/tests/Unit/Util/TimeUnitTest.php new file mode 100644 index 000000000..252bb7025 --- /dev/null +++ b/tests/Unit/Util/TimeUnitTest.php @@ -0,0 +1,335 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Unit\Util; + +use Generator; +use InvalidArgumentException; +use PhpBench\Tests\TestCase; +use PhpBench\Util\TimeUnit; + +class TimeUnitTest extends TestCase +{ + /** + * It should convertTo one time unit to another. + * + * @dataProvider provideAliases + * @dataProvider provideConvert + */ + public function testConvert($time, $unit, $destUnit, $expectedTime): void + { + $unit = new TimeUnit($unit, $destUnit); + $result = $unit->toDestUnit($time); + $this->assertEquals($expectedTime, $result); + } + + public function provideConvert() + { + return [ + [ + 0, + TimeUnit::SECONDS, + TimeUnit::MINUTES, + 0, + ], + [ + 60, + TimeUnit::SECONDS, + TimeUnit::MINUTES, + 1, + ], + [ + 1, + TimeUnit::SECONDS, + TimeUnit::MICROSECONDS, + 1000000, + ], + [ + 1, + TimeUnit::SECONDS, + TimeUnit::MILLISECONDS, + 1000, + ], + [ + 24, + TimeUnit::HOURS, + TimeUnit::DAYS, + 1, + ], + [ + 2.592e+8, + TimeUnit::MILLISECONDS, + TimeUnit::DAYS, + 3, + ], + [ + 24, + TimeUnit::HOURS, + TimeUnit::DAYS, + 1, + ], + [ + 1.1234, + TimeUnit::MICROSECONDS, + TimeUnit::MICROSECONDS, + 1.1234, + ], + ]; + } + + /** + * @return Generator + */ + public function provideAliases(): Generator + { + yield [ + 1, + TimeUnit::MICROSECOND, + TimeUnit::MICROSECOND, + 1 + ]; + + yield [ + 1, + TimeUnit::MILLISECOND, + TimeUnit::MICROSECOND, + 1000 + ]; + + yield [ + 1, + TimeUnit::SECOND, + TimeUnit::MICROSECOND, + 1000000 + ]; + + yield [ + 1, + TimeUnit::MINUTE, + TimeUnit::MICROSECOND, + 60000000 + ]; + + yield [ + 1, + TimeUnit::DAY, + TimeUnit::HOUR, + 24 + ]; + } + + /** + * It should convert one time unit to another in throughput mode. + * + * @dataProvider provideConvertThroughput + */ + public function testConvertThroughput($time, $unit, $destUnit, $expectedThroughput): void + { + $unit = new TimeUnit($unit, $destUnit); + $result = $unit->toDestUnit($time, null, TimeUnit::MODE_THROUGHPUT); + $this->assertEquals($expectedThroughput, $result); + } + + public function provideConvertThroughput() + { + return [ + [ + 0, + TimeUnit::SECONDS, + TimeUnit::MINUTES, + 0, + ], + [ + 1, + TimeUnit::SECONDS, + TimeUnit::MINUTES, + 60, + ], + [ + 60, + TimeUnit::SECONDS, + TimeUnit::MINUTES, + 1, + ], + [ + 1, + TimeUnit::SECONDS, + TimeUnit::MILLISECONDS, + 0.001, + ], + [ + 2, + TimeUnit::MILLISECONDS, + TimeUnit::SECONDS, + 500, + ], + ]; + } + + /** + * It should use the given values for getDestUnit and getMode. + */ + public function testGivenValuesModeAndDestUnit(): void + { + $unit = new TimeUnit(TimeUnit::SECONDS, TimeUnit::MINUTES, TimeUnit::MODE_TIME); + $this->assertEquals(TimeUnit::SECONDS, $unit->getDestUnit(TimeUnit::SECONDS)); + $this->assertEquals(TimeUnit::MODE_THROUGHPUT, $unit->getMode(TimeUnit::MODE_THROUGHPUT)); + } + + /** + * It should use the default values for mode and dest unit if null values are given. + */ + public function testDefaultValuesModeAndDestUnit(): void + { + $unit = new TimeUnit(TimeUnit::SECONDS, TimeUnit::MINUTES, TimeUnit::MODE_THROUGHPUT); + $this->assertEquals(TimeUnit::MINUTES, $unit->getDestUnit()); + $this->assertEquals(TimeUnit::MODE_THROUGHPUT, $unit->getMode()); + } + + /** + * It should resolve given values to the overridden values in the case that + * the values are overridden (dest unit and mode). + */ + public function testResolveDestUnitAndModeAndPrecision(): void + { + $unit = new TimeUnit(TimeUnit::SECONDS, TimeUnit::MINUTES, TimeUnit::MODE_THROUGHPUT, 10); + $this->assertEquals(TimeUnit::MILLISECONDS, $unit->resolveDestUnit(TimeUnit::MILLISECONDS)); + $this->assertEquals(TimeUnit::MODE_TIME, $unit->getMode(TimeUnit::MODE_TIME)); + $this->assertEquals(5, $unit->resolvePrecision(5)); + + $unit->overrideDestUnit(TimeUnit::DAYS); + $unit->overrideMode(TimeUnit::MODE_TIME); + $unit->overridePrecision(15); + + $this->assertEquals(TimeUnit::DAYS, $unit->resolveDestUnit(TimeUnit::MINUTES)); + $this->assertEquals(TimeUnit::MODE_TIME, $unit->resolveMode(TimeUnit::MODE_THROUGHPUT)); + $this->assertEquals(15, $unit->resolvePrecision(5)); + } + + /** + * It should return the destination suffix for default state. + */ + public function testDestSuffixDefaultState(): void + { + $unit = new TimeUnit(TimeUnit::SECONDS, TimeUnit::MINUTES, TimeUnit::MODE_THROUGHPUT); + $this->assertEquals('ops/m', $unit->getDestSuffix()); + + $unit = new TimeUnit(TimeUnit::SECONDS, TimeUnit::MINUTES, TimeUnit::MODE_TIME); + $this->assertEquals('m', $unit->getDestSuffix()); + } + + /** + * It should return the destination suffix for a given state. + */ + public function testDestSuffixGivenState(): void + { + $unit = new TimeUnit( + TimeUnit::SECONDS, TimeUnit::MINUTES, TimeUnit::MODE_THROUGHPUT + ); + $this->assertEquals('s', $unit->getDestSuffix( + TimeUnit::SECONDS, TimeUnit::MODE_TIME + )); + + $unit = new TimeUnit( + TimeUnit::SECONDS, TimeUnit::MINUTES, TimeUnit::MODE_TIME + ); + $this->assertEquals('ops/ms', $unit->getDestSuffix( + TimeUnit::MILLISECONDS, TimeUnit::MODE_THROUGHPUT + )); + } + + /** + * It should format a time into a human readable string. + */ + public function testFormat(): void + { + $unit = new TimeUnit( + TimeUnit::SECONDS, TimeUnit::MINUTES, TimeUnit::MODE_THROUGHPUT + ); + $result = $unit->format(30); + $this->assertEquals( + '2.000ops/m', + $result + ); + + $result = $unit->format(1800, TimeUnit::HOURS, TimeUnit::MODE_TIME); + $this->assertEquals( + '0.500h', + $result + ); + + $result = $unit->format(1800, TimeUnit::HOURS, TimeUnit::MODE_TIME, 7); + $this->assertEquals( + '0.5000000h', + $result + ); + } + + /** + * It should allow the precision to be overriden. + */ + public function testOverridePrecision(): void + { + $unit = new TimeUnit( + TimeUnit::SECONDS, TimeUnit::MINUTES, TimeUnit::MODE_THROUGHPUT, 7 + ); + + $unit->overridePrecision(5); + $result = $unit->format(1800, TimeUnit::HOURS, TimeUnit::MODE_TIME); + $this->assertEquals( + '0.50000h', + $result + ); + } + + public function testInvalidSourceFormat(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid time unit "arf"'); + TimeUnit::convertTo(1000, 'arf', TimeUnit::MICROSECONDS); + } + + public function testInvalidDestFormat(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid time unit "arf"'); + TimeUnit::convertTo(1000, TimeUnit::MICROSECONDS, 'arf'); + } + + /** + * @dataProvider provideSuitableUnit + */ + public function testResolveSuitableUnit(float $value, string $expectedUnit): void + { + self::assertEquals($expectedUnit, TimeUnit::resolveSuitableUnit($value)); + } + + /** + * @return Generator + */ + public function provideSuitableUnit(): Generator + { + yield [1, 'microseconds']; + + yield [100, 'microseconds']; + + yield [1000, 'milliseconds']; + + yield [10000, 'milliseconds']; + + yield [100000, 'milliseconds']; + + yield [1000000, 'seconds']; + + yield [60000000, 'minutes']; + } +} diff --git a/tests/Util/Approval.php b/tests/Util/Approval.php new file mode 100644 index 000000000..33dc4012f --- /dev/null +++ b/tests/Util/Approval.php @@ -0,0 +1,127 @@ +sections = $sections; + $this->expected = $expected; + $this->path = $path; + $this->delimiter = $delimiter; + } + + public static function create(string $path, int $configCount, string $delimiter = '---'): self + { + if (!file_exists($path)) { + if (!file_exists(dirname($path))) { + mkdir(dirname($path), 0777, true); + } + touch($path); + } + $parts = array_values( + (array)array_filter( + explode( + $delimiter, + (string)file_get_contents($path), + $configCount + ) + ) + ); + + $expected = null; + + if (count($parts) === 1) { + $expected = $parts[0]; + $parts = []; + } + + if ($parts && isset($parts[$configCount - 1])) { + $expected = $parts[$configCount - 1]; + unset($parts[$configCount - 1]); + } + + return new self($path, array_values($parts), $delimiter, $expected); + } + + /** + * @return array + */ + public function getConfig(int $offset): array + { + $rawConfig = $this->getSection($offset); + $config = json_decode($rawConfig, true); + + if (null === $config) { + throw new RuntimeException(sprintf( + 'Invalid JSON config: "%s"', + $rawConfig + )); + } + + return $config; + } + + public function getSection(int $offset): string + { + if (!isset($this->sections[$offset])) { + throw new RuntimeException(sprintf( + 'No section at offset "%s", have sections at offsets "%s"', + $offset, implode('", "', array_keys($this->sections)) + )); + } + + return $this->sections[$offset]; + } + + public function approve(string $actual, bool $force = false): void + { + if (null === $this->expected || $force || getenv('PHPBENCH_APPROVE')) { + file_put_contents($this->path, implode("\n---\n", array_merge( + array_map(function (string $section) { + return trim($section); + }, $this->sections), + [ + $actual + ] + ))); + + throw new SkippedTestError(sprintf('Approval generated for "%s"', $this->path)); + } + + Assert::assertEquals(trim($this->expected), trim($actual)); + } +} diff --git a/tests/Util/IterationBuilder.php b/tests/Util/IterationBuilder.php new file mode 100644 index 000000000..4cdf7060b --- /dev/null +++ b/tests/Util/IterationBuilder.php @@ -0,0 +1,42 @@ +variant = $variant; + } + + public function setResult(ResultInterface $result): self + { + $this->results[] = $result; + + return $this; + } + + public function end(): VariantBuilder + { + return $this->variant; + } + + public function build(Variant $variant): Iteration + { + return $variant->createIteration($this->results); + } +} diff --git a/tests/Util/TestUtil.php b/tests/Util/TestUtil.php new file mode 100644 index 000000000..06eebf98f --- /dev/null +++ b/tests/Util/TestUtil.php @@ -0,0 +1,195 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Tests\Util; + +use PhpBench\Environment\Information; +use PhpBench\Model\ParameterSet; +use PhpBench\Model\Result\MemoryResult; +use PhpBench\Model\Result\RejectionCountResult; +use PhpBench\Model\Result\TimeResult; +use PhpBench\Model\Suite; +use PhpBench\Model\SuiteCollection; +use PhpBench\Model\Variant; +use Prophecy\Prophecy\ObjectProphecy; +use RuntimeException; + +/** + * Utility class for configuring benchmarking prophecy objects. + */ +class TestUtil +{ + public static function configureSubjectMetadata(ObjectProphecy $subject, array $options = []): void + { + $options = array_merge([ + 'iterations' => 1, + 'name' => 'benchFoo', + 'beforeMethods' => [], + 'afterMethods' => [], + 'parameterSets' => [[[]]], + 'groups' => [], + 'revs' => 1, + 'warmup' => 0, + 'notApplicable' => false, + 'skip' => false, + 'sleep' => 0, + 'paramProviders' => [], + 'outputTimeUnit' => 'microseconds', + 'outputMode' => 'time', + ], $options); + + $subject->getIterations()->willReturn($options['iterations']); + $subject->getSleep()->willReturn($options['sleep']); + $subject->getName()->willReturn($options['name']); + $subject->getBeforeMethods()->willReturn($options['beforeMethods']); + $subject->getAfterMethods()->willReturn($options['afterMethods']); + $subject->getParameterSets()->willReturn($options['parameterSets']); + $subject->getGroups()->willReturn($options['groups']); + $subject->getRevs()->willReturn($options['revs']); + $subject->getSkip()->willReturn($options['skip']); + $subject->getWarmup()->willReturn($options['warmup']); + $subject->getParamProviders()->willReturn($options['paramProviders']); + $subject->getOutputTimeUnit()->willReturn($options['outputTimeUnit']); + $subject->getOutputMode()->willReturn($options['outputMode']); + } + + public static function getVariant(): Variant + { + $variants = self::createSuite()->getVariants(); + $variant = reset($variants); + + if (!$variant) { + throw new RuntimeException(sprintf( + 'Could not find a variant in test suite' + )); + } + + return $variant; + } + + public static function configureBenchmarkMetadata(ObjectProphecy $benchmark, array $options = []): void + { + $options = array_merge([ + 'class' => 'Benchmark', + 'beforeClassMethods' => [], + 'afterClassMethods' => [], + 'path' => 'example', + ], $options); + + $benchmark->getClass()->willReturn($options['class']); + $benchmark->getBeforeClassMethods()->willReturn($options['beforeClassMethods']); + $benchmark->getAfterClassMethods()->willReturn($options['afterClassMethods']); + $benchmark->getPath()->willReturn($options['path']); + $benchmark->getExecutor()->willReturn(null); + } + + /** + * @param array $options + */ + public static function createSuite(array $options = [], $suiteIndex = null): Suite + { + $options = array_merge([ + 'uuid' => $suiteIndex, + 'date' => '2016-02-06', + 'revs' => 5, + 'warmup' => 10, + 'sleep' => 1, + 'baseline' => null, + 'name' => 'test', + 'benchmarks' => ['TestBench'], + 'groups' => ['one', 'two', 'three'], + 'parameters' => [ + 'param1' => 'value1', + ], + 'subjects' => ['benchOne'], + 'env' => [], + 'output_time_unit' => 'microseconds', + 'output_time_precision' => 7, + 'output_mode' => 'time', + 'iterations' => [0, 10], + 'iterations_increase_per_subject' => 0, + ], $options); + + $dateTime = new \DateTime($options['date']); + $suite = new Suite( + $options['name'], + $dateTime, + null, + [], + [], + $options['uuid'] + ); + + + foreach ($options['benchmarks'] as $benchmarkClass) { + $benchmark = $suite->createBenchmark($benchmarkClass); + $timeOffset = 0; + + foreach ($options['subjects'] as $subjectName) { + $subject = $benchmark->createSubject($subjectName); + $subject->setSleep($options['sleep']); + $subject->setGroups($options['groups']); + $subject->setOutputTimeUnit($options['output_time_unit']); + $subject->setOutputTimePrecision($options['output_time_precision']); + $subject->setOutputMode($options['output_mode']); + $variant = $subject->createVariant(new ParameterSet('0', $options['parameters']), $options['revs'], $options['warmup']); + + foreach ($options['iterations'] as $time) { + $variant->createIteration(self::createResults($timeOffset + $time, 200, 0)); + } + + $timeOffset += $options['iterations_increase_per_subject']; + + $variant->computeStats(); + } + } + + $informations = []; + + foreach ($options['env'] as $name => $information) { + $informations[] = new Information($name, $information); + } + $suite->setEnvInformations($informations); + + if ($options['baseline']) { + $baselineSuite = self::createSuite(array_merge([ + 'name' => 'baseline', + ], $options['baseline'])); + $suite->mergeBaselines(new SuiteCollection([$baselineSuite])); + } + + return $suite; + } + + /** + * @var array + */ + public static function createCollection(array $suiteConfigs = []): SuiteCollection + { + $suites = []; + + foreach ($suiteConfigs as $suiteIndex => $suiteConfig) { + $suites[] = self::createSuite($suiteConfig, $suiteIndex); + } + + return new SuiteCollection($suites); + } + + public static function createResults($time, $memory = 0) + { + return [ + new TimeResult($time), + new MemoryResult($memory, $memory, $memory), + new RejectionCountResult(0), + ]; + } +} diff --git a/tests/Util/VariantBuilder.php b/tests/Util/VariantBuilder.php new file mode 100644 index 000000000..0fdefd743 --- /dev/null +++ b/tests/Util/VariantBuilder.php @@ -0,0 +1,66 @@ +revs = $revs; + + return $this; + } + + public function iteration(): IterationBuilder + { + return (function (IterationBuilder $builder) { + $this->iterations[] = $builder; + + return $builder; + })(new IterationBuilder($this)); + } + + public function build(): Variant + { + $suite = new Suite( + 'testSuite', + new DateTime() + ); + $benchmark = new Benchmark($suite, 'testBenchmark'); + $subject = new Subject($benchmark, 'foo'); + $variant = new Variant($subject, new ParameterSet('foo', []), $this->revs, 1, []); + + foreach ($this->iterations as $iteration) { + $iteration->build($variant); + } + + return $variant; + } +} diff --git a/tests/Util/Workspace.php b/tests/Util/Workspace.php new file mode 100644 index 000000000..3cc26ebc6 --- /dev/null +++ b/tests/Util/Workspace.php @@ -0,0 +1,127 @@ +path = $path; + } + + public static function create(string $path): self + { + if (empty($path)) { + throw new RuntimeException( + 'Workspace path cannot be empty' + ); + } + + return new self($path); + } + + public function exists(string $path): bool + { + return file_exists($this->path($path)); + } + + public function path(?string $path = null): string + { + if (null === $path) { + return $this->path; + } + + return Path::join($this->path, $path); + } + + public function getContents(string $path): string + { + if (false === $this->exists($path)) { + throw new InvalidArgumentException(sprintf( + 'File "%s" does not exist', + $path + )); + } + + $contents = file_get_contents($this->path($path)); + + if (false === $contents) { + throw new RuntimeException('file_get_contents returned false'); + } + + return $contents; + } + + public function reset(): void + { + if (file_exists($this->path)) { + $this->remove($this->path); + } + + mkdir($this->path); + } + + public function put(string $path, string $contents): Workspace + { + if (!$this->exists(dirname($path))) { + $this->mkdir(dirname($path)); + } + + file_put_contents($this->path($path), $contents); + + return $this; + } + + public function mkdir(string $path): Workspace + { + $path = $this->path($path); + + if (file_exists($path)) { + throw new InvalidArgumentException(sprintf( + 'Node "%s" already exists, cannot create directory', + $path + )); + } + + mkdir($path, 0777, true); + + return $this; + } + + + private function remove(string $path = ''): void + { + if ($path) { + $splFileInfo = new SplFileInfo($path); + + if (in_array($splFileInfo->getType(), ['socket', 'file', 'link'])) { + unlink($path); + + return; + } + } + + $files = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS), + RecursiveIteratorIterator::CHILD_FIRST + ); + + foreach ($files as $file) { + $this->remove($file->getPathName()); + } + + rmdir($path); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 000000000..9ccaa3d13 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,7 @@ + Date: Sun, 22 Mar 2026 09:35:33 +0000 Subject: [PATCH 02/16] add PHPBench Performance (customSmallerIsBetter) benchmark result for 9c7d0df43ea9b2470dc5ab611d873f1f08143ad8 --- benchmarks/data.js | 193 +++++++++++++++++++++++++++++ benchmarks/index.html | 281 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 474 insertions(+) create mode 100644 benchmarks/data.js create mode 100644 benchmarks/index.html diff --git a/benchmarks/data.js b/benchmarks/data.js new file mode 100644 index 000000000..40be6a5fa --- /dev/null +++ b/benchmarks/data.js @@ -0,0 +1,193 @@ +window.BENCHMARK_DATA = { + "lastUpdate": 1774172133411, + "repoUrl": "https://github.com/phpbench/phpbench", + "entries": { + "PHPBench Performance": [ + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "9c7d0df43ea9b2470dc5ab611d873f1f08143ad8", + "message": "Add github_action_benchmark example", + "timestamp": "2026-03-22T09:34:36Z", + "tree_id": "0d5772728eca9e9c9afbec02df997e614c85600c", + "url": "https://github.com/phpbench/phpbench/commit/9c7d0df43ea9b2470dc5ab611d873f1f08143ad8" + }, + "date": 1774172132136, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ExpressionGeneratorBench::benchGenerate", + "value": 7158.283953033134, + "range": "± 0.54%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,0)", + "value": 349.44814090019827, + "range": "± 2.60%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,0)", + "value": 351.61056751467675, + "range": "± 1.63%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,0)", + "value": 344.8258317025461, + "range": "± 2.00%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,25)", + "value": 2306.095890410927, + "range": "± 1.04%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,25)", + "value": 3404.856164383513, + "range": "± 0.54%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,25)", + "value": 5987.96771037176, + "range": "± 0.52%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,50)", + "value": 3769.622309197615, + "range": "± 1.73%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,50)", + "value": 5487.527397260266, + "range": "± 0.65%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,50)", + "value": 9110.702544031297, + "range": "± 0.66%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,75)", + "value": 5208.548923679033, + "range": "± 1.18%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,75)", + "value": 7419.43150684933, + "range": "± 0.74%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,75)", + "value": 12284.570450097734, + "range": "± 0.53%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "HtmlRendererBench::benchRender", + "value": 2847.572015655607, + "range": "± 0.99%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRowArrays", + "value": 231.84598825831446, + "range": "± 1.89%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRecords", + "value": 1076.987866927585, + "range": "± 0.41%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "RunCommandBench::benchDefault", + "value": 125604.79452054865, + "range": "± 0.93%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchInBand", + "value": 127389.30724070617, + "range": "± 1.33%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchNoEnv", + "value": 23868.007827788413, + "range": "± 1.15%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp. w/tol)", + "value": 73.73953033267993, + "range": "± 1.59%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp.)", + "value": 66.2232876712336, + "range": "± 1.31%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "AssertionProcessorBench::benchAssert", + "value": 966.1318982387519, + "range": "± 1.64%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "VariantSummaryFormatterBench::benchFormat", + "value": 1455.03992172212, + "range": "± 0.59%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/benchmarks/index.html b/benchmarks/index.html new file mode 100644 index 000000000..6c887805e --- /dev/null +++ b/benchmarks/index.html @@ -0,0 +1,281 @@ + + + + + + + Benchmarks + + + +

+
+ + + + + + + From eff469045be8c3259953a2ed659ececf52423fb4 Mon Sep 17 00:00:00 2001 From: github-action-benchmark Date: Sun, 22 Mar 2026 10:15:32 +0000 Subject: [PATCH 03/16] add PHPBench Performance (customSmallerIsBetter) benchmark result for 4ad451b76965635a7b76037b05d26243d5a98a8d --- benchmarks/data.js | 187 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/benchmarks/data.js b/benchmarks/data.js index 40be6a5fa..e031743c7 100644 --- a/benchmarks/data.js +++ b/benchmarks/data.js @@ -1,5 +1,5 @@ window.BENCHMARK_DATA = { - "lastUpdate": 1774172133411, + "lastUpdate": 1774174532264, "repoUrl": "https://github.com/phpbench/phpbench", "entries": { "PHPBench Performance": [ @@ -187,6 +187,191 @@ window.BENCHMARK_DATA = { "extra": "10 iterations, 10 revs" } ] + }, + { + "commit": { + "author": { + "email": "dan.t.leech@gmail.com", + "name": "dantleech", + "username": "dantleech" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "4ad451b76965635a7b76037b05d26243d5a98a8d", + "message": "Introduce CI integration guide (#1144)\n\n- Introduce CI integration guide\n- Downgrade sphinx tabs to previous release for github actions\n- Disable yaml/yml doctor rule\n- Do not generate unused references that cause warnings\n- Disable doctor indentation", + "timestamp": "2026-03-22T10:14:45Z", + "tree_id": "282613905a3e1970594bf627c5caa4cb3539fd44", + "url": "https://github.com/phpbench/phpbench/commit/4ad451b76965635a7b76037b05d26243d5a98a8d" + }, + "date": 1774174530790, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ExpressionGeneratorBench::benchGenerate", + "value": 7192.658904109632, + "range": "± 1.93%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,0)", + "value": 357.53816046966404, + "range": "± 2.47%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,0)", + "value": 362.0684931506786, + "range": "± 1.92%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,0)", + "value": 347.257338551859, + "range": "± 2.09%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,25)", + "value": 2310.6722113502997, + "range": "± 0.72%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,25)", + "value": 3462.3003913894277, + "range": "± 1.58%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,25)", + "value": 6103.252446183913, + "range": "± 1.58%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,50)", + "value": 3808.4442270057916, + "range": "± 0.96%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,50)", + "value": 5573.945205479395, + "range": "± 1.50%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,50)", + "value": 9321.35714285713, + "range": "± 1.64%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,75)", + "value": 5206.349315068498, + "range": "± 2.72%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,75)", + "value": 7627.772994129211, + "range": "± 2.58%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,75)", + "value": 12688.664383561658, + "range": "± 2.04%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "HtmlRendererBench::benchRender", + "value": 2860.0634050880753, + "range": "± 0.98%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRowArrays", + "value": 232.10254403131486, + "range": "± 1.54%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRecords", + "value": 1084.4947162426952, + "range": "± 0.75%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "RunCommandBench::benchDefault", + "value": 130539.19178082186, + "range": "± 1.71%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchInBand", + "value": 133803.92759295352, + "range": "± 1.43%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchNoEnv", + "value": 24439.017612524505, + "range": "± 1.44%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp. w/tol)", + "value": 75.44931506849416, + "range": "± 2.28%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp.)", + "value": 67.55205479452147, + "range": "± 2.28%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "AssertionProcessorBench::benchAssert", + "value": 965.8821917808274, + "range": "± 1.15%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "VariantSummaryFormatterBench::benchFormat", + "value": 1479.041487279855, + "range": "± 1.22%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + } + ] } ] } From f09a9ee5ba2e50d2658e9cf2616b35fc7ef1cc0d Mon Sep 17 00:00:00 2001 From: github-action-benchmark Date: Sun, 22 Mar 2026 10:28:19 +0000 Subject: [PATCH 04/16] add PHPBench Performance (customSmallerIsBetter) benchmark result for 661c8c6abbc7734986cf7bc6062c237fbb450461 --- benchmarks/data.js | 187 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/benchmarks/data.js b/benchmarks/data.js index e031743c7..583de49e7 100644 --- a/benchmarks/data.js +++ b/benchmarks/data.js @@ -1,5 +1,5 @@ window.BENCHMARK_DATA = { - "lastUpdate": 1774174532264, + "lastUpdate": 1774175299450, "repoUrl": "https://github.com/phpbench/phpbench", "entries": { "PHPBench Performance": [ @@ -372,6 +372,191 @@ window.BENCHMARK_DATA = { "extra": "10 iterations, 10 revs" } ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "661c8c6abbc7734986cf7bc6062c237fbb450461", + "message": "Do not specify base time unit in example", + "timestamp": "2026-03-22T10:27:20Z", + "tree_id": "41a3835e9148847946db182411ea82a5df818734", + "url": "https://github.com/phpbench/phpbench/commit/661c8c6abbc7734986cf7bc6062c237fbb450461" + }, + "date": 1774175298224, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ExpressionGeneratorBench::benchGenerate", + "value": 7086.557338551834, + "range": "± 1.22%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,0)", + "value": 341.990215264193, + "range": "± 1.91%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,0)", + "value": 345.84442270058685, + "range": "± 2.04%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,0)", + "value": 351.6741682974498, + "range": "± 1.46%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,25)", + "value": 2239.3796477495507, + "range": "± 0.73%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,25)", + "value": 3368.5185909980773, + "range": "± 1.01%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,25)", + "value": 5885.640900195718, + "range": "± 1.53%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,50)", + "value": 3706.8336594912166, + "range": "± 0.61%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,50)", + "value": 5397.406066536216, + "range": "± 1.16%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,50)", + "value": 9109.107632093934, + "range": "± 0.60%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,75)", + "value": 5133.2358121330035, + "range": "± 0.76%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,75)", + "value": 7282.2788649706745, + "range": "± 0.50%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,75)", + "value": 12137.170254403078, + "range": "± 0.63%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "HtmlRendererBench::benchRender", + "value": 2827.901565557676, + "range": "± 1.41%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRowArrays", + "value": 229.89393346379677, + "range": "± 2.14%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRecords", + "value": 1080.8726027397702, + "range": "± 0.71%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "RunCommandBench::benchDefault", + "value": 124873.2465753432, + "range": "± 0.97%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchInBand", + "value": 125114.44618395373, + "range": "± 0.51%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchNoEnv", + "value": 23625.04500978518, + "range": "± 0.97%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp. w/tol)", + "value": 74.81624266144706, + "range": "± 1.93%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp.)", + "value": 64.44579256360048, + "range": "± 1.96%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "AssertionProcessorBench::benchAssert", + "value": 964.3516634050874, + "range": "± 0.79%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "VariantSummaryFormatterBench::benchFormat", + "value": 1444.0195694716308, + "range": "± 0.65%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + } + ] } ] } From b57873809b943a64b1c4aed1362ec66c49f6b34f Mon Sep 17 00:00:00 2001 From: github-action-benchmark Date: Sun, 22 Mar 2026 10:37:13 +0000 Subject: [PATCH 05/16] add PHPBench Performance (customSmallerIsBetter) benchmark result for eb7bd89a56463c4b1086f33fb3e79fc8b636e0a2 --- benchmarks/data.js | 187 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/benchmarks/data.js b/benchmarks/data.js index 583de49e7..3f38527c2 100644 --- a/benchmarks/data.js +++ b/benchmarks/data.js @@ -1,5 +1,5 @@ window.BENCHMARK_DATA = { - "lastUpdate": 1774175299450, + "lastUpdate": 1774175833370, "repoUrl": "https://github.com/phpbench/phpbench", "entries": { "PHPBench Performance": [ @@ -557,6 +557,191 @@ window.BENCHMARK_DATA = { "extra": "10 iterations, 10 revs" } ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "eb7bd89a56463c4b1086f33fb3e79fc8b636e0a2", + "message": "Fix typos", + "timestamp": "2026-03-22T10:36:26Z", + "tree_id": "647886b2a4ff278f206d2d63b26fa863faec6c7d", + "url": "https://github.com/phpbench/phpbench/commit/eb7bd89a56463c4b1086f33fb3e79fc8b636e0a2" + }, + "date": 1774175832869, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ExpressionGeneratorBench::benchGenerate", + "value": 7316.974755381556, + "range": "± 1.33%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,0)", + "value": 366.5753424657578, + "range": "± 1.78%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,0)", + "value": 364.61839530333236, + "range": "± 2.47%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,0)", + "value": 373.83855185910966, + "range": "± 1.73%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,25)", + "value": 2450.965753424697, + "range": "± 1.17%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,25)", + "value": 3580.6604696673503, + "range": "± 0.89%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,25)", + "value": 6197.206457925749, + "range": "± 0.57%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,50)", + "value": 3982.0166340509204, + "range": "± 0.86%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,50)", + "value": 5849.289628180066, + "range": "± 0.99%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,50)", + "value": 9632.452054794143, + "range": "± 1.04%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,75)", + "value": 5585.08904109585, + "range": "± 0.81%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,75)", + "value": 7945.510763209411, + "range": "± 2.09%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,75)", + "value": 12888.684931507014, + "range": "± 0.94%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "HtmlRendererBench::benchRender", + "value": 2898.7013698630444, + "range": "± 0.63%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRowArrays", + "value": 231.8643835616479, + "range": "± 1.33%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRecords", + "value": 1079.3101761252663, + "range": "± 0.44%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "RunCommandBench::benchDefault", + "value": 135847.44618395457, + "range": "± 1.07%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchInBand", + "value": 138728.87475538187, + "range": "± 1.03%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchNoEnv", + "value": 24623.037181996013, + "range": "± 0.71%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp. w/tol)", + "value": 76.45753424657572, + "range": "± 2.20%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp.)", + "value": 67.45557729941221, + "range": "± 2.14%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "AssertionProcessorBench::benchAssert", + "value": 968.1851272015697, + "range": "± 1.31%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "VariantSummaryFormatterBench::benchFormat", + "value": 1454.7714285714237, + "range": "± 1.06%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + } + ] } ] } From eae03a77bccff9992642eaecf96a1d78646c1e52 Mon Sep 17 00:00:00 2001 From: github-action-benchmark Date: Sun, 22 Mar 2026 17:51:16 +0000 Subject: [PATCH 06/16] add PHPBench Performance (customSmallerIsBetter) benchmark result for 8888764ea1ef66db2dbb839d80a90a080bd8dec9 --- benchmarks/data.js | 187 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/benchmarks/data.js b/benchmarks/data.js index 3f38527c2..d945b9666 100644 --- a/benchmarks/data.js +++ b/benchmarks/data.js @@ -1,5 +1,5 @@ window.BENCHMARK_DATA = { - "lastUpdate": 1774175833370, + "lastUpdate": 1774201876107, "repoUrl": "https://github.com/phpbench/phpbench", "entries": { "PHPBench Performance": [ @@ -742,6 +742,191 @@ window.BENCHMARK_DATA = { "extra": "10 iterations, 10 revs" } ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "8888764ea1ef66db2dbb839d80a90a080bd8dec9", + "message": "Update doc", + "timestamp": "2026-03-22T17:50:31Z", + "tree_id": "bda621a607fe36aaa38aa91eb8331aec8737fd05", + "url": "https://github.com/phpbench/phpbench/commit/8888764ea1ef66db2dbb839d80a90a080bd8dec9" + }, + "date": 1774201875598, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ExpressionGeneratorBench::benchGenerate", + "value": 7166.860469667357, + "range": "± 0.49%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,0)", + "value": 354.5577299412971, + "range": "± 2.30%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,0)", + "value": 357.68786692760375, + "range": "± 2.63%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,0)", + "value": 353.8806262230935, + "range": "± 1.71%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,25)", + "value": 2278.3091976516844, + "range": "± 1.04%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,25)", + "value": 3426.364970645808, + "range": "± 0.74%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,25)", + "value": 5962.691780821944, + "range": "± 1.14%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,50)", + "value": 3742.907045009788, + "range": "± 1.61%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,50)", + "value": 5462.381604696659, + "range": "± 1.26%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,50)", + "value": 9171.136007827707, + "range": "± 0.86%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,75)", + "value": 5281.357142857076, + "range": "± 1.29%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,75)", + "value": 7593.484344422677, + "range": "± 1.36%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,75)", + "value": 12291.681017612651, + "range": "± 0.92%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "HtmlRendererBench::benchRender", + "value": 2825.335616438337, + "range": "± 1.16%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRowArrays", + "value": 231.3939334637943, + "range": "± 1.59%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRecords", + "value": 1082.1700587084015, + "range": "± 1.54%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "RunCommandBench::benchDefault", + "value": 128940.20939334795, + "range": "± 1.22%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchInBand", + "value": 127703.86692759267, + "range": "± 0.94%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchNoEnv", + "value": 24089.812133072322, + "range": "± 1.20%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp. w/tol)", + "value": 74.62328767123341, + "range": "± 1.61%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp.)", + "value": 66.93502935420658, + "range": "± 1.34%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "AssertionProcessorBench::benchAssert", + "value": 975.1786692759104, + "range": "± 1.26%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "VariantSummaryFormatterBench::benchFormat", + "value": 1452.8473581213111, + "range": "± 0.32%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + } + ] } ] } From 127514f8dcd3f4953dba0362731978d365b23880 Mon Sep 17 00:00:00 2001 From: github-action-benchmark Date: Sat, 4 Apr 2026 14:54:44 +0000 Subject: [PATCH 07/16] add PHPBench Performance (customSmallerIsBetter) benchmark result for e1fcce2f8072842fa9a814490378bea3bba29c42 --- benchmarks/data.js | 187 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/benchmarks/data.js b/benchmarks/data.js index d945b9666..e255e3611 100644 --- a/benchmarks/data.js +++ b/benchmarks/data.js @@ -1,5 +1,5 @@ window.BENCHMARK_DATA = { - "lastUpdate": 1774201876107, + "lastUpdate": 1775314483969, "repoUrl": "https://github.com/phpbench/phpbench", "entries": { "PHPBench Performance": [ @@ -927,6 +927,191 @@ window.BENCHMARK_DATA = { "extra": "10 iterations, 10 revs" } ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "e1fcce2f8072842fa9a814490378bea3bba29c42", + "message": "docs: Add an example on adding a diff column", + "timestamp": "2026-04-04T15:54:05+01:00", + "tree_id": "900c9edce7ab8970dab24d2e318f99b1dff63031", + "url": "https://github.com/phpbench/phpbench/commit/e1fcce2f8072842fa9a814490378bea3bba29c42" + }, + "date": 1775314483281, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "HtmlRendererBench::benchRender", + "value": 1836.7064579256387, + "range": "± 0.93%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,0)", + "value": 283.7681017612474, + "range": "± 1.23%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,0)", + "value": 274.8356164383572, + "range": "± 1.99%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,0)", + "value": 280.9129158512716, + "range": "± 2.12%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,25)", + "value": 2287.3727984343714, + "range": "± 2.16%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,25)", + "value": 3191.993150685009, + "range": "± 0.96%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,25)", + "value": 5163.3698630136605, + "range": "± 0.81%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,50)", + "value": 3668.3287671232865, + "range": "± 1.05%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,50)", + "value": 5316.0205479451115, + "range": "± 1.03%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,50)", + "value": 8242.037181996115, + "range": "± 1.93%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,75)", + "value": 5148.286692759224, + "range": "± 1.79%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,75)", + "value": 7184.0763209392735, + "range": "± 0.84%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,75)", + "value": 11063.824853229027, + "range": "± 0.98%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ExpressionGeneratorBench::benchGenerate", + "value": 6176.8133072406645, + "range": "± 0.55%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRowArrays", + "value": 208.22465753424706, + "range": "± 2.24%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRecords", + "value": 901.1853228962809, + "range": "± 0.92%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp. w/tol)", + "value": 57.84579256360099, + "range": "± 1.71%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp.)", + "value": 51.41448140900244, + "range": "± 1.34%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "VariantSummaryFormatterBench::benchFormat", + "value": 1186.0410958904115, + "range": "± 0.76%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "AssertionProcessorBench::benchAssert", + "value": 802.0776908023386, + "range": "± 0.68%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "RunCommandBench::benchDefault", + "value": 96894.75342465745, + "range": "± 0.63%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchInBand", + "value": 96063.6712328768, + "range": "± 1.42%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchNoEnv", + "value": 19844.318982387646, + "range": "± 0.66%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + } + ] } ] } From ee247b2c55288a94ea6034e0b801cb10d583f990 Mon Sep 17 00:00:00 2001 From: github-action-benchmark Date: Wed, 6 May 2026 19:20:31 +0000 Subject: [PATCH 08/16] add PHPBench Performance (customSmallerIsBetter) benchmark result for 2f2742b3bfd1416ca95d4e0338af0b6897cf4e64 --- benchmarks/data.js | 187 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/benchmarks/data.js b/benchmarks/data.js index e255e3611..affb5aca5 100644 --- a/benchmarks/data.js +++ b/benchmarks/data.js @@ -1,5 +1,5 @@ window.BENCHMARK_DATA = { - "lastUpdate": 1775314483969, + "lastUpdate": 1778095231128, "repoUrl": "https://github.com/phpbench/phpbench", "entries": { "PHPBench Performance": [ @@ -1112,6 +1112,191 @@ window.BENCHMARK_DATA = { "extra": "10 iterations, 1 revs" } ] + }, + { + "commit": { + "author": { + "email": "sasezaki+github@gmail.com", + "name": "sasezaki", + "username": "sasezaki" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "2f2742b3bfd1416ca95d4e0338af0b6897cf4e64", + "message": "Add export-ignore rules to .gitattributes (#1146)\n\n* Add missing export-ignore entries to .gitattributes\n\n* Remove /templates/ from export-ignore (used at runtime)", + "timestamp": "2026-05-06T21:19:39+02:00", + "tree_id": "90e9703a359be4f7f3a3eed18625b9d2f74141a7", + "url": "https://github.com/phpbench/phpbench/commit/2f2742b3bfd1416ca95d4e0338af0b6897cf4e64" + }, + "date": 1778095229677, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "RunCommandBench::benchDefault", + "value": 123466.39726027445, + "range": "± 2.11%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchInBand", + "value": 129975.26223092062, + "range": "± 0.89%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchNoEnv", + "value": 23456.637964774956, + "range": "± 0.54%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp. w/tol)", + "value": 76.20528375733652, + "range": "± 1.49%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp.)", + "value": 67.16986301369793, + "range": "± 2.01%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "VariantSummaryFormatterBench::benchFormat", + "value": 1425.938943248517, + "range": "± 0.94%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRowArrays", + "value": 255.6381604696698, + "range": "± 2.03%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRecords", + "value": 1116.0493150684913, + "range": "± 0.73%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "AssertionProcessorBench::benchAssert", + "value": 948.267123287678, + "range": "± 1.09%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,0)", + "value": 328.1203522504871, + "range": "± 2.06%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,0)", + "value": 338.3718199608729, + "range": "± 1.89%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,0)", + "value": 321.2866927592946, + "range": "± 2.31%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,25)", + "value": 2099.145792563613, + "range": "± 1.56%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,25)", + "value": 3248.0802348336438, + "range": "± 1.74%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,25)", + "value": 5742.154598825837, + "range": "± 1.26%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,50)", + "value": 3635.9706457925577, + "range": "± 1.15%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,50)", + "value": 5377.595890410945, + "range": "± 1.05%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,50)", + "value": 8978.643835616502, + "range": "± 1.15%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,75)", + "value": 5221.660469667289, + "range": "± 1.57%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,75)", + "value": 7835.084148728145, + "range": "± 1.94%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,75)", + "value": 12972.475538160425, + "range": "± 1.54%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ExpressionGeneratorBench::benchGenerate", + "value": 7609.2434442269805, + "range": "± 2.48%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "HtmlRendererBench::benchRender", + "value": 2433.768493150627, + "range": "± 0.62%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + } + ] } ] } From f6e542410437df3f7313aa7b157971c746f5bf17 Mon Sep 17 00:00:00 2001 From: github-action-benchmark Date: Sat, 6 Jun 2026 15:19:50 +0000 Subject: [PATCH 09/16] add PHPBench Performance (customSmallerIsBetter) benchmark result for 07b4fa73a8c706cd1bd98755ddadace3dba8248e --- benchmarks/data.js | 187 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/benchmarks/data.js b/benchmarks/data.js index affb5aca5..2f5acd1e1 100644 --- a/benchmarks/data.js +++ b/benchmarks/data.js @@ -1,5 +1,5 @@ window.BENCHMARK_DATA = { - "lastUpdate": 1778095231128, + "lastUpdate": 1780759190539, "repoUrl": "https://github.com/phpbench/phpbench", "entries": { "PHPBench Performance": [ @@ -1297,6 +1297,191 @@ window.BENCHMARK_DATA = { "extra": "10 iterations, 10 revs" } ] + }, + { + "commit": { + "author": { + "email": "dan.t.leech@gmail.com", + "name": "dantleech", + "username": "dantleech" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "07b4fa73a8c706cd1bd98755ddadace3dba8248e", + "message": "Allow lazy evaluation for functions (#1149)", + "timestamp": "2026-06-06T16:19:12+01:00", + "tree_id": "3f25ac29991acaa8f0a82c7b8cd7591f01646b3e", + "url": "https://github.com/phpbench/phpbench/commit/07b4fa73a8c706cd1bd98755ddadace3dba8248e" + }, + "date": 1780759190068, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "VariantSummaryFormatterBench::benchFormat", + "value": 1416.9023483366045, + "range": "± 1.81%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp. w/tol)", + "value": 72.08219178082234, + "range": "± 1.95%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp.)", + "value": 65.72348336595009, + "range": "± 1.85%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "AssertionProcessorBench::benchAssert", + "value": 941.4217221135034, + "range": "± 0.82%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ExpressionGeneratorBench::benchGenerate", + "value": 7158.73091976512, + "range": "± 0.83%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,0)", + "value": 331.8483365949212, + "range": "± 2.33%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,0)", + "value": 328.06164383561463, + "range": "± 2.43%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,0)", + "value": 335.5909980430631, + "range": "± 2.62%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,25)", + "value": 2057.090998043055, + "range": "± 1.31%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,25)", + "value": 3168.2446183952707, + "range": "± 1.21%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,25)", + "value": 5547.847358121311, + "range": "± 0.74%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,50)", + "value": 3537.7045009784724, + "range": "± 0.77%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,50)", + "value": 5273.9148727984575, + "range": "± 1.58%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,50)", + "value": 8792.689823874749, + "range": "± 1.24%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,75)", + "value": 5077.126223091975, + "range": "± 0.81%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,75)", + "value": 7398.478473581083, + "range": "± 1.15%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,75)", + "value": 12183.647749510621, + "range": "± 0.90%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "HtmlRendererBench::benchRender", + "value": 2421.8430528375557, + "range": "± 0.82%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "RunCommandBench::benchDefault", + "value": 121694.49902152584, + "range": "± 0.67%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchInBand", + "value": 122228.15068493379, + "range": "± 0.67%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchNoEnv", + "value": 22825.320939334848, + "range": "± 0.43%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRowArrays", + "value": 244.27964774951, + "range": "± 1.56%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRecords", + "value": 1131.1430528375774, + "range": "± 0.74%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + } + ] } ] } From f5d5cba231acff313d4b92938c41bec5fa19be13 Mon Sep 17 00:00:00 2001 From: github-action-benchmark Date: Sun, 7 Jun 2026 08:50:49 +0000 Subject: [PATCH 10/16] add PHPBench Performance (customSmallerIsBetter) benchmark result for fbab09e522838babcdca79085ff769f3bc734e77 --- benchmarks/data.js | 187 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/benchmarks/data.js b/benchmarks/data.js index 2f5acd1e1..7fc90f4e6 100644 --- a/benchmarks/data.js +++ b/benchmarks/data.js @@ -1,5 +1,5 @@ window.BENCHMARK_DATA = { - "lastUpdate": 1780759190539, + "lastUpdate": 1780822249379, "repoUrl": "https://github.com/phpbench/phpbench", "entries": { "PHPBench Performance": [ @@ -1482,6 +1482,191 @@ window.BENCHMARK_DATA = { "extra": "10 iterations, 10 revs" } ] + }, + { + "commit": { + "author": { + "email": "dan.t.leech@gmail.com", + "name": "dantleech", + "username": "dantleech" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "fbab09e522838babcdca79085ff769f3bc734e77", + "message": "Various fixes and improvements (#1150)\n\n- expr: Boolean node is a scalar value\n- expr: Support the nullsafe operator on bare variables\n- expr: Allow display as \"rstdev\"\n- tests: approval support for empty files", + "timestamp": "2026-06-07T09:50:04+01:00", + "tree_id": "7a7664abd6ad642bf61a1b20a3b78b3cbf767fc4", + "url": "https://github.com/phpbench/phpbench/commit/fbab09e522838babcdca79085ff769f3bc734e77" + }, + "date": 1780822248804, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "VariantSummaryFormatterBench::benchFormat", + "value": 1400.4219178082242, + "range": "± 1.10%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp. w/tol)", + "value": 75.25577299412814, + "range": "± 1.73%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp.)", + "value": 66.98669275929421, + "range": "± 1.89%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "AssertionProcessorBench::benchAssert", + "value": 946.1559686888431, + "range": "± 1.09%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ExpressionGeneratorBench::benchGenerate", + "value": 7236.970254403114, + "range": "± 0.93%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,0)", + "value": 330.1722113502974, + "range": "± 2.56%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,0)", + "value": 332.7534246575341, + "range": "± 2.13%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,0)", + "value": 336.2084148727947, + "range": "± 1.76%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,25)", + "value": 2120.821917808248, + "range": "± 1.27%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,25)", + "value": 3215.8336594911925, + "range": "± 1.36%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,25)", + "value": 5640.761252446135, + "range": "± 0.97%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,50)", + "value": 3723.438356164459, + "range": "± 0.83%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,50)", + "value": 5545.917808219199, + "range": "± 2.44%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,50)", + "value": 8996.177103718226, + "range": "± 2.55%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,75)", + "value": 5213.321917808213, + "range": "± 1.05%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,75)", + "value": 7815.63405088059, + "range": "± 1.73%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,75)", + "value": 12709.438356164253, + "range": "± 1.19%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "HtmlRendererBench::benchRender", + "value": 2435.761839530371, + "range": "± 1.22%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "RunCommandBench::benchDefault", + "value": 130963.65362035147, + "range": "± 1.14%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchInBand", + "value": 131874.44031311368, + "range": "± 0.38%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchNoEnv", + "value": 23659.045009784062, + "range": "± 1.16%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRowArrays", + "value": 246.85714285714198, + "range": "± 2.03%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRecords", + "value": 1133.304500978464, + "range": "± 1.24%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + } + ] } ] } From 524eab54ac65375220b018a8ed8790bd5c3fc8aa Mon Sep 17 00:00:00 2001 From: github-action-benchmark Date: Sun, 7 Jun 2026 09:33:45 +0000 Subject: [PATCH 11/16] add PHPBench Performance (customSmallerIsBetter) benchmark result for 5c72bb108eac249dd427953a90b5583e2e2c1a7f --- benchmarks/data.js | 187 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/benchmarks/data.js b/benchmarks/data.js index 7fc90f4e6..3a9ea5bf8 100644 --- a/benchmarks/data.js +++ b/benchmarks/data.js @@ -1,5 +1,5 @@ window.BENCHMARK_DATA = { - "lastUpdate": 1780822249379, + "lastUpdate": 1780824825136, "repoUrl": "https://github.com/phpbench/phpbench", "entries": { "PHPBench Performance": [ @@ -1667,6 +1667,191 @@ window.BENCHMARK_DATA = { "extra": "10 iterations, 10 revs" } ] + }, + { + "commit": { + "author": { + "email": "dan.t.leech@gmail.com", + "name": "dantleech", + "username": "dantleech" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "5c72bb108eac249dd427953a90b5583e2e2c1a7f", + "message": "Bump phpstan (#1151)\n\n* Bump PHPStan and related deps and fix some issues\n\n- PHPstan to 2.x and related packages.\n- Fix some of the newly reported issues.\n- Update baseline.\n\n* Support console 6.x", + "timestamp": "2026-06-07T10:33:04+01:00", + "tree_id": "4babbe93ec5a9c37a42b152f2c1db1c6d3e09735", + "url": "https://github.com/phpbench/phpbench/commit/5c72bb108eac249dd427953a90b5583e2e2c1a7f" + }, + "date": 1780824824564, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "VariantSummaryFormatterBench::benchFormat", + "value": 1406.174951076309, + "range": "± 1.34%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp. w/tol)", + "value": 73.41878669276029, + "range": "± 2.36%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp.)", + "value": 66.3851272015662, + "range": "± 1.73%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "AssertionProcessorBench::benchAssert", + "value": 943.0669275929661, + "range": "± 1.17%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ExpressionGeneratorBench::benchGenerate", + "value": 7132.07318982385, + "range": "± 0.67%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,0)", + "value": 322.77103718199277, + "range": "± 1.29%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,0)", + "value": 317.4960861056768, + "range": "± 2.72%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,0)", + "value": 319.386497064581, + "range": "± 2.58%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,25)", + "value": 2215.484344422657, + "range": "± 1.09%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,25)", + "value": 3335.157534246556, + "range": "± 1.31%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,25)", + "value": 5718.952054794527, + "range": "± 1.03%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,50)", + "value": 3593.8718199608784, + "range": "± 1.40%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,50)", + "value": 5327.438356164275, + "range": "± 0.95%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,50)", + "value": 8777.323874755482, + "range": "± 1.15%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,75)", + "value": 5126.949119373784, + "range": "± 1.34%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,75)", + "value": 7456.952054794601, + "range": "± 0.92%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,75)", + "value": 12194.98043052843, + "range": "± 1.50%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "HtmlRendererBench::benchRender", + "value": 2416.442465753444, + "range": "± 1.01%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "RunCommandBench::benchDefault", + "value": 122695.4637964773, + "range": "± 1.53%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchInBand", + "value": 122638.30136986238, + "range": "± 1.18%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchNoEnv", + "value": 22917.027397260368, + "range": "± 0.43%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRowArrays", + "value": 246.64814090019433, + "range": "± 1.04%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRecords", + "value": 1134.6976516634238, + "range": "± 1.52%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + } + ] } ] } From 91b92f7c56054ac358b8f5ce8311fad654c8b090 Mon Sep 17 00:00:00 2001 From: github-action-benchmark Date: Sun, 7 Jun 2026 09:45:28 +0000 Subject: [PATCH 12/16] add PHPBench Performance (customSmallerIsBetter) benchmark result for e8145c81d2ffffb249a2f9f6c6adec2ef1efc3f5 --- benchmarks/data.js | 187 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/benchmarks/data.js b/benchmarks/data.js index 3a9ea5bf8..2a609f573 100644 --- a/benchmarks/data.js +++ b/benchmarks/data.js @@ -1,5 +1,5 @@ window.BENCHMARK_DATA = { - "lastUpdate": 1780824825136, + "lastUpdate": 1780825528883, "repoUrl": "https://github.com/phpbench/phpbench", "entries": { "PHPBench Performance": [ @@ -1852,6 +1852,191 @@ window.BENCHMARK_DATA = { "extra": "10 iterations, 10 revs" } ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "e8145c81d2ffffb249a2f9f6c6adec2ef1efc3f5", + "message": "Delete dead snapshots", + "timestamp": "2026-06-07T10:44:43+01:00", + "tree_id": "fd326cb5ee7584559bea1407edd718400f91fefd", + "url": "https://github.com/phpbench/phpbench/commit/e8145c81d2ffffb249a2f9f6c6adec2ef1efc3f5" + }, + "date": 1780825528048, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "VariantSummaryFormatterBench::benchFormat", + "value": 1435.581017612527, + "range": "± 1.18%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp. w/tol)", + "value": 72.16144814090075, + "range": "± 2.41%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp.)", + "value": 64.4724070450097, + "range": "± 2.38%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "AssertionProcessorBench::benchAssert", + "value": 948.3616438356134, + "range": "± 1.06%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ExpressionGeneratorBench::benchGenerate", + "value": 7122.40567514674, + "range": "± 0.78%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,0)", + "value": 346.5185909980426, + "range": "± 2.07%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,0)", + "value": 347.25048923679014, + "range": "± 1.87%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,0)", + "value": 348.36888454011694, + "range": "± 1.91%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,25)", + "value": 2256.8590998043114, + "range": "± 1.83%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,25)", + "value": 3348.450097847347, + "range": "± 0.89%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,25)", + "value": 5898.365949119416, + "range": "± 0.63%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,50)", + "value": 3589.573385518604, + "range": "± 0.92%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,50)", + "value": 5266.256360078254, + "range": "± 1.07%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,50)", + "value": 9009.331702543925, + "range": "± 1.43%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,75)", + "value": 5102.496086105664, + "range": "± 0.85%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,75)", + "value": 7340.281800391405, + "range": "± 0.77%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,75)", + "value": 12174.765166340621, + "range": "± 0.58%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "HtmlRendererBench::benchRender", + "value": 2720.491585127196, + "range": "± 0.79%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "RunCommandBench::benchDefault", + "value": 116233.97847358105, + "range": "± 0.63%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchInBand", + "value": 116452.16438356153, + "range": "± 0.62%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchNoEnv", + "value": 23353.090019569034, + "range": "± 0.68%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRowArrays", + "value": 235.57045009784497, + "range": "± 1.32%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRecords", + "value": 1167.7178082191683, + "range": "± 0.42%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + } + ] } ] } From 3ca50aa58bbad2335a8cb010f40d123122f31d3f Mon Sep 17 00:00:00 2001 From: github-action-benchmark Date: Sun, 7 Jun 2026 10:40:46 +0000 Subject: [PATCH 13/16] add PHPBench Performance (customSmallerIsBetter) benchmark result for 4da0b8279e54f93d760fa078b5f0103c43c555b8 --- benchmarks/data.js | 187 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/benchmarks/data.js b/benchmarks/data.js index 2a609f573..9f86c3c09 100644 --- a/benchmarks/data.js +++ b/benchmarks/data.js @@ -1,5 +1,5 @@ window.BENCHMARK_DATA = { - "lastUpdate": 1780825528883, + "lastUpdate": 1780828846413, "repoUrl": "https://github.com/phpbench/phpbench", "entries": { "PHPBench Performance": [ @@ -2037,6 +2037,191 @@ window.BENCHMARK_DATA = { "extra": "10 iterations, 10 revs" } ] + }, + { + "commit": { + "author": { + "email": "dan.t.leech@gmail.com", + "name": "dantleech", + "username": "dantleech" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "4da0b8279e54f93d760fa078b5f0103c43c555b8", + "message": "Report Derivations (#1148)\n\n- Derive columns from existing columns.\n- One or multiple passes.\n- New \"aggregate-preview\" report which includes the new \"diff\" column\n making use of this feature.\n- Add documentation\n- Deprecates the `BASELINE_EXPRESSIONS` option\n- nl2br in HTML report for description", + "timestamp": "2026-06-07T11:40:05+01:00", + "tree_id": "77ea4b88bd97ae1d9578a0e039cac39335914c2e", + "url": "https://github.com/phpbench/phpbench/commit/4da0b8279e54f93d760fa078b5f0103c43c555b8" + }, + "date": 1780828845857, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "VariantSummaryFormatterBench::benchFormat", + "value": 1437.781409001963, + "range": "± 1.22%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp. w/tol)", + "value": 76.02446183952868, + "range": "± 2.04%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp.)", + "value": 67.78317025440306, + "range": "± 2.18%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "AssertionProcessorBench::benchAssert", + "value": 948.5890410958818, + "range": "± 0.67%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ExpressionGeneratorBench::benchGenerate", + "value": 7340.223874755323, + "range": "± 1.73%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,0)", + "value": 324.82876712328743, + "range": "± 2.81%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,0)", + "value": 335.0577299413002, + "range": "± 2.27%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,0)", + "value": 335.59491193737983, + "range": "± 2.24%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,25)", + "value": 2341.7690802348034, + "range": "± 2.05%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,25)", + "value": 3508.782778865013, + "range": "± 0.56%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,25)", + "value": 5922.218199608615, + "range": "± 1.14%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,50)", + "value": 3784.0782778864186, + "range": "± 2.04%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,50)", + "value": 5629.767123287557, + "range": "± 1.69%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,50)", + "value": 9115.519569471731, + "range": "± 1.50%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,75)", + "value": 5513.7573385518, + "range": "± 1.42%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,75)", + "value": 7838.204500978491, + "range": "± 0.67%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,75)", + "value": 12506.34931506864, + "range": "± 1.54%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "HtmlRendererBench::benchRender", + "value": 2445.7217221134906, + "range": "± 1.05%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "RunCommandBench::benchDefault", + "value": 134329.7710371817, + "range": "± 1.55%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchInBand", + "value": 134210.29158512584, + "range": "± 1.85%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchNoEnv", + "value": 24368.281800391516, + "range": "± 1.18%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRowArrays", + "value": 248.36751467710465, + "range": "± 1.07%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRecords", + "value": 1140.724461839533, + "range": "± 1.06%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + } + ] } ] } From b5cbfdd18fd6cd5abe4158ae2584fe11178c1a84 Mon Sep 17 00:00:00 2001 From: github-action-benchmark Date: Mon, 8 Jun 2026 19:10:08 +0000 Subject: [PATCH 14/16] add PHPBench Performance (customSmallerIsBetter) benchmark result for 3d13c0d5dcf8730a67b70fa7fb03b4556b5cc0fe --- benchmarks/data.js | 187 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/benchmarks/data.js b/benchmarks/data.js index 9f86c3c09..7907675c9 100644 --- a/benchmarks/data.js +++ b/benchmarks/data.js @@ -1,5 +1,5 @@ window.BENCHMARK_DATA = { - "lastUpdate": 1780828846413, + "lastUpdate": 1780945808729, "repoUrl": "https://github.com/phpbench/phpbench", "entries": { "PHPBench Performance": [ @@ -2222,6 +2222,191 @@ window.BENCHMARK_DATA = { "extra": "10 iterations, 10 revs" } ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "3d13c0d5dcf8730a67b70fa7fb03b4556b5cc0fe", + "message": "Update CHANGELOG", + "timestamp": "2026-06-08T20:09:20+01:00", + "tree_id": "9f1226f877cdefc8b3535ea39653b63784e0ce43", + "url": "https://github.com/phpbench/phpbench/commit/3d13c0d5dcf8730a67b70fa7fb03b4556b5cc0fe" + }, + "date": 1780945808169, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "VariantSummaryFormatterBench::benchFormat", + "value": 1417.408219178083, + "range": "± 1.05%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp. w/tol)", + "value": 76.2197651663393, + "range": "± 2.33%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp.)", + "value": 66.30782778864877, + "range": "± 2.39%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "AssertionProcessorBench::benchAssert", + "value": 947.3890410958861, + "range": "± 0.79%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ExpressionGeneratorBench::benchGenerate", + "value": 7280.481213307257, + "range": "± 1.38%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,0)", + "value": 339.1555772994127, + "range": "± 1.78%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,0)", + "value": 332.60958904109884, + "range": "± 2.69%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,0)", + "value": 329.9021526418786, + "range": "± 1.98%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,25)", + "value": 2287.749510763263, + "range": "± 1.20%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,25)", + "value": 3437.1497064579758, + "range": "± 1.06%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,25)", + "value": 5733.1780821917555, + "range": "± 0.95%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,50)", + "value": 3610.652641878668, + "range": "± 1.34%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,50)", + "value": 5460.598825831672, + "range": "± 1.21%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,50)", + "value": 9103.078277886347, + "range": "± 1.74%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,75)", + "value": 5301.772994129156, + "range": "± 1.73%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,75)", + "value": 7797.063600782756, + "range": "± 1.90%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,75)", + "value": 12539.71428571425, + "range": "± 2.09%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "HtmlRendererBench::benchRender", + "value": 2432.646575342472, + "range": "± 1.02%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "RunCommandBench::benchDefault", + "value": 133604.2328767113, + "range": "± 1.12%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchInBand", + "value": 132292.60469667264, + "range": "± 1.48%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchNoEnv", + "value": 24563.71428571384, + "range": "± 1.38%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRowArrays", + "value": 248.04970645792577, + "range": "± 1.88%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRecords", + "value": 1137.8266144813858, + "range": "± 0.51%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + } + ] } ] } From 61e0d5432b7d4919c839f486735b9f5861cdfc70 Mon Sep 17 00:00:00 2001 From: github-action-benchmark Date: Tue, 9 Jun 2026 19:39:59 +0000 Subject: [PATCH 15/16] add PHPBench Performance (customSmallerIsBetter) benchmark result for 7493f63fd8188e25d917bfdb29f10a797c2c55e2 --- benchmarks/data.js | 187 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/benchmarks/data.js b/benchmarks/data.js index 7907675c9..ad988bf27 100644 --- a/benchmarks/data.js +++ b/benchmarks/data.js @@ -1,5 +1,5 @@ window.BENCHMARK_DATA = { - "lastUpdate": 1780945808729, + "lastUpdate": 1781033999585, "repoUrl": "https://github.com/phpbench/phpbench", "entries": { "PHPBench Performance": [ @@ -2407,6 +2407,191 @@ window.BENCHMARK_DATA = { "extra": "10 iterations, 10 revs" } ] + }, + { + "commit": { + "author": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "committer": { + "email": "daniel@dantleech.com", + "name": "Daniel Leech", + "username": "dantleech" + }, + "distinct": true, + "id": "7493f63fd8188e25d917bfdb29f10a797c2c55e2", + "message": "Update doc to provide more reliable diff example", + "timestamp": "2026-06-09T20:39:02+01:00", + "tree_id": "dfafcbae5674844f5291d46bca7627094f5c4226", + "url": "https://github.com/phpbench/phpbench/commit/7493f63fd8188e25d917bfdb29f10a797c2c55e2" + }, + "date": 1781033998584, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "VariantSummaryFormatterBench::benchFormat", + "value": 1443.2962818003746, + "range": "± 1.71%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp. w/tol)", + "value": 73.76810176125302, + "range": "± 2.42%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp.)", + "value": 66.04520547945077, + "range": "± 2.52%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "AssertionProcessorBench::benchAssert", + "value": 964.1884540117344, + "range": "± 2.11%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ExpressionGeneratorBench::benchGenerate", + "value": 7105.36086105681, + "range": "± 2.76%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,0)", + "value": 336.6780821917804, + "range": "± 2.93%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,0)", + "value": 336.5851272015634, + "range": "± 2.88%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,0)", + "value": 332.2690802348335, + "range": "± 2.01%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,25)", + "value": 2238.428571428585, + "range": "± 2.34%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,25)", + "value": 3367.2602739725453, + "range": "± 2.42%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,25)", + "value": 5614.600782778897, + "range": "± 2.91%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,50)", + "value": 3640.575342465745, + "range": "± 2.20%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,50)", + "value": 5444.502935420702, + "range": "± 1.53%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,50)", + "value": 8932.369863013475, + "range": "± 2.75%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,75)", + "value": 5240.075342465658, + "range": "± 1.69%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,75)", + "value": 7524.369863013677, + "range": "± 1.16%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,75)", + "value": 12387.089041095656, + "range": "± 2.45%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "HtmlRendererBench::benchRender", + "value": 2762.28180039137, + "range": "± 1.47%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "RunCommandBench::benchDefault", + "value": 119127.34050880629, + "range": "± 1.07%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchInBand", + "value": 119865.00782778696, + "range": "± 0.73%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchNoEnv", + "value": 23501.30332681042, + "range": "± 1.47%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRowArrays", + "value": 232.99041095890524, + "range": "± 1.75%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRecords", + "value": 1166.512524461838, + "range": "± 1.77%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + } + ] } ] } From 46769fbc4f992a379caddefb80b464a8c972a27e Mon Sep 17 00:00:00 2001 From: github-action-benchmark Date: Sun, 14 Jun 2026 18:28:29 +0000 Subject: [PATCH 16/16] add PHPBench Performance (customSmallerIsBetter) benchmark result for eac0a110acba2124a29d06481f53d7d0401e54b5 --- benchmarks/data.js | 187 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/benchmarks/data.js b/benchmarks/data.js index ad988bf27..226a93cb3 100644 --- a/benchmarks/data.js +++ b/benchmarks/data.js @@ -1,5 +1,5 @@ window.BENCHMARK_DATA = { - "lastUpdate": 1781033999585, + "lastUpdate": 1781461709784, "repoUrl": "https://github.com/phpbench/phpbench", "entries": { "PHPBench Performance": [ @@ -2592,6 +2592,191 @@ window.BENCHMARK_DATA = { "extra": "10 iterations, 10 revs" } ] + }, + { + "commit": { + "author": { + "email": "dan.t.leech@gmail.com", + "name": "dantleech", + "username": "dantleech" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "eac0a110acba2124a29d06481f53d7d0401e54b5", + "message": "Do not show \"ERR\" if baseline not there (#1154)", + "timestamp": "2026-06-14T19:27:50+01:00", + "tree_id": "58762b597ab4f798e535dcf5191985327baad2ae", + "url": "https://github.com/phpbench/phpbench/commit/eac0a110acba2124a29d06481f53d7d0401e54b5" + }, + "date": 1781461709264, + "tool": "customSmallerIsBetter", + "benches": [ + { + "name": "ExpressionGeneratorBench::benchGenerate", + "value": 7339.508219178014, + "range": "± 1.64%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,0)", + "value": 328.2191780821927, + "range": "± 2.68%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,0)", + "value": 331.6164383561675, + "range": "± 1.85%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,0)", + "value": 331.52054794520336, + "range": "± 2.56%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,25)", + "value": 2227.069471624254, + "range": "± 0.99%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,25)", + "value": 3381.132093933442, + "range": "± 1.41%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,25)", + "value": 5735.424657534215, + "range": "± 0.66%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,50)", + "value": 3627.748532289599, + "range": "± 1.53%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,50)", + "value": 5428.977495107582, + "range": "± 2.08%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,50)", + "value": 8860.753424657505, + "range": "± 1.52%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (text,75)", + "value": 5174.2622309198005, + "range": "± 2.61%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (bar_chart_aggregate,75)", + "value": 7671.75342465756, + "range": "± 2.15%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "ComponentGeneratorBench::benchGenerate (table_aggregate,75)", + "value": 12645.741682974563, + "range": "± 1.65%", + "unit": "μs", + "extra": "10 iterations, 2 revs" + }, + { + "name": "HtmlRendererBench::benchRender", + "value": 2487.1463796478083, + "range": "± 0.76%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "AssertionProcessorBench::benchAssert", + "value": 954.4041095890321, + "range": "± 0.59%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp. w/tol)", + "value": 74.5751467710364, + "range": "± 2.26%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "ParserBench::benchEvaluate (comp.)", + "value": 66.43992172211348, + "range": "± 1.34%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRowArrays", + "value": 246.16438356164514, + "range": "± 2.02%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "DataFrameBench::benchCreateFromRecords", + "value": 1125.9391389432392, + "range": "± 0.62%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "VariantSummaryFormatterBench::benchFormat", + "value": 1423.0765166340632, + "range": "± 0.87%", + "unit": "μs", + "extra": "10 iterations, 10 revs" + }, + { + "name": "RunCommandBench::benchDefault", + "value": 130768.33072406825, + "range": "± 1.07%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchInBand", + "value": 129478.91780822005, + "range": "± 1.43%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + }, + { + "name": "RunCommandBench::benchNoEnv", + "value": 23535.882583170496, + "range": "± 0.99%", + "unit": "μs", + "extra": "10 iterations, 1 revs" + } + ] } ] }

aah-oAD)Ak33n*FCf!6CyZg|Gbb+; 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 y_+p?>YATl%`WkGJS?4#{`F`6@)JE{o zOwD5X2XfE*V7_M_m7>fyh7wzKZZrc5u9TO1jw#vVY(U8ssybbiEJnEyB@!SeP)n*n zk%aDw+l{6d+?!l3cFKa^F?lPtfK9YuY%jdCZkbBcM!C>rl~PH$2IOIm4d#PEDV*|< zsI^Gr(9$mRi%papGu$EESz{w4?!e;*-E0sk_WZ8u&O6PlFn6Vkh9&(Y2U4`(j=3*H zy}4fazbzpexmil8V6u?{_C64Y6v4uJ_pcE@0`l}`y@yn(v4^;L8;&Z(=G4I-oZ z$A==(?*{(?t&hEK!!QhlcRmHTBnDbPKnnCH?v$cK(J_lG(}@sSRz#|aQRLk#tEo|B z>C#&W@a{g4clUnh0i5cHz0!nLuO%2qo9gNDWwTOxM@xXn*Us}vlNZywB9m?Bv)3N=7{)yfcvPI$aywfrbK?yEdDI(mv{D80L1ERq z)@);-=I;~V!(iMN<>yj`he%<-I!dCZ{D{RSbI~yBiT&%5GkgK1R!eUaF%Z7*ub2ZO zn`&tQ2@Xxu7TO9SDpU{>hqkKR&7^DadbhSWZ6N*c__5iCH|eUxhdk_=Z@zhW#`}kP zk_Q1NDaxG02q#&r1m$>n^6|sj3$luUxZ0qCWv!qJ=vM{~&w#*C6V$_1Shh44b4ESRzaCwdQscCn>P zHGN-+(HSb5B23`5FE35pZ`T_$&f^zc)H?QC$Kfa^f~3~iYE@P|nWljXv6Lz86*r8= zb>^iGMOcx2V4RmO|g%5^@u>J}Nr3eH_lpp!Jw;$9A(tS19Ff zb*K`N-Afml(}x&yW4({rahaeFGdhFN&+KGU)=NXqENj^_ytl`*8obVb=~dVHcByoo z|AZWczHF`w2_($69E9JR*-T`q>q8OP%OoRErZA3Ef16xUx0qojL z!fvbD2QQs1dtS}duw*doNLxa_u-)v+`gEMrRjJYK@2&7P1N)(L=V~Z-9Qz)dVdLu}AUMLBAu2Xl_>RYC#_u+pvh~_XduZ^%3 z@n&}bWcq2cS$l1>u$rB|a#86;?F?V__1EsRt;+jYb}EfO`qd^*HY+>g2?xH zHQ9$7o#=l|Z0&E=*em2MeQbEQVG8&8YaUqs!)j|ktGQhvKhMvnve@V6WAG2Nu`kFd z;Nr?l%uOvWNK8&uaLG?DDay=Cjd5{u^s(mR($~`BQqWRxC@4p0M%DpZ`(E$e%G%!4@D&f zp4DN+FevlZtjT}^-Mo3(!;lyOBhfYoNfbybsj>FI_mH9`>S8Bu0|e89Wr;lZ?>jvD z@#k_=dR{?tDoc`3IOmBHtf1HDzns0l{TY7B@1afV1DvF{q)xQ!5;qds*FPEWwSa2rmEmOAU>6*ScVL1SMZ+WkY)x zB-yx?Es0Rtq(VO)Zun+u)(OOvC{1j>%@Ds~geI`yFg`rchHWFAWDM{L7J=DHw7+ z5*H2iZf*=jM3tz(_&%w*>!vO=k~Jyn7&lCQ^OFf})~T_xO49VVDpL&U71X*yXOq)9 z;uzyyuc>&qdP}m5roRfF&nqQaN`po~Uw&%aP?X-1%%5}@H%gTADdh$cZ?K+4cKP$#u{co-#!hY z*aEx8%ck!O~wf14|WWf*blaf=ioV$vZ4~1{(8Xlkyzu{ z4Jg!8Mq8E1zNQezy+U+I-uz#2LG@W@J0K8QCo6PPq_4JT);lfO9Kbo~HJcBj5r+il zbbV$p=IjG%bou@S2()SXf1>TFQN~sf(7aSzo0UGghPdegODiy<%@n9-X>20!@?|}+ zTg@<=&EVD1`YpT-2e2K@YBZc4hwO#fgWxvpd+ear$ue`?n%&XDwqndTJ=7E= z!w=@P24J7-R@kD`0odMyy6d_vYx8MEcJs9|z3&MB$G(3CQ?}xNnQenO4EJjHZJ>r^ z-|itUs4*U@GqdUY;@lD`QIyyuBHuN82qfX`S8)t9kAi&u% znlHkyNa32QOSH2O2TnLUmp^%*0p(h4bJ|7_{?4!15AH~K08N_dOotFTj!isF85}px zG@T^l9MU1K6_R{+vO}Bv_wJn}5JG?qIEh{5L*(1rmwWcP-IGM;C>lq#T0kaLM8u(R z8ak4*fPOfAdEEbS5mP?>(ES{7Dg+CIXSG@^ijz*lAfS%a;zcP%J%=cX$p4XA8O4Ft z(l=Kgl~;s&&sK{>nJkDWWimF2{LrO^Q4wKWa)kb^!^0XJ!hB^fW9GY*gX0q+fC<3y z2;*i$pMng*ogzleA-+Eahf^Xcyt(MZwE_qt!lh`TL93SNFWIgATLZkJc<3_+95gJU zdjaqqjOWI)W!V6QDC^i|!v3XJ?3By8T2^ZHEKw{qdX^~KN>NKSMCv+(%Q0pZQw{S} zw^CU)<;4x*IdM8{C4M-&QGgvdIi?hS^a3Us@!3CA0D*wsLPtt_3CNCxqf5>)0c4VM zpLew`dMl5^j3!rC8WJ8m(k!=G*Lq1#)pa1pOf>DB)8~t;Rg=Cht@%U~Nv#*>{*}28 zM<6j|kKND!=nMd{V%0)zHt)+xIEg~RB%MPB2XpP}O08Barzh4TMFQ8%O>PvG>uo4; zQ2>uq8S|iQV!OT3!DeV(CLSWRA-`W_H`!kW>`Q?=CWw8%0mE2g;MXz3P%4tKN8BkR_$;DDkt8v-H_ID<(A&InD19AuHtFQAyS164=aAie3Ksx_fmzIBhJpakE~XT*%V@2e(50P^hIMt;#HwzWXB9 z`R~7YU>& zTFSgn^WHJ{<1+U}`XynkW!xMIBwk}4G2Tv#1Wc_W5-l+BUn{r3*~oMw`iS{;jAuMK zEzbkoB0+N<8nYTtXbzjq;w#2+gJxnyMg=27ND(r*oF!Gd^|+uo(Nzj^4Vv&rZo4as z*=}zDlNhA$gGU3keiRbu%)HsSfje`nb_;V(@KJ=e3ky)!dwlfzm>;k#h+E7>8%OzJ z-Ikj+y*3RcHX2|Y87b9Q7sCo%P3KPc%`A*mGioJ`veWVc0R{C(?RNJ^<-ZjXYT((sk3uM#ibB?v!D(-n4@}v7Q+9ADLOfF4P)S=9VAcQr?tf5ej64U*4HiOs0kMnjLa%N`etG)t zv6iiU^h3J}v%H5>w!9t6)>+lEm8aEXEnN0K3YT4}aCr}?aCy5FuCuCzD}de`7`*NG zw|NjOJas2<r;dz@5S(9ra~M*WyFn4k z%4nqiHX{Q4-fvyAzT<0xC0JfD>SH0vRtBSxPl!z6VS$KfhR%}z)vsrcvx=6Oi3Qaz zR$AER2*Li&wxV^|fTL_hR5&D}Cu73XH-~88PJ7i*Q5t%qLTjd?E_31YDlP=;+fz8o zzM+~1bl;pDzwW|M!dQF}XZ~ETLmLdELU&P&_}-~5jp5GeUTbBGln0^g-&@wRV83JL zdjr^W(q~s%|3;M=&>O7HefmW&`S1P5tkXt5ap*YIKN|?Sj+bywrs{R?4e?`KW{x7O z-E32JgKZkK(Hp2!hpFDyrazD{)j$JG9e6z<0~qzHTlfBxxYWd!w(d7d-Wpfi?e5-o zIZWc%Igb}{n4a%-c32R_5;S5IPy6dXh+uM(`5MSfC&lYp8Fq_Vz{2rGSE>U!jcD>M zL*J#JMpk>iOOr%>f(H>DTqAW={za{barR4%rmf>&v@rwmYwbVHkxfqmF%X9D{S^~J zQXmL-F;NVfXjG!nlXxi00FB-0+7E#k{=02=Srg!_&ZWEaPM?|Sdv;zd3&u#~XbX`c zjCAIVB%F*chj)`lk7hGq%-enCYMo)>q^3%pd&=T3$h=T`h9TYWbQpBtqOd~cCo<2y z6H|pD*Jqg$)(JnA z80&CXSzj>dyrI28Dz%@^!cUdV0#mqf)N6WrC%vMCZIx4WkMwINM!80esvqGBEUI~{ z@w`M>$WJ6l4B!puRa=FR1_x<@w1cpj13iZz*RU8|&<3ub#`mBN+>UP^AUtg}B)xSH zB0uxSAUU;xpZS0vfXazM7(9*Y&dC<<0zbN2sNp@{9wg3|{add{(+kNDkH6-YPwfuP zl2m#nX!gfuZP_>NJ#ACmw$|VCE7UtocQ@TEO?qdZJESC~KvH@sP$2DeD8rbwVcmG` zjqQbSoBsFbyhyer%P$LTnxxqeuxm?4M@Q#HIyyS~_W5iwd+;Fkra?CI#(~&M#(5gX z!S7q&Z|wg5^E^mzo<4Xm&+IpwNgM~`JWS%Jj<;KBcon3-9|qYx%8zm{52it!yGm}q z9S5^IFb7@=h~+_gd@~DN1qeTYF-wCi10aCbbe3dc9+b~I)hoY#KMx~6NL{rMi|lt7 zE&UD5)7Q79!Jga-{eld@;NqCW4@5aeI zwx8jbv&$eq^U_Nj7QKt-Q`ft2I*SrNu%BjUgh2Iia{dRfhxHurx1MBqnkVzwtU`}$ zeh%+|fU6H5J`fKDjhGx8F^;?}gH{BL*$ikNo=1Vm6XD+gBZp%zif+U>4S?;$(a*c$ z4b%;TYy@x+)DEynntO4+CVqhz7dIlGc)19GV{gO-)S9P(xJl*$5xNeeNL(lBYxtH= z@Np`9FZa$pczG4Xev%?wYKL$X3Q0c+G7%@a@N!`PS&j%^c~R&iTKOapmq8q)@WZFd zfC1pGa6E?hZwTDE2~L4P8eIe0S%P?r@Is6O=u{Z19%R^|SVJ9}M9A|PphY)fd|By- z*bS#)uE-_4*cX2!VGQ4ZAbb1A!kf*a5b%YbygJz0INqi*Tid(a@HJCno?$c8IAE0} zQw2YRK^;$omx-Tlu*VsUI}C&fbL{o%10GdPw07x_In(DSNgqo;Y6a>>X_%BG_+$HLJL(!6I5TqIV2! zoNetK9{*N@BbV6R-QL(^L}hbEd#IK~sftyDs>pX&UaGw|(E$~WoVYU9G`XWoesJM| z5=Xw915+dy#H}+g%`?C>iW)%6j{LlPcCc}Hd~~+8{bJ+Q?(q@y=Y%W3rK`JETl4Lhc4x$Y;v_XHM`$oNvQ8 zA9A;!z2hg`ta|8fnLr~UPHHG$v`wl;OuZQnLraG>jNs2Z*?*id5D{zyon;#Er3pv_ z&V(soDODum8fGK0>BZta5G-gzF`tnMg5mQ698_md05vxv)JDNSq-EX6kPc*;sqy4V zL4lATruhxxF^&Syt-NZ{VCV%NrLuG*6N1%ytB|<}Q_xE(O-|94r2&iq7_7xGASgV; zWfksfcj~3p2`-7p7K@6SSoub^l>dZsnHZrV{S^jD?3 zFM}LsRid)kV3MOkWeItd01w1CGb}K}MheLe3IGLfK(|5KXKCWkVInACC?|=JQ)Cz( zn0-m`01oD22pgV9uylczy~|((D0!&i*DOkZJBB&G(n#F(H|MIeKmeL!>*LiQ=!Nm~ z=o#ysesA7$yhJk&uQFh~-cY#9ka6xq%a5L6&-(-T{{||JLLcfp9t@rq_^#>*tEVrZ z=hGxrX6&s-KZC$S`W5m7E+Hd_>Y^;I0IF#oj~0*;xHn2*-2tv`%J z`bN&=-dfBjAys(r(KA@qRp9prjog@PRs+9}=V=Pu5%IbsuQ7EJg^J``=mS1zv08eN z(|WoEM_3soaFtEiVHI}br?H#D1QBMj7pxAC}^b%5Gp8?-VM z)g?|DKVC~D?w4d4o3$YHm>e}NouwYs^>h1eFrLH0NAKP|Q*u->G`M(#7h!6y=IoAJ zUZi&=+Z84ANt#?|qHrd)NEKmO=_ssL`x;M*^>uTaGtkO53!-d+MnZu^`u@Dz9}HCm zo`IS_X@aNJSxMqftrY~ib&Cy*TeAR|f;^tz=$b=hvtS%vgwh*U@Vd^G_CVKKnP7U4 zHs%%B7>9`o#*;*lZBg`kDppsO|6nd&9Pa-lu&$UlTN%Z!k~%mIQO>6u`LJCAvHg0s zgyoX>0=N?Aed5b64Vl#0wUqg0o~7lNgV!>c{f#GvG}wT~=r`i#*e9}Pyrw}!bk~K! zjbMs*VjyPT*SK7L&S9uuKYdWw4ts4nx3fiyn_INQ4xrBMyrSjQE!trgGux5gmd=eI zG2hoWu6}BxYjTZi1!b3nrUf=$F4ys+c&Xr`s{S~l8Lz#XmnOULa0VzZ#YC*?*!YlU zp)~7+w%4%m8B9Q(P36VTMZZ^+#Hnh_OjWLIO22^40k4A__*Xy{w9!H9f>huJjs=9g zA2wI4kn>3cLceR9FWnMy;jh)i-!&?b_=Z;`hTq~2>q1As+ff0rz$LLI?M8ek3$x)bc-;QPSbKhr%j4JA=!8=_78>i>`9xz zWPS3H_3cvlDP-v}xr84N8w)+5LQm{6t;L-hqWPozjAi%G$pihZZ|v5NL+0Yl zmeFkKLMn$$b#^Gd))F0bSJpUC45ua+uNWUmK>Vz$78Vpd_Pc-%1M3>bpcEGsI(8rO zT(`7h(x=GEw{G#$k&BLvwRat!ZPQ0>M4DG|ii}P468%{kT!qOz1B_s}2Ng4J_;y=0 zm0_01m|>^!f8U_wK*6=vi**!cfC!6iTXXGp+L6iWLOy8?o>w}2 z_+u`}$g5YeiRRXm-oR}vWBU+T8+hW{%dnS=jE&~RJZ@>PRaNWMZDvV>GA&%#ZL5yE z8DXGI69>vQQ@IVd#NELAV%nbyWcwQ8?JV1cV>LpoJ9Z}*=GO*Fc4t;=6vZK7kTLaP zIjIqyrA(y^VA-qncBm58+w&wr7uE_BVJQOS`F>x=Z&=_mRP6%mQxsE0q{3{2R!k&J zDyFi4raAYZ~%lkp%9`2s7OG8Zk3!W3h82wzdxrw>Lp&Y>mVLMgo)E@G8b*#A83a3jMhkf!Vp7<6h@--Ju*{ z(wmLke*P|=(I~jce@wy{ovR3HlEeW|2dK`BDzb}oHY)ZMx_Kz&vXB3roXR;TW6vRY zYCN%OM0G+e6V?E7DRI8?KS(P$*!We-I8k|+0J=a$ztJXg2w)t2 z*_FTt1{b4h8?!d1p-?Ix2D)et*&*=!k!5w=id2rcIiX^*i|2)uteFR5`hyz9`sf+X z@5*_u0$&?BvEjp9du!+DcxP|(7ztIV)#a-!$9ok@@2Xp`#+m~i?k2S*Mr$N?HOow8 z2+P9&Xa@6i;oUbHklS0okJo@X-UBboz=37jw$Enmt_#Ye&z8+9@V8mEVl@6i#%)M? zw${WQle5g;`{20YFa`>G@FRLHPni^lQb6;jUVMpX$*zj{XN-%G=NEHV{Pex3MMmTU zit|x!ElD9d!b85b>q!EOP>N~AvzOVNU72zkLB+LW4K_G03W4D?aQuJ{8AubSZ!I>) zc;C^;GsRU0S%D1|hVD2Xm)_#gKTB_Yp$NCI%5pHj^jn@7fOK)&j3qdHc94@ zj|(g=UcYH}H<6JPS4ef25e{6h$_N9xPZ`n0f}z)Q`zEm&pN{cp(W(}tNs@yaN;&C; zPseIlxydo`VIIkjSU!`XAYO);jDf|CPHoT(Sd3v|9b_R04<8%M`OqhNcPR%6mMUAW z>^r}gmFmL234>1=D}RnjsyM$t{xTeeI^=gs1$cQRJ!uKuWmxXa%0XBqxmWVjfb z4^@N_!B0+t)9`ZgafvWt{8@%SNEt52=0g=?!tmpkVtMElb33s&7{`?k!Oo{KXU?g3 zyO$!*AE+CfLo1o}i6neHv`$(WmEw5MP}>0?3JTAgBuD{xk&l;lQouNs-9aEY7@Yb# z62MuwXS}%^!IW=C%C01RTb5OY zm_aJ3y~fqlUY#mD8Mv{*;>%S4fLce%mj_^h);?y*3D|0Dd??CS%aLG-aoLP3mBJUI zZ{&mS4=C#WG3A}nkXKED!FMFzt^|VURo7iL*4BU>`=imwie2cE6^WFMJaVG9)EuR^ zh-NVy=uvWsa-@KPg#1yESb5T0mCgsA&;g6l9B_B%zuTy6s%2rkel21qvNI3*brp1* z$6|U(ZfejdEL28I4BJG&dy2kNo#M7D+eyfjc;=xJVN)rgCw=eJQ_e8eHI(V!PyX|y zgvylA69G}@Fi29de&xUq*J&115^E|*Ksj$8NxV}=kAFMZKHJ#aa-HEJ1uJ_ho{6uW z$1{q4$Tm@6Q0gUtj0)w!7U$^RX2W^EmPgx zBKO*zR?24BmDAED>90niC(BSUzy7d&s3o&sj&@N!`K(-OmCOF&r=(kwbjhkkJK-hh z%p>Xyj1=Q|40(!K&sDL&B1r-KRE~AsMNi2Cr7C3MRw3%e^Uap{VYK7^=rg^M1Qt_Y7|>FWK0ap`5w00m(v z&(SxZ^>GPSA>}cI?PvxAhyxr>bL{_|gy4+$#Bd;DKUbZ6rwX}$G6zD3; z=CzFkxYX^QPx=^uykty{YgY0sXSbz+EA{vy19rbmENLv$(ekej+m(`qheeexi*9`b zr&xEB#$IIEEjTMyr)^2ogC#54dr1~5%b_#3-Iq8lyqhFZUvwu)R$I<4lFUvvRL!)l zHM$P}qWee1Nel;L+=?ME7>LquEp6ZAhemFYuFDSI&3kum77XL`Z^{v`I5<0Tr^d&3 z+CR~7RM6_YmkanS4HjNZein0C{tZ&&+y+^S)c8vhnI!!zF}L0n_;3W~HZK0o@?s=# zqYQ}U6|B~l;R1er1*mO`QKa9*>zYY63KTToO-&^#YF6^YwC2L%dlD$h5ZNbuhYEwv z;hQSKVc zmWx>7T8GB1qqWZl(66^LwjUI#asoxay? zJ<3F~7AVXoYciDk+9R`*HWRj5td>Dld8CWcy$7+n&p~Ywx)y0`xG=pcm=&NM(js@E zv?2?W(59KZ0+Tvia}TDxnAy5;P`gFWCJO72bbch&I9%}{ zI@4K{_(2~%ufwVjQD&ktQddi4M(Yj?=o~z?&et^}p3Wxl@&G8iNylK&9l$sm7`nR1 zp%|y-<%`%9M+y;5`aC9sYFA{+~KX>Nk;r^=w@%?Z09dgv{+LWAoTAlh~;}_h3 zYw4A$+qEra(z#pvhg;i+9lKT3lug-d4SDK2+r?b|#Qgon4YSIP$y;9ToqJhu5P3N+ zGBKijn)uWD6}#Lv2rl{ry{m0`TBaTr{H?1G(XsnL)!yt(0GW@~n40eqY_teoSEUu_>bAl8FnD7Pdyr@C7L!PU*qm9+w$ zj9zc0aO<>4l~-2mxv~ds1?{Pj;sHqthEZm?D^Tc(qwV7^(=I%*dHVk5#kU%H!C66< zF!1W?@Q-KUB`J#4k8||j|M*GAn;fAOL$tCW&b{*LH+yZ*@iS$XJB%n$&^Rb8_9AjJ zbiHuVU;xe^-Wi={55w76o*WUZ7Q@4g)vsrBmFj}d$)i(JzpD`wV4R2Wr;I9+!9pMf zq2rb==mgZ9BtD+^y$XH4=4+Cq`FNhwroqA1{*gN2vbKi6f6?y&hrx}-oTBo8M1Tq2 z?V`$8+;()IUxBSeEh{!Gax@!%6YLn%7bm&u0+WD4k_LPm zn>Wvs@+dQhPq$FNzZk{{)IZ& z6~?%h^kgU%EDpYl!uU1q!mX{Y&L*=_9OSDrKPkxi2>VB5{L2Em{ni6z9jvL}W_*&Q z_qupe#O|!Tp+nK4M`xn(S4lfnexSwrtYrC=E|SE0Zv$YGKu)J<>rg)Od3&`dk6snn zxC4ds?p|MCkNy|r-Wa#dVq&2rtvImsZCLBGve+&4RzW=(WC@Z+{|6rZ<4Kw5gd`1n zI?{E;be`oX0;x)piP1|iw!O9Lm35}Vq1!bzPNQ}(v2e3)VRlB3qJL1{Oe|9-tjIK` z#Tz65x)u+2_FfLnzBTx+jbe3NOC{<6pD`n8 zS&gLy#HthTs^DV2qvQ&Ui~;1uJSx}}T`L+unW>uh$l%)zMf!NDdI2^_0WH*y7>We% zhrAB6fa`#xYS(FkXUS5{JbRb{%kw!|Ns9Z*3h?>-k`RGjqyjaAO{ZpU);-&V z%<51chj?g4S+<>naGi0NiAfMHH+@hHT*;Qb7fAx}oU%8tn$S+HF4)NTUfSMBhddM3 z=XRR3%VgTAzg%7Gq{M3WXX@0DOMN)1w5yKZ^V(Hty)`LaAXeE$y-Z8n;E_H#+f^gJ zG~F~d(P6re?Ao)O?$VRwc1kt+l-fL6dS&u%;bWI$3ehMW6lWCf1^vU)CtAv@Q`_xW zcjunW==zrYb z*<+QW)v+DS71XQf(UhPpS7rh$mxr}imKNREub`sA=|&RML0Lh(^jJ3lx;H*kJ*#nY zw=!J4-?K7lftKo2&sGr3#5khN5qvGRcA}B5xz02r-yHQ(IGXF1wbWE)OAj zjq3>_iu}t%shW3*h4HJn^R|k*Tcy>I%5FI8z@0Yv$*}Q|^K9lBjlJjGq%(-krCjK` zNo;Rp>Fu9T<64jCvA(k4zkfaWf6Z81Z`(E$e)q39zzoC=92-M{Jvd9fG+BoNX^S=O z_K*YxEzuG;iBw6-ZqVhw?;Pr4O1{WRI$(ORC5q?reLNRZKhLr_>-8i{xye|-;Zg-w z3(0RUU!7l__IkPT#x7KvDtUXsf|wV-r_Bi@bbFsLF47crhJkva2Rw!xwhtjJ>E(3Z5d=Y(4nGIrmGdVu)!7*GYxe!sVod5(%T81h$ zER+TtuZ;jXGe!v#dLu2@k|iLOqJol@^xctcY9G zF){9sbhfUH2@On!q}Mr8DzVBveP$Njn&*}qU|O?#SP2`W-b||u-4FSK<%xwQOL9(b zPkKSZjA`QSee01eM+IHS@DX);s%^|H>gP2vAqeRlXdoO{;(w4i<0Kj$m}*8f@s}mj zkczK7ytymKChi~ow>+?DsPAJugPS{Cw3fHm+#2T%Mi3+}goq$q^f6o(_6-fZ`*d{$ zt5^gvq?}1p&!-g=20~%jJmKMnmzvvLOIRuqHoeT}i3rw_h09n6@nBZpLcetC3}l`p zLnm8sPFLT?!koPHaNZo5FG(SLbca%<{HYUp*uekWHvF_?hzUv9iKlk(A98CivdBl) z22iT&!ZDjU_?;*MDijcEu|ymq%@Uq+47(!kJyR!SWpa;YC`j^WtQZ>%A2yaMEc+GO z`-IJ^QJ<$%DlAUa^*!_2;Ta5_yPSn>Wf{{f1w$Pxa`6uRf`VUb2A&Zrg>~Pbwc5dk zp?2VSjd?mn4_!s6-_msymd$n&&RKFaYQBuE&CGN%iLlY-^KqckNn`#bL%fq0FMj-~ zExF0*UL-@;66$3cQSL#ma4m?T0j$EON=g0?96*Oc^MTq1FtxE zE78KRKC^8)JMz@}mF9pAS3z5OSfIac}P*OS^r5yQ{T zhek!MUEalDRZPDdi92$CH&xGvdFZaZI>55rsuL`Usj!eL+;Y;kaJ?7YINH8h;4$3W zD`LgNGjm(Zh-~06ZqbA-4y2T>Zs%|CFpuMNj{S)TIkh1A5>QCHCsG-Tr3iCYbrQgA zl!lc)!FyD%SHhs7E&Azz9%+?l?m$o96g=VesUPV~g>*e|eov^#U7XDYX|D|N82jqO-S!r;WaBIL8}|n{%9b;U;e11<#ej?Juim z!AG6DDCN?HZQIf*ELc*+arr#}nS0L3YVF{_bK{Wn)Oxl{%Kr4@TL`Zg*bayx+8=>#C@P z(5S2o*ohR3q8Hj!7_Tn6Lm`|jF;-F`%)H{N!};uHdOx2oSSuA7?^u=MaeWrd3d8&f zW3mJ?Li3|p4}aKjw^mp+c6ISNNR~&ru*m*{UQa*|4w{}@T%m=~_(zh2B{~9Jqe``) zE%o6s_?6KZL27O4inLH76<$YR8TqQV_Di4wLFbgMxp9PH$?Sbr)S`*TDxl(CnwJBy z6F|#}YeJ{L!iskR{A7gs>9Lqna2&{iVFO7%fMo~Hc@nbq;q*ov@+_RUI6@L`vOC~mqv0UDhW&}_BSa}Ww;)Gu`fx6CSfYm zqi;edE|9XV=JN>V;Kaa2Wlc#h8VH`k?;Is;e>}jUK_yTFNdT^p86h(;mJe`8TN4q4 z3?(XNC{jo*QiRvrLdah*cHy+7lBaMrLKy6!?Jw2ZV=tBY5@bnx4 z>SkXE-Zv*ncqJ*g;U*5Ef{I1p?TIeVsdZdtF0*&YK366e`qNjRhW=mo#*84os~!^k z7$Mpk7SU_BfK%syk0B!znnuacoN&5&S2m5RN5y0EA;&<&R6%FdR6PfpDEG#LkE@Lv zF;%$5P1K%p_mTF;ZerOKM%2_cyQ|vJkohHM^b1!6tvsYyE97=Rmo5~L@w~?exR9wh zinL(`Mf&T&uCqawE<@o?e$5ms!=V4`=G_kX7#W03E&D~|SekARMQqc5FyDT&-rR|C z|6$SHdOic(_)YvgyeZ?^EbQgDA5%ln>Yr-9)o{7*M5`@!iv9zwR?BYNFc7@!EA~*p zN#IxoiUNhxBzEK8dMNrhHAO&CYl$dD2_zM#Nb~QdBvKDrwv3{>*dk|lhPyka-`^H# z5d?x{s0u<6HaXRj3B2EYSbw_z_=rMZ1wpC&ox5BJq?+a8YGn5_CwiO9Ox>>|r6r-- zt&E|+(iAgdQt~p}ONWI-N-84Ne=(xMi;Dm*pw%3Y0{f045ZR5uR6^iC^HWepwi%Frb_$1&!QB?WRpv%P=Yw;FCrX zJ6yXSJO^MNqZ(F^L_(9`k0?2>a#wqWWRFG-yrQ-gE~5T4XZ=EU?LmkpKoo3I8=e@p zGSBK0R~r#gxUfMkwkFNk6UIM&+BJ{0F4Ubz-NdP?8_9UYiCCx0hj)`SQig8G{8Fq6 zB1z^BG!XAWtR4$FwCUsxOH)RiQP`HE5%}<6B$<$s>N6rG`90vB+qZd_ZQeg#?>y&q z`eq`k=ao*Gy1eNngKMuLV^F(v-FCtQIT;qzwZOx5!fITFepUIayPzqlVvS{MJp{1nu5T9_DVm zbrSU{r2mI*V?8jzIJC;Kt~#qsH`O608$0E`RctRpW25iJ-4mu*E_cN48w%! zw2T%s&%yydWJ;B&!Z&ZNF zQ>k7rBW)C+rkOc}{zel_$@*S0eDdR%E2PBn=>CV*6kc8ia0#VESgN>4 z?H>&`oxwIi2_`|vGi6955V$K23^!bIR{*$%7$quVES^iv8N%~IaQPb=&6TdtM3NZQ zZf4oSfC{2DRFP8fsx4V6u|hQ9lR?QESUVp)2VjR44vR;kpv?a(3n5%(7s?fXw1VJ_ zSG{QP<*!}rSF&wZLJR?-VS~n!FxaRpEm!OU5z#oSbDGqWG38Ile*UWGJkGLF_e0_; zO}ovA4=4E8&KP@m+VhORvPDi5NgIKEF&?7UQX_^dDI&osYzkTNKRj4Qi!Z1-CrXh& z{j$^gW7=2TlXy{vNNXF&!2E91a!S~-)-;;CGj&p5-01VQ^tozDW0VIE)jr?%)+-~VzNwBg zS7+fx#4q6-qw5aJ7-feX;T`wNhZC5fPK=FqjyV>vX3$RC#Eog*|2Ky|9f+FdwMe_EyKl;+n{ftBh?yT?Sg-e zQprxkFc7`_E9StVR6sv~twrU4)B~s-Dke4OBBzWx6JtJ z)?|h;&2w~y3xsJQ$V!ci>D^?uxL>2DOU6pqtvo7DuZ7LsVzP8(xuE9e@(=V}WSH~C zGhZsK>JNI&l}si!EpxAN{~^$jEVLIJ4TlVd5VfHJIPhHwyl1F^GKBfl3|1xDPw<+M z*uog8Q=p*WfLnwVHCj%-Nopq(gs-uZ`VG2%Hy2)Q#DwC`L1lF6>7v)@!CP>EAA~w- z0PQ{dV&IRr4C6Jo;6mv1J2nT_P&8Jqy$?8{D*kRq*Uny-Y3KTv1c!g~zWS z=-6<}^IeOTry9`$DYM19JGi47S8J9_B?Y{eT7(+Byof@3jZ)B{Vn5r@*oHcd9MurR zIiXCt^PAWg{;%7qbr20fKx`)z0Gs{b7OAv)C;8C~DN+Oty7p?K-!A(^yV?7M8(fce zZ|2|2jt`={Z; ze(*wAIU;+h5L%}KX^XpdyLsEaPH4kQh{$KkiyKF0XKh2Dob+YW2xwp{lTR7R480>@ zHQ*?L(8z!tU_RjcWL%2b^id3_Ai%GHwqrPL#J7MnDIV67B-lG=@_Al*(o@QXg{P9- zVlSf!@Cdy$jESnAmN(I@2}2}%*;?fq8uvyk=%ZCZI}5+FZ*GfehRU-DgXX)ZD}q4r z2#}{17x$`c4}a3-FaC?9x%Rxzt=L^*o%dbR9s3XB2ZdElkJ>O0z2{fVVGn>PdoSHC zbho#ax)r?vii|TGtR^Pd4&7?Yf3Lq{hyx@-BC+4ho0;bse|fC7Rg!R0qO1r-c-~PZ z7{}G~xB1KJ`vJL{CP^*ho!5e>x1A{EYQB+55UQG$=>_zHZm}e*Uu09DclTCvs@RSL z5rX_pOB6Xy+dJkZ%~7R9K6$bp243ja#;L)fqjjF zp#&WvP=Q1-o`W*+NF4z}jvNJ1+Jte*GK7y=!T4{8vpY^S*L^|@A|<%Ao{T%Rx!D+W z2omsJAJkaNAHCq4Ntwa$mp$EY~Lk`>(Z{RYZ{TETtxg%K*L9MC}2dsooTf zo(@HtQVmg}rfT3S>q^4pX#z2rlLQ8E?ABGFss(q)>Sn2QTUpQ`&d+-|PqU9L$k@mDb?OyutOb_sROIhmDz6WnCy%>2qf$npC>U+^8e^=Le}CjF4d^ zkeXpOv8I58Zclg*XFOvEmer)tmTqX8*{6_X6HgpLj@m94)P4eylKc)+lo2l4 zC-xW+!89oZ| zmqx)VXua>fcwm;{5BEnRp)|9e?RF;l(Qttq>MGC%*RuEUx+zH8q?<<1GOMpc+fdnx zMo-T@19eE?7M`A+L6&b5PMacyAvMLK%XZ#z&(>}$T)3}IGDrVP>j=6gC zsdQ&IZX2aKnA*R^oT<}+Rgnr;jT9Dfss$;Hj4l#&E`_U^+YK(NU~X`U^1C@WAFWh| z0@k_>#jJq?VLO8SJ#x1;Lj)?^K>Q)4A5D|lc1`m{^UQRtZD*y;{VV&Nn8!^KGZlcj zNrP-wm;w?SH+To@^9I#cr?h`5gP z?Tqfzpk)(qrE*lkyZ0a7Ub6p}s_FfJWMnCgX!E&KW(*^k^+W!b;*VN>$IXy%7t=0x zam(R%_WSaigDJGj1b5}IoVR~!{&>9bF80MxGgkL-s0jhqbJ{Qv zz57?}AqQYmxRg{{0-A{K# zRz#6vIhulTg!O@2Efj9oUssRYZzoiC8AYWDBTtgq{XyquyV@D68Mp199ox0!vMh(_?4l8&G$z*mZ$|9ASu8zrc$9&u&yTHRr!VrK}b=dW|lIAG9p2E zy_Z7$gwT!ioG2F&m&_Qb6|SU3U8#I<=VR?kRTULw0DoIlNsIRXqt^)NArWwYVj4O# z9f1oKTAZjPxZypYhe_VtiyYgteUK6=y2vbiNl#=?)oo8lSNQE^slK*p#Z)`!o*SD9^WjT(Bo#EUEdY8FaKN5d0j}kP z3fDQ~AZPDSokccjw53+PRyL8vo5s)$?~s#FQghaYvN3XR@MsKriXnR0{g+}g1eO{#kGM;t#1};=0P9iCp zq|AD=xXoaZCG&>Z%P*K_c{3h={gk+|2$7Aj|JoZ@-D63zIm(IScxM#qq3rG0Nf)^F zTeLm_?&=}_INZFG>9iT?uqk6Vp*3Ob1~?7Zn@?r6WasD~g;c?A+b|5h`zv_p!3o;r zzHDnVWVaqVWVfa;6q&Jw$%-S%*@iCvKFXHmL}_co2gBqe-xDA8`MxgeBvGtFQ!|dR z+HiNiw_J>&k4FTVpljw*RwuBYVwDtk~u!+e%!C-OiWX zHHurYSHQGpf3pg8fs4zC`^XBk8PkP{V!~uCkgCn6(*&l_@`a@?nu0PKFpD6sS-$OAw)q$Pr#2q)@*gR(P6GuX(~HGX^?oOKDNJ z@>&YSzySWSsPaA|{7+sJpobX4!;xtShvI6wNUg<@Sip@vJFW{zOy~+(hgn<#jhjjF z{Bi{_2MJ%``Nae3W-A3BPCCo@UJe{PMyugSePI5kC~{RuOk9ZCJ$ap#A`*fjlaTIyOBq6(va*JV+eYv= zCVma#L5xkm7d|$1r{Uv)6Rp14DfthLRKarEFc7`_EB25BFa++UX+leHIi#hxknsq4 zF`CMxR=XeXt9_Luf)uDKLJ?La)shL^tUfOvH(!q^ z^dd=W72iB@qIab%)MmL=S`w<;lf@h8HQi%DHeX6!7vc)w2ibC5vE9z++|`0=Rtg|e zl0RvIGRH;##{Nlj)G3j%KqCm2GOr8sM}dK7@N16(*54lCP@`n1 zz{miJMR{Z*2W@Kt+LW$LAqY7NltddQ6N+UBuMeDwpAb87nwgaIgmR)3IE72DQ8p^S z+xX}lX{s_sDZt+vMb@IjdGeZo`HTp7I1&ktW`o<~O0uIV2X3hMw+{}cUf}ha-91XI z#8D1@nv@ zY7}emF?v#l5(eLfat7c)+Q^XiGsnFdDxk#;4UoQaGbSymHnm~L&@vDZklnNJ+#S45J+&%oa>3ij>C zNH6Tamcmu{NS3V+d}MRH^X}c`oh{H&cQFks(nWxKH>n@`Yjrdp`?-yqGV~H!6NX-Z z)o{J}rmQyEDftK0RZVN#KoGs}ub4x?j!CRSDHNqm!g@84mRc*ZvvLc; z|GgvY!>bS1AtZ`TU3_1C+oEw-LKNg7 zw^^EIrot9BrmHTzPrS{HLFII2#?YnJTeM@y&y92snN4Z6Sd+6-x#ngp`j<*Dm2GJC z_(K{Q57{xdsASm@`42o?Fq;XO!G0d#fr^xW5O8_~e-bnxp=uaZI~8aGBq!G!O$oLb*mlVUxy|lXm7-$06yFl+-#-wl1icy%DEZo5YCh* za)lJ!kAjE7R0;R@9KzZZ%1u2UFY$bzSCuT!nM*WXUVDRn?$#-TcA{yq%HvKgy22W- z*%}E(`-;&WBB%WxYOX;mM~g@GI(ZLwyz>#6!?0Hpq=~%1Jzg!DF>EkJ7t_+jW^($fhsR)nPZYa!!JFm#wC>9X+}QJy{lByazQ581%}?G z=&aDlJBNm?cl+}&tmv*dnsr(&1ud8&k>r4NmLaQQVCe(kFPf6ATY(0WEkl@oPJmc5 z=28TLU9B1Pk|jZdIr@Vo@P*V#pTwb~Os0uqZte9Et!!Ro`!WPqEvl^oS;7Z{jSgZM zB7_u**G2FwHkd+#elqE!*6w%dXHe*%^Fvb9)R8*9Tx8b3vUC;dd@kG~E)6G9+}x`w zg18#Iw$$!R1YT;#c)h#z4ajA1u<lgT{!rks!r{4#Ho%#cvRc&wD zFcAKpUvWc~5-Mn=X`f)QW{?S_hE^nMcXVvz<%zz zyXVfAkDoTHjbTu-MzJ9w!kC4U#}psOpY7{MXNy!mUdKWrMK1b=k%|M%h^^O*<|r>+ zh>*t%CvjD<5`4xmKLYEkPNA;5k>^EW7$d~n# zkfl`C_Tu#>VG$ONLPmvzk?lFb-R-S2SKF88=LVdEw_<4$!2&_yB6wLLg&R)71i?eT zMGg@@lQfZlbQ^O<*D8L97z&_F!kWl%1#(rT2^@cn&i3HbMuo`3IZ(@P^vh!BO=9txqVHl?x5DwnGV zwdONk4Nyx}4z84LAha~ghbPUZ4>p`h)q+aCv!KwcApT0YfB`&YFuly0XeJDXU{;}; zonz>MemS%;!Masl%KtJgRXt(7SGLYFyK|lSbQFvx_S_!%&fLpME;UA!R3E*nonX=PWofk2nyshyd>mI0s1RV=#0=E@yh ze(P;ur@>ddpLy8*nI}h?atd1(M<+AdFd>WSW;IMXI3w!2o=yA=*1LQ%od~$yJ}}WM zkEvho6=7%kZ1>7l$7&EDQ3J?o9H1was)k`3h)RNOvSd|vC`U<(^~iKWC1@}Jb96VK zJ2OA;Mxrt^bawrHNW4lio2~HNHk2#g9ar z+SlqnfaiGL^k$}>eil~o-mpwCJGt9-#hF#QJ6xiy`oL;;hxil2RH7%bOq!Igo5G&PR2k=nSW4{7cIc*-|2{i$A>m6}9`bR0?mg$8Yv(qLQsq-nT)GLUX}6 zQq2UfzGrfyE`lxDu-EgT2akWF?;%8}l*xoIoN1^aaNvZOl+hGOmWdYyT7W_gIz^ag z%pwC$Ma=jHL{f+MN(P3(l1yVPBvPU=h95@K4NZwORc=5O7H5loTRb^2hkRh%xIzvnA@)4$Sm*G19MfK+N%dkBhPUUTmx+M~nb!y{6!|GW` zwl%`x*5w)N&J`6|My%vJ5E4Gm!YBZi02haqD}--rA+igs@9C$!#;1gBN@*Ogr2*lw ziMj;mt+Hcr#@6#nj6x)4qOC!-wG~8?+^MT#`~J=A85i3bRX+{w|7(%+pj>B+LQ?X2 zGdw?2(~g!U)t$YXG5DoUKKOS{x-ldqQ`oJ_)SIAcL8nahCx=GHjj0}k8pWDJu#nx! zD5es1DLHHfi?8-j2&J9voiisEZi%^{=x&KQ*7pED%%8$=whYYM;1nH~kL+n?Ycq@~74n{8)LQ^${jC48Md22rc5GjGsP)xBiP>y+^nCDXB>yQ{t!Is0=P z*Z-)_u?^1SpY`%+)bR4xyMmqf4~O0z2{fVVGn>PdoSHCwDh)Wx23lQA>$0e zYT^Xjp{=(3_m1Oy5C_OcNJRFVdGp@blMj#CJ_`cL64V*v2#b^(B_ysEpXN`iFGrMS z8U(racb;QrcBxAAYQEJ*F>b1r=>_zX?=fMk&uN?|@&@21+s3#MyIsk-%Oy7=mB5r@ ze>VxL2&e5G`?879Y?z9)w$Obet&{Pj!K{g zk^u_I6d@xpwkF_3>6!{bh)|+ph9ZU3B1Cwd#X|l9--*+NDDMLvGp)fXoW}-Lq4I~# zkIs>zDk@3?{xK-S3SF-UuL00QB4Bo83beH@vdQgoro@rTfx9#gUBa)Fnnbr^+)gKk zmpy5N>qd0&=B!1n5MO;KGOG{BB{47SPJlkd@BlCCCZPDIHqJ7pSW?;0)?H%CT=_N@ zyg3n@4NoP7<=hO~VdKi6(vk6*2aV`)S7xniSTMRy+2_veg?{(3#X@@gE_rarUM)T} z4f1exNfqv_F&?xkuuj=@!Y-V;_9`^FlBK^l4W*m+?Oe~mccDym$&5aFhwP(ws2<*P z6R#829q;3;fjzWdPp&ijr>d`j3fIHm*XU=OO!fvPd2B>RW6n-NZRqy7@yO^`4qqCk z5Mri~+sQX6LID}yH~11RWGIdz%vns4ZqV96X^@4JlKA31{#OqId-Hu{DeO&S^rm|( z43~#^U^%YbST}dhJ~@MCV_bh!Tn2b_bLxHjLo^r;%c^RBdnCFcAKpUvZPF5~MC@U&~O%fUT2|K){!&RB1>qjWx&490!Uj{`<}jA>l=I zvm)7!@1DDR?y%3UM{5M`EnpdE;*W6V5pfCQzh3dLe>2j zA2ZJia^0KHw`nZJmX6X)3KaTa{D?e5wV&pwyN+`cGmbfwqX$pEKm0lKeej^w@x0C& zMI==xD754D7)6&EarHI+9ja4qcQ!RBYCzc9>FadX8WRQbhwc*_{ z@mUP6+57m$a-ElE?dsHws)imY{8LbdB>-Vp7Ap$zyKA>QXXiKu^Hb$lLFq)v4G7!- z^+~f7M{gl!rqOQe0>co>f=3{vh803A3lR|qEP|~pKotUBN+W7-j*2`21x(~R;QZyG zMP%-pwKs`13{?AVm0gp(UgP`<@tl1woouuBbez#y;~tTEnX->OkA!}IpV5WT1I`B4 za)D{a1C2&W)|!B{`=z9!#Amrraid_x@ghEkiG)@OSE#@=$L0i2Eh@Rm5fShVkt8}z zhmv58V32|CRLb(496jSQeVqEE$@oG-1eT_Oh^lLMGD!VK(n)noC+hrRXezQ!ty>mr z1Cj`@BUr_V5M>O92f=#Y-u{>K`KeQJT8r?GmUgN@^D6mxFL5n9Uqs!)A4`be_eK)DOe*x80O>f&U488kT5JigGLAq?$v`#j( z1GdZ1L)RYKqM&6u5t>PcM5hae{P)QpZDhDfihc~34u(aMkMEI`jwksn4}z4$91Vq* zjTC&#%baZdDLM)Qg}=z=CRc%w)CTeKLNNk?DygG0u7)sxI8Y2s_DSRj-fxSJRH&jnV^?8Bz%;w zzjG2?|3eH{H!kA~VSni70Ov(AmtrlHDnBpsQu6G3N{VGB$%J)g=*dS@G=hbeX@yqT zpjr0sxq&(iE0((ZDeh(@%spK zV^C9Z(~wZ64K>^0)4CNVbHDcI2F0Bd8a}PAMY0Zlh-lN?ND4D1vz61Lb925JdaT9< zVx>|PGYFqKR)E&;CB$Rs(fT~{psTK;-~#6-x={SOUbSIsvS=+C2U1YjI__j{lN@xM zn@6aNQm(yPKc$eaS;D;=bMOjrux}6G-c?6w27b^5$r6N4$z2o@MTsF5Fj@Yv*JV-B zUPE@yqn4!^m2$0uo&DVMX8o5PjpO(q_H=hY2apupJcrI&4S{~+CfDCp@E7e_ZByGu z5dQ98v1LfG6JtW#PY_cGxRhxMj3JZh5XL=AXZr?Oa )AHZDdoS`ymK>8wJN;12 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

5#;O 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 z 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 PBm7~ml{jFkzxsawq}0>L literal 0 HcmV?d00001 diff --git a/docs/images/html.png b/docs/images/html.png new file mode 100644 index 0000000000000000000000000000000000000000..bf4e57ef31ef32764eb0896c4cdb42b707d33a64 GIT binary patch literal 17244 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 aztvZNTvVg+#s~fvAuFjUQ7rb@|NjBfyfP&K literal 0 HcmV?d00001 diff --git a/docs/images/profile.png b/docs/images/profile.png new file mode 100644 index 0000000000000000000000000000000000000000..326fe93c1204f186c2ce07d3c3a48f8b72d7e7f7 GIT binary patch literal 67640 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 AXaE2J literal 0 HcmV?d00001 diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 000000000..fb4108426 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,37 @@ +Introduction +============ + +PHPBench is a benchmark runner for PHP analagous to `PHPUnit`_ but for +performance rather than correctness. + +Performance can be monitored and measured in a number of ways: profiling +(via. `Xdebug`_ or `Blackfire`_), manually instrumenting your code +(e.g. `Symfony Stopwatch`_) or with APM tools such as `NewRelic`_ or `Tideways`_. + +PHPBench compliments these tools (and in some cases can integrate with them) +allowing you to profile the `wall time`_ and memory usage of specific +scenarios and code units independently of the application context. + +Features include but are not limited to: + +- :ref:`Revolutions `: Repeat your code many times to determine average execution + *time*. +- :ref:`Iterations `: Sample your revolutions many times and review aggregated + statistical data. +- **Process Isolation**: Each iteration is executed in a separate process. +- :doc:`Reporting `: Customizable reports and various output formats (e.g. + console, CSV, Markdown, HTML). +- Report :doc:`storage` and :ref:`comparison `: Store benchmarks locally to be used as a + :ref:`baseline ` reference, or to reference them later. +- **Memory Usage**: Keep an eye on the amount of memory used by benchmarking + subjects. +- :doc:`Assertions `: Assert that code is performing within acceptable limits, or + that it has not regressed from a previously recorded :ref:`baseline `. + +.. _wall time: https://en.wikipedia.org/wiki/Elapsed_real_time +.. _Symfony Stopwatch: http://symfony.com/doc/current/components/stopwatch.html +.. _Xdebug: http://xdebug.org +.. _Blackfire: https://blackfire.io/ +.. _NewRelic: http://newrelic.com +.. _Tideways: https://tideways.com/ +.. _PHPUnit: http://phpunit.de diff --git a/docs/installing.rst b/docs/installing.rst new file mode 100644 index 000000000..563e5b056 --- /dev/null +++ b/docs/installing.rst @@ -0,0 +1,57 @@ +Installing +========== + +PHPBench can be installed either as dependency for your project or as a global +package. + +Composer Install +---------------- + +To install PHPBench as a dependency of your project: + +.. code-block:: php + + $ composer require phpbench/phpbench --dev + + +You may then run PHPBench from your project's directory as follows: + +.. code-block:: bash + + $ ./vendor/bin/phpbench + +Install as a PHAR package +------------------------- + +You can download the phar_ and the `public key`_, this can be done with CURL +as follows: + +.. code-block:: bash + + $ curl -o phpbench.phar https://phpbench.github.io/phpbench/phpbench.phar + $ curl -o phpbench.phar.pubkey https://phpbench.github.io/phpbench/phpbench.phar.pubkey + +You will probably then want make it executable and put it in your systems +global path, on Linux systems: + +.. code-block:: bash + + $ chmod 0755 phpbench.phar + $ sudo mv phpbench.phar /usr/local/bin/phpbench + $ sudo mv phpbench.phar.pubkey /usr/local/bin/phpbench.pubkey + +You can update the version at any time by using the ``self-update`` command: + +.. code-block:: bash + + $ phpbench self-update + +.. warning:: + + Installing as a PHAR means that you are always updating to the latest + version, the latest version may include BC breaks. Therefore it is + recommended to include the package as a project dependency for + continuous-integration. + +.. _phar: https://phpbench.github.io/phpbench/phpbench.phar +.. _public key: https://phpbench.github.io/phpbench/phpbench.phar.pubkey diff --git a/docs/quick-start.rst b/docs/quick-start.rst new file mode 100644 index 000000000..638f03f28 --- /dev/null +++ b/docs/quick-start.rst @@ -0,0 +1,288 @@ +Quick Start +=========== + +This tutorial will walk you through creating a simple project that +uses PHPBench as a dependency. + +Create your project +------------------- + +Create a directory for the tutorial: + +.. code-block:: bash + + $ mkdir phpbench-tutorial + $ cd phpbench-tutorial + +And create the following Composer_ file within it: + +.. code-block:: json + + { + "name": "acme/phpbench-test", + "require-dev": { + "phpbench/phpbench": "^1.0" + }, + "autoload": { + "psr-4": { + "Acme\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Acme\\Tests\\": "tests/" + } + }, + "minimum-stability": "dev", + "prefer-stable": true + } + +Now perform a Composer install: + +.. code-block:: bash + + $ composer install + +PHPBench should now be installed. Please create the following directories: + +.. code-block:: bash + + $ mkdir -p tests/Benchmark + $ mkdir src + +PHPBench configuration +---------------------- + +In order for PHPBench to be able to autoload files from your library, you +should specify the path to your bootstrap file (i.e. ``vendor/autoload.php``). +This can be done in the PHPBench :doc:`configuration `. + +Create a ``phpbench.json`` file in the projects root directory: + +.. code-block:: json + + { + "runner.bootstrap": "vendor/autoload.php" + } + +.. note:: + + PHPBench does not **require** a bootstrap (or a configuration file for + that matter). You may omit it if you do not need autoloading, or you want + to include files manually. + +.. warning:: + + Some PHP extensions such as Xdebug will affect the performance of your + benchmark subjects and you may want to disable them, see :ref:`Disabling + the PHP INI file `. + +Creating and running a benchmark +-------------------------------- + +You will need some code to benchmark, create a simple class in ``lib`` which +consumes *time itself*: + +.. code-block:: php + + namespace Acme; + + class TimeConsumer + { + public function consume() + { + usleep(100); + } + } + + +In order to benchmark your code you will need to execute that code within +a method of a benchmarking class. By default the class name **must** +have the ``Bench`` suffix and each benchmark method must be prefixed +with ``bench``. Create the following class: + +.. code-block:: php + + // tests/Benchmark/TimeConsumerBench.php + use Acme\TimeConsumer; + + class TimeConsumerBench + { + public function benchConsume() + { + $consumer = new TimeConsumer(); + $consumer->consume(); + } + } + +Now you can execute the benchmark as follows: + +.. code-block:: bash + + $ php vendor/bin/phpbench run tests/Benchmark/TimeConsumerBench.php --report=default + +And you should see some output similar to the following: + +.. code-block:: bash + + Running benchmarks. + + \TimeConsumerBench + + benchConsume I0 P0 [μ Mo]/r: 173.00μs [μSD μRSD]/r: 0.00μs 0.00% + + 1 subjects, 1 iterations, 1 revs, 0 rejects + ⅀T: 173μs μSD/r 0.00μs μRSD/r: 0.00% + min [mean mode] max: 173.00 [173.00 1732.00] 173.00 (μs/r) + + +-------------------+---------------+-------+--------+------+------+-----+----------+------------+---------+-------+ + | benchmark | subject | group | params | revs | iter | rej | mem | time | z-score | diff | + +-------------------+---------------+-------+--------+------+------+-----+----------+------------+---------+-------+ + | TimeConsumerBench | benchConsume | | [] | 1 | 0 | 0 | 265,936b | 173.0000μs | 0.00σ | 1.00x | + +-------------------+---------------+-------+--------+------+------+-----+----------+------------+---------+-------+ + +The code was only executed once (as indicated by the ``revs`` column). To +achieve a better measurement increase the revolutions: + +.. code-block:: php + + // ... + + class TimeConsumerBench + { + /** + * @Revs(1000) + */ + public function benchConsume() + { + // ... + } + } + +:ref:`Revolutions ` in PHPBench represent the number of times +that the code is executed consecutively within a single measurement. + +Currently we only execute the benchmark subject a single time, to build +confidence in the result increase the number of :ref:`iterations ` +using the ``@Iterations`` annotation: + +.. code-block:: php + + // ... + + class TimeConsumerBench + { + /** + * @Revs(1000) + * @Iterations(5) + */ + public function benchConsume() + { + // ... + } + } + +Now when you run the report you should see that it contains 5 rows. One +measurement for each iteration, and each iteration executed the code 1000 +times. + +.. note:: + + You can override the number of iterations and revolutions on the CLI using + the ``--iterations`` and ``--revs`` options. + +At this point it would be better for you to use the :ref:`aggregate ` +report rather than :ref:`default `: + +.. code-block:: bash + + $ php vendor/bin/phpbench run tests/Benchmark/TimeConsumerBench.php --report=aggregate + +Increase Stability +------------------ + +You will see the columns `stdev` and `rstdev`. `stdev` is the `standard deviation`_ +of the set of iterations and `rstdev` is `relative standard deviation`_. + +Stability can be inferred from `rstdev`, with 0% being the best and anything +about 2% should be treated as suspicious. + +To increase stability you can use the ``--retry-threshold`` to automatically +:ref:`repeat the iterations ` until the `diff` (the +percentage difference from the lowest measurement) fits within a given +threshold: + +.. note:: + + You can see the `diff` value for each iteration in the `default` report. + +.. code-block:: bash + + $ php vendor/bin/phpbench run tests/Benchmark/TimeConsumerBench.php --report=aggregate --retry-threshold=5 + +.. warning:: + + Depending on system stability, the lower the ``retry-threshold`` the + longer it will take to resolve a stable set of results. + +Customize Reports +----------------- + +PHPBench allows you to customize reports on the command line: + +.. code-block:: bash + + $ php vendor/bin/phpbench run tests/Benchmark/TimeConsumerBench.php --report='{"extends": "aggregate", "cols": ["subject", "mode"]}' + +Above we configure a new report which extends the :ref:`default +` report that we have already used, but we use only the +``subject`` and ``mode`` columns. A full list of all the options for the +default reports can be found in the :doc:`report-generators` chapter. + +Configuration +------------- + +To finish off, add the path and new report to the configuration file: + +.. code-block:: json + + { + "runner.path": "tests/Benchmark", + "report.generators": { + "consumation_of_time": { + "extends": "default", + "title": "The Consumation of Time", + "description": "Benchmark how long it takes to consume time", + "cols": [ "subject", "mode" ] + } + } + } + +Above you tell PHPBench where the benchmarks are located and you define a new +report, ``consumation_of_time``, with a title, description and sort order. + +We can now run the new report: + +.. code-block:: bash + + $ php vendor/bin/phpbench run --report=consumation_of_time + +.. note:: + + Note that we did not specify the path to the benchmark file, by default all + benchmarks under the given or configured path will be executed. + +Summary +------- + +In this tutorial you learnt to + +- :doc:`Configure ` PHPBench for a project +- Create a benchmarking class +- Use :ref:`revolutions ` and :ref:`iterations ` to more accurately profile your code +- Increase stability with the :ref:`retry threshold ` +- Use :doc:`reports ` + +.. _Composer: http://getcomposer.org +.. _relative standard deviation: https://en.wikipedia.org/wiki/Coefficient_of_variation +.. _standard deviation: https://en.wikipedia.org/wiki/Standard_deviation diff --git a/docs/reference.rst b/docs/reference.rst new file mode 100644 index 000000000..f6f014831 --- /dev/null +++ b/docs/reference.rst @@ -0,0 +1,15 @@ +Reference +========= + +.. toctree:: + :maxdepth: 2 + + assertions + annotributes + expression + configuration + environment + executors + report-generators + report-renderers + storage diff --git a/docs/regression-testing.rst b/docs/regression-testing.rst new file mode 100644 index 000000000..b7fd15193 --- /dev/null +++ b/docs/regression-testing.rst @@ -0,0 +1,65 @@ +.. _comparison: + +Regression Testing +================== + +Sometimes you need to ensure that modifications to existing code do not cause +performance regressions. + +PHPBench allows you to store results and use them as a baseline for subsequent +runs. + +.. _baseline: + +Creating a baseline +------------------- + +The baseline is a tagged benchmark result. When creating a baseline we should +try and ensure it is as accurate as possible, it is therefore recommended to +use find a low value of :ref:`retry_threshold` and use a appropriate amount of +iterations (e.g. 10): + +Run your benchmark on the code to which you want to compare: + +.. code-block:: bash + + $ phpbench run tests/Benchmark/MyBenchmark.php --tag=original --retry-threshold=5 --iterations=10 + +.. _ref: + +Compare against the baseline +---------------------------- + +Switch to the new version of your code and run PHPBench again: + +.. code-block:: bash + + $ phpbench run tests/Benchmark/MyBenchmark.php --report=aggregate --ref=original --retry-threshold=5 --iterations=10 + + +Note that: + +- ``--ref`` is used to reference the previous, tagged, run, which will merge + the previous benchmarks into the results. - ``--report=aggregate`` shows + the aggregate report, and if a baseline is present it will show differences. + +The final report should look something like: + +.. image:: images/baseline.png + +The ``mean``, ``mode`` and ``rstdev`` columns show the percentage difference +from the previous run. + +Assertions +---------- + +You can compare against baselines in assertions, e.g. + +.. code-block:: bash + + $ phpbench run tests/Benchmark/MyBenchmark.php --ref=original --retry-threshold=5 --iterations=10 --assert="variant.mode <= baseline.mode +/- 5%" + +We assert the current run's ``mode`` is less than or equal to the baseline +``mode`` and tolerate a variance within 5% of the baseline ``mode``. + +See :doc:`assertions` for more information. diff --git a/docs/report-generators.rst b/docs/report-generators.rst new file mode 100644 index 000000000..2356e74a5 --- /dev/null +++ b/docs/report-generators.rst @@ -0,0 +1,12 @@ +Report Generators +================= + +PHPBench reports are generated with report generators. These can subsequently +be rendered with :doc:`report renderers `. + +.. toctree:: + :maxdepth: 2 + + report-generators/expression + report-generators/composite + report-generators/env diff --git a/docs/report-generators/composite.rst b/docs/report-generators/composite.rst new file mode 100644 index 000000000..4d42617ac --- /dev/null +++ b/docs/report-generators/composite.rst @@ -0,0 +1,22 @@ +Composite +========= + +This report generates multiple reports. + +.. approved:: ../../examples/Command/report-generators-composite + :language: bash + :section: 0 + +---------- + + +.. approved:: ../../examples/Command/report-generators-composite + :language: bash + :section: 1 + +---------- + +.. approved:: ../../examples/Command/report-generators-composite + :language: bash + :section: 2 + diff --git a/docs/report-generators/env.rst b/docs/report-generators/env.rst new file mode 100644 index 000000000..15cd21d97 --- /dev/null +++ b/docs/report-generators/env.rst @@ -0,0 +1,14 @@ +.. _generator_env: + +Env +=== + +Generates a report showing the environmental metrics from when the benchmark +was executed. + +Class: ``PhpBench\Report\Generator\EnvGenerator``. + +Options: + +- **title**: *(string)* Title of the report. +- **description**: *(string)* Description of the report. diff --git a/docs/report-generators/expression.rst b/docs/report-generators/expression.rst new file mode 100644 index 000000000..c5d2fe97e --- /dev/null +++ b/docs/report-generators/expression.rst @@ -0,0 +1,120 @@ +.. _generator_expression: + +Expression Report +================= + +The expression generator is the generator that allows you to analyze your +benchmarking results. It uses the PHPBench :doc:`expression language +` to evaluate tabular data: + +.. approved:: ../../examples/Command/report-generators-expression + :language: javascript + :section: 1 + +Yields something like: + +.. approved:: ../../examples/Command/report-generators-expression + :language: bash + :section: 2 + +Options: + +- **title**: *(string)* Title of the report. +- **description**: *(string)* Description of the report. +- **cols**: *(array)* List of columns/expressions to show +- **expressions**: *(array)* Set of available expressions +- **baseline_expressions**: *(array)* Set of expressions that will be used + when a baseline is present +- **break**: *(array)* List of columns; break into multiple tables based on +- **aggregate**: *(array)* List of fields to aggregate on. +- **include_baseline**: *(bool)* Include the baseline rows + +.. _generator_expression_columns: + +Columns +------- + +The visible columns are dicated by the ``cols`` configuration: + +.. approved:: ../../examples/Command/report-generators-column-visibility + :language: javascript + :section: 0 + +When using the report: + +.. approved:: ../../examples/Command/report-generators-column-visibility + :language: shell + :section: 1 + +It will only show the selected columns: + +.. approved:: ../../examples/Command/report-generators-column-visibility + :language: bash + :section: 2 + +You can also override expressions by passing a map: + +.. approved:: ../../examples/Command/report-generators-column-override + :language: javascript + :section: 0 + +Which yields: + +.. approved:: ../../examples/Command/report-generators-column-override + :language: bash + :section: 2 + +.. _generator_expression_break: + +Break +----- + +You can partition the report into mupltiple tables by using the ``break`` option: + +.. approved:: ../../examples/Command/report-generators-break + :language: javascript + :section: 0 + +Now each benchmark class will get its own table: + +.. approved:: ../../examples/Command/report-generators-break + :language: bash + :section: 2 + +.. _generator_expression_expressions: + +Expressions +----------- + +The expressions define the available columns, you can add or override +expressions: + +.. approved:: ../../examples/Command/report-generators-expressions + :language: javascript + :section: 0 + +Which yields: + +.. approved:: ../../examples/Command/report-generators-expressions + :language: bash + :section: 2 + +Data +---- + +The expressions act on table data. You can get a list of all available columns +with: + +.. approved:: ../../examples/Command/report-generators-data + :language: bash + :section: 1 + +Yielding: + +.. approved:: ../../examples/Command/report-generators-data + :language: bash + :section: 2 + +Note that any additional result and :doc:`envronment <../environment>` data will +also be included in the form `result__` and +`env__`. diff --git a/docs/report-renderers.rst b/docs/report-renderers.rst new file mode 100644 index 000000000..c4412476f --- /dev/null +++ b/docs/report-renderers.rst @@ -0,0 +1,62 @@ +Report Renderers +================ + +Reports are rendered to an output medium using classes +implementing the ``PhpBench\Report\RendererInterface``. + +The configuration for a renderer is known here as an *output*. The user may +define new outputs either in the :doc:`configuration ` file or +on the CLI. The renderer may also supply default outputs. + +.. _renderer_console: + +``console`` +----------- + +Renders directly to the console. + +Class: ``PhpBench\Report\Renderer\ConsoleRenderer``. + +Options: + +- **table_style**: *(string)* Table style to use, one of: ``default``, + ``compact``, ``borderless`` or ``symfony-style-guide``. + +Default outputs: + +- ``console``: Renderers the report directly to the console. This is the + **default** output method. + +``delimited`` +------------- + +The delimited renderer outputs the report as a delimited value list (for +example a tab separated list of values). Such data can be easily imported into +applications such as GNUPlot_. + +Class: ``PhpBench\Report\Renderer\DelimitedRenderer``. + +Options: + +- **delimiter**: *(string)*: Path to the output file (existing files will be + overwritten). +- **header**: *(boolean)*: If a header should be included in the output. + +Default outputs: + +- ``delimiter``: The delimiter to use. + +``debug`` +--------- + +Output the raw XML of the report document. Useful for debugging. + +Options: + +**none** + +Default outputs: + +- ``debug``: Outputs the report document's XML. + +.. _GNUPlot: http://www.gnuplot.info/ diff --git a/docs/reports.rst b/docs/reports.rst new file mode 100644 index 000000000..eefebcf64 --- /dev/null +++ b/docs/reports.rst @@ -0,0 +1,132 @@ +Reports +======= + +PHPBench includes a reporting framework. :doc:`Report +generators ` provide report data which can subsequently be +rendered by :doc:`report renderer +`. + +This chapter will deal with generating reports and assume that the ``console`` +renderer is used. + +Generating Reports +------------------ + +To report after a benchmarking run: + +.. approved:: ../examples/Command/run-reports-aggregate + :language: bash + :section: 1 + +Multiple reports can be specified: + +.. approved:: ../examples/Command/run-reports-aggregate-and-env + :language: bash + :section: 1 + +The report command operates in a similar way but requires you to provide some +data, either from XML dumps or by using a :doc:`storage ` UUID or tag: + +.. approved:: ../examples/Command/report-ref-latest-and-aggregate + :language: bash + :section: 1 + +For more information on storage see :doc:`storage `. + +Configuring Reports +------------------- + +All reports can be configured either in the :ref:`report configuration +` or directly on the command line using a simplified +JSON encoded string instead of the report name: + +To configure a report in ``phpbench.json``: + +.. approved:: ../examples/Command/run-configuring-reports-phpbenchjson + :language: javascript + :section: 0 + +Then run it with: + +.. approved:: ../examples/Command/run-configuring-reports-phpbenchjson + :language: bash + :section: 1 + +You can also configure reports directly from the command line using simplified +JSON: + +.. approved:: ../examples/Command/run-configuring-reports + :language: bash + :section: 1 + +In each case it is required to specify the ``generator`` key which corresponds +to the registered name of the :doc:`report generator `. + +You may also **extend** an existing report configuration: + +.. approved:: ../examples/Command/run-configuring-reports-extend + :language: bash + :section: 1 + +This will merge the given keys onto the configuration for the `aggregate report`_. + +Default Reports +--------------- + +.. _report_aggregate: + +``aggregate`` +~~~~~~~~~~~~~ + +Shows aggregate details of each set of iterations: + +.. approved:: ../examples/Command/run-reports-aggregate + :language: bash + :section: 2 + +It is uses the ``table`` generator, see :ref:`generator_expression` for more information. + +.. _report_default: + +``default`` +~~~~~~~~~~~ + +The default report presents the result of *each iteration*: + +.. approved:: ../examples/Command/report-generators-composite + :language: javascript + :section: 0 + +It is uses the ``table`` generator, see :ref:`generator_expression` for more information. + +.. _report_env: + +``env`` +~~~~~~~ + +This report shows information about the environment that the benchmarks were +executed in. + +.. approved:: ../examples/Command/run-reports-env + :language: bash + :section: 2 + +Generator: :ref:`generator_env`. + +Columns: + +- **provider**: Name of the environment provider (see + ``PhpBench\Environment\Provider`` in the code for more information). +- **key**: Information key. +- **value**: Information value. + +See the :doc:`environment` chapter for more information. + +.. note:: + + The information available will differ depending on platform. For example, + ``unit-sysload`` is unsurprisingly only available on UNIX platforms, where + as the VCS field will appear only when a *supported* VCS system is being + used. + +.. _aggregate report: https://github.com/phpbench/phpbench/blob/master/lib/Extension/config/report/generators.php diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 000000000..29a6ed30d --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,5 @@ +docutils==0.13.1 +Pygments==2.7.4 +sphinx==1.8.5 +git+https://github.com/fabpot/sphinx-php.git@v2.0.0#egg_name=sphinx-php +sphinx-tabs==1.1.13 diff --git a/docs/storage.rst b/docs/storage.rst new file mode 100644 index 000000000..fbd0c2963 --- /dev/null +++ b/docs/storage.rst @@ -0,0 +1,127 @@ +Storage +======= + +PHPBench allows benchmarking results to be persisted using a configured +storage driver. You can inspect the results with either the ``show`` or +``report`` commands. + +XML Storage Driver +------------------ + +PHPBench will use XML storage by default (other storage drivers can be added +through extensions). + +The XML storage driver will place benchmarks in a folder called ``.phpbench`` +by default, this can be changed in the configuration as follows: + +.. code-block:: json + + { + "xml_storage_path": "path/to/my/folder" + } + +Storing Results +--------------- + +In order to store benchmarking runs you simply need to give ``--store`` option +when running your benchmarks: + +.. code-block:: bash + + $ phpbench run --store + +You can :ref:`tag ` _and_ store runs with the ``--tag`` option to make them easier to +reference: + +.. code-block:: bash + + $ phpbench run --tag=my_tag_name + +Tags must be alpha-numeric and may also contain underscores. + +Viewing the History +------------------- + +Once you have stored some benchmark runs you can use the history command to +see what you have got: + +.. code-block:: bash + + $ phpbench log + run 875c827946204db23eadd4b10e76b7189e10dde2 + Date: 2016-03-19T09:46:52+01:00 + Branch: git_log + Tag: + Scale: 1 subjects, 60 iterations, 120 revolutions + Summary: (best [mean] worst) = 433.467 [988.067] 504.600 (μs) + ⅀T: 59,284.000μs μRSD/r: 9.911% + + run 9d38a760e6ebec0a466c80f148264a7a4bb7a203 + Date: 2016-03-19T09:46:39+01:00 + Branch: git_log + Tag: + Scale: 1 subjects, 30 iterations, 30 revolutions + Summary: (best [mean] worst) = 461.800 [935.720] 503.300 (μs) + ⅀T: 28,071.600μs μRSD/r: 4.582% + + ... + +Report Generation +----------------- + +You can report on a single given run ID using the ``show`` command: + +.. code-block:: bash + + $ phpbench show 9d38a760e6ebec0a466c80f148264a7a4bb7a203 + +You may also specify a different report with the ``--report`` option. In order +to compare two or more reports, you should use the ``report`` command as +detailed in the following section. + +Tags +---- + +UUIDs are difficult to work with, but you can use tags + +``latest`` +~~~~~~~~~~ + +Latest is a special tag which always returns the latest benchmark. + +.. code-block:: bash + + $ phpbench show latest + +And also you may use the ``-`` suffix to view the "nth" entry in +the history from the latest: + +.. code-block:: bash + + $ phpbench show latest-1 + +Would show the second latest entry. Meta UUIDs can be used anywhere where you +would normally specify a UUID, including queries. + +.. _storage_tags: + +tags +~~~~ + +Reference a tagged run. If you store a suite: + +.. code-block:: bash + + $ phpbench run --tag=my_tag + +Then you can reference it with ``my_tag`` + +.. code-block:: bash + + $ phpbench show my_tag + +Or report on it: + +.. code-block:: bash + + $ phpbench report --ref=my_tag --report=aggregate diff --git a/docs/writing-benchmarks.rst b/docs/writing-benchmarks.rst new file mode 100644 index 000000000..3bb37ea21 --- /dev/null +++ b/docs/writing-benchmarks.rst @@ -0,0 +1,61 @@ +Writing Benchmarks +================== + +Benchmark classes have the following characteristics: + +- The class and filename must be the same. +- Class methods that start with ``bench`` will be executed by the benchrunner + and timed. + +PHPBench does not require that the benchmark class be aware of PHPBench +library - it does not need to extend a parent class or implement an interface. + +The following is a simple benchmark class: + +.. code-block:: php + + // HashBench.php + class HashBench + { + public function benchMd5() + { + hash('md5', 'Hello World!'); + } + + public function benchSha1() + { + hash('sha1', 'Hello World!'); + } + } + +And it can be executed as follows: + +.. code-block:: bash + + $ phpbench run examples/HashBench.php --progress=dots + Running benchmarks. + + ... + + 3 subjects, 30 iterations, 30000 revs, 0 rejects + ⅀T: 30543μs μSD/r 0.05μs μRSD/r: 4.83% + min mean max: 0.78 1.02 1.47 (μs/r) + +.. note:: + + The above command does not generate a report, add ``--report=default`` to + view something useful. + +PHPBench reads docblock annotations in the benchmark class. Annotations can be +placed in the class docblock, or on individual methods docblocks. + +.. note:: + + Instead of prefixing a method with ``bench`` you can use the + ``@Subject`` annotation or specify a :ref:`custom pattern `. + +Benchmark Configuration +----------------------- + +See the :doc:`Annotations and Attributes` reference to see how +you can configure your benchmarks. diff --git a/examples/Annotations/AnnotatedBench.php b/examples/Annotations/AnnotatedBench.php new file mode 100644 index 000000000..5d8172aab --- /dev/null +++ b/examples/Annotations/AnnotatedBench.php @@ -0,0 +1,174 @@ + [ 'string' => 'Hello World!' ]; + yield 'goodbye' => [ 'string' => 'Goodbye Cruel World!' ]; + } + +// endsection: paramProviders +// section: beforeMethods + public function setUp(): void + { + // do somrthing before the benchmark + } + +// endsection: beforeMethods + +// section: afterMethods + public function tearDown(): void + { + // do somrthing after the benchmark + } + +// endsection: afterMethods +// section: beforeClassMethods + public static function setUpBeforeClass(): void + { + // do somrthing before the benchmark + } + +// endsection: beforeClassMethods +// section: afterClassMethods + public static function tearDownAfterClass(): void + { + // do somrthing after the benchmark + } + +// endsection: afterClassMethods + +// section: paramIterable + /** + * @ParamProviders({"provideStringsAsArray"}) + */ + public function benchIterable(array $params): void + { + $helloThenGoodbye = $params['string']; + } + + public function provideStringsAsArray(): array + { + return [ + 'hello' => [ 'string' => 'Hello World!' ], + 'goodbye' => [ 'string' => 'Goodbye Cruel World!' ] + ]; + } + +// endsection: paramIterable + +// section: paramMultiple + /** + * @ParamProviders({"provideStrings", "provideNumbers"}) + */ + public function benchHash($params) + { + hash($params['algorithm'], $params['string']); + } + + public function provideStrings() + { + yield 'hello' => [ 'string' => 'Hello World!' ]; + yield 'goodbye' => [ 'string' => 'Goodbye Cruel World!' ]; + } + + public function provideNumbers() + { + yield 'md5' => [ 'algorithm' => 'md5' ]; + yield 'sha1' => [ 'algorithm' => 'sha1' ]; + } + +// endsection: paramMultiple +// section: all +} +// endsection: all diff --git a/examples/Assertion/ExampleAssertionsBench.php b/examples/Assertion/ExampleAssertionsBench.php new file mode 100644 index 000000000..51ba3e122 --- /dev/null +++ b/examples/Assertion/ExampleAssertionsBench.php @@ -0,0 +1,51 @@ + [ 'string' => 'Hello World!' ]; + yield 'goodbye' => [ 'string' => 'Goodbye Cruel World!' ]; + } + +// endsection: paramProviders +// section: beforeMethods + public function setUp(): void + { + // do somrthing before the benchmark + } + +// endsection: beforeMethods + +// section: afterMethods + public function tearDown(): void + { + // do somrthing after the benchmark + } + +// endsection: afterMethods +// section: beforeClassMethods + public static function setUpBeforeClass(): void + { + // do somrthing before the benchmark + } + +// endsection: beforeClassMethods +// section: afterClassMethods + public static function tearDownAfterClass(): void + { + // do somrthing after the benchmark + } + +// endsection: afterClassMethods + +// section: paramIterable + #[Bench\ParamProviders('provideStringsAsArray')] + public function benchIterable(array $params): void + { + $helloThenGoodbye = $params['string']; + } + + public function provideStringsAsArray(): array + { + return [ + 'hello' => [ 'string' => 'Hello World!' ], + 'goodbye' => [ 'string' => 'Goodbye Cruel World!' ] + ]; + } + +// endsection: paramIterable + +// section: paramMultiple + #[Bench\ParamProviders(['provideStrings', 'provideNumbers'])] + public function benchHash(array $params) + { + hash($params['algorithm'], $params['string']); + } + + public function provideStrings() + { + yield 'hello' => [ 'string' => 'Hello World!' ]; + yield 'goodbye' => [ 'string' => 'Goodbye Cruel World!' ]; + } + + public function provideNumbers() + { + yield 'md5' => [ 'algorithm' => 'md5' ]; + yield 'sha1' => [ 'algorithm' => 'sha1' ]; + } + +// endsection: paramMultiple +// section: all +} +// endsection: all diff --git a/examples/Benchmark/Macro/BaseBenchCase.php b/examples/Benchmark/Macro/BaseBenchCase.php new file mode 100644 index 000000000..98746033b --- /dev/null +++ b/examples/Benchmark/Macro/BaseBenchCase.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Examples\Benchmark\Macro; + +use PhpBench\DependencyInjection\Container; +use PhpBench\Extension\ConsoleExtension; +use PhpBench\Extension\CoreExtension; +use PhpBench\Extension\ExpressionExtension; +use PhpBench\Extension\ReportExtension; +use PhpBench\Extension\RunnerExtension; +use PhpBench\Extension\StorageExtension; +use PhpBench\Tests\Util\Workspace; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\BufferedOutput; + +/** + * Base class for PHPBench macro benchmarks. + * + * These benchmarks use seconds as the time unit. + * + * @OutputTimeUnit("seconds", precision=3) + * @Iterations(10) + * @Warmup(1) + * @BeforeClassMethods({"resetWorkspace"}) + * @AfterClassMethods({"removeWorkspace"}) + */ +class BaseBenchCase +{ + private $container; + + private $extensions = [ + CoreExtension::class, + RunnerExtension::class, + ReportExtension::class, + StorageExtension::class, + ExpressionExtension::class, + ConsoleExtension::class, + ]; + + private $config = []; + + /** + * The constructor can be used as a quick way to setup the + * class and as an alternative to explicitly declaring methods + * to execute before the benchmark. + */ + public function __construct() + { + $this->container = $this->getContainer(); + } + + protected static function workspace(): Workspace + { + return Workspace::create(__DIR__ . '/../../../tests/Workspace'); + } + + /** + * This method is called in a separate process before the iterations + * are executed. See the annotations in the header of this class. + */ + public static function resetWorkspace() + { + self::workspace()->reset(); + } + + /** + * This method is called in a separate process after the iterations + * are executed. See the annotations in the header of this class. + */ + public static function removeWorkspace() + { + // do something + } + + protected function getContainer() + { + $container = new Container($this->extensions, array_merge([ + ConsoleExtension::PARAM_DISABLE_OUTPUT => true, + ], $this->config)); + $container->init(); + + return $container; + } + + public function runCommand($serviceId, $args) + { + $input = new ArrayInput($args); + $output = new BufferedOutput(); + $command = $this->getContainer([ + CoreExtension::PARAM_WORKING_DIR => $this->workspace()->path() + ])->get($serviceId); + $exitCode = $command->run($input, $output); + + if ($exitCode !== 0) { + throw new \RuntimeException(sprintf( + 'Got non-zero exit code when executing command "%s"', + $serviceId + )); + } + + return $output; + } + + protected static function getFunctionalBenchmarkPath() + { + return __DIR__ . '/benchmarks'; + } + + protected function addContainerExtensionClass($extensionClass) + { + $this->extensions[] = $extensionClass; + } + + protected function setContainerConfig(array $containerConfig) + { + $this->config = $containerConfig; + } +} diff --git a/examples/Benchmark/Macro/LogBench.php b/examples/Benchmark/Macro/LogBench.php new file mode 100644 index 000000000..e82fa974d --- /dev/null +++ b/examples/Benchmark/Macro/LogBench.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Examples\Benchmark\Macro; + +use PhpBench\Console\Command\LogCommand; +use PhpBench\Console\Command\RunCommand; + +/** + * Benchmark for the log command. + * + * @BeforeClassMethods({"resetWorkspace", "createResults"}) + */ +class LogBench extends BaseBenchCase +{ + public static function createResults() + { + for ($i = 0; $i < 10; $i++) { + // instantiate the benchmark class (this) so that we can + // run a command. + $case = new self(); + $case->runCommand(RunCommand::class, [ + 'path' => self::getFunctionalBenchmarkPath(), + '--executor' => 'debug', + '--iterations' => [100], + '--store' => true, + '--progress' => 'none', + ]); + } + } + + public function benchLog() + { + $this->runCommand(LogCommand::class, [ + '--no-pagination' => true, + ]); + } +} diff --git a/examples/Benchmark/Macro/ReportBench.php b/examples/Benchmark/Macro/ReportBench.php new file mode 100644 index 000000000..9a537e5fe --- /dev/null +++ b/examples/Benchmark/Macro/ReportBench.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Examples\Benchmark\Macro; + +use PhpBench\Console\Command\ReportCommand; +use PhpBench\Console\Command\RunCommand; + +/** + * Benchmark for the report generation command. + * + * @BeforeClassMethods({"resetWorkspace", "generateDump"}) + */ +class ReportBench extends BaseBenchCase +{ + /** + * Generate an XML dump before benchmarking the report + * generation command. + */ + public static function generateDump() + { + // instantiate the benchmark class (this) so that we can + // run a command. + $case = new self(); + $case->runCommand(RunCommand::class, [ + 'path' => self::getFunctionalBenchmarkPath(), + '--dump-file' => self::workspace()->path('dump.xml'), + ]); + } + + public function benchAggregate() + { + $this->runCommand(ReportCommand::class, [ + '--file' => [$this->workspace()->path('/dump.xml')], + '--report' => ['aggregate'], + ]); + } + + public function benchDefault() + { + $this->runCommand(ReportCommand::class, [ + '--file' => [$this->workspace()->path('dump.xml')], + '--report' => ['default'], + ]); + } + + public function benchEnv() + { + $this->runCommand(ReportCommand::class, [ + '--file' => [$this->workspace()->path('dump.xml')], + '--report' => ['env'], + ]); + } +} diff --git a/examples/Benchmark/Macro/RunBench.php b/examples/Benchmark/Macro/RunBench.php new file mode 100644 index 000000000..0152e6a92 --- /dev/null +++ b/examples/Benchmark/Macro/RunBench.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Examples\Benchmark\Macro; + +use PhpBench\Console\Command\RunCommand; + +/** + * This benchmark executes the run command using the benchmark classes + * from the functional tests (which have "empty" subjects). + */ +class RunBench extends BaseBenchCase +{ + public function benchRun() + { + $this->runCommand(RunCommand::class, [ + 'path' => $this->getFunctionalBenchmarkPath(), + ]); + } + + public function benchRunAndReport() + { + $this->runCommand(RunCommand::class, [ + 'path' => $this->getFunctionalBenchmarkPath(), + '--report' => ['aggregate'], + ]); + } +} diff --git a/examples/Benchmark/Macro/benchmarks/NothingBench.php b/examples/Benchmark/Macro/benchmarks/NothingBench.php new file mode 100644 index 000000000..184d8ba6c --- /dev/null +++ b/examples/Benchmark/Macro/benchmarks/NothingBench.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Examples\Benchmark\Macro\benchmarks; + +class NothingBench +{ + public function benchNothing() + { + } +} diff --git a/examples/Benchmark/Micro/DependencyInjection/ContainerBench.php b/examples/Benchmark/Micro/DependencyInjection/ContainerBench.php new file mode 100644 index 000000000..06cd0425e --- /dev/null +++ b/examples/Benchmark/Micro/DependencyInjection/ContainerBench.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Examples\Benchmark\Micro\DependencyInjection; + +use PhpBench\DependencyInjection\Container; + +/** + * @Iterations(10) + * @Revs(10) + * @OutputTimeUnit("milliseconds", precision=6) + */ +class ContainerBench +{ + public function benchInitNoExtensions() + { + $container = new Container(); + $container->init(); + } + + public function benchInitCoreExtension() + { + $container = new Container([ + 'PhpBench\Extension\CoreExtension', + ]); + $container->init(); + } +} diff --git a/examples/Benchmark/Micro/HashingBench.php b/examples/Benchmark/Micro/HashingBench.php new file mode 100644 index 000000000..17634d39b --- /dev/null +++ b/examples/Benchmark/Micro/HashingBench.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Examples\Benchmark\Micro; + +function hash_algos() +{ + yield ['algo' => 'md5']; + + yield ['algo' => 'sha256']; +} + +/** + * @Revs(1000) + * @Iterations(10) + */ +class HashingBench +{ + /** + * @Assert("mode(variant.time.avg) < 1ms") + * @ParamProviders({"\PhpBench\Examples\Benchmark\Micro\hash_algos"}) + */ + public function benchAlgos($params) + { + return hash($params['algo'], 'Hello World'); + } +} diff --git a/examples/Benchmark/Micro/Math/KdeBench.php b/examples/Benchmark/Micro/Math/KdeBench.php new file mode 100644 index 000000000..2426f3bb1 --- /dev/null +++ b/examples/Benchmark/Micro/Math/KdeBench.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Examples\Benchmark\Micro\Math; + +use PhpBench\Math\Kde; +use PhpBench\Math\Statistics; + +/** + * @BeforeMethods({"generatePoints"}) + * @ParamProviders({"providePoints"}) + * @Revs(100) + * @Iterations(10) + * @OutputTimeUnit("milliseconds", precision=4) + */ +class KdeBench +{ + private $points = []; + + public function generatePoints($params) + { + $this->points = Statistics::linspace(1, 10, $params['points']); + } + + public function benchKde() + { + $kde = new Kde([ + 50, 40, 55, 52, 60, 55, 43, 45, 34, 22, + ]); + + $kde->evaluate($this->points); + } + + public function providePoints() + { + yield 'ten points' => [ 'points' => 10 ]; + + yield 'twenty points' => [ 'points' => 20 ]; + + yield 'forty points' => [ 'points' => 40 ]; + } +} diff --git a/examples/Benchmark/Micro/Math/StatisticsBench.php b/examples/Benchmark/Micro/Math/StatisticsBench.php new file mode 100644 index 000000000..7f0f9d891 --- /dev/null +++ b/examples/Benchmark/Micro/Math/StatisticsBench.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Examples\Benchmark\Micro\Math; + +use PhpBench\Math\Statistics; + +/** + * @Revs(100) + * @Iterations(10) + * @OutputTimeUnit("microseconds", precision=2) + * @BeforeMethods({"setUp"}) + */ +class StatisticsBench +{ + public function setUp(): void + { + Statistics::variance([]); + } + + public function benchVariance() + { + Statistics::variance([ + 10, 100, 42, 84, 11, 12, 9, 6, + ]); + } + + public function benchStDev() + { + Statistics::stdev([ + 10, 100, 42, 84, 11, 12, 9, 6, + ]); + } +} diff --git a/examples/Benchmark/NothingBench.php b/examples/Benchmark/NothingBench.php new file mode 100644 index 000000000..1c7027109 --- /dev/null +++ b/examples/Benchmark/NothingBench.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Examples\Benchmark; + +class NothingBench +{ + public function benchNothing(): void + { + } +} + diff --git a/examples/Command/report-generators-break b/examples/Command/report-generators-break new file mode 100644 index 000000000..023b28d42 --- /dev/null +++ b/examples/Command/report-generators-break @@ -0,0 +1,19 @@ +{ + "report.generators": { + "my-report": { + "generator": "expression", + "break": ["benchmark"], + "cols": ["benchmark","subject", "set", "revs", "its", "mem_peak", "mode", "rstdev"] + } + } +} +--- +phpbench run --report=my-report --executor=debug . --progress=none +--- +NothingBench ++--------------+-----+------+-----+----------+----------+--------+ +| subject | set | revs | its | mem_peak | mode | rstdev | ++--------------+-----+------+-----+----------+----------+--------+ +| benchNothing | 0 | 1 | 1 | 100b | 10.000μs | ±0.00% | ++--------------+-----+------+-----+----------+----------+--------+ + diff --git a/examples/Command/report-generators-column-override b/examples/Command/report-generators-column-override new file mode 100644 index 000000000..9b65b9cf4 --- /dev/null +++ b/examples/Command/report-generators-column-override @@ -0,0 +1,22 @@ +{ + "report.generators": { + "my-report": { + "generator": "expression", + "cols": { + "subject": null, + "mode": null, + "hello": "format(\"Hello World: %s\", \"Foobar\")" + } + } + } +} +--- +phpbench run --report=my-report --executor=debug NothingBench.php --progress=none +--- + ++--------------+----------+---------------------+ +| subject | mode | hello | ++--------------+----------+---------------------+ +| benchNothing | 10.000μs | Hello World: Foobar | ++--------------+----------+---------------------+ + diff --git a/examples/Command/report-generators-column-visibility b/examples/Command/report-generators-column-visibility new file mode 100644 index 000000000..f6cfbe96d --- /dev/null +++ b/examples/Command/report-generators-column-visibility @@ -0,0 +1,18 @@ +{ + "report.generators": { + "my-report": { + "generator": "expression", + "cols": ["subject", "mode"] + } + } +} +--- +phpbench run --report=my-report --executor=debug NothingBench.php --progress=none +--- + ++--------------+----------+ +| subject | mode | ++--------------+----------+ +| benchNothing | 10.000μs | ++--------------+----------+ + diff --git a/examples/Command/report-generators-composite b/examples/Command/report-generators-composite new file mode 100644 index 000000000..1578a8d17 --- /dev/null +++ b/examples/Command/report-generators-composite @@ -0,0 +1,36 @@ +{ + "runner.path": "NothingBench.php", + "runner.executor": "debug", + "runner.env_enabled_providers": ["test"], + "report.generators": { + "all": { + "generator": "composite", + "reports": [ "default", "aggregate", "env" ] + } + } +} +--- +phpbench run --report=all +--- + ++------+--------------+--------------+-----+------+----------+----------+--------------+----------------+ +| iter | benchmark | subject | set | revs | mem_peak | time_avg | comp_z_value | comp_deviation | ++------+--------------+--------------+-----+------+----------+----------+--------------+----------------+ +| 0 | benchNothing | benchNothing | 0 | 1 | 100b | 10.000μs | +0.00σ | +0.00% | ++------+--------------+--------------+-----+------+----------+----------+--------------+----------------+ + + ++--------------+--------------+-----+------+-----+----------+----------+--------+ +| benchmark | subject | set | revs | its | mem_peak | mode | rstdev | ++--------------+--------------+-----+------+-----+----------+----------+--------+ +| NothingBench | benchNothing | 0 | 1 | 1 | 100b | 10.000μs | ±0.00% | ++--------------+--------------+-----+------+-----+----------+----------+--------+ + +Suite E3X6AeMdP7L9E7E0X0A7McP1L8E1EdXbAbMbP7La xxxx-xx-xx xx-xx-xx ++----------+----------+-------+ +| provider | key | value | ++----------+----------+-------+ +| test | example1 | 1 | +| test | example2 | 2 | ++----------+----------+-------+ + diff --git a/examples/Command/report-generators-data b/examples/Command/report-generators-data new file mode 100644 index 000000000..adff7c830 --- /dev/null +++ b/examples/Command/report-generators-data @@ -0,0 +1,38 @@ +{ + "runner.env_enabled_providers": ["test"] +} +--- +phpbench run --report='extends:bare,vertical:true' --executor=debug NothingBench.php --progress=none +--- + ++------------------------+---------------+ +| field | value | ++------------------------+---------------+ +| has_baseline | false | +| benchmark_name | NothingBench | +| benchmark_class | \NothingBench | +| subject_name | benchNothing | +| subject_groups | [] | +| subject_time_unit | null | +| subject_time_precision | null | +| subject_time_mode | null | +| variant_name | 0 | +| variant_params | [] | +| variant_revs | 1 | +| variant_iterations | 1 | +| suite_tag | | +| suite_date | xxxx-xx-xx | +| suite_time | xx-xx-xx | +| iteration_index | 0 | +| env_test_example1 | 1 | +| env_test_example2 | 2 | +| result_mem_peak | 100 | +| result_mem_real | 100 | +| result_mem_final | 100 | +| result_time_net | 10 | +| result_time_revs | 1 | +| result_time_avg | 10 | +| result_comp_z_value | 0 | +| result_comp_deviation | 0 | ++------------------------+---------------+ + diff --git a/examples/Command/report-generators-expression b/examples/Command/report-generators-expression new file mode 100644 index 000000000..cf3f3ce98 --- /dev/null +++ b/examples/Command/report-generators-expression @@ -0,0 +1,12 @@ +{ +} +--- +phpbench run --report=expression --executor=debug NothingBench.php --progress=none +--- + ++-----------+--------------+--------------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ +| tag | benchmark | subject | set | revs | its | mem_peak | best | mode | mean | worst | stdev | rstdev | ++-----------+--------------+--------------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ +| | NothingBench | benchNothing | 0 | 1 | 1 | 100b | 10.000μs | 10.000μs | 10.000μs | 10.000μs | 0.000μs | ±0.00% | ++-----------+--------------+--------------+-----+------+-----+----------+----------+----------+----------+----------+---------+--------+ + diff --git a/examples/Command/report-generators-expressions b/examples/Command/report-generators-expressions new file mode 100644 index 000000000..8d28ff3d0 --- /dev/null +++ b/examples/Command/report-generators-expressions @@ -0,0 +1,21 @@ +{ + "report.generators": { + "my-report": { + "generator": "expression", + "expressions": { + "mode": "\"This is the mode: \" ~ mode(result_time_avg)" + }, + "cols": [ "benchmark", "subject", "mode" ] + } + } +} +--- +phpbench run --report=my-report --executor=debug NothingBench.php --progress=none +--- + ++--------------+--------------+----------------------+ +| benchmark | subject | mode | ++--------------+--------------+----------------------+ +| NothingBench | benchNothing | This is the mode: 10 | ++--------------+--------------+----------------------+ + diff --git a/examples/Command/report-ref-latest-and-aggregate b/examples/Command/report-ref-latest-and-aggregate new file mode 100644 index 000000000..8ef03e21f --- /dev/null +++ b/examples/Command/report-ref-latest-and-aggregate @@ -0,0 +1,15 @@ +{ + "runner.path": "NothingBench.php", + "runner.executor": "debug" +} +--- +phpbench run --store +phpbench report --ref=latest --report=aggregate +--- + ++--------------+--------------+-----+------+-----+----------+----------+--------+ +| benchmark | subject | set | revs | its | mem_peak | mode | rstdev | ++--------------+--------------+-----+------+-----+----------+----------+--------+ +| NothingBench | benchNothing | 0 | 1 | 1 | 100b | 10.000μs | ±0.00% | ++--------------+--------------+-----+------+-----+----------+----------+--------+ + diff --git a/examples/Command/run-configuring-reports b/examples/Command/run-configuring-reports new file mode 100644 index 000000000..0953ddcce --- /dev/null +++ b/examples/Command/run-configuring-reports @@ -0,0 +1,14 @@ +{ + "runner.path": "NothingBench.php", + "runner.executor": "debug" +} +--- +phpbench run --report='"generator": "expression", "cols": [ "benchmark", "tag", "subject", "mean" ], "break": ["benchmark"]' +--- +NothingBench ++-----------+--------------+----------+ +| tag | subject | mean | ++-----------+--------------+----------+ +| | benchNothing | 10.000μs | ++-----------+--------------+----------+ + diff --git a/examples/Command/run-configuring-reports-extend b/examples/Command/run-configuring-reports-extend new file mode 100644 index 000000000..623c71766 --- /dev/null +++ b/examples/Command/run-configuring-reports-extend @@ -0,0 +1,14 @@ +{ + "runner.path": "NothingBench.php", + "runner.executor": "debug" +} +--- +phpbench run --report='extends: aggregate, break: ["benchmark", "set"]' +--- +NothingBench-0 ++--------------+------+-----+----------+----------+--------+ +| subject | revs | its | mem_peak | mode | rstdev | ++--------------+------+-----+----------+----------+--------+ +| benchNothing | 1 | 1 | 100b | 10.000μs | ±0.00% | ++--------------+------+-----+----------+----------+--------+ + diff --git a/examples/Command/run-configuring-reports-phpbenchjson b/examples/Command/run-configuring-reports-phpbenchjson new file mode 100644 index 000000000..e37330eeb --- /dev/null +++ b/examples/Command/run-configuring-reports-phpbenchjson @@ -0,0 +1,21 @@ +{ + "runner.path": "NothingBench.php", + "runner.executor": "debug", + "report.generators": { + "my-report": { + "generator": "expression", + "cols": [ "benchmark", "tag", "subject", "mean" ], + "break": ["benchmark"] + } + } +} +--- +phpbench run --report=my-report +--- +NothingBench ++-----------+--------------+----------+ +| tag | subject | mean | ++-----------+--------------+----------+ +| | benchNothing | 10.000μs | ++-----------+--------------+----------+ + diff --git a/examples/Command/run-reports-aggregate b/examples/Command/run-reports-aggregate new file mode 100644 index 000000000..86bce5da1 --- /dev/null +++ b/examples/Command/run-reports-aggregate @@ -0,0 +1,14 @@ +{ + "runner.path": "NothingBench.php", + "runner.executor": "debug" +} +--- +phpbench run --report=aggregate +--- + ++--------------+--------------+-----+------+-----+----------+----------+--------+ +| benchmark | subject | set | revs | its | mem_peak | mode | rstdev | ++--------------+--------------+-----+------+-----+----------+----------+--------+ +| NothingBench | benchNothing | 0 | 1 | 1 | 100b | 10.000μs | ±0.00% | ++--------------+--------------+-----+------+-----+----------+----------+--------+ + diff --git a/examples/Command/run-reports-aggregate-and-env b/examples/Command/run-reports-aggregate-and-env new file mode 100644 index 000000000..5b870f219 --- /dev/null +++ b/examples/Command/run-reports-aggregate-and-env @@ -0,0 +1,16 @@ +{ + "runner.path": "NothingBench.php", + "runner.executor": "debug" +} +--- +phpbench run --report=aggregate --report=env +--- + ++--------------+--------------+-----+------+-----+----------+----------+--------+ +| benchmark | subject | set | revs | its | mem_peak | mode | rstdev | ++--------------+--------------+-----+------+-----+----------+----------+--------+ +| NothingBench | benchNothing | 0 | 1 | 1 | 100b | 10.000μs | ±0.00% | ++--------------+--------------+-----+------+-----+----------+----------+--------+ + +Suite E3X6AeMdP7L9E7E0X0A7McP1L8E1EdXbAbMbP7La xxxx-xx-xx xx-xx-xx + diff --git a/examples/Command/run-reports-default b/examples/Command/run-reports-default new file mode 100644 index 000000000..b6f8b35fb --- /dev/null +++ b/examples/Command/run-reports-default @@ -0,0 +1,17 @@ +{ + "runner.path": "NothingBench.php", + "runner.executor": "debug" +} +--- +phpbench run --report=default --iterations=4 +--- + ++------+--------------+--------------+-----+------+----------+----------+--------------+----------------+ +| iter | benchmark | subject | set | revs | mem_peak | time_avg | comp_z_value | comp_deviation | ++------+--------------+--------------+-----+------+----------+----------+--------------+----------------+ +| 0 | benchNothing | benchNothing | 0 | 1 | 100b | 10.000μs | +0.00σ | +0.00% | +| 1 | benchNothing | benchNothing | 0 | 1 | 100b | 10.000μs | +0.00σ | +0.00% | +| 2 | benchNothing | benchNothing | 0 | 1 | 100b | 10.000μs | +0.00σ | +0.00% | +| 3 | benchNothing | benchNothing | 0 | 1 | 100b | 10.000μs | +0.00σ | +0.00% | ++------+--------------+--------------+-----+------+----------+----------+--------------+----------------+ + diff --git a/examples/Command/run-reports-env b/examples/Command/run-reports-env new file mode 100644 index 000000000..384c6e2c2 --- /dev/null +++ b/examples/Command/run-reports-env @@ -0,0 +1,16 @@ +{ + "runner.path": "NothingBench.php", + "runner.executor": "debug", + "runner.env_enabled_providers": ["test"] +} +--- +phpbench run --report=env +--- +Suite E3X6AeMdP7L9E7E0X0A7McP1L8E1EdXbAbMbP7La xxxx-xx-xx xx-xx-xx ++----------+----------+-------+ +| provider | key | value | ++----------+----------+-------+ +| test | example1 | 1 | +| test | example2 | 2 | ++----------+----------+-------+ + diff --git a/examples/Expression/arithmetic_1 b/examples/Expression/arithmetic_1 new file mode 100644 index 000000000..3a02fa391 --- /dev/null +++ b/examples/Expression/arithmetic_1 @@ -0,0 +1,4 @@ +1 + 1 = 2 +2 + 3 * 4 + 5 = 19 +2 + (3 * 4) + 5 = 19 +(2 + 3) * 4 + 5 = 25 diff --git a/examples/Expression/coalesce_1 b/examples/Expression/coalesce_1 new file mode 100644 index 000000000..61c08988f --- /dev/null +++ b/examples/Expression/coalesce_1 @@ -0,0 +1 @@ +coalesce(null, null, 10) = 10 diff --git a/examples/Expression/comparison_1 b/examples/Expression/comparison_1 new file mode 100644 index 000000000..341fc7be4 --- /dev/null +++ b/examples/Expression/comparison_1 @@ -0,0 +1 @@ +10 microseconds < 20 microseconds diff --git a/examples/Expression/display_as_time_1 b/examples/Expression/display_as_time_1 new file mode 100644 index 000000000..2f525f7c1 --- /dev/null +++ b/examples/Expression/display_as_time_1 @@ -0,0 +1 @@ +display_as_time(100000, "seconds", 2, "throughput") = 100000 diff --git a/examples/Expression/first_1 b/examples/Expression/first_1 new file mode 100644 index 000000000..84dd7b1b5 --- /dev/null +++ b/examples/Expression/first_1 @@ -0,0 +1 @@ +first(["one", "two"]) = "one" diff --git a/examples/Expression/func_format b/examples/Expression/func_format new file mode 100644 index 000000000..c0249bba4 --- /dev/null +++ b/examples/Expression/func_format @@ -0,0 +1,2 @@ +format("hello %s", "daniel") = "hello daniel" +format("%2.4f%%", rstdev([1,2])) = "33.3333%" diff --git a/examples/Expression/func_max b/examples/Expression/func_max new file mode 100644 index 000000000..1de359e24 --- /dev/null +++ b/examples/Expression/func_max @@ -0,0 +1 @@ +max([10, 12, 4, 20]) = 20 diff --git a/examples/Expression/func_mean b/examples/Expression/func_mean new file mode 100644 index 000000000..f22055b9a --- /dev/null +++ b/examples/Expression/func_mean @@ -0,0 +1 @@ +mean([1, 2, 1, 2]) = 1.5 diff --git a/examples/Expression/func_min b/examples/Expression/func_min new file mode 100644 index 000000000..775e1b528 --- /dev/null +++ b/examples/Expression/func_min @@ -0,0 +1 @@ +min([10, 12, 4, 20]) = 4 diff --git a/examples/Expression/func_mode b/examples/Expression/func_mode new file mode 100644 index 000000000..5efda08a6 --- /dev/null +++ b/examples/Expression/func_mode @@ -0,0 +1 @@ +mode([1, 2, 1, 2]) = 1.5 +/- 0.00000000001 diff --git a/examples/Expression/func_percent_diff b/examples/Expression/func_percent_diff new file mode 100644 index 000000000..c4459e6ad --- /dev/null +++ b/examples/Expression/func_percent_diff @@ -0,0 +1 @@ +percent_diff(1, 2) = 100 diff --git a/examples/Expression/func_rstdev b/examples/Expression/func_rstdev new file mode 100644 index 000000000..0eb645a25 --- /dev/null +++ b/examples/Expression/func_rstdev @@ -0,0 +1,3 @@ +rstdev([1,2]) = 33.3333 +/- 0.0001 +rstdev([1,2], true) = 47.1405 +/- 0.0001 +format("%2.4f%%", rstdev([1,2])) = "33.3333%" diff --git a/examples/Expression/func_stdev b/examples/Expression/func_stdev new file mode 100644 index 000000000..9762f8e35 --- /dev/null +++ b/examples/Expression/func_stdev @@ -0,0 +1 @@ +stdev([1,2]) = 0.5 diff --git a/examples/Expression/func_variance b/examples/Expression/func_variance new file mode 100644 index 000000000..44af4139e --- /dev/null +++ b/examples/Expression/func_variance @@ -0,0 +1 @@ +variance([1,2]) = 0.25 diff --git a/examples/Expression/join_1 b/examples/Expression/join_1 new file mode 100644 index 000000000..9585b526d --- /dev/null +++ b/examples/Expression/join_1 @@ -0,0 +1 @@ +join(",", ["one", "two"]) = "one,two" diff --git a/examples/Expression/logical_operators_1 b/examples/Expression/logical_operators_1 new file mode 100644 index 000000000..25c8b0543 --- /dev/null +++ b/examples/Expression/logical_operators_1 @@ -0,0 +1,2 @@ +1 < 2 and 3 < 4 +(1 < 2 and 3 < 4) or true diff --git a/examples/Expression/memory_unit_1 b/examples/Expression/memory_unit_1 new file mode 100644 index 000000000..1a88d8ce2 --- /dev/null +++ b/examples/Expression/memory_unit_1 @@ -0,0 +1,2 @@ +1 megabytes < 2 megabytes +1 bytes + 2 megabytes diff --git a/examples/Expression/string_operators_1 b/examples/Expression/string_operators_1 new file mode 100644 index 000000000..4e0163832 --- /dev/null +++ b/examples/Expression/string_operators_1 @@ -0,0 +1 @@ +"mode: " ~ mode([12, 12, 12]) ~ " \o/" diff --git a/examples/Expression/time_unit_1 b/examples/Expression/time_unit_1 new file mode 100644 index 000000000..a5c405894 --- /dev/null +++ b/examples/Expression/time_unit_1 @@ -0,0 +1,3 @@ +20 milliseconds +20 seconds < 20 days +360 days = (3600 seconds * 24) * 360 diff --git a/examples/Expression/time_unit_2 b/examples/Expression/time_unit_2 new file mode 100644 index 000000000..8a5ac1912 --- /dev/null +++ b/examples/Expression/time_unit_2 @@ -0,0 +1 @@ +1000000 as seconds = 1 second diff --git a/examples/Expression/time_unit_3 b/examples/Expression/time_unit_3 new file mode 100644 index 000000000..71819369a --- /dev/null +++ b/examples/Expression/time_unit_3 @@ -0,0 +1 @@ +1000000 as seconds precision 4 = 1 second diff --git a/examples/Expression/tolerance_1 b/examples/Expression/tolerance_1 new file mode 100644 index 000000000..230594850 --- /dev/null +++ b/examples/Expression/tolerance_1 @@ -0,0 +1 @@ +9 milliseconds <= 10 milliseconds +/- 10% diff --git a/examples/Expression/tolerance_2 b/examples/Expression/tolerance_2 new file mode 100644 index 000000000..0bcf678a8 --- /dev/null +++ b/examples/Expression/tolerance_2 @@ -0,0 +1 @@ +9 milliseconds <= 10 milliseconds +/- 2 milliseconds diff --git a/examples/Extension/AcmeExtension.php b/examples/Extension/AcmeExtension.php new file mode 100644 index 000000000..94b5bf353 --- /dev/null +++ b/examples/Extension/AcmeExtension.php @@ -0,0 +1,84 @@ +setDefaults([ + self::PARAM_NUMBER_OF_CATS => 7 + ]); + // endsection: command_di + // section: all + } + + // endsection: all + // section: all + public function load(Container $container): void + { + // endsection: all + // section: command_di + $container->register(CatsCommand::class, function (Container $container) { + return new CatsCommand($container->getParameter(self::PARAM_NUMBER_OF_CATS)); + }, [ + ConsoleExtension::TAG_CONSOLE_COMMAND => [] + ]); + // endsection: command_di + + // section: progress_logger_di + $container->register(CatLogger::class, function (Container $container) { + return new CatLogger( + $container->get(ConsoleExtension::SERVICE_OUTPUT_ERR) + ); + }, [ + RunnerExtension::TAG_PROGRESS_LOGGER => [ + 'name' => 'cats', + ] + ]); + // endsection: progress_logger_di + + // section: report_generator_di + $container->register(AcmeGenerator::class, function (Container $container) { + return new AcmeGenerator(); + }, [ + ReportExtension::TAG_REPORT_GENERATOR => [ + 'name' => 'catordog', + ] + ]); + // endsection: report_generator_di + + // section: executor_di + $container->register(AcmeExecutor::class, function (Container $container) { + return new AcmeExecutor(); + }, [ + RunnerExtension::TAG_EXECUTOR => [ + 'name' => 'acme', + ] + ]); + // endsection: executor_di + // section: all + } +} +// endsection: all diff --git a/examples/Extension/Command/CatsCommand.php b/examples/Extension/Command/CatsCommand.php new file mode 100644 index 000000000..25fc1893d --- /dev/null +++ b/examples/Extension/Command/CatsCommand.php @@ -0,0 +1,36 @@ +numberOfCats = $numberOfCats; + parent::__construct(); + } + + protected function configure() + { + $this->setName('cats'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $output->writeln(str_repeat('🐈', $this->numberOfCats)); + + return 0; + } +} + +// endsection: command diff --git a/examples/Extension/Executor/AcmeExecutor.php b/examples/Extension/Executor/AcmeExecutor.php new file mode 100644 index 000000000..b2863fdf8 --- /dev/null +++ b/examples/Extension/Executor/AcmeExecutor.php @@ -0,0 +1,29 @@ +setDefaults([ + self::CONFIG_MICROSECONDS => 5 + ]); + } + + public function execute(ExecutionContext $context, Config $config): ExecutionResults + { + return ExecutionResults::fromResults( + new TimeResult($config[self::CONFIG_MICROSECONDS]) + ); + } +} diff --git a/examples/Extension/ProgressLogger/CatLogger.php b/examples/Extension/ProgressLogger/CatLogger.php new file mode 100644 index 000000000..afebb48ae --- /dev/null +++ b/examples/Extension/ProgressLogger/CatLogger.php @@ -0,0 +1,69 @@ +output = $output; + } + + public function startSuite(Suite $suite) + { + } + + public function endSuite(Suite $suite) + { + } + + public function benchmarkStart(Benchmark $benchmark) + { + } + + public function benchmarkEnd(Benchmark $benchmark) + { + } + + public function subjectStart(Subject $subject) + { + } + + public function subjectEnd(Subject $subject) + { + } + + public function variantStart(Variant $variant) + { + } + + public function variantEnd(Variant $variant) + { + } + + public function iterationStart(Iteration $iteration) + { + $this->output->write('🐈'); + } + + public function iterationEnd(Iteration $iteration) + { + } + + public function retryStart(int $rejectionCount) + { + } +} diff --git a/examples/Extension/Report/AcmeGenerator.php b/examples/Extension/Report/AcmeGenerator.php new file mode 100644 index 000000000..4f8b518a2 --- /dev/null +++ b/examples/Extension/Report/AcmeGenerator.php @@ -0,0 +1,48 @@ +setDefaults([ + 'title' => 'Cats report', + 'description' => 'Are cats really cats or are they dogs?', + ]); + } + + public function generate(SuiteCollection $suiteCollection, Config $config): Reports + { + $rows = []; + + foreach ([ + [ '🐈', 'Yes' ], + [ '🐕', 'No' ], + ] as [$symbol, $isCat]) { + $rows[] = [ + 'symbol' => new StringNode($symbol), + 'is_cat' => new StringNode($isCat), + ]; + } + + return Reports::fromReport( + Report::fromTables( + [ + Table::fromRowArray($rows, 'Cat or dog?'), + ], + $config['title'], + $config['description'] + ) + ); + } +} diff --git a/examples/OutputTest/AssertionTestBench.php b/examples/OutputTest/AssertionTestBench.php new file mode 100644 index 000000000..f0479de14 --- /dev/null +++ b/examples/OutputTest/AssertionTestBench.php @@ -0,0 +1,14 @@ + 2 ms") + */ + public function benchFoo(): void + { + usleep(1); + } +} diff --git a/examples/OutputTest/OutputTestBench.php b/examples/OutputTest/OutputTestBench.php new file mode 100644 index 000000000..d9a6b3476 --- /dev/null +++ b/examples/OutputTest/OutputTestBench.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Extensions\XDebug\Command\Handler; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Filesystem\Filesystem; +use Webmozart\PathUtil\Path; + +class OutputDirHandler +{ + /** + * @var string + */ + private $outputDir; + + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var string + */ + private $cwd; + + public function __construct(string $outputDir, string $cwd, Filesystem $filesystem = null) + { + $this->outputDir = $outputDir; + $this->filesystem = $filesystem ?: new Filesystem(); + $this->cwd = $cwd; + } + + public static function configure(Command $command): void + { + $command->addOption('outdir', null, InputOption::VALUE_REQUIRED, 'Output directory'); + } + + public function handleOutputDir(InputInterface $input, OutputInterface $output): string + { + $outputDir = Path::makeAbsolute($input->getOption('outdir') ?: $this->outputDir, $this->cwd); + + if (!$this->filesystem->exists($outputDir)) { + $output->writeln(sprintf( + '// creating non-existing directory %s', + $outputDir + )); + $this->filesystem->mkdir($outputDir); + } + + return $outputDir; + } +} diff --git a/extensions/xdebug/lib/Command/ProfileCommand.php b/extensions/xdebug/lib/Command/ProfileCommand.php new file mode 100644 index 000000000..daee1dcf2 --- /dev/null +++ b/extensions/xdebug/lib/Command/ProfileCommand.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Extensions\XDebug\Command; + +use PhpBench\Benchmark\RunnerConfig; +use PhpBench\Console\Command\Handler\RunnerHandler; +use PhpBench\Extensions\XDebug\Command\Handler\OutputDirHandler; +use PhpBench\Extensions\XDebug\XDebugUtil; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Process\ExecutableFinder; +use Symfony\Component\Process\Process; + +class ProfileCommand extends Command +{ + private $runnerHandler; + private $filesystem; + private $outputDirHandler; + + public function __construct( + RunnerHandler $runnerHandler, + OutputDirHandler $outputDirHandler, + Filesystem $filesystem = null + ) { + parent::__construct(); + $this->runnerHandler = $runnerHandler; + $this->filesystem = $filesystem ?: new Filesystem(); + $this->outputDirHandler = $outputDirHandler; + } + + public function configure(): void + { + $this->setName('xdebug:profile'); + $this->setDescription(<<<'EOT' +Generate and optionally visualize profiles with Xdebug +EOT + ); + RunnerHandler::configure($this); + OutputDirHandler::configure($this); + $this->addOption('gui', null, InputOption::VALUE_NONE); + $this->addOption('gui-bin', null, InputOption::VALUE_REQUIRED, 'Bin to use to display cachegrind output', 'kcachegrind'); + } + + public function execute(InputInterface $input, OutputInterface $output) + { + $outputDir = $this->outputDirHandler->handleOutputDir($input, $output); + $guiBin = null; + + if ($input->getOption('gui')) { + $finder = new ExecutableFinder(); + $guiBin = $finder->find($input->getOption('gui-bin')); + + if (null === $guiBin) { + throw new \InvalidArgumentException(sprintf( + 'Could not locate GUI bin "%s"', $input->getOption('gui-bin') + )); + } + } + + $generatedFiles = []; + $config = RunnerConfig::create() + ->withExecutor([ + 'executor' => 'xdebug_profile', + 'output_dir' => $outputDir, + 'callback' => function ($iteration) use ($outputDir, $guiBin, &$generatedFiles): void { + $generatedFiles[] = $generatedFile = $outputDir . DIRECTORY_SEPARATOR . XDebugUtil::filenameFromContext($iteration, '.cachegrind'); + + if ($guiBin) { + $process = Process::fromShellCommandline(sprintf( + $guiBin . ' ' . $generatedFile + )); + $process->run(); + } + }, + ]) + ->withIterations([1]); + $this->runnerHandler->runFromInput($input, $output, $config); + + $output->write(PHP_EOL); + $output->writeln(sprintf( + '%s profile(s) generated:', + count($generatedFiles) + )); + $output->write(PHP_EOL); + + foreach ($generatedFiles as $generatedFile) { + if (!file_exists($generatedFile)) { + throw new \InvalidArgumentException(sprintf( + 'Profile "%s" was not generated. Perhaps you do not have Xdebug installed or the extension is not enabled for your benchmark?', + $generatedFile + )); + } + + $output->writeln(sprintf(' %s', $generatedFile)); + } + + return 0; + } +} diff --git a/extensions/xdebug/lib/Executor/ProfileExecutor.php b/extensions/xdebug/lib/Executor/ProfileExecutor.php new file mode 100644 index 000000000..c02f36203 --- /dev/null +++ b/extensions/xdebug/lib/Executor/ProfileExecutor.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Extensions\XDebug\Executor; + +use PhpBench\Executor\Benchmark\TemplateExecutor; +use PhpBench\Executor\BenchmarkExecutorInterface; +use PhpBench\Executor\ExecutionContext; +use PhpBench\Executor\ExecutionResults; +use PhpBench\Extensions\XDebug\XDebugUtil; +use PhpBench\Registry\Config; +use RuntimeException; +use Symfony\Component\OptionsResolver\OptionsResolver; +use Webmozart\PathUtil\Path; + +class ProfileExecutor implements BenchmarkExecutorInterface +{ + /** + * @var TemplateExecutor + */ + private $innerExecutor; + + /** + * @var string + */ + private $cwd; + + public function __construct(TemplateExecutor $innerExecutor, string $cwd) + { + $this->innerExecutor = $innerExecutor; + $this->cwd = $cwd; + } + + /** + * {@inheritdoc} + */ + public function configure(OptionsResolver $options): void + { + $this->innerExecutor->configure($options); + + $options->setDefaults([ + 'callback' => function (): void { + }, + 'output_dir' => 'xdebug', + ]); + } + + public function execute(ExecutionContext $context, Config $config): ExecutionResults + { + $outputDir = $config['output_dir']; + $callback = $config['callback']; + $name = XDebugUtil::filenameFromContext($context, '.cachegrind'); + + $config[TemplateExecutor::OPTION_PHP_CONFIG] = $this->resolveXdebugIniSettings($outputDir, $name); + $results = $this->innerExecutor->execute($context, $config); + + $callback($context); + + return $results; + } + + /** + * @return array + */ + private function resolveXdebugIniSettings(string $outputDir, string $name): array + { + $xdebugVersion = phpversion('xdebug'); + + if (false === $xdebugVersion) { + throw new RuntimeException( + 'Xdebug is not installed' + ); + } + + $outputPath = Path::makeAbsolute($outputDir, $this->cwd); + + if (substr($xdebugVersion, 0, 1) === '3') { + return [ + 'xdebug.mode' => 'profile', + 'xdebug.output_dir' => $outputPath, + 'xdebug.profiler_output_name' => $name, + ]; + } + + return [ + 'xdebug.profiler_enable' => 1, + 'xdebug.profiler_output_dir' => $outputPath, + 'xdebug.profiler_output_name' => $name, + ]; + } +} diff --git a/extensions/xdebug/lib/XDebugExtension.php b/extensions/xdebug/lib/XDebugExtension.php new file mode 100644 index 000000000..84abced3f --- /dev/null +++ b/extensions/xdebug/lib/XDebugExtension.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Extensions\XDebug; + +use PhpBench\Compat\SymfonyOptionsResolverCompat; +use PhpBench\Console\Command\Handler\RunnerHandler; +use PhpBench\DependencyInjection\Container; +use PhpBench\DependencyInjection\ExtensionInterface; +use PhpBench\Executor\Benchmark\RemoteExecutor; +use PhpBench\Executor\CompositeExecutor; +use PhpBench\Executor\Method\RemoteMethodExecutor; +use PhpBench\Extension\ConsoleExtension; +use PhpBench\Extension\CoreExtension; +use PhpBench\Extension\RunnerExtension; +use PhpBench\Extensions\XDebug\Command\Handler\OutputDirHandler; +use PhpBench\Extensions\XDebug\Command\ProfileCommand; +use PhpBench\Extensions\XDebug\Executor\ProfileExecutor; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class XDebugExtension implements ExtensionInterface +{ + const PARAM_OUTPUT_DIR = 'xdebug.command_handler_output_dir'; + + public function configure(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + self::PARAM_OUTPUT_DIR => '.phpbench/xdebug-profile', + ]); + $resolver->setAllowedTypes(self::PARAM_OUTPUT_DIR, ['string']); + SymfonyOptionsResolverCompat::setInfos($resolver, [ + self::PARAM_OUTPUT_DIR => 'Output directory for generated XDebug profiles', + ]); + } + + public function load(Container $container): void + { + $container->register(ProfileCommand::class, function (Container $container) { + return new ProfileCommand( + $container->get(RunnerHandler::class), + $container->get(self::PARAM_OUTPUT_DIR) + ); + }, [ + ConsoleExtension::TAG_CONSOLE_COMMAND => [] + ]); + + $container->register(self::PARAM_OUTPUT_DIR, function (Container $container) { + return new OutputDirHandler( + $container->getParameter(self::PARAM_OUTPUT_DIR), + $container->getParameter(CoreExtension::PARAM_WORKING_DIR) + ); + }); + + $container->register(ProfileExecutor::class, function (Container $container) { + return new CompositeExecutor( + new ProfileExecutor( + $container->get(RemoteExecutor::class), + $container->getParameter(CoreExtension::PARAM_WORKING_DIR) + ), + $container->get(RemoteMethodExecutor::class) + ); + }, [ + RunnerExtension::TAG_EXECUTOR => [ + 'name' => 'xdebug_profile' + ] + ]); + + $container->mergeParameter(RunnerExtension::PARAM_EXECUTORS, require(__DIR__ . '/config/executors.php')); + } +} diff --git a/extensions/xdebug/lib/XDebugUtil.php b/extensions/xdebug/lib/XDebugUtil.php new file mode 100644 index 000000000..db952a2be --- /dev/null +++ b/extensions/xdebug/lib/XDebugUtil.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Extensions\XDebug; + +use PhpBench\Executor\ExecutionContext; + +class XDebugUtil +{ + public static function filenameFromContext(ExecutionContext $context, $extension = ''): string + { + $name = sprintf( + '%s%s%s', + $context->getClassName(), + $context->getMethodName(), + $context->getParameterSetName() + ); + + + return md5($name) . $extension; + } +} diff --git a/extensions/xdebug/lib/config/executors.php b/extensions/xdebug/lib/config/executors.php new file mode 100644 index 000000000..00b49ee68 --- /dev/null +++ b/extensions/xdebug/lib/config/executors.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +return [ + 'xdebug_profile' => [ + 'executor' => 'xdebug_profile', + ], +]; diff --git a/extensions/xdebug/tests/System/ProfileCommandTest.php b/extensions/xdebug/tests/System/ProfileCommandTest.php new file mode 100644 index 000000000..0b2237278 --- /dev/null +++ b/extensions/xdebug/tests/System/ProfileCommandTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Extensions\XDebug\Tests\System; + +class ProfileCommandTest extends XDebugTestCase +{ + /** + * It should run when given a path. + * It should show the default (simple) report. + */ + public function testCommand(): void + { + $process = $this->phpbench('xdebug:profile benchmarks/set1/BenchmarkBench.php --filter=benchDoNothing -vvv'); + $this->assertExitCode(0, $process); + $this->assertStringContainsString('profile(s) generated', $process->getErrorOutput()); + $lines = explode("\n", $process->getErrorOutput()); + + // get the filename from the output and check it exists. + $line = trim($lines[count($lines) - 2]); + $this->assertFileExists($line); + } + + /** + * It die if an unknown gui-bin is specified. + */ + public function testCommandBadGui(): void + { + $process = $this->phpbench('xdebug:profile benchmarks/set1/BenchmarkBench.php --gui --gui-bin=idontexistdotcom1234'); + $this->assertStringContainsString( + 'Could not locate GUI bin "idontexistdotcom1234"', + $process->getErrorOutput() + ); + $this->assertExitCode(1, $process); + } + + /** + * It should launch a gui. + */ + public function testGui(): void + { + $env = getenv('PATH'); + putenv('PATH=' . $env . ':' . __DIR__ . '/bin'); + $process = $this->phpbench('xdebug:profile benchmarks/set1/BenchmarkBench.php --gui --gui-bin=foogrind'); + putenv('PATH=' . $env); + $this->assertExitCode(0, $process); + } + + /** + * Specify custom output dir. + */ + public function testOutputDir(): void + { + $process = $this->phpbench('xdebug:profile benchmarks/set1/BenchmarkBench.php --outdir=profilenew --filter=benchDoNothing'); + $lines = explode("\n", $process->getErrorOutput()); + // get the filename from the output and check it exists. + $line = trim($lines[count($lines) - 2]); + $this->assertStringContainsString('profilenew', $line); + $this->assertFileExists($line); + } +} diff --git a/extensions/xdebug/tests/System/XDebugTestCase.php b/extensions/xdebug/tests/System/XDebugTestCase.php new file mode 100644 index 000000000..d1e7cf68b --- /dev/null +++ b/extensions/xdebug/tests/System/XDebugTestCase.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Extensions\XDebug\Tests\System; + +use PhpBench\Tests\System\SystemTestCase; +use Symfony\Component\Process\Process; + +class XDebugTestCase extends SystemTestCase +{ + protected function setUp(): void + { + if (!extension_loaded('xdebug')) { + $this->markTestSkipped('XDebug not enabled.'); + } + + parent::setUp(); + } + + public function phpbench($command, $workingDir = '.'): Process + { + $command .= ' --extension="PhpBench\\Extensions\\XDebug\\XDebugExtension"'; + + return parent::phpbench($command, $workingDir); + } +} diff --git a/extensions/xdebug/tests/System/bin/foogrind b/extensions/xdebug/tests/System/bin/foogrind new file mode 100755 index 000000000..d28e50a8a --- /dev/null +++ b/extensions/xdebug/tests/System/bin/foogrind @@ -0,0 +1,5 @@ +#!/usr/bin/env php + + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Extensions\XDebug\Tests\Unit; + +use DTL\Invoke\Invoke; +use PhpBench\Executor\ExecutionContext; +use PhpBench\Extensions\XDebug\XDebugUtil; +use PhpBench\Model\Iteration; +use PhpBench\Tests\TestCase; + +class XDebugUtilTest extends TestCase +{ + private $iteration; + private $subject; + private $benchmark; + private $parameters; + + /** + * It should generate a filename for an iteration. + * + * @dataProvider provideGenerate + */ + public function testGenerate($class, $subject, $expected): void + { + $params = [ + 'classPath' => '/foobar', + 'parameterSetName' => '7', + 'parameters' => ['asd'], + 'className' => $class, + 'methodName' => $subject, + ]; + + $result = XDebugUtil::filenameFromContext(Invoke::new(ExecutionContext::class, $params)); + $this->assertEquals( + $expected, + $result + ); + } + + public function provideGenerate() + { + return [ + [ + 'Benchmark', + 'Subject', + '2214b023e25587e253082262814e6c37' + ], + [ + 'Benchmark\\Foo', + 'Subject\\//asd', + '25133125bf4eca7a08502711d2c8403d' + ], + ]; + } +} diff --git a/lib/Assertion/AssertionProcessor.php b/lib/Assertion/AssertionProcessor.php new file mode 100644 index 000000000..4bb31301d --- /dev/null +++ b/lib/Assertion/AssertionProcessor.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Assertion; + +use PhpBench\Assertion\Exception\AssertionError; +use PhpBench\Expression\Ast\BooleanNode; +use PhpBench\Expression\Ast\ToleratedTrue; +use PhpBench\Expression\Evaluator; +use PhpBench\Expression\ExpressionLanguage; +use PhpBench\Expression\Printer; +use PhpBench\Expression\Printer\EvaluatingPrinter; +use PhpBench\Model\Variant; + +class AssertionProcessor +{ + /** + * @var Evaluator + */ + private $evaluator; + + /** + * @var Printer + */ + private $printer; + + /** + * @var ParameterProvider + */ + private $provider; + + /** + * @var EvaluatingPrinter + */ + private $evaluatingPrinter; + + /** + * @var ExpressionLanguage + */ + private $expressionLanaugage; + + public function __construct( + ExpressionLanguage $expressionLanaugage, + Evaluator $evaluator, + Printer $printer, + EvaluatingPrinter $evaluatingPrinter, + ParameterProvider $provider + ) { + $this->evaluator = $evaluator; + $this->printer = $printer; + $this->provider = $provider; + $this->evaluatingPrinter = $evaluatingPrinter; + $this->expressionLanaugage = $expressionLanaugage; + } + + public function assert(Variant $variant, string $assertion): AssertionResult + { + $node = $this->expressionLanaugage->parse($assertion); + $params = $this->provider->provideFor($variant); + $evaluated = $this->evaluator->evaluate($node, $params); + + $message = sprintf( + "%s\n= %s\n= %s", + $this->printer->print($node), + $this->evaluatingPrinter->withParams($params)->print($node), + $this->printer->print($evaluated) + ); + + if ($evaluated instanceof BooleanNode) { + if ($evaluated->value()) { + return AssertionResult::ok(); + } + + return AssertionResult::fail($message); + } + + if ($evaluated instanceof ToleratedTrue) { + return AssertionResult::tolerated($message); + } + + throw new AssertionError(sprintf( + 'Assertion expression must evaluate to a boolean-like value, got "%s" as "%s"', + get_class($evaluated), + $this->printer->print($evaluated) + )); + } +} diff --git a/lib/Assertion/AssertionResult.php b/lib/Assertion/AssertionResult.php new file mode 100644 index 000000000..6775ec4db --- /dev/null +++ b/lib/Assertion/AssertionResult.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Assertion; + +class AssertionResult +{ + public const TOLERATED = 'tolerated'; + public const FAIL = 'fail'; + public const OK = 'ok'; + + /** + * @var string + */ + private $type; + + /** + * @var string|null + */ + private $message; + + public function __construct(string $type, ?string $message = null) + { + $this->type = $type; + $this->message = $message; + } + + public static function tolerated(string $message = ''): self + { + return new self(self::TOLERATED, $message); + } + + public static function fail(string $message = ''): self + { + return new self(self::FAIL, $message); + } + + public static function ok(): self + { + return new self(self::OK); + } + + public function isTolerated(): bool + { + return $this->type === self::TOLERATED; + } + + public function isFail(): bool + { + return $this->type === self::FAIL; + } + + public function getMessage(): string + { + return $this->message ?? ''; + } + + public function type(): string + { + return $this->type; + } +} diff --git a/lib/Assertion/Exception/AssertionError.php b/lib/Assertion/Exception/AssertionError.php new file mode 100644 index 000000000..569a0ea9d --- /dev/null +++ b/lib/Assertion/Exception/AssertionError.php @@ -0,0 +1,9 @@ + + */ + public function provideFor(Variant $variant) + { + return (function (array $variantData) use ($variant) { + return [ + 'subject' => $this->subjectData($variant->getSubject()), + 'variant' => $variantData, + 'baseline' => $variant->getBaseline() ? $this->buildVariantData($variant->getBaseline()) : $variantData, + DisplayAsPrinter::PARAM_OUTPUT_TIME_UNIT => $variant->getSubject()->getOutputTimeUnit(), + DisplayAsPrinter::PARAM_OUTPUT_TIME_PRECISION => $variant->getSubject()->getOutputTimePrecision(), + ]; + })($this->buildVariantData($variant)); + } + + /** + * @return array>> + */ + private function buildVariantData(Variant $variant) + { + $data = []; + + foreach ($variant->getIterations() as $iteration) { + foreach ($iteration->getResults() as $result) { + $metrics = $result->getMetrics(); + $resultKey = (string)$result->getKey(); + + foreach ($metrics as $name => $value) { + $name = (string)$name; + + if (!isset($data[$resultKey][$name])) { + $data[$resultKey][$name] = []; + } + $data[$resultKey][(string)$name][] = $value; + } + } + } + + return $data; + } + + /** + * @return array + */ + private function subjectData(Subject $subject): array + { + return [ + 'time_unit' => $subject->getOutputTimeUnit(), + 'time_precision' => $subject->getOutputTimePrecision(), + 'time_mode' => $subject->getOutputMode(), + ]; + } +} diff --git a/lib/Assertion/VariantAssertionResults.php b/lib/Assertion/VariantAssertionResults.php new file mode 100644 index 000000000..79f21cac1 --- /dev/null +++ b/lib/Assertion/VariantAssertionResults.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Assertion; + +use ArrayIterator; +use IteratorAggregate; +use PhpBench\Model\Variant; + +/** + * @implements IteratorAggregate + */ +class VariantAssertionResults implements IteratorAggregate, \Countable +{ + /** + * @var Variant + */ + private $variant; + + /** + * @var array + */ + private $results = []; + + /** + * @param array $results + */ + public function __construct(Variant $variant, array $results = []) + { + $this->variant = $variant; + + foreach ($results as $result) { + $this->add($result); + } + } + + /** + * @return ArrayIterator + */ + public function getIterator(): ArrayIterator + { + return new \ArrayIterator($this->results); + } + + public function add(AssertionResult $result): void + { + $this->results[] = $result; + } + + /** + * @return AssertionResult[] + */ + public function asArray(): array + { + return $this->results; + } + + public function getVariant(): Variant + { + return $this->variant; + } + + /** + * {@inheritdoc} + */ + public function count(): int + { + return count($this->results); + } + + public function hasFailures(): bool + { + foreach ($this->results as $result) { + if ($result->isFail()) { + return true; + } + } + + return false; + } + + /** + * @return self + */ + public function failures(): self + { + return new self($this->variant, array_filter($this->results, function (AssertionResult $result) { + return $result->isFail(); + })); + } + + /** + * @return self + */ + public function tolerations(): self + { + return new self($this->variant, array_filter($this->results, function (AssertionResult $result) { + return $result->isTolerated(); + })); + } +} diff --git a/lib/Attributes/AbstractMethodsAttribute.php b/lib/Attributes/AbstractMethodsAttribute.php new file mode 100644 index 000000000..16b0d1c0c --- /dev/null +++ b/lib/Attributes/AbstractMethodsAttribute.php @@ -0,0 +1,19 @@ +methods = (array)$methods; + } +} diff --git a/lib/Attributes/AfterClassMethods.php b/lib/Attributes/AfterClassMethods.php new file mode 100644 index 000000000..ea8f58ac4 --- /dev/null +++ b/lib/Attributes/AfterClassMethods.php @@ -0,0 +1,10 @@ +expression = $expression; + } +} diff --git a/lib/Attributes/BeforeClassMethods.php b/lib/Attributes/BeforeClassMethods.php new file mode 100644 index 000000000..f6bb79501 --- /dev/null +++ b/lib/Attributes/BeforeClassMethods.php @@ -0,0 +1,10 @@ + + */ + public $config; + + /** + * @param array $config + */ + public function __construct(string $name, array $config = []) + { + $this->name = $name; + $this->config = $config; + } +} diff --git a/lib/Attributes/Format.php b/lib/Attributes/Format.php new file mode 100644 index 000000000..09bdd76af --- /dev/null +++ b/lib/Attributes/Format.php @@ -0,0 +1,17 @@ +format = $format; + } +} diff --git a/lib/Attributes/Groups.php b/lib/Attributes/Groups.php new file mode 100644 index 000000000..300632f02 --- /dev/null +++ b/lib/Attributes/Groups.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Attributes; + +use Attribute; + +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] +final class Groups +{ + /** + * @var string[] + */ + public $groups; + + /** + * @param string[] $groups + */ + public function __construct(array $groups) + { + $this->groups = $groups; + } +} diff --git a/lib/Attributes/Iterations.php b/lib/Attributes/Iterations.php new file mode 100644 index 000000000..9fa83a708 --- /dev/null +++ b/lib/Attributes/Iterations.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Attributes; + +use Attribute; + +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] +class Iterations +{ + /** + * @var int[] + */ + public $iterations; + + public function __construct(int | array $iterations) + { + $this->iterations = (array)$iterations; + } +} diff --git a/lib/Attributes/OutputMode.php b/lib/Attributes/OutputMode.php new file mode 100644 index 000000000..8d114ca0a --- /dev/null +++ b/lib/Attributes/OutputMode.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Attributes; + +use Attribute; + +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] +class OutputMode +{ + /** + * @var string + */ + private $mode; + + public function __construct(string $mode) + { + $this->mode = $mode; + } + + public function getMode() + { + return $this->mode; + } +} diff --git a/lib/Attributes/OutputTimeUnit.php b/lib/Attributes/OutputTimeUnit.php new file mode 100644 index 000000000..e3742f030 --- /dev/null +++ b/lib/Attributes/OutputTimeUnit.php @@ -0,0 +1,25 @@ +timeUnit = $timeUnit; + $this->precision = $precision; + } +} diff --git a/lib/Attributes/ParamProviders.php b/lib/Attributes/ParamProviders.php new file mode 100644 index 000000000..2f508876b --- /dev/null +++ b/lib/Attributes/ParamProviders.php @@ -0,0 +1,22 @@ +providers = (array)$providers; + } +} diff --git a/lib/Attributes/RetryThreshold.php b/lib/Attributes/RetryThreshold.php new file mode 100644 index 000000000..39c22b18a --- /dev/null +++ b/lib/Attributes/RetryThreshold.php @@ -0,0 +1,17 @@ +retryThreshold = $retryThreshold; + } +} diff --git a/lib/Attributes/Revs.php b/lib/Attributes/Revs.php new file mode 100644 index 000000000..d965e6df2 --- /dev/null +++ b/lib/Attributes/Revs.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Attributes; + +use Attribute; + +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] +final class Revs +{ + /** + * @var int[] + */ + public $revs; + + /** + * @param int|int[] $revs + */ + public function __construct(int | array $revs) + { + $this->revs = (array)$revs; + } +} diff --git a/lib/Attributes/Skip.php b/lib/Attributes/Skip.php new file mode 100644 index 000000000..2b1b3957c --- /dev/null +++ b/lib/Attributes/Skip.php @@ -0,0 +1,10 @@ +sleep = $sleep; + } +} diff --git a/lib/Attributes/Subject.php b/lib/Attributes/Subject.php new file mode 100644 index 000000000..01f2b7b2c --- /dev/null +++ b/lib/Attributes/Subject.php @@ -0,0 +1,10 @@ +timeout = $timeout; + } +} diff --git a/lib/Attributes/Warmup.php b/lib/Attributes/Warmup.php new file mode 100644 index 000000000..0a0996050 --- /dev/null +++ b/lib/Attributes/Warmup.php @@ -0,0 +1,22 @@ +revs = (array) $revs; + } +} diff --git a/lib/Benchmark/Baseline/Baselines.php b/lib/Benchmark/Baseline/Baselines.php new file mode 100644 index 000000000..e7276b037 --- /dev/null +++ b/lib/Benchmark/Baseline/Baselines.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Baseline; + +/** + * Class providing some static methods which are used + * to provide base line measurements. + * + * @see \PhpBench\Benchmark\BaselineManager + */ +class Baselines +{ + /** + * Do nothing. + */ + public static function nothing($revs): void + { + for ($i = 0; $i < $revs; $i++) { + } + } + + /** + * Calculate an md5 hash. + */ + public static function md5($revs): void + { + for ($i = 0; $i < $revs; $i++) { + /** @phpstan-ignore-next-line */ + md5('lorem ipusm'); + } + } + + /** + * Open a file, write a string to it $revs times, then + * read each line back. + */ + public static function fwriteFread($revs): void + { + $tempName = tempnam(sys_get_temp_dir(), 'phpbench_baseline'); + $handle = fopen($tempName, 'w'); + + for ($i = 0; $i < $revs; $i++) { + fwrite($handle, 'lorum ipsum'); + } + + fclose($handle); + + $handle = fopen($tempName, 'r'); + + $line = true; + + while ($line) { + $line = fgets($handle); + } + + fclose($handle); + } +} diff --git a/lib/Benchmark/BenchmarkFinder.php b/lib/Benchmark/BenchmarkFinder.php new file mode 100644 index 000000000..59cf8f67f --- /dev/null +++ b/lib/Benchmark/BenchmarkFinder.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark; + +use Generator; +use PhpBench\Benchmark\Metadata\BenchmarkMetadata; +use PhpBench\Benchmark\Metadata\MetadataFactory; +use SplFileInfo; +use Symfony\Component\Finder\Finder; +use Webmozart\PathUtil\Path; + +/** + * This class finds a benchmark (or benchmarks depending on the path), loads + * their metadata and builds a collection of BenchmarkMetadata instances. + */ +class BenchmarkFinder +{ + /** + * @var MetadataFactory + */ + private $factory; + + /** + * @var string + */ + private $cwd; + + public function __construct(MetadataFactory $factory, string $cwd) + { + $this->factory = $factory; + $this->cwd = $cwd; + } + + /** + * @param array $paths + * @param array $subjectFilter + * @param array $groupFilter + * + * @return Generator + */ + public function findBenchmarks(array $paths, array $subjectFilter = [], array $groupFilter = []): Generator + { + foreach ($this->findFiles($paths) as $file) { + if (!is_file($file)) { + continue; + } + + $benchmark = $this->factory->getMetadataForFile($file->getPathname()); + + if (null === $benchmark) { + continue; + } + + if ($groupFilter) { + $benchmark->filterSubjectGroups($groupFilter); + } + + if ($subjectFilter) { + $benchmark->filterSubjectNames($subjectFilter); + } + + if (false === $benchmark->hasSubjects()) { + continue; + } + + yield $benchmark; + } + } + + /** + * @return Generator + */ + private function findFiles(array $paths): Generator + { + $finder = new Finder(); + $search = false; + + foreach ($paths as $path) { + $path = Path::makeAbsolute($path, $this->cwd); + + if (!file_exists($path)) { + throw new \InvalidArgumentException(sprintf( + 'File or directory "%s" does not exist (cwd: %s)', + $path, + $this->cwd + )); + } + + if (is_dir($path)) { + $search = true; + $finder->in($path)->name('*.php'); + + continue; + } + + if (is_file($path)) { + yield new SplFileInfo($path); + + continue; + } + } + + if ($search === false) { + return; + } + + foreach ($finder as $file) { + yield $file; + } + } +} diff --git a/lib/Benchmark/CartesianParameterIterator.php b/lib/Benchmark/CartesianParameterIterator.php new file mode 100644 index 000000000..53d905413 --- /dev/null +++ b/lib/Benchmark/CartesianParameterIterator.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark; + +use PhpBench\Model\ParameterSet; + +class CartesianParameterIterator implements \Iterator +{ + private $sets = []; + private $index = 0; + private $max; + private $current = []; + private $break = false; + + /** + * @var string + */ + private $key; + + public function __construct(array $parameterSets) + { + foreach ($parameterSets as $parameterSet) { + $this->sets[] = new \ArrayIterator($parameterSet); + } + + if (empty($parameterSets)) { + $this->break = true; + } + + $this->max = count($parameterSets) - 1; + } + + public function current() + { + return $this->getParameterSet(); + } + + public function next(): void + { + for ($index = 0; $index <= $this->max; $index++) { + $this->sets[$index]->next(); + + if (true === $this->sets[$index]->valid()) { + break; + } + + $this->sets[$index]->rewind(); + + if ($index === $this->max) { + $this->break = true; + + break; + } + } + + $this->index++; + $this->update(); + } + + public function key(): string + { + return $this->key; + } + + public function rewind(): void + { + $this->index = 0; + + foreach ($this->sets as $set) { + $set->rewind(); + } + $this->update(); + } + + public function valid(): bool + { + return false === $this->break; + } + + private function update(): void + { + $this->current = []; + $key = []; + + foreach ($this->sets as $set) { + $this->current = array_merge( + $this->current, + $set->current() ?: [] + ); + $key[] = $set->key(); + } + $this->key = implode(',', $key); + } + + private function getParameterSet(): ParameterSet + { + return new ParameterSet($this->key, $this->current); + } +} diff --git a/lib/Benchmark/Exception/RetryLimitReachedException.php b/lib/Benchmark/Exception/RetryLimitReachedException.php new file mode 100644 index 000000000..37581cd8c --- /dev/null +++ b/lib/Benchmark/Exception/RetryLimitReachedException.php @@ -0,0 +1,9 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Exception; + +class StopOnErrorException extends \Exception +{ +} diff --git a/lib/Benchmark/Metadata/AnnotationReader.php b/lib/Benchmark/Metadata/AnnotationReader.php new file mode 100644 index 000000000..8ed36ab92 --- /dev/null +++ b/lib/Benchmark/Metadata/AnnotationReader.php @@ -0,0 +1,229 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata; + +use Doctrine\Common\Annotations\AnnotationException; +use Doctrine\Common\Annotations\AnnotationRegistry; +use Doctrine\Common\Annotations\DocParser; +use Doctrine\Common\Annotations\TokenParser; +use PhpBench\Reflection\ReflectionClass; +use PhpBench\Reflection\ReflectionMethod; + +/** + * Annotation reader. + */ +class AnnotationReader +{ + private $useImports = []; + private $importUse = false; + + private static $phpBenchImports = [ + 'BeforeMethods' => Annotations\BeforeMethods::class, + 'BeforeClassMethods' => Annotations\BeforeClassMethods::class, + 'AfterMethods' => Annotations\AfterMethods::class, + 'AfterClassMethods' => Annotations\AfterClassMethods::class, + 'ParamProviders' => Annotations\ParamProviders::class, + 'Groups' => Annotations\Groups::class, + 'Iterations' => Annotations\Iterations::class, + 'Revs' => Annotations\Revs::class, + 'Skip' => Annotations\Skip::class, + 'Sleep' => Annotations\Sleep::class, + 'OutputTimeUnit' => Annotations\OutputTimeUnit::class, + 'OutputMode' => Annotations\OutputMode::class, + 'Warmup' => Annotations\Warmup::class, + 'Subject' => Annotations\Subject::class, + 'Assert' => Annotations\Assert::class, + 'Executor' => Annotations\Executor::class, + 'Timeout' => Annotations\Timeout::class, + 'Format' => Annotations\Format::class, + 'RetryThreshold' => Annotations\RetryThreshold::class, + ]; + + private static $globalIgnoredNames = [ + // Annotation tags + 'Annotation' => true, 'Attribute' => true, 'Attributes' => true, + /* Can we enable this? 'Enum' => true, */ + 'Required' => true, + 'Target' => true, + // Widely used tags (but not existent in phpdoc) + 'fix' => true, 'fixme' => true, + 'override' => true, + // PHPDocumentor 1 tags + 'abstract' => true, 'access' => true, + 'code' => true, + 'deprec' => true, + 'endcode' => true, 'exception' => true, + 'final' => true, + 'ingroup' => true, 'inheritdoc' => true, 'inheritDoc' => true, + 'magic' => true, + 'name' => true, + 'toc' => true, 'tutorial' => true, + 'private' => true, + 'static' => true, 'staticvar' => true, 'staticVar' => true, + 'throw' => true, + // PHPDocumentor 2 tags. + 'api' => true, + 'category' => true, 'copyright' => true, + 'deprecated' => true, + 'example' => true, + 'filesource' => true, + 'global' => true, + 'ignore' => true, /* Can we enable this? 'index' => true, */ 'internal' => true, + 'license' => true, 'link' => true, + 'method' => true, + 'package' => true, 'param' => true, 'property' => true, 'property-read' => true, 'property-write' => true, + 'return' => true, + 'see' => true, 'since' => true, 'source' => true, 'subpackage' => true, + 'throws' => true, 'todo' => true, 'TODO' => true, + 'usedby' => true, + 'var' => true, 'version' => true, + // PHPUnit tags + 'author' => true, + 'after' => true, + 'afterClass' => true, + 'backupGlobals' => true, + 'backupStaticAttributes' => true, + 'before' => true, + 'beforeClass' => true, + 'codeCoverageIgnore' => true, + 'covers' => true, + 'coversDefaultClass' => true, + 'coversNothing' => true, + 'dataProvider' => true, + 'depends' => true, + 'expectedException' => true, + 'expectedExceptionCode' => true, + 'expectedExceptionMessage' => true, + 'expectedExceptionMessageRegExp' => true, + 'group' => true, + 'large' => true, + 'medium' => true, + 'preserveGlobalState' => true, + 'requires' => true, + 'runTestsInSeparateProcesses' => true, + 'runInSeparateProcess' => true, + 'small' => true, + 'test' => true, + 'testdox' => true, + 'ticket' => true, + 'uses' => true, + // PHPCheckStyle + 'SuppressWarnings' => true, + // PHPStorm + 'noinspection' => true, + // PEAR + 'package_version' => true, + // PlantUML + 'startuml' => true, 'enduml' => true, + ]; + + /** + * @var DocParser + */ + private $docParser; + + /** + * Set import use to true in order to use imported annotations, otherwise + * import the PHPBench annotations directly. + * + */ + public function __construct(bool $importUse = false) + { + $this->docParser = new DocParser(); + $this->docParser->setIgnoredAnnotationNames(self::$globalIgnoredNames); + $this->importUse = $importUse; + + AnnotationRegistry::registerLoader(function ($classFqn) { + if (class_exists($classFqn)) { + return true; + } + }); + } + + /** + * Return annotations for the given class. + */ + public function getClassAnnotations(ReflectionClass $class) + { + $this->collectImports($class); + + return $this->parse($class->comment, sprintf('benchmark: %s', $class->class)); + } + + public function getMethodAnnotations(ReflectionMethod $method) + { + $this->collectImports($method->reflectionClass); + + return $this->parse($method->comment, sprintf('subject %s::%s', $method->class, $method->name)); + } + + private function collectImports(ReflectionClass $class): void + { + $imports = $this->importUse === true ? $this->getUseImports($class) : $this->getPhpBenchImports(); + $this->docParser->setImports($imports); + } + + private function getPhpBenchImports() + { + static $phpBenchImports; + + if ($phpBenchImports) { + return $phpBenchImports; + } + + foreach (self::$phpBenchImports as $key => $value) { + $phpBenchImports[strtolower($key)] = $value; + } + + return $phpBenchImports; + } + + private function getUseImports(ReflectionClass $class) + { + if (isset($this->useImports[$class->class])) { + return $this->useImports[$class->class]; + } + + $content = file_get_contents($class->path); + $tokenizer = new TokenParser('parseUseStatements($class->namespace); + $this->useImports[$class->class] = $useImports; + + return $useImports; + } + + /** + * Delegates to the doctrine DocParser but catches annotation not found errors and throws + * something useful. + * + * @see \Doctrine\Common\Annotations\DocParser + */ + private function parse($input, $context = ''): array + { + try { + $annotations = $this->docParser->parse($input, $context); + } catch (AnnotationException $e) { + if (!preg_match('/The annotation "(.*)" .* was never imported/', $e->getMessage(), $matches)) { + throw $e; + } + + throw new \InvalidArgumentException(sprintf( + 'Unrecognized annotation %s, valid PHPBench annotations: @%s', + $matches[1], + implode(', @', array_keys(self::$phpBenchImports)) + ), 0, $e); + } + + return $annotations; + } +} diff --git a/lib/Benchmark/Metadata/Annotations/AbstractArrayAnnotation.php b/lib/Benchmark/Metadata/Annotations/AbstractArrayAnnotation.php new file mode 100644 index 000000000..7b82beacb --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/AbstractArrayAnnotation.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Attributes({ + * @Attribute("extend", required = true, type="boolean"), + * }) + */ +abstract class AbstractArrayAnnotation +{ + private $extend = false; + + public function __construct($params) + { + if (isset($params['extend'])) { + $this->extend = $params['extend']; + } + } + + public function getExtend() + { + return $this->extend; + } +} diff --git a/lib/Benchmark/Metadata/Annotations/AbstractMethodsAnnotation.php b/lib/Benchmark/Metadata/Annotations/AbstractMethodsAnnotation.php new file mode 100644 index 000000000..3abdabf3a --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/AbstractMethodsAnnotation.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Taget({"METHOD", "CLASS"}) + * @Attributes({ + * @Attribute("value", required = true, type = "array"), + * }) + */ +abstract class AbstractMethodsAnnotation extends AbstractArrayAnnotation +{ + private $methods; + + public function __construct($params) + { + parent::__construct($params); + $this->methods = (array) $params['value']; + } + + public function getMethods(): array + { + return $this->methods; + } +} diff --git a/lib/Benchmark/Metadata/Annotations/AfterClassMethods.php b/lib/Benchmark/Metadata/Annotations/AfterClassMethods.php new file mode 100644 index 000000000..8c3fea977 --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/AfterClassMethods.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Annotation() + */ +class AfterClassMethods extends AbstractMethodsAnnotation +{ +} diff --git a/lib/Benchmark/Metadata/Annotations/AfterMethods.php b/lib/Benchmark/Metadata/Annotations/AfterMethods.php new file mode 100644 index 000000000..4f848c913 --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/AfterMethods.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Annotation() + */ +class AfterMethods extends AbstractMethodsAnnotation +{ +} diff --git a/lib/Benchmark/Metadata/Annotations/Assert.php b/lib/Benchmark/Metadata/Annotations/Assert.php new file mode 100644 index 000000000..ee1fbf55d --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/Assert.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Annotation + * @Taget({"METHOD", "CLASS"}) + * @Attributes({ + * @Attribute("value", required = true, type="string") + * }) + */ +class Assert +{ + /** + * @var string + */ + private $expression; + + /** + * @param array $params + */ + public function __construct(array $params) + { + $this->expression = $params['value']; + } + + public function getExpression(): string + { + return $this->expression; + } +} diff --git a/lib/Benchmark/Metadata/Annotations/BeforeClassMethods.php b/lib/Benchmark/Metadata/Annotations/BeforeClassMethods.php new file mode 100644 index 000000000..42ea5df50 --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/BeforeClassMethods.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Annotation + * @Taget({"CLASS"}) + * @Attributes({ + * @Attribute("value", required = true, type = "array"), + * }) + */ +class BeforeClassMethods extends AbstractMethodsAnnotation +{ +} diff --git a/lib/Benchmark/Metadata/Annotations/BeforeMethods.php b/lib/Benchmark/Metadata/Annotations/BeforeMethods.php new file mode 100644 index 000000000..8c6b19c5f --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/BeforeMethods.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Annotation() + */ +class BeforeMethods extends AbstractMethodsAnnotation +{ +} diff --git a/lib/Benchmark/Metadata/Annotations/Executor.php b/lib/Benchmark/Metadata/Annotations/Executor.php new file mode 100644 index 000000000..ecfaf24af --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/Executor.php @@ -0,0 +1,31 @@ +name = $params['value']; + unset($params['value']); + + $this->config = $params ?? []; + } + + public function getConfig(): array + { + return $this->config; + } + + public function getName(): string + { + return $this->name; + } +} diff --git a/lib/Benchmark/Metadata/Annotations/Format.php b/lib/Benchmark/Metadata/Annotations/Format.php new file mode 100644 index 000000000..c1cfd6a7a --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/Format.php @@ -0,0 +1,25 @@ +format = $params['value']; + } + + public function getFormat(): string + { + return $this->format; + } +} diff --git a/lib/Benchmark/Metadata/Annotations/Groups.php b/lib/Benchmark/Metadata/Annotations/Groups.php new file mode 100644 index 000000000..9e0acb1c3 --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/Groups.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Annotation + * @Taget({"METHOD", "CLASS"}) + * @Attributes({ + * @Attribute("value", required = true, type="array"), + * }) + */ +class Groups extends AbstractArrayAnnotation +{ + private $groups; + + public function __construct($params) + { + parent::__construct($params); + $this->groups = (array) $params['value']; + } + + public function getGroups(): array + { + return $this->groups; + } +} diff --git a/lib/Benchmark/Metadata/Annotations/Iterations.php b/lib/Benchmark/Metadata/Annotations/Iterations.php new file mode 100644 index 000000000..f69dedfaa --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/Iterations.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Annotation + * @Taget({"METHOD", "CLASS"}) + * @Attributes({ + * @Attribute("value", required = true, type="mixed") + * }) + */ +class Iterations +{ + private $iterations; + + public function __construct($params) + { + $this->iterations = (array) $params['value']; + } + + public function getIterations(): array + { + return $this->iterations; + } +} diff --git a/lib/Benchmark/Metadata/Annotations/OutputMode.php b/lib/Benchmark/Metadata/Annotations/OutputMode.php new file mode 100644 index 000000000..6d7884194 --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/OutputMode.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Annotation + * @Taget({"METHOD", "CLASS"}) + * @Attributes({ + * @Attribute("value", required = true, type="string") + * }) + */ +class OutputMode +{ + private $mode; + + public function __construct($params) + { + $this->mode = $params['value']; + } + + public function getMode() + { + return $this->mode; + } +} diff --git a/lib/Benchmark/Metadata/Annotations/OutputTimeUnit.php b/lib/Benchmark/Metadata/Annotations/OutputTimeUnit.php new file mode 100644 index 000000000..af7134a68 --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/OutputTimeUnit.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Annotation + * @Taget({"METHOD", "CLASS"}) + * @Attributes({ + * @Attribute("value", required = true, type="string"), + * @Attribute("precision", required = false, type="integer") + * }) + */ +class OutputTimeUnit +{ + private $timeUnit; + private $precision; + + public function __construct($timeUnit) + { + $this->timeUnit = $timeUnit['value']; + + if (isset($timeUnit['precision'])) { + $this->precision = $timeUnit['precision']; + } + } + + public function getTimeUnit() + { + return $this->timeUnit; + } + + public function getPrecision() + { + return $this->precision; + } +} diff --git a/lib/Benchmark/Metadata/Annotations/ParamProviders.php b/lib/Benchmark/Metadata/Annotations/ParamProviders.php new file mode 100644 index 000000000..386822918 --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/ParamProviders.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Annotation + * @Taget({"METHOD", "CLASS"}) + * @Attributes({ + * @Attribute("value", required = true, type="array"), + * }) + */ +class ParamProviders extends AbstractArrayAnnotation +{ + private $providers; + + public function __construct($params) + { + parent::__construct($params); + $this->providers = (array) $params['value']; + } + + public function getProviders(): array + { + return $this->providers; + } +} diff --git a/lib/Benchmark/Metadata/Annotations/RetryThreshold.php b/lib/Benchmark/Metadata/Annotations/RetryThreshold.php new file mode 100644 index 000000000..88f68e791 --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/RetryThreshold.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Annotation + * @Taget({"METHOD", "CLASS"}) + * @Attributes({ + * @Attribute("value", required = true, type="float") + * }) + */ +class RetryThreshold +{ + /** + * @var float + */ + private $retryThreshold; + + public function __construct($params) + { + $this->retryThreshold = (float) $params['value']; + } + + public function getRetryThreshold(): float + { + return $this->retryThreshold; + } +} diff --git a/lib/Benchmark/Metadata/Annotations/Revs.php b/lib/Benchmark/Metadata/Annotations/Revs.php new file mode 100644 index 000000000..c5d8f86e0 --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/Revs.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Annotation + * @Taget({"METHOD", "CLASS"}) + * @Attributes({ + * @Attribute("value", required = true, type="mixed"), + * }) + */ +class Revs +{ + private $revs; + + public function __construct($revs) + { + $this->revs = (array) $revs['value']; + } + + public function getRevs(): array + { + return $this->revs; + } +} diff --git a/lib/Benchmark/Metadata/Annotations/Skip.php b/lib/Benchmark/Metadata/Annotations/Skip.php new file mode 100644 index 000000000..8ad7ef034 --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/Skip.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Annotation + * @Taget({"METHOD", "CLASS"}) + */ +class Skip +{ +} diff --git a/lib/Benchmark/Metadata/Annotations/Sleep.php b/lib/Benchmark/Metadata/Annotations/Sleep.php new file mode 100644 index 000000000..75773fc27 --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/Sleep.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Annotation + * @Taget({"METHOD", "CLASS"}) + * @Attributes({ + * @Attribute("value", required = true, type="integer") + * }) + */ +class Sleep +{ + private $sleep; + + public function __construct($params) + { + $this->sleep = (int) $params['value']; + } + + public function getSleep(): int + { + return $this->sleep; + } +} diff --git a/lib/Benchmark/Metadata/Annotations/Subject.php b/lib/Benchmark/Metadata/Annotations/Subject.php new file mode 100644 index 000000000..dad5687ca --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/Subject.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Annotation + * @Taget({"METHOD"}) + */ +class Subject +{ +} diff --git a/lib/Benchmark/Metadata/Annotations/Timeout.php b/lib/Benchmark/Metadata/Annotations/Timeout.php new file mode 100644 index 000000000..8ef40e9b1 --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/Timeout.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Annotation + * @Taget({"METHOD", "CLASS"}) + * @Attributes({ + * @Attribute("value", required = true, type="float") + * }) + */ +class Timeout +{ + private $timeout; + + public function __construct($params) + { + $this->timeout = (float) $params['value']; + } + + public function getTimeout(): float + { + return $this->timeout; + } +} diff --git a/lib/Benchmark/Metadata/Annotations/Warmup.php b/lib/Benchmark/Metadata/Annotations/Warmup.php new file mode 100644 index 000000000..be08d75ac --- /dev/null +++ b/lib/Benchmark/Metadata/Annotations/Warmup.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Annotations; + +/** + * @Annotation + * @Taget({"METHOD", "CLASS"}) + * @Attributes({ + * @Attribute("value", required = true, type="mixed") + * }) + */ +class Warmup +{ + private $revs; + + public function __construct($revs) + { + $this->revs = (array) $revs['value']; + } + + public function getRevs(): array + { + return $this->revs; + } +} diff --git a/lib/Benchmark/Metadata/AssertionMetadata.php b/lib/Benchmark/Metadata/AssertionMetadata.php new file mode 100644 index 000000000..d2278de9d --- /dev/null +++ b/lib/Benchmark/Metadata/AssertionMetadata.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata; + +class AssertionMetadata +{ + /** + * @var array + */ + private $config; + + public function __construct(array $config) + { + $this->config = $config; + } + + public function getConfig(): array + { + return $this->config; + } +} diff --git a/lib/Benchmark/Metadata/BenchmarkMetadata.php b/lib/Benchmark/Metadata/BenchmarkMetadata.php new file mode 100644 index 000000000..89c04ab59 --- /dev/null +++ b/lib/Benchmark/Metadata/BenchmarkMetadata.php @@ -0,0 +1,208 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata; + +use PhpBench\Model\Benchmark; + +/** + * Benchmark metadata class. + */ +class BenchmarkMetadata +{ + /** + * @var string + */ + private $path; + + /** + * @var string + */ + private $class; + + /** + * @var SubjectMetadata[] + */ + private $subjects = []; + + /** + * @var string[] + */ + private $beforeClassMethods = []; + + /** + * @var string[] + */ + private $afterClassMethods = []; + + /** + * @var ExecutorMetadata|null + */ + private $executorMetadata; + + /** + */ + public function __construct(string $path, string $class) + { + $this->path = $path; + $this->class = $class; + } + + /** + * Get the file path of this benchmark. + */ + public function getPath(): string + { + return $this->path; + } + + /** + * Get or create a new SubjectMetadata instance with the given name. + * + */ + public function getOrCreateSubject(string $name): SubjectMetadata + { + if (isset($this->subjects[$name])) { + return $this->subjects[$name]; + } + + $this->subjects[$name] = new SubjectMetadata($this, $name); + + return $this->subjects[$name]; + } + + /** + * Get the subject metadata instances for this benchmark metadata. + * + * @return SubjectMetadata[] + */ + public function getSubjects(): array + { + return $this->subjects; + } + + /** + * Remove all subjects whose name is not in the given list. + * + * @param string[] $filters + */ + public function filterSubjectNames(array $filters): void + { + foreach (array_keys($this->subjects) as $subjectName) { + $unset = true; + + foreach ($filters as $filter) { + if (preg_match( + sprintf('{^.*?%s.*?$}', $filter), + sprintf('%s::%s', $this->getClass(), $subjectName) + )) { + $unset = false; + + break; + } + } + + if (true === $unset) { + unset($this->subjects[$subjectName]); + } + } + } + + /** + * Remove all the subjects which are not contained in the given list of groups. + * + * @param string[] $groups + */ + public function filterSubjectGroups(array $groups): void + { + foreach ($this->subjects as $subjectName => $subject) { + if (0 === count(array_intersect($subject->getGroups(), $groups))) { + unset($this->subjects[$subjectName]); + } + } + } + + /** + * Return true if there are subjects in this benchmark metadata, false if not. + */ + public function hasSubjects(): bool + { + return 0 !== count($this->subjects); + } + + /** + * Return the benchmark class. + */ + public function getClass(): string + { + return $this->class; + } + + /** + * Return any methods that should be called before the benchmark class is executed. + */ + public function getBeforeClassMethods(): array + { + return $this->beforeClassMethods; + } + + /** + * Set any methods that should be called before the benchmark class is executed. + * + */ + public function setBeforeClassMethods(array $beforeClassMethods): void + { + $this->beforeClassMethods = $beforeClassMethods; + } + + /** + * Return any methods that should be called after the benchmark class is executed. + */ + public function getAfterClassMethods(): array + { + return $this->afterClassMethods; + } + + /** + * Set any methods that should be called after the benchmark class is executed. + * + */ + public function setAfterClassMethods(array $afterClassMethods): void + { + $this->afterClassMethods = $afterClassMethods; + } + + public function getIterator(): array + { + return $this->subjects; + } + + public function getExecutor(): ?ExecutorMetadata + { + return $this->executorMetadata; + } + + public function setExecutor(ExecutorMetadata $serviceMetadata): void + { + $this->executorMetadata = $serviceMetadata; + } + + public function merge(self $benchmarkMetadata): void + { + $this->beforeClassMethods = array_merge($this->beforeClassMethods, $benchmarkMetadata->beforeClassMethods); + $this->afterClassMethods = array_merge($this->afterClassMethods, $benchmarkMetadata->afterClassMethods); + + foreach ($benchmarkMetadata->getSubjects() as $subject) { + $this->getOrCreateSubject($subject->getName())->merge($subject); + } + } +} diff --git a/lib/Benchmark/Metadata/Driver/AnnotationDriver.php b/lib/Benchmark/Metadata/Driver/AnnotationDriver.php new file mode 100644 index 000000000..ccde638a4 --- /dev/null +++ b/lib/Benchmark/Metadata/Driver/AnnotationDriver.php @@ -0,0 +1,251 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata\Driver; + +use PhpBench\Benchmark\Metadata\AnnotationReader; +use PhpBench\Benchmark\Metadata\Annotations; +use PhpBench\Benchmark\Metadata\Annotations\AbstractArrayAnnotation; +use PhpBench\Benchmark\Metadata\Annotations\AfterClassMethods; +use PhpBench\Benchmark\Metadata\Annotations\BeforeClassMethods; +use PhpBench\Benchmark\Metadata\Annotations\Subject; +use PhpBench\Benchmark\Metadata\BenchmarkMetadata; +use PhpBench\Benchmark\Metadata\DriverInterface; +use PhpBench\Benchmark\Metadata\ExecutorMetadata; +use PhpBench\Benchmark\Metadata\SubjectMetadata; +use PhpBench\Reflection\ReflectionHierarchy; +use PhpBench\Reflection\ReflectorInterface; + +class AnnotationDriver implements DriverInterface +{ + private $reflector; + private $reader; + private $subjectPattern; + + public function __construct(ReflectorInterface $reflector, $subjectPattern = '^bench', AnnotationReader $reader = null) + { + $this->reflector = $reflector; + $this->reader = $reader ?: new AnnotationReader(); + $this->subjectPattern = $subjectPattern; + } + + public function getMetadataForHierarchy(ReflectionHierarchy $hierarchy): BenchmarkMetadata + { + $primaryReflection = $hierarchy->getTop(); + $benchmark = new BenchmarkMetadata($primaryReflection->path, $primaryReflection->class); + + $this->buildBenchmark($benchmark, $hierarchy); + + return $benchmark; + } + + private function buildBenchmark(BenchmarkMetadata $benchmark, ReflectionHierarchy $hierarchy): void + { + $annotations = []; + $reflectionHierarchy = array_reverse(iterator_to_array($hierarchy)); + + foreach ($reflectionHierarchy as $reflection) { + $benchAnnotations = $this->reader->getClassAnnotations( + $reflection + ); + + $annotations = array_merge($annotations, $benchAnnotations); + + foreach ($annotations as $annotation) { + $this->processBenchmark($benchmark, $annotation); + } + } + + foreach ($reflectionHierarchy as $reflection) { + foreach ($reflection->methods as $reflectionMethod) { + $hasPrefix = (bool) preg_match('{' . $this->subjectPattern . '}', $reflectionMethod->name); + $hasAnnotation = false; + $subjectAnnotations = null; + + // if the prefix is false check to see if it has a `@Subject` annotation + if (false === $hasPrefix) { + $subjectAnnotations = $this->reader->getMethodAnnotations( + $reflectionMethod + ); + + foreach ($subjectAnnotations as $annotation) { + if ($annotation instanceof Subject) { + $hasAnnotation = true; + } + } + } + + if (false === $hasPrefix && false === $hasAnnotation) { + continue; + } + + if (null === $subjectAnnotations) { + $subjectAnnotations = $this->reader->getMethodAnnotations( + $reflectionMethod + ); + } + + $subject = $benchmark->getOrCreateSubject($reflectionMethod->name); + + // apply the benchmark annotations to the subject + foreach ($annotations as $annotation) { + $this->processSubject($subject, $annotation); + } + + $this->buildSubject($subject, $subjectAnnotations); + } + } + } + + private function buildSubject(SubjectMetadata $subject, $annotations): void + { + foreach ($annotations as $annotation) { + if ($annotation instanceof BeforeClassMethods) { + throw new \InvalidArgumentException(sprintf( + '@BeforeClassMethods annotation can only be applied at the class level (%s)', + $subject->getBenchmark()->getClass() . '::' . $subject->getName() + )); + } + + if ($annotation instanceof AfterClassMethods) { + throw new \InvalidArgumentException(sprintf( + '@AfterClassMethods annotation can only be applied at the class level (%s)', + $subject->getBenchmark()->getClass() . '::' . $subject->getName() + )); + } + + $this->processSubject($subject, $annotation); + } + } + + private function processSubject(SubjectMetadata $subject, $annotation): void + { + if ($annotation instanceof Annotations\BeforeMethods) { + $subject->setBeforeMethods( + $this->resolveValue( + $annotation, + $subject->getBeforeMethods(), + $annotation->getMethods() + ) + ); + } + + if ($annotation instanceof Annotations\AfterMethods) { + $subject->setAfterMethods( + $this->resolveValue( + $annotation, + $subject->getAfterMethods(), + $annotation->getMethods() + ) + ); + } + + if ($annotation instanceof Annotations\ParamProviders) { + $subject->setParamProviders( + $this->resolveValue( + $annotation, + $subject->getParamProviders(), + $annotation->getProviders() + ) + ); + } + + if ($annotation instanceof Annotations\Iterations) { + $subject->setIterations($annotation->getIterations()); + } + + if ($annotation instanceof Annotations\Sleep) { + $subject->setSleep($annotation->getSleep()); + } + + if ($annotation instanceof Annotations\Groups) { + $subject->setGroups( + $this->resolveValue( + $annotation, + $subject->getGroups(), + $annotation->getGroups() + ) + ); + } + + if ($annotation instanceof Annotations\Revs) { + $subject->setRevs($annotation->getRevs()); + } + + if ($annotation instanceof Annotations\Warmup) { + $subject->setWarmup($annotation->getRevs()); + } + + if ($annotation instanceof Annotations\Skip) { + $subject->setSkip(true); + } + + if ($annotation instanceof Annotations\OutputTimeUnit) { + $subject->setOutputTimeUnit($annotation->getTimeUnit()); + $subject->setOutputTimePrecision($annotation->getPrecision()); + } + + if ($annotation instanceof Annotations\OutputMode) { + $subject->setOutputMode($annotation->getMode()); + } + + if ($annotation instanceof Annotations\Assert) { + $subject->addAssertion($annotation->getExpression()); + } + + if ($annotation instanceof Annotations\Format) { + $subject->setFormat($annotation->getFormat()); + } + + if ($annotation instanceof Annotations\Executor) { + $subject->setExecutor(new ExecutorMetadata($annotation->getName(), $annotation->getConfig())); + } + + if ($annotation instanceof Annotations\Timeout) { + $subject->setTimeout($annotation->getTimeout()); + } + + if ($annotation instanceof Annotations\RetryThreshold) { + $subject->setRetryThreshold($annotation->getRetryThreshold()); + } + } + + public function processBenchmark(BenchmarkMetadata $benchmark, $annotation): void + { + if ($annotation instanceof Annotations\Executor) { + $benchmark->setExecutor(new ExecutorMetadata($annotation->getName(), $annotation->getConfig())); + } + + if ($annotation instanceof BeforeClassMethods) { + $benchmark->setBeforeClassMethods($annotation->getMethods()); + } + + if ($annotation instanceof AfterClassMethods) { + $benchmark->setAfterClassMethods($annotation->getMethods()); + } + } + + private function resolveValue(AbstractArrayAnnotation $annotation, ?array $currentValues, ?array $annotationValues): ?array + { + if ($currentValues === null) { + return $annotationValues; + } + + if ($annotationValues === null) { + return $currentValues; + } + $values = $annotation->getExtend() === true ? $currentValues : []; + $values = array_merge($values, $annotationValues); + + return $values; + } +} diff --git a/lib/Benchmark/Metadata/Driver/AttributeDriver.php b/lib/Benchmark/Metadata/Driver/AttributeDriver.php new file mode 100644 index 000000000..f33840092 --- /dev/null +++ b/lib/Benchmark/Metadata/Driver/AttributeDriver.php @@ -0,0 +1,217 @@ +subjectPattern = $subjectPattern; + } + + public function getMetadataForHierarchy(ReflectionHierarchy $hierarchy): BenchmarkMetadata + { + $primaryReflection = $hierarchy->getTop(); + $benchmark = new BenchmarkMetadata($primaryReflection->path, $primaryReflection->class); + + $this->buildBenchmark($benchmark, $hierarchy); + + return $benchmark; + } + + private function buildBenchmark(BenchmarkMetadata $benchmark, ReflectionHierarchy $hierarchy): void + { + $attributes = []; + $reflectionHierarchy = array_reverse(iterator_to_array($hierarchy)); + + foreach ($reflectionHierarchy as $reflection) { + assert($reflection instanceof ReflectionClass); + $benchAttributes = $reflection->attributes; + + $attributes = array_merge($attributes, $benchAttributes); + + foreach ($attributes as $attribute) { + $this->processBenchmark($benchmark, $attribute); + } + } + + foreach ($reflectionHierarchy as $reflection) { + foreach ($reflection->methods as $reflectionMethod) { + assert($reflectionMethod instanceof ReflectionMethod); + $hasPrefix = (bool) preg_match('{' . $this->subjectPattern . '}', $reflectionMethod->name); + $hasAttribute = false; + $subjectAttributes = null; + + // if the prefix is false check to see if it has a `@Subject` attribute + if (false === $hasPrefix) { + $subjectAttributes = $reflectionMethod->attributes; + + foreach ($subjectAttributes as $attribute) { + if ($attribute instanceof Subject) { + $hasAttribute = true; + } + } + } + + if (false === $hasPrefix && false === $hasAttribute) { + continue; + } + + if (null === $subjectAttributes) { + $subjectAttributes = $reflectionMethod->attributes; + } + + $subject = $benchmark->getOrCreateSubject($reflectionMethod->name); + + // apply the benchmark attributes to the subject + foreach ($attributes as $attribute) { + $this->processSubject($subject, $attribute); + } + + $this->buildSubject($subject, $subjectAttributes); + } + } + } + + /** + * @param object[] $attributes + */ + private function buildSubject(SubjectMetadata $subject, $attributes): void + { + foreach ($attributes as $attribute) { + if ($attribute instanceof BeforeClassMethods) { + throw new \InvalidArgumentException(sprintf( + '@BeforeClassMethods attribute can only be applied at the class level (%s)', + $subject->getBenchmark()->getClass() . '::' . $subject->getName() + )); + } + + if ($attribute instanceof AfterClassMethods) { + throw new \InvalidArgumentException(sprintf( + '@AfterClassMethods attribute can only be applied at the class level (%s)', + $subject->getBenchmark()->getClass() . '::' . $subject->getName() + )); + } + + $this->processSubject($subject, $attribute); + } + } + + /** + * @param object $attribute + */ + private function processSubject(SubjectMetadata $subject, $attribute): void + { + if ($attribute instanceof Attributes\BeforeMethods) { + $subject->setBeforeMethods(array_merge( + $subject->getBeforeMethods(), + $attribute->methods + )); + } + + if ($attribute instanceof Attributes\AfterMethods) { + $subject->setAfterMethods(array_merge( + $subject->getAfterMethods(), + $attribute->methods + )); + } + + if ($attribute instanceof Attributes\ParamProviders) { + $subject->setParamProviders(array_merge( + $subject->getParamProviders(), + $attribute->providers + )); + } + + if ($attribute instanceof Attributes\Iterations) { + $subject->setIterations($attribute->iterations); + } + + if ($attribute instanceof Attributes\Sleep) { + $subject->setSleep($attribute->sleep); + } + + if ($attribute instanceof Attributes\Groups) { + $subject->setGroups(array_merge( + $subject->getGroups(), + $attribute->groups + )); + } + + if ($attribute instanceof Attributes\Revs) { + $subject->setRevs($attribute->revs); + } + + if ($attribute instanceof Attributes\Warmup) { + $subject->setWarmup($attribute->revs); + } + + if ($attribute instanceof Attributes\Skip) { + $subject->setSkip(true); + } + + if ($attribute instanceof Attributes\OutputTimeUnit) { + $subject->setOutputTimeUnit($attribute->timeUnit); + $subject->setOutputTimePrecision($attribute->precision); + } + + if ($attribute instanceof Attributes\OutputMode) { + $subject->setOutputMode($attribute->getMode()); + } + + if ($attribute instanceof Attributes\Assert) { + $subject->addAssertion($attribute->expression); + } + + if ($attribute instanceof Attributes\Format) { + $subject->setFormat($attribute->format); + } + + if ($attribute instanceof Attributes\Executor) { + $subject->setExecutor(new ExecutorMetadata($attribute->name, $attribute->config)); + } + + if ($attribute instanceof Attributes\Timeout) { + $subject->setTimeout($attribute->timeout); + } + + if ($attribute instanceof Attributes\RetryThreshold) { + $subject->setRetryThreshold($attribute->retryThreshold); + } + } + + /** + * @param object $attribute + */ + public function processBenchmark(BenchmarkMetadata $benchmark, $attribute): void + { + if ($attribute instanceof Attributes\Executor) { + $benchmark->setExecutor(new ExecutorMetadata($attribute->name, $attribute->config)); + } + + if ($attribute instanceof BeforeClassMethods) { + $benchmark->setBeforeClassMethods($attribute->methods); + } + + if ($attribute instanceof AfterClassMethods) { + $benchmark->setAfterClassMethods($attribute->methods); + } + } +} diff --git a/lib/Benchmark/Metadata/Driver/ChainDriver.php b/lib/Benchmark/Metadata/Driver/ChainDriver.php new file mode 100644 index 000000000..b06e25657 --- /dev/null +++ b/lib/Benchmark/Metadata/Driver/ChainDriver.php @@ -0,0 +1,38 @@ +drivers = $drivers; + } + + /** + * {@inheritDoc} + */ + public function getMetadataForHierarchy(ReflectionHierarchy $classHierarchy): BenchmarkMetadata + { + $primaryReflection = $classHierarchy->getTop(); + $benchmark = new BenchmarkMetadata($primaryReflection->path, $primaryReflection->class); + + foreach ($this->drivers as $driver) { + $benchmark->merge($driver->getMetadataForHierarchy($classHierarchy)); + } + + return $benchmark; + } +} diff --git a/lib/Benchmark/Metadata/Driver/ConfigDriver.php b/lib/Benchmark/Metadata/Driver/ConfigDriver.php new file mode 100644 index 000000000..788e39a9a --- /dev/null +++ b/lib/Benchmark/Metadata/Driver/ConfigDriver.php @@ -0,0 +1,149 @@ +assert = $assert; + $this->executor = $executor; + $this->format = $format; + $this->iterations = $iterations; + $this->mode = $mode; + $this->timeUnit = $timeUnit; + $this->revs = $revs; + $this->timeout = $timeout; + $this->warmup = $warmup; + $this->innerDriver = $innerDriver; + $this->retryThreshold = $retryThreshold; + } + + + /** + * {@inheritDoc} + */ + public function getMetadataForHierarchy(ReflectionHierarchy $classHierarchy): BenchmarkMetadata + { + $metadata = $this->innerDriver->getMetadataForHierarchy($classHierarchy); + + foreach ($metadata->getSubjects() as $subject) { + $this->setDefaults($subject); + } + + return $metadata; + } + + private function setDefaults(SubjectMetadata $subject): void + { + if (empty($subject->getAssertions())) { + $subject->setAssertions($this->assert); + } + + if ($this->executor && null === $subject->getExecutor()) { + $subject->setExecutor(new ExecutorMetadata($this->executor, [])); + } + + if ($this->format && null === $subject->getFormat()) { + $subject->setFormat($this->format); + } + + if ($this->iterations && null === $subject->getIterations()) { + $subject->setIterations($this->iterations); + } + + if ($this->revs && null === $subject->getRevs()) { + $subject->setRevs($this->revs); + } + + if ($this->mode && null === $subject->getOutputMode()) { + $subject->setOutputMode($this->mode); + } + + if ($this->timeUnit && null === $subject->getOutputTimeUnit()) { + $subject->setOutputTimeUnit($this->timeUnit); + } + + if ($this->timeout && null === $subject->getTimeout()) { + $subject->setTimeout($this->timeout); + } + + if ($this->warmup && [0] === $subject->getWarmup()) { + $subject->setWarmup($this->warmup); + } + + if ($this->retryThreshold && null === $subject->getRetryThreshold()) { + $subject->setRetryThreshold($this->retryThreshold); + } + } +} diff --git a/lib/Benchmark/Metadata/DriverInterface.php b/lib/Benchmark/Metadata/DriverInterface.php new file mode 100644 index 000000000..8b3d66241 --- /dev/null +++ b/lib/Benchmark/Metadata/DriverInterface.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata; + +use PhpBench\Reflection\ReflectionHierarchy; + +/** + * Interface for metadata drivers. + */ +interface DriverInterface +{ + /** + * Return the metadata for the given class FQN. + */ + public function getMetadataForHierarchy(ReflectionHierarchy $classHierarchy): BenchmarkMetadata; +} diff --git a/lib/Benchmark/Metadata/ExecutorMetadata.php b/lib/Benchmark/Metadata/ExecutorMetadata.php new file mode 100644 index 000000000..249d5f8ce --- /dev/null +++ b/lib/Benchmark/Metadata/ExecutorMetadata.php @@ -0,0 +1,39 @@ +name = $name; + $this->config = $options; + } + + public function getName(): string + { + return $this->name; + } + + public function getConfig(): array + { + return $this->config; + } + + public function getRegistryConfig(): array + { + return array_merge($this->config, [ + 'executor' => $this->getName(), + ]); + } +} diff --git a/lib/Benchmark/Metadata/MetadataFactory.php b/lib/Benchmark/Metadata/MetadataFactory.php new file mode 100644 index 000000000..af2543563 --- /dev/null +++ b/lib/Benchmark/Metadata/MetadataFactory.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata; + +use PhpBench\Model\Subject; +use PhpBench\Reflection\ReflectionHierarchy; +use PhpBench\Reflection\ReflectorInterface; + +/** + * Benchmark Metadata Factory. + */ +class MetadataFactory +{ + /** + * @var ReflectorInterface + */ + private $reflector; + + /** + * @var DriverInterface + */ + private $driver; + + public function __construct(ReflectorInterface $reflector, DriverInterface $driver) + { + $this->reflector = $reflector; + $this->driver = $driver; + } + + /** + * Return a Benchmark instance for the given file or NULL if the + * given file contains no classes, or the class in the given file is + * abstract. + */ + public function getMetadataForFile(string $file): ?BenchmarkMetadata + { + $hierarchy = $this->reflector->reflect($file); + + if ($hierarchy->isEmpty()) { + return null; + } + + try { + $top = $hierarchy->getTop(); + } catch (\InvalidArgumentException $exception) { + return null; + } + + if (true === $top->abstract) { + return null; + } + + $metadata = $this->driver->getMetadataForHierarchy($hierarchy); + $this->validateBenchmark($hierarchy, $metadata); + + // validate the subject and load the parameter sets + foreach ($metadata->getSubjects() as $subject) { + $this->validateSubject($hierarchy, $subject); + $paramProviders = $subject->getParamProviders(); + + if (!$paramProviders) { + continue; + } + $parameterSets = $this->reflector->getParameterSets($metadata->getPath(), $paramProviders); + + foreach ($parameterSets as $parameterSet) { + if (!is_array($parameterSet)) { + throw new \InvalidArgumentException(sprintf( + 'Each parameter set must be an array, got "%s" for %s::%s', + gettype($parameterSet), + $metadata->getClass(), + $subject->getName() + )); + } + } + $subject->setParameterSets($parameterSets); + } + + return $metadata; + } + + private function validateSubject(ReflectionHierarchy $benchmarkReflection, SubjectMetadata $subject): void + { + foreach (['getBeforeMethods' => 'before', 'getAfterMethods' => 'after'] as $methodName => $context) { + foreach ($subject->$methodName() as $method) { + $this->validateMethodExists($context, $benchmarkReflection, $method); + } + } + } + + private function validateBenchmark(ReflectionHierarchy $hierarchy, BenchmarkMetadata $benchmark): void + { + foreach (['getBeforeClassMethods' => 'before class', 'getAfterClassMethods' => 'after class'] as $methodName => $context) { + foreach ($benchmark->$methodName() as $method) { + $this->validateMethodExists($context, $hierarchy, $method, true); + } + } + } + + private function validateMethodExists($context, ReflectionHierarchy $benchmarkReflection, $method, $isStatic = false): void + { + if (false === $benchmarkReflection->hasMethod($method)) { + throw new \InvalidArgumentException(sprintf( + 'Unknown %s method "%s" in benchmark class "%s"', + $context, $method, $benchmarkReflection->getTop()->class + )); + } + + if ($isStatic !== $benchmarkReflection->hasStaticMethod($method)) { + throw new \InvalidArgumentException(sprintf( + '%s method "%s" must %s static in benchmark class "%s"', + $context, $method, + $isStatic ? 'be' : 'not be', + $benchmarkReflection->getTop()->class + )); + } + } +} diff --git a/lib/Benchmark/Metadata/SubjectMetadata.php b/lib/Benchmark/Metadata/SubjectMetadata.php new file mode 100644 index 000000000..4e0c2e4b5 --- /dev/null +++ b/lib/Benchmark/Metadata/SubjectMetadata.php @@ -0,0 +1,422 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark\Metadata; + +/** + * Metadata for benchmarkMetadata subjects. + */ +class SubjectMetadata +{ + /** + * @var string + */ + private $name; + + /** + * @var array[] + */ + private $parameterSets = []; + + /** + * @var string[]|null + */ + private $groups; + + /** + * @var string[]|null + */ + private $beforeMethods; + + /** + * @var string[]|null + */ + private $afterMethods; + + /** + * @var string[]|null + */ + private $paramProviders; + + /** + * @var float|null + */ + private $retryThreshold; + + /** + * @var null|int[] + */ + private $iterations = null; + + /** + * @var null|int[] + */ + private $revs = null; + + /** + * @var null|int[] + */ + private $warmup; + + /** + * @var bool|null + */ + private $skip; + + /** + * @var int|null + */ + private $sleep; + + /** + * @var string|null + */ + private $outputTimeUnit = null; + + /** + * @var int|null + */ + private $outputTimePrecision = null; + + /** + * @var string|null + */ + private $outputMode = null; + + /** + * @var BenchmarkMetadata + */ + private $benchmarkMetadata; + + /** + * @var null|array + */ + private $assertions; + + /** + * @var ExecutorMetadata|null + */ + private $executorMetadata; + + /** + * @var float|null + */ + private $timeout; + + /** + * @var int|null + */ + private $retryLimit = null; + + /** + * @var string|null + */ + private $format = null; + + /** + */ + public function __construct(BenchmarkMetadata $benchmarkMetadata, string $name) + { + $this->name = $name; + $this->benchmarkMetadata = $benchmarkMetadata; + } + + /** + * Return the method name of this subject. + */ + public function getName(): string + { + return $this->name; + } + + /** + * Set the parameter sets for this subject. + * + * @param array[] $parameterSets + */ + public function setParameterSets(array $parameterSets): void + { + $this->parameterSets = $parameterSets; + } + + /** + * Return the parameter sets for this subject. + * + * @return array[] + */ + public function getParameterSets(): array + { + return $this->parameterSets; + } + + /** + * Return the benchmarkMetadata metadata for this subject. + */ + public function getBenchmark(): BenchmarkMetadata + { + return $this->benchmarkMetadata; + } + + /** + * @return string[] + */ + public function getGroups(): array + { + return $this->groups ?: []; + } + + public function inGroups(array $groups): bool + { + return (bool) count(array_intersect($this->groups, $groups)); + } + + public function setGroups(array $groups): void + { + $this->groups = $groups; + } + + public function getBeforeMethods(): array + { + return $this->beforeMethods ?: []; + } + + public function setBeforeMethods(array $beforeMethods): void + { + $this->beforeMethods = $beforeMethods; + } + + public function getAfterMethods(): array + { + return $this->afterMethods ?: []; + } + + public function setAfterMethods(array $afterMethods): void + { + $this->afterMethods = $afterMethods; + } + + public function getParamProviders(): array + { + return $this->paramProviders ?: []; + } + + public function setParamProviders(array $paramProviders): self + { + $this->paramProviders = $paramProviders; + + return $this; + } + + public function getIterations(): ?array + { + return $this->iterations; + } + + public function setIterations(array $iterations): void + { + $this->iterations = $iterations; + } + + public function getRevs(): ?array + { + return $this->revs; + } + + public function setRevs(array $revs): void + { + $this->revs = $revs; + } + + public function getSkip(): bool + { + return $this->skip ?: false; + } + + public function setSkip(bool $skip): void + { + $this->skip = $skip; + } + + public function getSleep(): int + { + return $this->sleep ?: 0; + } + + public function setSleep(int $sleep): void + { + $this->sleep = $sleep; + } + + public function getOutputTimeUnit(): ?string + { + return $this->outputTimeUnit; + } + + public function setOutputTimeUnit(?string $outputTimeUnit): void + { + $this->outputTimeUnit = $outputTimeUnit; + } + + public function getOutputTimePrecision(): ?int + { + return $this->outputTimePrecision; + } + + public function setOutputTimePrecision(?int $outputTimePrecision): void + { + $this->outputTimePrecision = $outputTimePrecision; + } + + public function getOutputMode(): ?string + { + return $this->outputMode; + } + + public function setOutputMode(?string $outputMode): void + { + $this->outputMode = $outputMode; + } + + /** + * @return int[] + */ + public function getWarmup(): array + { + return $this->warmup ?: [0]; + } + + /** + * @param int[] $warmup + */ + public function setWarmup(array $warmup): void + { + $this->warmup = $warmup; + } + + public function getRetryThreshold(): ?float + { + return $this->retryThreshold; + } + + public function setRetryThreshold(?float $retryThreshold): void + { + $this->retryThreshold = $retryThreshold; + } + + public function addAssertion(string $assertion): void + { + $this->assertions[] = $assertion; + } + + /** + * @param array $assertions + */ + public function setAssertions(array $assertions): void + { + $this->assertions = []; + + foreach ($assertions as $assertion) { + $this->addAssertion($assertion); + } + } + + /** + * @return string[] + */ + public function getAssertions(): array + { + return $this->assertions ?: []; + } + + public function setFormat(string $format): void + { + $this->format = $format; + } + + public function getFormat(): ?string + { + return $this->format; + } + + public function getExecutor(): ?ExecutorMetadata + { + return $this->executorMetadata; + } + + public function setExecutor(ExecutorMetadata $serviceMetadata): void + { + $this->executorMetadata = $serviceMetadata; + } + + public function getTimeout(): ?float + { + return $this->timeout; + } + + public function setTimeout(?float $timeout): void + { + $this->timeout = $timeout; + } + + public function setRetryLimit(int $retryLimit): void + { + $this->retryLimit = $retryLimit; + } + + public function getRetryLimit(): ?int + { + return $this->retryLimit; + } + + public function merge(SubjectMetadata $subject): void + { + // merge + foreach ([ + 'groups', + 'beforeMethods', + 'afterMethods', + 'paramProviders', + 'iterations', + 'revs', + 'assertions', + 'warmup', + ] as $toMerge) { + if ($subject->$toMerge === null) { + continue; + } + + if ($this->$toMerge === null) { + $this->$toMerge = $subject->$toMerge; + + continue; + } + $this->$toMerge = array_merge($this->$toMerge, $subject->$toMerge); + } + + // replace + foreach ([ + 'skip', + 'sleep', + 'outputTimeUnit', + 'outputTimePrecision', + 'outputMode', + 'retryThreshold', + 'executorMetadata', + 'timeout', + ] as $toReplace) { + if ($subject->$toReplace === null) { + continue; + } + $this->$toReplace = $subject->$toReplace; + } + } +} diff --git a/lib/Benchmark/Runner.php b/lib/Benchmark/Runner.php new file mode 100644 index 000000000..c0ea41500 --- /dev/null +++ b/lib/Benchmark/Runner.php @@ -0,0 +1,369 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark; + +use PhpBench\Assertion\AssertionProcessor; +use PhpBench\Benchmark\Exception\RetryLimitReachedException; +use PhpBench\Benchmark\Exception\StopOnErrorException; +use PhpBench\Benchmark\Metadata\BenchmarkMetadata; +use PhpBench\Benchmark\Metadata\SubjectMetadata; +use PhpBench\Environment\Supplier; +use PhpBench\Executor\BenchmarkExecutorInterface; +use PhpBench\Executor\ExecutionContext; +use PhpBench\Executor\HealthCheckInterface; +use PhpBench\Executor\MethodExecutorContext; +use PhpBench\Executor\MethodExecutorInterface; +use PhpBench\Model\Benchmark; +use PhpBench\Model\Iteration; +use PhpBench\Model\ResolvedExecutor; +use PhpBench\Model\Result\RejectionCountResult; +use PhpBench\Model\Subject; +use PhpBench\Model\Suite; +use PhpBench\Model\Variant; +use PhpBench\Progress\Logger\NullLogger; +use PhpBench\Progress\LoggerInterface; +use PhpBench\Registry\Config; +use PhpBench\Registry\ConfigurableRegistry; + +/** + * The benchmark runner. + */ +final class Runner +{ + const DEFAULT_ASSERTER = 'comparator'; + + /** + * @var ConfigurableRegistry + */ + private $executorRegistry; + + /** + * @var Supplier + */ + private $envSupplier; + + /** + * @var float + */ + private $retryThreshold; + + /** + * @var string + */ + private $configPath; + + /** + * @var AssertionProcessor + */ + private $assertionProcessor; + + /** + * @var LoggerInterface + */ + private $logger; + + public function __construct( + ConfigurableRegistry $executorRegistry, + Supplier $envSupplier, + AssertionProcessor $assertion, + float $retryThreshold = null, + string $configPath = null + ) { + $this->logger = new NullLogger(); + $this->executorRegistry = $executorRegistry; + $this->envSupplier = $envSupplier; + $this->retryThreshold = $retryThreshold; + $this->configPath = $configPath; + $this->assertionProcessor = $assertion; + } + + /** + * Set the progress logger to use. + * + */ + public function setProgressLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } + + /** + * Run all benchmarks (or all applicable benchmarks) in the given path. + * + * The $name argument will set the "name" attribute on the "suite" element. + * + * @param iterable $benchmarkMetadatas + */ + public function run(iterable $benchmarkMetadatas, RunnerConfig $config): Suite + { + $suite = new Suite( + $config->getTag(), + new \DateTime(), + $this->configPath + ); + $suite->setEnvInformations($this->envSupplier->getInformations()); + + // log the start of the suite run. + $this->logger->startSuite($suite); + + try { + /* @var BenchmarkMetadata $benchmarkMetadata */ + foreach ($benchmarkMetadatas as $benchmarkMetadata) { + $benchmark = $suite->createBenchmark($benchmarkMetadata->getClass()); + $this->runBenchmark($config, $benchmark, $benchmarkMetadata); + } + } catch (StopOnErrorException $e) { + } + + $suite->generateUuid(); + + $this->logger->endSuite($suite); + + return $suite; + } + + private function runBenchmark( + RunnerConfig $config, + Benchmark $benchmark, + BenchmarkMetadata $benchmarkMetadata + ): void { + // determine the executor + $executorConfig = $this->executorRegistry->getConfig($config->getExecutor()); + /** @var BenchmarkExecutorInterface $executor */ + $executor = $this->executorRegistry->getService( + $benchmarkMetadata->getExecutor() ? $benchmarkMetadata->getExecutor()->getName() : $executorConfig['executor'] + ); + + $this->executeBeforeMethods($benchmarkMetadata, $executor); + + $subjectMetadatas = array_filter($benchmarkMetadata->getSubjects(), function ($subjectMetadata) { + if ($subjectMetadata->getSkip()) { + return false; + } + + return true; + }); + + // the keys are subject names, convert them to numerical indexes. + $subjectMetadatas = array_values($subjectMetadatas); + + /** @var SubjectMetadata $subjectMetadata */ + foreach ($subjectMetadatas as $subjectMetadata) { + + // override parameters + $subjectMetadata->setIterations($config->getIterations($subjectMetadata->getIterations() ?? [1])); + $subjectMetadata->setRevs($config->getRevolutions($subjectMetadata->getRevs() ?? [1])); + $subjectMetadata->setWarmup($config->getWarmup($subjectMetadata->getWarmup())); + $subjectMetadata->setSleep($config->getSleep($subjectMetadata->getSleep())); + $subjectMetadata->setRetryThreshold($config->getRetryThreshold($subjectMetadata->getRetryThreshold())); + + if ($config->getAssertions()) { + $subjectMetadata->setAssertions($config->getAssertions()); + } + + if (null !== $config->getFormat()) { + $subjectMetadata->setFormat($config->getFormat()); + } + + // resolve executor config for this subject + $executorConfig = $this->executorRegistry->getConfig($config->getExecutor()); + + if ($executorMetadata = $subjectMetadata->getExecutor()) { + /** @var BenchmarkExecutorInterface $executor */ + $executor = $this->executorRegistry->getService($executorMetadata->getName()); + $executorConfig = $this->executorRegistry->getConfig($executorMetadata->getRegistryConfig()); + } + $resolvedExecutor = ResolvedExecutor::fromNameAndConfig($executorConfig['executor'], $executorConfig); + + $benchmark->createSubjectFromMetadataAndExecutor($subjectMetadata, $resolvedExecutor); + } + + $this->logger->benchmarkStart($benchmark); + + foreach ($benchmark->getSubjects() as $index => $subject) { + $subjectMetadata = $subjectMetadatas[$index]; + + $this->logger->subjectStart($subject); + $this->runSubject($executor, $config, $subject, $subjectMetadata); + $this->logger->subjectEnd($subject); + } + $this->logger->benchmarkEnd($benchmark); + + $this->executeAfterMethods($benchmarkMetadata, $executor); + } + + private function executeBeforeMethods(BenchmarkMetadata $benchmarkMetadata, BenchmarkExecutorInterface $executor): void + { + if (!$executor instanceof MethodExecutorInterface) { + return; + } + + if (!$benchmarkMetadata->getBeforeClassMethods()) { + return; + } + + $executor->executeMethods( + MethodExecutorContext::fromBenchmarkMetadata($benchmarkMetadata), + $benchmarkMetadata->getBeforeClassMethods() + ); + } + + private function executeAfterMethods(BenchmarkMetadata $benchmarkMetadata, BenchmarkExecutorInterface $executor): void + { + if (!$executor instanceof MethodExecutorInterface) { + return; + } + + if (!$benchmarkMetadata->getAfterClassMethods()) { + return; + } + + $executor->executeMethods( + MethodExecutorContext::fromBenchmarkMetadata($benchmarkMetadata), + $benchmarkMetadata->getAfterClassMethods() + ); + } + + private function runSubject(BenchmarkExecutorInterface $executor, RunnerConfig $config, Subject $subject, SubjectMetadata $subjectMetadata): Subject + { + if ($executor instanceof HealthCheckInterface) { + $executor->healthCheck(); + } + + $parameterSets = $config->getParameterSets($subjectMetadata->getParameterSets()); + $paramsIterator = new CartesianParameterIterator($parameterSets); + + // create the variants. + foreach ($paramsIterator as $parameterSet) { + foreach ($subjectMetadata->getIterations() as $nbIterations) { + foreach ($subjectMetadata->getRevs() as $revolutions) { + foreach ($subjectMetadata->getWarmup() as $warmup) { + $variant = $subject->createVariant($parameterSet, $revolutions, $warmup); + $variant->spawnIterations($nbIterations); + } + } + } + } + + // run the variants. + $stopException = null; + + foreach ($subject->getVariants() as $variant) { + if ($stopException) { + $subject->remove($variant); + + continue; + } + + try { + $this->runVariant($executor, $subject->getExecutor()->getConfig(), $config, $subjectMetadata, $variant); + } catch (StopOnErrorException $stopException) { + } + } + + if ($stopException) { + throw $stopException; + } + + return $subject; + } + + private function runVariant( + BenchmarkExecutorInterface $executor, + Config $executorConfig, + RunnerConfig $config, + SubjectMetadata $subjectMetadata, + Variant $variant + ): void { + $this->logger->variantStart($variant); + $rejectCount = []; + + if ($baseline = $config->getBaselines()->findBaselineForVariant($variant)) { + $variant->attachBaseline($baseline); + } + + try { + foreach ($variant->getIterations() as $iteration) { + $rejectCount[spl_object_hash($iteration)] = 0; + $this->runIteration($executor, $executorConfig, $iteration, $subjectMetadata); + } + } catch (\Exception $e) { + $variant->setException($e); + $this->logger->variantEnd($variant); + + if ($config->getStopOnError()) { + throw new StopOnErrorException(); + } + + return; + } + + $this->endVariant($subjectMetadata, $variant); + + while ($variant->getRejectCount() > 0) { + $this->logger->retryStart($variant->getRejectCount()); + $this->logger->variantStart($variant); + + foreach ($variant->getRejects() as $reject) { + $rejectCount[spl_object_hash($reject)]++; + + if ($subjectMetadata->getRetryLimit() && $rejectCount[spl_object_hash($reject)] > $subjectMetadata->getRetryLimit()) { + throw new RetryLimitReachedException(sprintf( + 'Retry limit of %s exceeded', + $subjectMetadata->getRetryLimit() + )); + } + + $this->runIteration($executor, $executorConfig, $reject, $subjectMetadata); + } + $this->endVariant($subjectMetadata, $variant); + + if (!isset($reject)) { + continue; + } + + $reject->setResult(new RejectionCountResult($rejectCount[spl_object_hash($reject)])); + } + } + + private function endVariant(SubjectMetadata $subjectMetadata, Variant $variant): void + { + $variant->computeStats(); + $variant->resetAssertionResults(); + + foreach ($subjectMetadata->getAssertions() as $assertion) { + $result = $this->assertionProcessor->assert($variant, $assertion); + $variant->addAssertionResult($result); + } + + $this->logger->variantEnd($variant); + } + + public function runIteration(BenchmarkExecutorInterface $executor, Config $executorConfig, Iteration $iteration, SubjectMetadata $subjectMetadata): void + { + $this->logger->iterationStart($iteration); + + $results = $executor->execute(ExecutionContext::fromSubjectMetadataAndIteration($subjectMetadata, $iteration), $executorConfig); + + foreach ($results as $result) { + $iteration->setResult($result); + } + + $sleep = $subjectMetadata->getSleep(); + + if ($sleep) { + usleep($sleep); + } + + $this->logger->iterationEnd($iteration); + } +} diff --git a/lib/Benchmark/RunnerConfig.php b/lib/Benchmark/RunnerConfig.php new file mode 100644 index 000000000..110f5fb3a --- /dev/null +++ b/lib/Benchmark/RunnerConfig.php @@ -0,0 +1,430 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark; + +use InvalidArgumentException; +use PhpBench\Model\SuiteCollection; + +/** + * The benchmark runner context. + */ +class RunnerConfig +{ + /** + * @var string|array + */ + private $executor = 'remote'; + + /** + * @var string|null + */ + private $tag; + + /** + * @var array + */ + private $filters = []; + + /** + * @var string[] + */ + private $groups = []; + + /** + * @var int[] + */ + private $iterations = []; + + /** + * @var int[] + */ + private $revolutions = []; + + /** + * @var float + */ + private $retryThreshold; + + /** + * @var int + */ + private $sleep; + + /** + * @var int[] + */ + private $warmup = []; + + /** + * @var int + */ + private $outputTimePrecision; + + /** + * @var string + */ + private $outputTimeUnit; + + /** + * @var bool + */ + private $stopOnError = true; + + /** + * @var array|null + */ + private $assertions; + + /** + * @var string|null + */ + private $format; + + /** + * @var array + */ + private $parameters = []; + + /** + * @var SuiteCollection + */ + private $baselines; + + private function __construct() + { + $this->baselines = new SuiteCollection(); + } + + public static function create(): self + { + return new self(); + } + + public function merge(self $config): self + { + $default = new self(); + $new = clone $this; + + /** @phpstan-ignore-next-line Phpstan doesn't understand this + and it's rather ugly in anycase */ + foreach ($config as $property => $value) { + if ($value !== $default->$property) { + $new->$property = $value; + } + } + + return $new; + } + + /** + * Return the name to assign to this suite. + */ + public function getTag(): ?string + { + return $this->tag; + } + + /** + * Override the number of iterations to execute. + */ + public function getIterations($default = null): array + { + return $this->iterations ?: $default; + } + + /** + * Get the number of rev(olutions) to run. + * + * @param array $default + */ + public function getRevolutions(array $default = null): ?array + { + return $this->revolutions ?: $default; + } + + /** + * Return the number of warmup revolutions that should be exectuted. + * + * @param array $default + */ + public function getWarmup(array $default = null): ?array + { + return $this->warmup ?: $default; + } + + /** + * Override parameters. + * + * @return mixed[] + */ + public function getParameterSets($default = null) + { + $parameters = $this->parameters ? [[$this->parameters]] : $default; + + if (!$parameters) { + return [[[]]]; + } + + return $parameters; + } + + /** + * Override the sleep interval (in microseconds). + * + */ + public function getSleep($default = null) + { + if (null === $this->sleep) { + return $default; + } + + return $this->sleep; + } + + /** + * Get the deviation threshold beyond which the iteration should + * be retried. + * + * A value of NULL will disable retry. + */ + public function getRetryThreshold(float $default = null): ?float + { + return $this->retryThreshold ?: $default; + } + + /** + * Return the output time unit. + */ + public function getOutputTimeUnit(string $default = null): ?string + { + return $this->outputTimeUnit ?: $default; + } + + /** + * Return the output time precision. + * + * @return string + */ + public function getOutputTimePrecision(string $default = null) + { + return $this->outputTimePrecision ?: $default; + } + + /** + * Return either an executor configuration name or an actual configuration. + * + * @return array|string + */ + public function getExecutor() + { + return $this->executor; + } + + /** + * Return true if the runner should exit on the first exception encountered. + * + * @retrun bool + */ + public function getStopOnError(): bool + { + return $this->stopOnError; + } + + /** + * Return assertions (which will override any metadata based assertions). + */ + public function getAssertions(): array + { + return $this->assertions ?: []; + } + + public function withExecutor($executor = null): self + { + $new = clone $this; + $new->executor = $executor; + + return $new; + } + + public function withTag(string $tag = null): self + { + $new = clone $this; + $new->tag = $tag; + + return $new; + } + + public function withFilters(array $filters = null): self + { + $new = clone $this; + $new->filters = $filters; + + return $new; + } + + public function withGroups(array $groups = null): self + { + $new = clone $this; + $new->groups = $groups; + + return $new; + } + + public function withIterations(array $iterations = null): self + { + $this->assertArrayValuesGreaterThanZero($iterations); + + $new = clone $this; + $new->iterations = $iterations; + + return $new; + } + + public function withRevolutions(array $revolutions = null): self + { + $this->assertArrayValuesGreaterThanZero('revs', $revolutions); + + $new = clone $this; + $new->revolutions = $revolutions; + + return $new; + } + + public function withParameters(array $parameters = null): self + { + $new = clone $this; + $new->parameters = $parameters; + + return $new; + } + + public function withRetryThreshold(float $retryThreshold = null): self + { + $this->assertGreaterThanZero('retry threshold', $retryThreshold); + + $new = clone $this; + $new->retryThreshold = $retryThreshold; + + return $new; + } + + public function withSleep(int $sleep = null): self + { + $this->assertGreaterThanZero('sleep', $sleep); + + $new = clone $this; + $new->sleep = $sleep; + + return $new; + } + + public function withWarmup(array $warmup = null): self + { + $this->assertArrayValuesGreaterThanZero('warmup', $warmup); + + $new = clone $this; + $new->warmup = $warmup; + + return $new; + } + + public function withOutputTimePrecision(int $outputTimePrecision = null): self + { + $new = clone $this; + $new->outputTimePrecision = $outputTimePrecision; + + return $new; + } + + public function withOutputTimeUnit(string $outputTimeUnit = null): self + { + $new = clone $this; + $new->outputTimeUnit = $outputTimeUnit; + + return $new; + } + + public function withStopOnError(bool $stopOnError = null): self + { + $new = clone $this; + $new->stopOnError = $stopOnError; + + return $new; + } + + public function withAssertions(array $assertions = null): self + { + $new = clone $this; + $new->assertions = $assertions; + + return $new; + } + + public function withFormat(?string $format = null): self + { + $new = clone $this; + $new->format = $format; + + return $new; + } + + public function getFormat(): ?string + { + return $this->format ?: null; + } + + public function withBaselines(SuiteCollection $baselines): self + { + $new = clone($this); + $new->baselines = $baselines; + + return $new; + } + + public function getBaselines(): SuiteCollection + { + return $this->baselines; + } + + private function assertArrayValuesGreaterThanZero($field, array $values = []): void + { + $values = array_filter($values, function ($value) { + return $value <= 0; + }); + + if (empty($values)) { + return; + } + + throw new InvalidArgumentException(sprintf( + 'All values for "%s" must be greater than 0, the following were less than 0 "%s"', + $field, implode('", "', $values) + )); + } + + private function assertGreaterThanZero(string $field, float $value = null): void + { + if (null === $value) { + return; + } + + if ($value > 0) { + return; + } + + throw new InvalidArgumentException(sprintf( + '"%s" must be greater than 0, got "%s"', + $field, $value + )); + } +} diff --git a/lib/Benchmark/SamplerManager.php b/lib/Benchmark/SamplerManager.php new file mode 100644 index 000000000..d1c4b4511 --- /dev/null +++ b/lib/Benchmark/SamplerManager.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Benchmark; + +/** + * The sampler manager is responsible for collecting and executing + * sampler benchmarks. + * + * Baseline benchmarks are standard microbenchmarks which can be used to + * determine the "sampler" performance of the test platform. + * + * These measurements can be used to establish a sampler speed for the system, + * or to provide counterweights to iteration measurements (and so attempt to + * cancel out any fluctuations of the test platforms performance). + */ +class SamplerManager +{ + /** + * @var mixed[] + */ + private $callables; + + /** + * Add a sampler callable. The callable can be any + * callable accepted by call_user_func. + * + * Throws an invalid argument exception if the name has + * already been registered. + * + * + * @throws \InvalidArgumentException + */ + public function addSamplerCallable(string $name, $callable): void + { + if (isset($this->callables[$name])) { + throw new \InvalidArgumentException(sprintf( + 'Baseline callable "%s" has already been registered.', + $name + )); + } + + if (!is_callable($callable)) { + throw new \InvalidArgumentException(sprintf( + 'Given sampler "%s" callable "%s" is not callable.', + $name, is_string($callable) ? $callable : gettype($callable) + )); + } + + $this->callables[$name] = $callable; + } + + /** + * Return mean time taken to execute the named sampler + * callable in microseconds. + */ + public function sample($name, $revs): float + { + if (!isset($this->callables[$name])) { + throw new \InvalidArgumentException(sprintf( + 'Unknown sampler callable "%s", known baseline callables: "%s"', + $name, implode('", "', array_keys($this->callables)) + )); + } + + $start = microtime(true); + call_user_func($this->callables[$name], $revs); + + return (microtime(true) - $start) / $revs * 1E6; + } +} diff --git a/lib/Compat/SymfonyOptionsResolverCompat.php b/lib/Compat/SymfonyOptionsResolverCompat.php new file mode 100644 index 000000000..78444e6c5 --- /dev/null +++ b/lib/Compat/SymfonyOptionsResolverCompat.php @@ -0,0 +1,23 @@ + $infoMap + */ + public static function setInfos(OptionsResolver $resolver, array $infoMap): void + { + if (!method_exists($resolver, 'setInfo')) { + return; + } + + foreach ($infoMap as $option => $help) { + $resolver->setInfo($option, $help); + } + } +} diff --git a/lib/Console/Application.php b/lib/Console/Application.php new file mode 100644 index 000000000..5ef8b5e5b --- /dev/null +++ b/lib/Console/Application.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Console; + +use PhpBench\PhpBench; +use Symfony\Component\Console\Application as BaseApplication; +use Symfony\Component\Console\Input\InputOption; + +/** + * PhpBench application. + * + * @author Daniel Leech + */ +class Application extends BaseApplication +{ + public function __construct() + { + parent::__construct( + 'phpbench', + PhpBench::VERSION + ); + } + + /** + * {@inheritdoc} + */ + protected function getDefaultInputDefinition(): \Symfony\Component\Console\Input\InputDefinition + { + $default = parent::getDefaultInputDefinition(); + $default->addOptions([ + new InputOption('--profile', null, InputOption::VALUE_REQUIRED, 'Configuration file'), + new InputOption('--config', null, InputOption::VALUE_REQUIRED, 'Use the specified configuration profile'), + new InputOption('--extension', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Enable an extension'), + new InputOption('--theme', null, InputOption::VALUE_REQUIRED, 'Theme'), + new InputOption('--working-dir', null, InputOption::VALUE_REQUIRED, 'Working directory'), + ]); + + return $default; + } +} diff --git a/lib/Console/CharacterReader.php b/lib/Console/CharacterReader.php new file mode 100644 index 000000000..0ffb9691d --- /dev/null +++ b/lib/Console/CharacterReader.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Console; + +class CharacterReader +{ + /** + * If readline is installed, then prevent the user having to + * press in order to paginate. + */ + public function __construct() + { + // we could use extension_loaded but HHVM returns true and + // still doesn't have this function.. + if (function_exists('readline_callback_handler_install')) { + readline_callback_handler_install('', function (): void { + }); + } + } + + /** + * Wait for a single character input and return it. + * + * @return ?string + */ + public function read(): ?string + { + while (false !== $character = fgetc(STDIN)) { + return $character; + } + + return null; + } +} diff --git a/lib/Console/Command/ConfigReferenceCommand.php b/lib/Console/Command/ConfigReferenceCommand.php new file mode 100644 index 000000000..fae68f0ee --- /dev/null +++ b/lib/Console/Command/ConfigReferenceCommand.php @@ -0,0 +1,40 @@ +dumper = $dumper; + $this->stdout = $stdout; + } + + protected function configure(): void + { + $this->setName('config:dump-reference'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->stdout->writeln($this->dumper->dump(), OutputInterface::OUTPUT_RAW); + + return 0; + } +} diff --git a/lib/Console/Command/EvaluateCommand.php b/lib/Console/Command/EvaluateCommand.php new file mode 100644 index 000000000..0afa20f55 --- /dev/null +++ b/lib/Console/Command/EvaluateCommand.php @@ -0,0 +1,119 @@ +lexer = $lexer; + $this->parser = $parser; + $this->evaluator = $evaluator; + $this->printer = $printer; + $this->evalPrinter = $evalPrinter; + $this->stdout = $stdout; + } + + public function configure(): void + { + $this->setName('eval'); + $this->setDescription('Evaluate an expression with the PHPBench expression language'); + $this->addArgument(self::ARG_EXPR, InputArgument::REQUIRED); + $this->addArgument(self::ARG_PARAMS, InputArgument::OPTIONAL, 'JSON encoded parameters'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $expr = $input->getArgument(self::ARG_EXPR); + assert(is_string($expr)); + $params = $input->getArgument(self::ARG_PARAMS); + assert(is_string($params) || is_null($params)); + + $tokens = $this->lexer->lex($expr); + $node = $this->parser->parse($tokens); + $params = $this->resolveParams($params); + $evaluated = $this->evaluator->evaluate($node, $params); + $this->stdout->writeln( + sprintf( + "%s\n= %s\n= %s", + $this->printer->print($node), + $this->evalPrinter->withParams($params)->print($node), + $this->printer->print($evaluated) + ) + ); + + return 0; + } + + /** + * @return array + */ + private function resolveParams(?string $params): array + { + if (null === $params) { + return []; + } + + if (null === $params = json_decode($params, true)) { + throw new RuntimeException(sprintf( + 'Could not decode JSON: %s', + json_last_error_msg() + )); + } + + return $params; + } +} diff --git a/lib/Console/Command/Handler/DumpHandler.php b/lib/Console/Command/Handler/DumpHandler.php new file mode 100644 index 000000000..579789700 --- /dev/null +++ b/lib/Console/Command/Handler/DumpHandler.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Console\Command\Handler; + +use PhpBench\Model\SuiteCollection; +use PhpBench\Serializer\XmlEncoder; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class DumpHandler +{ + public const OPT_DUMP_FILE = 'dump-file'; + public const OPT_DUMP = 'dump'; + + /** + * @var XmlEncoder + */ + private $xmlEncoder; + + /** + * @var OutputInterface + */ + private $stdout; + + public function __construct(XmlEncoder $xmlEncoder, OutputInterface $stdout) + { + $this->xmlEncoder = $xmlEncoder; + $this->stdout = $stdout; + } + + public static function configure(Command $command): void + { + $command->addOption(self::OPT_DUMP_FILE, 'd', InputOption::VALUE_OPTIONAL, 'Dump XML result to named file'); + $command->addOption(self::OPT_DUMP, null, InputOption::VALUE_NONE, 'Dump XML result to stdout and suppress all other output'); + } + + public function dumpFromInput(InputInterface $input, OutputInterface $output, SuiteCollection $collection): void + { + if (false === $input->getOption(self::OPT_DUMP_FILE) && false === $input->getOption(self::OPT_DUMP)) { + return; + } + + $dom = $this->xmlEncoder->encode($collection); + + if ($dumpFile = $input->getOption(self::OPT_DUMP_FILE)) { + $xml = (string)$dom->dump(); + file_put_contents($dumpFile, $xml); + $output->writeln('Dumped result to ' . $dumpFile); + } + + if ($input->getOption(self::OPT_DUMP)) { + $xml = $dom->dump(); + $this->stdout->write($xml); + } + } +} diff --git a/lib/Console/Command/Handler/ReportHandler.php b/lib/Console/Command/Handler/ReportHandler.php new file mode 100644 index 000000000..a511179e2 --- /dev/null +++ b/lib/Console/Command/Handler/ReportHandler.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Console\Command\Handler; + +use PhpBench\Model\SuiteCollection; +use PhpBench\Report\ReportManager; +use PhpBench\Storage\StorageRegistry; +use PhpBench\Util\Cast; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; + +class ReportHandler +{ + public const OPT_REPORT = 'report'; + public const OPT_OUTPUT = 'output'; + + /** + * @var ReportManager + */ + private $reportManager; + + /** + * @var StorageRegistry + */ + private $storageRegistry; + + public function __construct(ReportManager $reportManager) + { + $this->reportManager = $reportManager; + } + + public static function configure(Command $command): void + { + $command->addOption(self::OPT_REPORT, null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Report name or configuration in JSON format'); + $command->addOption(self::OPT_OUTPUT, 'o', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Specify output', ['console']); + } + + public function validateReportsFromInput(InputInterface $input): void + { + $this->reportManager->validateReportNames(Cast::toStrings((array)$input->getOption(self::OPT_REPORT))); + } + + public function reportsFromInput(InputInterface $input, SuiteCollection $collection): void + { + $reports = Cast::toStrings((array)$input->getOption(self::OPT_REPORT)); + $outputs = Cast::toStrings((array)$input->getOption(self::OPT_OUTPUT)); + + $this->reportManager->renderReports($collection, $reports, $outputs); + } +} diff --git a/lib/Console/Command/Handler/RunnerHandler.php b/lib/Console/Command/Handler/RunnerHandler.php new file mode 100644 index 000000000..ba8c5b52d --- /dev/null +++ b/lib/Console/Command/Handler/RunnerHandler.php @@ -0,0 +1,163 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Console\Command\Handler; + +use InvalidArgumentException; +use PhpBench\Benchmark\BenchmarkFinder; +use PhpBench\Benchmark\Runner; +use PhpBench\Benchmark\RunnerConfig; +use PhpBench\Model\Suite; +use PhpBench\Progress\LoggerRegistry; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class RunnerHandler +{ + public const ARG_PATH = 'path'; + public const OPT_FILTER = 'filter'; + public const OPT_GROUP = 'group'; + public const OPT_PARAMETERS = 'parameters'; + public const OPT_ASSERT = 'assert'; + public const OPT_REVS = 'revs'; + public const OPT_PROGRESS = 'progress'; + public const OPT_BOOTSTRAP = 'bootstrap'; + public const OPT_EXECUTOR = 'executor'; + public const OPT_STOP_ON_ERROR = 'stop-on-error'; + public const OPT_PHP_BIN = 'php-binary'; + public const OPT_PHP_CONFIG = 'php-config'; + public const OPT_PHP_WRAPPER = 'php-wrapper'; + public const OPT_PHP_DISABLE_INI = 'php-disable-ini'; + const OPT_FORMAT = 'format'; + + /** + * @var LoggerRegistry + */ + private $loggerRegistry; + + /** + * @var string|null + */ + private $defaultProgress; + + /** + * @var array + */ + private $benchPaths; + + /** + * @var Runner + */ + private $runner; + + /** + * @var BenchmarkFinder + */ + private $finder; + + public function __construct( + Runner $runner, + LoggerRegistry $loggerRegistry, + BenchmarkFinder $finder, + ?string $defaultProgress = null, + array $benchPaths = null + ) { + $this->runner = $runner; + $this->loggerRegistry = $loggerRegistry; + $this->defaultProgress = $defaultProgress; + $this->benchPaths = $benchPaths; + $this->finder = $finder; + } + + public static function configure(Command $command): void + { + $command->addArgument(self::ARG_PATH, InputArgument::OPTIONAL, 'Path to benchmark(s)'); + $command->addOption(self::OPT_FILTER, [], InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore all benchmarks not matching command filter (can be a regex)'); + $command->addOption(self::OPT_GROUP, [], InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Group to run (can be specified multiple times)'); + $command->addOption(self::OPT_PARAMETERS, null, InputOption::VALUE_REQUIRED, 'Override parameters to use in (all) benchmarks'); + $command->addOption(self::OPT_ASSERT, null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Override assertions'); + $command->addOption(self::OPT_FORMAT, null, InputOption::VALUE_REQUIRED, 'Set progress logger format'); + $command->addOption(self::OPT_REVS, null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Override number of revs (revolutions) on (all) benchmarks'); + $command->addOption(self::OPT_PROGRESS, 'l', InputOption::VALUE_REQUIRED, 'Progress logger to use'); + + // command option is parsed before the container is compiled. + $command->addOption(self::OPT_BOOTSTRAP, 'b', InputOption::VALUE_REQUIRED, 'Set or override the bootstrap file.'); + $command->addOption(self::OPT_EXECUTOR, [], InputOption::VALUE_REQUIRED, 'Executor to use', 'remote'); + $command->addOption(self::OPT_STOP_ON_ERROR, [], InputOption::VALUE_NONE, 'Stop on the first error encountered'); + + // Launcher options (processed in PhpBench.php before the container is initialized). + $command->addOption(self::OPT_PHP_BIN, null, InputOption::VALUE_REQUIRED, 'Alternative PHP binary to use'); + $command->addOption(self::OPT_PHP_CONFIG, null, InputOption::VALUE_REQUIRED, 'JSON-like object of PHP INI settings'); + $command->addOption(self::OPT_PHP_WRAPPER, null, InputOption::VALUE_REQUIRED, 'Prefix process launch command with this string'); + $command->addOption(self::OPT_PHP_DISABLE_INI, null, InputOption::VALUE_NONE, 'Do not load the PHP INI file'); + } + + public function runFromInput(InputInterface $input, OutputInterface $output, RunnerConfig $config): Suite + { + $default = RunnerConfig::create() + ->withRevolutions($input->getOption(self::OPT_REVS)) + ->withParameters($this->getParameters($input->getOption(self::OPT_PARAMETERS))) + ->withExecutor($input->getOption(self::OPT_EXECUTOR)) + ->withStopOnError($input->getOption(self::OPT_STOP_ON_ERROR)) + ->withAssertions($input->getOption(self::OPT_ASSERT)) + ->withFormat($input->getOption(self::OPT_FORMAT)); + + $config = $default->merge($config); + + $progressLoggerName = $input->getOption(self::OPT_PROGRESS) ?: $this->defaultProgress; + + if ($input->hasOption('quiet') && $input->getOption('quiet')) { + $progressLoggerName = 'none'; + } + + $progressLogger = $this->loggerRegistry->getProgressLogger($progressLoggerName); + $this->runner->setProgressLogger($progressLogger); + + $paths = (array)($input->getArgument(self::ARG_PATH) ?: $this->benchPaths); + + if (empty($paths)) { + throw new InvalidArgumentException( + 'You must either specify or configure a path' + ); + } + + return $this->runner->run($this->finder->findBenchmarks( + $paths, + $input->getOption(self::OPT_FILTER), + $input->getOption(self::OPT_GROUP) + ), $config); + } + + private function getParameters($parametersJson) + { + if (null === $parametersJson) { + return; + } + + $parameters = []; + + if ($parametersJson) { + $parameters = json_decode($parametersJson, true); + + if (null === $parameters) { + throw new \InvalidArgumentException(sprintf( + 'Could not decode parameters JSON string: "%s"', $parametersJson + )); + } + } + + return $parameters; + } +} diff --git a/lib/Console/Command/Handler/SuiteCollectionHandler.php b/lib/Console/Command/Handler/SuiteCollectionHandler.php new file mode 100644 index 000000000..50b0d5b23 --- /dev/null +++ b/lib/Console/Command/Handler/SuiteCollectionHandler.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Console\Command\Handler; + +use PhpBench\Model\SuiteCollection; +use PhpBench\Serializer\XmlDecoder; +use PhpBench\Storage\StorageRegistry; +use PhpBench\Storage\UuidResolver; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Webmozart\Assert\Assert; + +class SuiteCollectionHandler +{ + /** + * @var XmlDecoder + */ + private $xmlDecoder; + + /** + * @var StorageRegistry + */ + private $storage; + + /** + * @var UuidResolver + */ + private $refResolver; + + public function __construct( + XmlDecoder $xmlDecoder, + StorageRegistry $storage, + UuidResolver $refResolver + ) { + $this->xmlDecoder = $xmlDecoder; + $this->storage = $storage; + $this->refResolver = $refResolver; + } + + public static function configure(Command $command): void + { + $command->addOption('ref', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Reference to an existing run - can be a UUID or tag or special word (e.g. latest)'); + $command->addOption('file', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Report XML file'); + } + + public function suiteCollectionFromInput(InputInterface $input): SuiteCollection + { + $files = $input->getOption('file'); + $refs = $input->getOption('ref'); + assert(is_array($files)); + assert(is_array($refs)); + + if (!$files && !$refs) { + throw new \InvalidArgumentException( + 'You must specify at least one of `--file` and/or `--ref`' + ); + } + + $collection = new SuiteCollection(); + + if ($files) { + $collection->mergeCollection( + $this->xmlDecoder->decodeFiles($files) + ); + } + + if ($refs) { + foreach ($refs as $ref) { + $collection->mergeCollection($this->storage->getService()->fetch( + $this->refResolver->resolve($ref) + )); + } + } + + return $collection; + } +} diff --git a/lib/Console/Command/Handler/TimeUnitHandler.php b/lib/Console/Command/Handler/TimeUnitHandler.php new file mode 100644 index 000000000..62c7e7067 --- /dev/null +++ b/lib/Console/Command/Handler/TimeUnitHandler.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Console\Command\Handler; + +use PhpBench\Util\Cast; +use PhpBench\Util\TimeUnit; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; + +class TimeUnitHandler +{ + /** + * @var TimeUnit + */ + private $timeUnit; + + public function __construct( + TimeUnit $timeUnit + ) { + $this->timeUnit = $timeUnit; + } + + public static function configure(Command $command): void + { + $command->addOption('time-unit', null, InputOption::VALUE_REQUIRED, 'Override the time unit'); + $command->addOption('precision', null, InputOption::VALUE_REQUIRED, 'Override the measurement precision'); + $command->addOption('mode', null, InputOption::VALUE_REQUIRED, 'Override the unit display mode ("throughput", "time")'); + } + + public function timeUnitFromInput(InputInterface $input): void + { + $timeUnit = Cast::toStringOrNull($input->getOption('time-unit')); + $mode = Cast::toStringOrNull($input->getOption('mode')); + $precision = Cast::toIntOrNull($input->getOption('precision')); + + if ($timeUnit) { + $this->timeUnit->overrideDestUnit($timeUnit); + } + + if ($mode) { + $this->timeUnit->overrideMode($mode); + } + + if (null !== $precision) { + $this->timeUnit->overridePrecision($precision); + } + } +} diff --git a/lib/Console/Command/LogCommand.php b/lib/Console/Command/LogCommand.php new file mode 100644 index 000000000..f36eec6b8 --- /dev/null +++ b/lib/Console/Command/LogCommand.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Console\Command; + +use PhpBench\Console\Application; +use PhpBench\Console\CharacterReader; +use PhpBench\Console\Command\Handler\TimeUnitHandler; +use PhpBench\Registry\Registry; +use PhpBench\Util\TimeUnit; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Terminal; + +class LogCommand extends Command +{ + private $storage; + private $timeUnit; + private $timeUnitHandler; + private $characterReader; + + /** + * @var OutputInterface + */ + private $stdout; + + public function __construct( + Registry $storage, + TimeUnit $timeUnit, + TimeUnitHandler $timeUnitHandler, + CharacterReader $characterReader = null, + OutputInterface $stdout + ) { + parent::__construct(); + $this->storage = $storage; + $this->timeUnitHandler = $timeUnitHandler; + $this->timeUnit = $timeUnit; + $this->characterReader = $characterReader ?: new CharacterReader(); + $this->stdout = $stdout; + } + + public function configure(): void + { + $this->setName('log'); + $this->setDescription('List previously executed and stored benchmark runs.'); + $this->setHelp(<<<'EOT' +Show a list of previously executed benchmark runs. + + $ %command.full_name% + +NOTE: This is only possible when a storage driver has been configured. +EOT + ); + // allow common time unit options + TimeUnitHandler::configure($this); + + $this->addOption('no-pagination', 'P', InputOption::VALUE_NONE, 'Do not paginate'); + } + + public function execute(InputInterface $input, OutputInterface $output): int + { + $this->timeUnitHandler->timeUnitFromInput($input); + $paginate = false === $input->getOption('no-pagination'); + + // if we have an application, get the terminal dimensions, if the + // terminal dimensions are null then set the height to the arbitrary + // value of 100. + $height = 100; + + /** @var Application|null $application */ + $application = $this->getApplication(); + + if ($application) { + $height = (new Terminal())->getHeight(); + $height = $height ?: 100; + } + + $height -= 1; // reduce height by one to accommodate the pagination prompt + $nbRows = 0; + $totalRows = 0; + + foreach ($this->storage->getService()->history() as $entry) { + $lines = []; + $lines[] = sprintf('run %s', $entry->getRunId()); + $lines[] = sprintf('Date: ' . $entry->getDate()->format('c')); + $lines[] = sprintf('Branch: ' . $entry->getVcsBranch()); + $lines[] = sprintf('Tag: ' . ($entry->getTag() ?: '')); + $lines[] = sprintf('Scale: ' . '%d subjects, %d iterations, %d revolutions', $entry->getNbSubjects(), $entry->getNbIterations(), $entry->getNbRevolutions()); + + $lines[] = sprintf( + 'Summary: (best [mean] worst) = %s [%s] %s (%s)', + number_format($this->timeUnit->toDestUnit($entry->getMinTime()), 3), + number_format($this->timeUnit->toDestUnit($entry->getMeanTime()), 3), + number_format($this->timeUnit->toDestUnit($entry->getMaxTime()), 3), + $this->timeUnit->getDestSuffix() + ); + + $lines[] = sprintf( + ' ⅀T: %s μRSD/r: %s%%', + $this->timeUnit->format($entry->getTotalTime(), null, TimeUnit::MODE_TIME), + number_format($entry->getMeanRelStDev(), 3) + ); + $lines[] = ''; + + $nbRows = $this->writeLines($this->stdout, $nbRows, $height, $lines); + + // if pagination is diabled, then just pretend that the console height + // is always greater than the number of rows. + if (false === $paginate) { + $height += $nbRows; + } + + if ($paginate && $nbRows >= $height) { + $output->write(sprintf( + 'lines %s-%s any key to continue, to quit', + $totalRows, $totalRows + $nbRows + )); + $character = $this->characterReader->read(); + + if ($character == 'q') { + break; + } + $output->write(PHP_EOL); + + $totalRows += $nbRows; + $nbRows = 0; + } + } + + return 0; + } + + private function writeLines($output, $nbRows, $height, $lines) + { + $limit = count($lines); + + // if the output will exceed the height of the terminal + if ($nbRows + $limit > $height) { + // set the limit to the different and subtract one (for the prompt) + $limit = $height - $nbRows; + } + + for ($i = 0; $i < $limit; $i++) { + $line = $lines[$i]; + $output->writeln($line); + $nbRows++; + } + + return $nbRows; + } +} diff --git a/lib/Console/Command/ReportCommand.php b/lib/Console/Command/ReportCommand.php new file mode 100644 index 000000000..9ec272f3c --- /dev/null +++ b/lib/Console/Command/ReportCommand.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Console\Command; + +use PhpBench\Console\Command\Handler\DumpHandler; +use PhpBench\Console\Command\Handler\ReportHandler; +use PhpBench\Console\Command\Handler\SuiteCollectionHandler; +use PhpBench\Console\Command\Handler\TimeUnitHandler; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class ReportCommand extends Command +{ + /** + * @var ReportHandler + */ + private $reportHandler; + + /** + * @var TimeUnitHandler + */ + private $timeUnitHandler; + + /** + * @var SuiteCollectionHandler + */ + private $collectionHandler; + + /** + * @var DumpHandler + */ + private $dumpHandler; + + public function __construct( + ReportHandler $reportHandler, + TimeUnitHandler $timeUnitHandler, + SuiteCollectionHandler $collectionHandler, + DumpHandler $dumpHandler + ) { + parent::__construct(); + $this->reportHandler = $reportHandler; + $this->timeUnitHandler = $timeUnitHandler; + $this->collectionHandler = $collectionHandler; + $this->dumpHandler = $dumpHandler; + } + + public function configure(): void + { + $this->setName('report'); + $this->setDescription('Generate a report from storage or an XML file'); + $this->setHelp(<<<'EOT' +Generate report from the latest stored suite: + + $ %command.full_name% --ref=latest --report=aggregate + +Generate report from a tag: + + $ %command.full_name% --ref=foobar-tag --report=aggregate + +Generate report from a UUID (as determined from the log command: + + $ %command.full_name% --ref=133a2605fac74edabf046edeb9c5f7c4dc1a3aac --report=aggregate + +Generate from an XML file: + + $ %command.full_name% --file=dump.xml + +To dump an XML file, use the run command with the +dump-file option. + +You can specify the options multiple times to compare multiple suites. +EOT + ); + ReportHandler::configure($this); + TimeUnitHandler::configure($this); + SuiteCollectionHandler::configure($this); + DumpHandler::configure($this); + } + + public function execute(InputInterface $input, OutputInterface $output): int + { + if (!$input->getOption('report')) { + throw new \InvalidArgumentException( + 'You must specify or configure at least one report, e.g.: --report=default' + ); + } + + $this->timeUnitHandler->timeUnitFromInput($input); + $collection = $this->collectionHandler->suiteCollectionFromInput($input); + $this->dumpHandler->dumpFromInput($input, $output, $collection); + $this->reportHandler->reportsFromInput($input, $collection); + + return 0; + } +} diff --git a/lib/Console/Command/RunCommand.php b/lib/Console/Command/RunCommand.php new file mode 100644 index 000000000..3f70473c3 --- /dev/null +++ b/lib/Console/Command/RunCommand.php @@ -0,0 +1,176 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Console\Command; + +use PhpBench\Benchmark\RunnerConfig; +use PhpBench\Console\Command\Handler\DumpHandler; +use PhpBench\Console\Command\Handler\ReportHandler; +use PhpBench\Console\Command\Handler\RunnerHandler; +use PhpBench\Console\Command\Handler\SuiteCollectionHandler; +use PhpBench\Console\Command\Handler\TimeUnitHandler; +use PhpBench\Model\SuiteCollection; +use PhpBench\Registry\Registry; +use PhpBench\Storage\DriverInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class RunCommand extends Command +{ + public const EXIT_CODE_ERROR = 1; + public const EXIT_CODE_FAILURE = 2; + + public const OPT_ITERATIONS = 'iterations'; + public const OPT_WARMUP = 'warmup'; + public const OPT_RETRY_THRESHOLD = 'retry-threshold'; + public const OPT_SLEEP = 'sleep'; + public const OPT_TAG = 'tag'; + public const OPT_STORE = 'store'; + public const OPT_TOLERATE_FAILURE = 'tolerate-failure'; + + /** + * @var RunnerHandler + */ + private $runnerHandler; + + /** + * @var ReportHandler + */ + private $reportHandler; + + /** + * @var TimeUnitHandler + */ + private $timeUnitHandler; + + /** + * @var DumpHandler + */ + private $dumpHandler; + + /** + * @var Registry + */ + private $storage; + + /** + * @var SuiteCollectionHandler + */ + private $suiteCollectionHandler; + + public function __construct( + RunnerHandler $runnerHandler, + ReportHandler $reportHandler, + SuiteCollectionHandler $suiteCollectionHandler, + TimeUnitHandler $timeUnitHandler, + DumpHandler $dumpHandler, + Registry $storage + ) { + parent::__construct(); + $this->runnerHandler = $runnerHandler; + $this->reportHandler = $reportHandler; + $this->suiteCollectionHandler = $suiteCollectionHandler; + $this->timeUnitHandler = $timeUnitHandler; + $this->dumpHandler = $dumpHandler; + $this->storage = $storage; + } + + public function configure(): void + { + RunnerHandler::configure($this); + ReportHandler::configure($this); + SuiteCollectionHandler::configure($this); + TimeUnitHandler::configure($this); + DumpHandler::configure($this); + + $this->setName('run'); + $this->setDescription('Run benchmarks'); + $this->setHelp(<<<'EOT' +Run benchmark files at given path + + $ %command.full_name% /path/to/bench + +All bench marks under the given path will be executed recursively. +EOT + ); + $this->addOption(self::OPT_ITERATIONS, null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Override number of iteratios to run in (all) benchmarks'); + $this->addOption(self::OPT_WARMUP, null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Override number of warmup revolutions on all benchmarks'); + $this->addOption(self::OPT_RETRY_THRESHOLD, 'r', InputOption::VALUE_REQUIRED, 'Set target allowable deviation', null); + $this->addOption(self::OPT_SLEEP, null, InputOption::VALUE_REQUIRED, 'Number of microseconds to sleep between iterations'); + $this->addOption(self::OPT_TAG, null, InputOption::VALUE_REQUIRED, 'Tag to apply to stored result (useful when comparing reports)'); + $this->addOption(self::OPT_STORE, null, InputOption::VALUE_NONE, 'Persist the results'); + $this->addOption(self::OPT_TOLERATE_FAILURE, null, InputOption::VALUE_NONE, 'Return 0 exit code even when failures occur'); + } + + public function execute(InputInterface $input, OutputInterface $output): int + { + $this->timeUnitHandler->timeUnitFromInput($input); + $this->reportHandler->validateReportsFromInput($input); + + $retryThreshold = $input->getOption(self::OPT_RETRY_THRESHOLD); + $sleep = $input->getOption(self::OPT_SLEEP); + + $baselines = $this->resolveBaselines($input); + + $config = RunnerConfig::create() + ->withTag((string)$input->getOption(self::OPT_TAG)) + ->withRetryThreshold($retryThreshold !== null ? (float) $retryThreshold : null) + ->withSleep($sleep !== null ? (int) $sleep : null) + ->withIterations($input->getOption(self::OPT_ITERATIONS)) + ->withWarmup($input->getOption(self::OPT_WARMUP)) + ->withBaselines($baselines); + + $suite = $this->runnerHandler->runFromInput($input, $output, $config); + + $collection = new SuiteCollection([$suite]); + $this->dumpHandler->dumpFromInput($input, $output, $collection); + + if (true === $input->getOption(self::OPT_STORE) || $input->getOption(self::OPT_TAG)) { + $output->write('Storing results ... '); + + /** @var DriverInterface $driver */ + $driver = $this->storage->getService(); + + $message = $driver->store($collection); + + $output->writeln('OK'); + $output->writeln(sprintf('Run: %s', $suite->getUuid())); + + if ($message) { + $output->writeln($message); + } + } + + $this->reportHandler->reportsFromInput($input, $collection); + + if ($suite->getErrorStacks()) { + return self::EXIT_CODE_ERROR; + } + + if (false === $input->getOption(self::OPT_TOLERATE_FAILURE) && $suite->getFailures()) { + return self::EXIT_CODE_FAILURE; + } + + return 0; + } + + private function resolveBaselines(InputInterface $input): SuiteCollection + { + if ($input->getOption('ref') || $input->getOption('file')) { + return $this->suiteCollectionHandler->suiteCollectionFromInput($input); + } + + return new SuiteCollection(); + } +} diff --git a/lib/Console/Command/SelfUpdateCommand.php b/lib/Console/Command/SelfUpdateCommand.php new file mode 100644 index 000000000..277961add --- /dev/null +++ b/lib/Console/Command/SelfUpdateCommand.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Console\Command; + +use Humbug\SelfUpdate\Updater; +use PhpBench\PhpBench; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class SelfUpdateCommand extends Command +{ + private $updater; + + public function __construct(Updater $updater = null) + { + parent::__construct(); + $this->updater = $updater; + } + + public function configure(): void + { + $this->setName('self-update'); + $this->setDescription('Update the application to the latest version.'); + $this->addOption('rollback', null, InputOption::VALUE_NONE, 'Rollback to previous version.'); + } + + public function execute(InputInterface $input, OutputInterface $output) + { + // lazily instantiate the updater because otherwise it requires root + // privleges when instantaited when the PHAR is in a non-writable + // directory. + if (!$this->updater) { + $this->updater = new Updater(); + $this->updater->getStrategy()->setPharUrl(PhpBench::PHAR_URL); + $this->updater->getStrategy()->setVersionUrl(PhpBench::PHAR_VERSION_URL); + } + + if ($input->getOption('rollback')) { + return $this->doRollback($output); + } else { + return $this->doUpdate($output); + } + } + + private function doUpdate(OutputInterface $output): int + { + $result = $this->updater->update(); + + if (!$result) { + $output->writeln('No update required, PHPBench is already at the latest version.'); + + return 0; + } + + $output->writeln(sprintf( + 'PHPBench was updated from "%s" to "%s"', + $this->updater->getOldVersion(), + $this->updater->getNewVersion() + )); + + return 0; + } + + private function doRollback(OutputInterface $output): int + { + $result = $this->updater->rollback(); + + if (!$result) { + throw new \RuntimeException( + 'Could not rollback.' + ); + } + + $output->writeln('Successfully rolled back.'); + + return 0; + } +} diff --git a/lib/Console/Command/ShowCommand.php b/lib/Console/Command/ShowCommand.php new file mode 100644 index 000000000..d49e89c5b --- /dev/null +++ b/lib/Console/Command/ShowCommand.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Console\Command; + +use PhpBench\Console\Command\Handler\DumpHandler; +use PhpBench\Console\Command\Handler\ReportHandler; +use PhpBench\Console\Command\Handler\TimeUnitHandler; +use PhpBench\Registry\Registry; +use PhpBench\Storage\UuidResolver; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Command to show/report on a specific run. + */ +class ShowCommand extends Command +{ + private $storage; + private $reportHandler; + private $timeUnitHandler; + private $dumpHandler; + private $refResolver; + + public function __construct( + Registry $storage, + ReportHandler $reportHandler, + TimeUnitHandler $timeUnitHandler, + DumpHandler $dumpHandler, + UuidResolver $refResolver + ) { + parent::__construct(); + $this->storage = $storage; + $this->reportHandler = $reportHandler; + $this->timeUnitHandler = $timeUnitHandler; + $this->dumpHandler = $dumpHandler; + $this->refResolver = $refResolver; + } + + /** + * {@inheritdoc} + */ + public function configure(): void + { + $this->setName('show'); + $this->setDescription('Show the details of a specific run.'); + $this->addArgument('run_id', InputArgument::REQUIRED, 'Run ID'); + $this->setHelp(<<<'EOT' +Show the results of a specific run. + + $ %command.full_name% + +Any report can be used to display the results: + + $ %command.full_name% --report=env + +EOT + ); + ReportHandler::configure($this); + TimeUnitHandler::configure($this); + DumpHandler::configure($this); + } + + /** + * {@inheritdoc} + */ + public function execute(InputInterface $input, OutputInterface $output): int + { + if (!$input->getOption('report')) { + $input->setOption('report', ['aggregate']); + } + + $storage = $this->storage->getService(); + $collection = $storage->fetch($this->refResolver->resolve($input->getArgument('run_id'))); + $this->timeUnitHandler->timeUnitFromInput($input); + $this->dumpHandler->dumpFromInput($input, $output, $collection); + $this->reportHandler->reportsFromInput($input, $collection); + + return 0; + } +} diff --git a/lib/Development/ConfigDumper.php b/lib/Development/ConfigDumper.php new file mode 100644 index 000000000..28117f2f7 --- /dev/null +++ b/lib/Development/ConfigDumper.php @@ -0,0 +1,96 @@ +extensions = $extensions; + } + + public function dump(): string + { + if (!method_exists(OptionsResolver::class, 'getInfo')) { + return 'Config reference generation requires Symfony Options Resolver ^5.0'; + } + + $sections = [ + self::TITLE, + $this->underline(self::TITLE, '='), + '', + '.. include:: configuration-prelude.rst', + '', + '.. contents::', + ' :depth: 2', + ' :local:', + '', + ]; + + foreach ($this->extensions as $extensionClass) { + $optionsResolver = new OptionsResolver(); + $extension = new $extensionClass; + assert($extension instanceof ExtensionInterface); + $extension->configure($optionsResolver); + + if (!$optionsResolver->getDefinedOptions()) { + continue; + } + $inspector = new OptionsResolverIntrospector($optionsResolver); + $sections[] = $this->generateSection($extensionClass, $optionsResolver, $inspector); + } + + return implode("\n", $sections); + } + + private function generateSection(string $extensionClass, OptionsResolver $optionsResolver, OptionsResolverIntrospector $inspector): string + { + $shortName = substr($extensionClass, (int)strrpos($extensionClass, '\\') + 1); + $shortName = substr($shortName, 0, (int)strrpos($shortName, 'Extension')); + $section = [ + $shortName, + $this->underline($shortName, '-'), + '', + sprintf('Extension class: ``%s``', $extensionClass), + '' + ]; + + foreach ($optionsResolver->getDefinedOptions() as $option) { + $section[] = sprintf('.. _configuration_%s:', str_replace('.', '_', $option)); + $section[] = ''; + $section[] = $option; + $section[] = $this->underline($option, '~'); + $section[] = ''; + $section[] = $optionsResolver->getInfo($option); + $section[] = ''; + $section[] = sprintf('Default: ``%s``', json_encode($inspector->getDefault($option))); + $section[] = ''; + $section[] = sprintf('Types: ``%s``', json_encode($inspector->getAllowedTypes($option))); + $section[] = ''; + } + + return implode("\n", $section); + } + + private function underline(string $string, string $char): string + { + return str_repeat($char, mb_strlen($string)); + } +} diff --git a/lib/Environment/Information.php b/lib/Environment/Information.php new file mode 100644 index 000000000..cac33e83b --- /dev/null +++ b/lib/Environment/Information.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Environment; + +/** + * Represents information about the VCS system used by the current working + * directory. + */ +class Information implements \ArrayAccess, \IteratorAggregate +{ + private $name; + private $information; + + /** + */ + public function __construct(string $name, array $information) + { + $this->name = $name; + $this->information = $this->flattenInformation($information); + } + + /** + * Return the name of this information, it should represent the domain of + * the infomration, e.g. "vcs", "uname". + * + * If an information is mutually exclusive then it should use a standard + * name representing the category of the thing (e.g. "vcs"). This allows + * reports and such things to reference it reliably. + */ + public function getName(): string + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function offsetGet($offset) + { + return $this->information[$offset]; + } + + /** + * {@inheritdoc} + */ + public function offsetSet($offset, $value): void + { + throw new \BadMethodCallException(sprintf( + 'Environmental information is immutable. Tried to set key "%s" with value "%s"', + $offset, $value + )); + } + + /** + * {@inheritdoc} + */ + public function offsetExists($offset): bool + { + return array_key_exists($offset, $this->information); + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($offset): void + { + throw new \BadMethodCallException(sprintf( + 'Environmental information is immutable. Tried to unset key "%s"', + $offset + )); + } + + /** + * {@inheritdoc} + */ + public function getIterator(): \ArrayIterator + { + return new \ArrayIterator($this->information); + } + + public function toArray(): array + { + return $this->information; + } + + private function flattenInformation(array $information, $prefix = ''): array + { + $transformed = []; + + foreach ($information as $key => $value) { + $key = $prefix ? $prefix . '_' . $key : $key; + + if (is_array($value)) { + $transformed = array_merge($transformed, $this->flattenInformation($value, $key)); + + continue; + } + + $transformed[$key] = $value; + } + + return $transformed; + } +} diff --git a/lib/Environment/Provider/AbstractRemoteProvider.php b/lib/Environment/Provider/AbstractRemoteProvider.php new file mode 100644 index 000000000..3c4d6d1e1 --- /dev/null +++ b/lib/Environment/Provider/AbstractRemoteProvider.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Environment\Provider; + +use PhpBench\Environment\Information; +use PhpBench\Environment\ProviderInterface; +use PhpBench\Remote\Launcher; + +abstract class AbstractRemoteProvider implements ProviderInterface +{ + /** + * @var Launcher + */ + private $launcher; + + public function __construct(Launcher $launcher) + { + $this->launcher = $launcher; + } + + public function isApplicable(): bool + { + return true; + } + + public function getInformation(): Information + { + return new Information( + $this->name(), + $this->getData() + ); + } + + private function getData(): array + { + return $this->launcher->payload($this->template())->launch(); + } + + abstract protected function name(): string; + + abstract protected function template(): string; +} diff --git a/lib/Environment/Provider/Git.php b/lib/Environment/Provider/Git.php new file mode 100644 index 000000000..32202ece4 --- /dev/null +++ b/lib/Environment/Provider/Git.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Environment\Provider; + +use PhpBench\Environment\ProviderInterface; +use PhpBench\Environment\VcsInformation; +use Symfony\Component\Process\ExecutableFinder; +use Symfony\Component\Process\Process; + +/** + * Return information about the git environment. + */ +class Git implements ProviderInterface +{ + private $exeFinder; + private $exeName; + private $exePath; + + /** + * @var string + */ + private $cwd; + + public function __construct(string $cwd, ExecutableFinder $exeFinder = null, $exeName = 'git') + { + $this->exeFinder = $exeFinder ?: new ExecutableFinder(); + $this->exeName = $exeName; + $this->cwd = $cwd; + } + + /** + * {@inheritdoc} + */ + public function isApplicable(): bool + { + if (null === $this->getGitPath()) { + return false; + } + + $index = sprintf('%s/.git', $this->cwd); + + if (file_exists($index)) { + return true; + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getInformation(): \PhpBench\Environment\Information + { + $process = $this->exec('symbolic-ref HEAD'); + + if (0 !== $process->getExitCode() && stristr($process->getErrorOutput(), 'ref HEAD is not')) { + $branchName = '(unnamed branch)'; + } elseif (0 === $process->getExitCode()) { + preg_match('{^refs/heads/(.*)$}', $process->getOutput(), $matches); + $branchName = $matches[1]; + } else { + throw new \RuntimeException(sprintf( + 'Encountered error when determining git branch exide code: %s, stderr: "%s"', + $process->getExitCode(), + $process->getErrorOutput() + )); + } + + $commitshRef = sprintf( + '%s/%s/%s', + $this->cwd, + '.git/refs/heads', + $branchName + ); + + if (!file_exists($commitshRef)) { + $version = null; + } else { + $version = trim(file_get_contents($commitshRef)); + } + + return new VcsInformation('git', $branchName, $version); + } + + private function exec($cmd): Process + { + $cmd = sprintf('%s %s', escapeshellarg($this->getGitPath()), $cmd); + $process = Process::fromShellCommandline($cmd, $this->cwd); + $process->run(); + + return $process; + } + + private function getGitPath(): ?string + { + if (null !== $this->exePath) { + return $this->exePath; + } + + $this->exePath = $this->exeFinder->find($this->exeName, null); + + return $this->exePath; + } +} diff --git a/lib/Environment/Provider/Opcache.php b/lib/Environment/Provider/Opcache.php new file mode 100644 index 000000000..0afa83cdc --- /dev/null +++ b/lib/Environment/Provider/Opcache.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Environment\Provider; + +use PhpBench\Environment\Information; + +/** + * Return PHP information. + */ +class Opcache extends AbstractRemoteProvider +{ + protected function name(): string + { + return 'opcache'; + } + + protected function template(): string + { + return __DIR__ . '/template/opcache.template'; + } +} diff --git a/lib/Environment/Provider/Php.php b/lib/Environment/Provider/Php.php new file mode 100644 index 000000000..8d3f721a3 --- /dev/null +++ b/lib/Environment/Provider/Php.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Environment\Provider; + +use PhpBench\Environment\Information; + +/** + * Return PHP information. + */ +class Php extends AbstractRemoteProvider +{ + public function name(): string + { + return 'php'; + } + + public function template(): string + { + return __DIR__ . '/template/php.template'; + } +} diff --git a/lib/Environment/Provider/Sampler.php b/lib/Environment/Provider/Sampler.php new file mode 100644 index 000000000..776f091a4 --- /dev/null +++ b/lib/Environment/Provider/Sampler.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Environment\Provider; + +use PhpBench\Benchmark\SamplerManager; +use PhpBench\Environment\Information; +use PhpBench\Environment\ProviderInterface; + +/** + * Runs basic micro-benchmarks via. the BaselineManager to determine some baseline + * characteristics of the underlying system under test. + */ +class Sampler implements ProviderInterface +{ + /** + * @var SamplerManager + */ + private $manager; + + private $enabled = []; + + public function __construct(SamplerManager $manager, array $enabled) + { + $this->manager = $manager; + $this->enabled = $enabled; + } + + /** + * {@inheritdoc} + */ + public function isApplicable(): bool + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getInformation(): Information + { + $results = []; + + foreach ($this->enabled as $callbackName) { + $results[$callbackName] = $this->manager->sample($callbackName, 1000); + } + + return new Information( + 'sampler', + $results + ); + } +} diff --git a/lib/Environment/Provider/TestProvider.php b/lib/Environment/Provider/TestProvider.php new file mode 100644 index 000000000..d887ef03c --- /dev/null +++ b/lib/Environment/Provider/TestProvider.php @@ -0,0 +1,28 @@ + 1, + 'example2' => 2, + ]); + } +} diff --git a/lib/Environment/Provider/Uname.php b/lib/Environment/Provider/Uname.php new file mode 100644 index 000000000..512610d52 --- /dev/null +++ b/lib/Environment/Provider/Uname.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Environment\Provider; + +use PhpBench\Environment\Information; +use PhpBench\Environment\ProviderInterface; + +/** + * Return the OS information (windows and unix). + */ +class Uname implements ProviderInterface +{ + public function isApplicable(): bool + { + return true; + } + + public function getInformation(): Information + { + $uname = []; + + foreach ([ + 'os' => 's', + 'host' => 'n', + 'release' => 'r', + 'version' => 'v', + 'machine' => 'm', + ] as $key => $mode) { + $uname[$key] = php_uname($mode); + } + + return new Information('uname', $uname); + } +} diff --git a/lib/Environment/Provider/UnixSysload.php b/lib/Environment/Provider/UnixSysload.php new file mode 100644 index 000000000..31eb338a5 --- /dev/null +++ b/lib/Environment/Provider/UnixSysload.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Environment\Provider; + +use PhpBench\Environment\Information; +use PhpBench\Environment\ProviderInterface; + +/** + * Return the load average for unix systems (1, 5, and 15 minute intervals). + */ +class UnixSysload implements ProviderInterface +{ + public function isApplicable(): bool + { + return false === stristr(PHP_OS, 'win'); + } + + public function getInformation(): Information + { + $load = sys_getloadavg(); + $load = array_combine([ + 'l1', 'l5', 'l15', + ], $load); + + return new Information('unix-sysload', $load); + } +} diff --git a/lib/Environment/Provider/template/opcache.template b/lib/Environment/Provider/template/opcache.template new file mode 100644 index 000000000..66dd83dc3 --- /dev/null +++ b/lib/Environment/Provider/template/opcache.template @@ -0,0 +1,23 @@ + extension_loaded('Zend OPcache'), + 'enabled' => false, + ]; + + if (false === $stats['extension_loaded']) { + return $stats; + } + + $opcacheStats = opcache_get_status(); + + $stats['enabled'] = false !== $opcacheStats; + + return $stats; +} + diff --git a/lib/Environment/Provider/template/php.template b/lib/Environment/Provider/template/php.template new file mode 100644 index 000000000..f4a434e14 --- /dev/null +++ b/lib/Environment/Provider/template/php.template @@ -0,0 +1,10 @@ + in_array('xdebug', get_loaded_extensions()), + 'version' => phpversion(), + 'ini' => php_ini_loaded_file(), + 'extensions' => implode(', ', get_loaded_extensions()), +]); + +exit(0); diff --git a/lib/Environment/ProviderInterface.php b/lib/Environment/ProviderInterface.php new file mode 100644 index 000000000..17a5ff3f2 --- /dev/null +++ b/lib/Environment/ProviderInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Environment; + +/** + * Implementors of this interface provide information about + * a specific feature of the environment, e.g. VCS, OS, etc. + */ +interface ProviderInterface +{ + /** + * Return true if the instance detects a VCS repository + * in the current CWD. + */ + public function isApplicable(): bool; + + /** + * Return information about the detected VCS repository. + */ + public function getInformation(): Information; +} diff --git a/lib/Environment/Supplier.php b/lib/Environment/Supplier.php new file mode 100644 index 000000000..67d104657 --- /dev/null +++ b/lib/Environment/Supplier.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Environment; + +/** + * Supplies information from the current environment. + * + * Indidual "information" is provided by "providers", which are responsible for + * saying if they can provide any information in the current environment, for + * example: if there is a .git directory, then the GIT provider will return + * some information. + */ +class Supplier +{ + /** + * @var ProviderInterface[] + */ + private $providers = []; + + /** + * Add a provider. + * + */ + public function addProvider(ProviderInterface $provider): void + { + $this->providers[] = $provider; + } + + /** + * Return information from the current environment. + * + * @return Information[] + */ + public function getInformations(): array + { + $informations = []; + + foreach ($this->providers as $provider) { + if (false === $provider->isApplicable()) { + continue; + } + + $informations[] = $provider->getInformation(); + } + + return $informations; + } +} diff --git a/lib/Environment/VcsInformation.php b/lib/Environment/VcsInformation.php new file mode 100644 index 000000000..3e8ed79e8 --- /dev/null +++ b/lib/Environment/VcsInformation.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Environment; + +/** + * VCS specific information. All VCS providers should + * return this object to ensure they all provide the same + * information. + */ +class VcsInformation extends Information +{ + public function __construct(string $system, $branch, $version) + { + parent::__construct('vcs', [ + 'system' => $system, + 'branch' => $branch, + 'version' => $version, + ]); + } +} diff --git a/lib/Exception/ConfigurationPreProcessingError.php b/lib/Exception/ConfigurationPreProcessingError.php new file mode 100644 index 000000000..343373c48 --- /dev/null +++ b/lib/Exception/ConfigurationPreProcessingError.php @@ -0,0 +1,9 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Executor\Benchmark; + +use PhpBench\Executor\BenchmarkExecutorInterface; +use PhpBench\Executor\ExecutionContext; +use PhpBench\Executor\ExecutionResults; +use PhpBench\Model\Result\MemoryResult; +use PhpBench\Model\Result\TimeResult; +use PhpBench\Registry\Config; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * This executor for testing purposes. It always returns constant times, it + * does not actually execute any benchmarking. + */ +class DebugExecutor implements BenchmarkExecutorInterface +{ + private $variantTimes = []; + private $index = 0; + + /** + * {@inheritdoc} + */ + public function execute(ExecutionContext $context, Config $config): ExecutionResults + { + $results = ExecutionResults::new(); + + // add 100 bytes of memory. + $memory = 100; + $results->add(new MemoryResult($memory, $memory, $memory)); + + if (!$config['times']) { + $results->add(new TimeResult(0, $context->getRevolutions())); + + return $results; + } + + $contextHash = spl_object_hash($context); + + if (!isset($this->variantTimes[$contextHash])) { + $this->variantTimes[$contextHash] = $config['times']; + } + + if (!isset($this->variantTimes[$contextHash][$this->index])) { + $this->index = 0; + } + + $time = $this->variantTimes[$contextHash][$this->index]; + $this->index++; + + if ($config['spread']) { + $index = $context->getIterationIndex() % count($config['spread']); + $spreadDiff = $config['spread'][$index]; + $time = $time + $spreadDiff; + } + + $results->add(new TimeResult($time, $context->getRevolutions())); + + return $results; + } + + /** + * {@inheritdoc} + */ + public function configure(OptionsResolver $options): void + { + $options->setDefaults([ + 'times' => [10], + 'spread' => [0], + 'memories' => null, + ]); + + $options->setAllowedTypes('times', 'array'); + $options->setAllowedTypes('spread', 'array'); + $options->setAllowedTypes('memories', ['null', 'array']); + } +} diff --git a/lib/Executor/Benchmark/LocalExecutor.php b/lib/Executor/Benchmark/LocalExecutor.php new file mode 100644 index 000000000..a2fe98bd0 --- /dev/null +++ b/lib/Executor/Benchmark/LocalExecutor.php @@ -0,0 +1,87 @@ +bootstrap = $bootstrap; + } + + /** + * {@inheritDoc} + */ + public function configure(OptionsResolver $options): void + { + } + + public function execute(ExecutionContext $context, Config $config): ExecutionResults + { + if ($this->bootstrap) { + require_once($this->bootstrap); + } + + $benchmark = $this->createBenchmark($context); + + $methodName = $context->getMethodName(); + $parameters = $context->getParameters(); + + foreach ($context->getBeforeMethods() as $afterMethod) { + $benchmark->$afterMethod($parameters); + } + + for ($i = 0; $i < $context->getWarmup() ?: 0; $i++) { + $benchmark->$methodName($parameters); + } + + $start = microtime(true); + + for ($i = 0; $i < $context->getRevolutions(); $i++) { + $benchmark->$methodName($parameters); + } + + $end = microtime(true); + + foreach ($context->getAfterMethods() as $afterMethod) { + $benchmark->$afterMethod($parameters); + } + + return ExecutionResults::fromResults( + new TimeResult((int)(($end - $start) * 1E6), $context->getRevolutions()) + ); + } + + /** + * @return object + */ + private function createBenchmark(ExecutionContext $context) + { + $className = $context->getClassName(); + + if (!class_exists($className)) { + require_once($context->getClassPath()); + } + + if (!class_exists($className)) { + throw new ExecutionError(sprintf( + 'Benchmark class "%s" does not exist', $className + )); + } + + return new $className; + } +} diff --git a/lib/Executor/Benchmark/MemoryCentricMicrotimeExecutor.php b/lib/Executor/Benchmark/MemoryCentricMicrotimeExecutor.php new file mode 100644 index 000000000..8cda1d81a --- /dev/null +++ b/lib/Executor/Benchmark/MemoryCentricMicrotimeExecutor.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Executor\Benchmark; + +use PhpBench\Remote\Launcher; + +class RemoteExecutor extends TemplateExecutor +{ + public function __construct(Launcher $launcher) + { + parent::__construct($launcher, __DIR__ . '/template/remote.template'); + } +} diff --git a/lib/Executor/Benchmark/TemplateExecutor.php b/lib/Executor/Benchmark/TemplateExecutor.php new file mode 100644 index 000000000..c60947a02 --- /dev/null +++ b/lib/Executor/Benchmark/TemplateExecutor.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Executor\Benchmark; + +use PhpBench\Executor\BenchmarkExecutorInterface; +use PhpBench\Executor\Exception\ExecutionError; +use PhpBench\Executor\ExecutionContext; +use PhpBench\Executor\ExecutionResults; +use PhpBench\Model\Result\MemoryResult; +use PhpBench\Model\Result\TimeResult; +use PhpBench\Registry\Config; +use PhpBench\Remote\Exception\ScriptErrorException; +use PhpBench\Remote\Launcher; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class TemplateExecutor implements BenchmarkExecutorInterface +{ + public const OPTION_PHP_CONFIG = 'php_config'; + private const PHP_OPTION_MAX_EXECUTION_TIME = 'max_execution_time'; + + /** + * @var Launcher + */ + private $launcher; + + /** + * @var string + */ + private $templatePath; + + public function __construct(Launcher $launcher, string $templatePath) + { + $this->launcher = $launcher; + $this->templatePath = $templatePath; + } + + public function execute(ExecutionContext $context, Config $config): ExecutionResults + { + $tokens = $this->createTokens($context); + $payload = $this->launcher->payload($this->templatePath, $tokens, $context->getTimeout()); + $payload->mergePhpConfig(array_merge( + [ + self::PHP_OPTION_MAX_EXECUTION_TIME => 0, + ], + $config[self::OPTION_PHP_CONFIG] ?? [] + )); + + try { + $result = $payload->launch(); + } catch (ScriptErrorException $error) { + throw new ExecutionError($error->getMessage(), 0, $error); + } + + if (isset($result['buffer']) && $result['buffer']) { + throw new \RuntimeException(sprintf( + 'Benchmark made some noise: %s', + $result['buffer'] + )); + } + + return ExecutionResults::fromResults( + TimeResult::fromArray($result['time']), + MemoryResult::fromArray($result['mem']) + ); + } + + /** + * {@inheritdoc} + */ + public function configure(OptionsResolver $options): void + { + $options->setDefaults([ + self::OPTION_PHP_CONFIG => [ + ] + ]); + } + + protected function createTokens(ExecutionContext $context) : array + { + return [ + 'class' => $context->getClassName(), + 'file' => $context->getClassPath(), + 'subject' => $context->getMethodName(), + 'revolutions' => $context->getRevolutions(), + 'beforeMethods' => var_export($context->getBeforeMethods(), true), + 'afterMethods' => var_export($context->getAfterMethods(), true), + 'parameters' => $context->getParameters() ? var_export($context->getParameters(), true) : '', + 'warmup' => $context->getWarmup() ?: 0, + ]; + } +} diff --git a/lib/Executor/Benchmark/TestExecutor.php b/lib/Executor/Benchmark/TestExecutor.php new file mode 100644 index 000000000..56ac4ea18 --- /dev/null +++ b/lib/Executor/Benchmark/TestExecutor.php @@ -0,0 +1,105 @@ + + */ + private $executedContexts = []; + + /** + * @var ExecutionContext|null + */ + private $lastContext; + + /** + * @var Variant|null + */ + private $lastVariant; + + private $index = 0; + + /** + * {@inheritDoc} + */ + public function configure(OptionsResolver $options): void + { + $options->setDefaults([ + 'results' => [], + 'exception' => null, + ]); + } + + public function execute(ExecutionContext $context, Config $config): ExecutionResults + { + if ($config['exception']) { + throw $config['exception']; + } + $this->executedContexts[] = $context; + $this->lastContext = $context; + + return ExecutionResults::fromResults($config['results'][$this->index++ % count($config['results'])]); + } + + public function executeMethods(MethodExecutorContext $context, array $methods): void + { + $this->executedMethods = array_merge($this->executedMethods, $methods); + } + + /** + * {@inheritDoc} + */ + public function healthCheck(): void + { + $this->healthChecked = true; + } + + public function lastContextOrException(): ExecutionContext + { + if (null === $this->lastContext) { + throw new RuntimeException( + 'No subject has been executed' + ); + } + + return $this->lastContext; + } + + public function hasMethodBeenExecuted(string $name): bool + { + return in_array($name, $this->executedMethods); + } + + public function hasHealthBeenChecked(): bool + { + return $this->healthChecked; + } + + public function getExecutedContextCount(): int + { + return count($this->executedContexts); + } +} diff --git a/lib/Executor/Benchmark/template/memory.template b/lib/Executor/Benchmark/template/memory.template new file mode 100644 index 000000000..b8dc2b8a6 --- /dev/null +++ b/lib/Executor/Benchmark/template/memory.template @@ -0,0 +1,79 @@ +$beforeMethod({{ parameters }}); +} + +// run warmup if required +if ($warmup) { + for ($i = 0; $i < $warmup; $i++) { + $benchmark->{{ subject }}({{ parameters }}); + } +} + +// disable garbage collection +cleanGarbageCollectionAndDisable(); + +// run benchmark +$time = 0.00; +for ($i = 0; $i < {{ revolutions }}; $i++) { + $startTime = microtime(true); + + $benchmark->{{ subject }}({{ parameters }}); + + $endTime = microtime(true); + + $time += ($endTime - $startTime) * 1000000; + + cleanGarbageCollectionAndDisable(); +} + +// run after methods +foreach ($afterMethods as $afterMethod) { + $benchmark->$afterMethod({{ parameters }}); +} + +$buffer = ob_get_contents(); +ob_end_clean(); + +echo serialize([ + 'mem' => [ + 'peak' => memory_get_peak_usage(), + 'final' => memory_get_usage(), + 'real' => memory_get_usage(true), + ], + 'time' => [ + 'net' => (int) $time, + ], + 'buffer' => $buffer, +]); + +function cleanGarbageCollectionAndDisable () : void +{ + gc_enable(); + gc_collect_cycles(); + gc_disable(); +} + +exit(0); diff --git a/lib/Executor/Benchmark/template/remote.template b/lib/Executor/Benchmark/template/remote.template new file mode 100644 index 000000000..f40e719a7 --- /dev/null +++ b/lib/Executor/Benchmark/template/remote.template @@ -0,0 +1,72 @@ +$beforeMethod({{ parameters }}); +} + +// run warmup if required +if ($warmup) { + for ($i = 0; $i < $warmup; $i++) { + $benchmark->{{ subject }}({{ parameters }}); + } +} + +// run benchmark +$startTime = microtime(true); + +for ($i = 0; $i < {{ revolutions }}; $i++) { + $benchmark->{{ subject }}({{ parameters }}); +} + +$endTime = microtime(true); + +$time = ($endTime - $startTime) * 1000000; + +// run after methods +foreach ($afterMethods as $afterMethod) { + $benchmark->$afterMethod({{ parameters }}); +} + +$buffer = ob_get_contents(); +ob_end_clean(); + +echo serialize([ + 'mem' => [ + // observer effect - getting memory usage affects memory usage. order + // counts, peak is probably the best metric. + 'peak' => memory_get_peak_usage(), + 'final' => memory_get_usage(), + 'real' => memory_get_usage(true), + ], + 'time' => [ + 'revs' => {{ revolutions }}, + 'net' => (int) $time, + ], + 'buffer' => $buffer, +]); + +exit(0); diff --git a/lib/Executor/BenchmarkExecutorInterface.php b/lib/Executor/BenchmarkExecutorInterface.php new file mode 100644 index 000000000..a5fa6425b --- /dev/null +++ b/lib/Executor/BenchmarkExecutorInterface.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Executor; + +use PhpBench\Executor\Exception\ExecutionError; +use PhpBench\Registry\Config; +use PhpBench\Registry\RegistrableInterface; + +interface BenchmarkExecutorInterface extends RegistrableInterface +{ + /** + * @throws ExecutionError + */ + public function execute(ExecutionContext $context, Config $config): ExecutionResults; +} diff --git a/lib/Executor/CompositeExecutor.php b/lib/Executor/CompositeExecutor.php new file mode 100644 index 000000000..79ffd50db --- /dev/null +++ b/lib/Executor/CompositeExecutor.php @@ -0,0 +1,61 @@ +benchmarkExecutor = $benchmarkExecutor; + $this->methodExecutor = $methodExecutor; + $this->healthCheck = $healthCheck ?: new AlwaysFineHealthCheck(); + } + + /** + * {@inheritDoc} + */ + public function configure(OptionsResolver $options): void + { + $this->benchmarkExecutor->configure($options); + } + + public function execute(ExecutionContext $context, Config $config): ExecutionResults + { + return $this->benchmarkExecutor->execute($context, $config); + } + + /** + * {@inheritDoc} + */ + public function healthCheck(): void + { + $this->healthCheck->healthCheck(); + } + + /** + * @param array $methods + */ + public function executeMethods(MethodExecutorContext $context, array $methods): void + { + $this->methodExecutor->executeMethods($context, $methods); + } +} diff --git a/lib/Executor/Exception/ExecutionError.php b/lib/Executor/Exception/ExecutionError.php new file mode 100644 index 000000000..03518fe46 --- /dev/null +++ b/lib/Executor/Exception/ExecutionError.php @@ -0,0 +1,9 @@ + + */ + private $parameters; + + /** + * @var string + */ + private $classPath; + + /** + * @var int + */ + private $iterationIndex; + + /** + * @var int + */ + private $warmup; + + /** + * @var array + */ + private $afterMethods; + + /** + * @var array + */ + private $beforeMethods; + + /** + * @var int + */ + private $revolutions; + + /** + * @var string + */ + private $methodName; + + /** + * @var float|null + */ + private $timeOut; + + /** + * @var string + */ + private $parameterSetName; + + public function __construct( + string $className, + string $classPath, + string $methodName, + int $revolutions = 1, + array $beforeMethods = [], + array $afterMethods = [], + array $parameters = [], + int $warmup = 0, + int $iterationIndex = 0, + ?float $timeOut = null, + string $parameterSetName = '' + ) { + $this->className = $className; + $this->classPath = $classPath; + $this->methodName = $methodName; + $this->revolutions = $revolutions; + $this->beforeMethods = $beforeMethods; + $this->afterMethods = $afterMethods; + $this->parameters = $parameters; + $this->warmup = $warmup; + $this->iterationIndex = $iterationIndex; + $this->timeOut = $timeOut; + $this->parameterSetName = $parameterSetName; + } + + public static function fromSubjectMetadataAndIteration(SubjectMetadata $subjectMetadata, Iteration $iteration): self + { + return new self( + $subjectMetadata->getBenchmark()->getClass(), + $subjectMetadata->getBenchmark()->getPath(), + $subjectMetadata->getName(), + $iteration->getVariant()->getRevolutions(), + $subjectMetadata->getBeforeMethods(), + $subjectMetadata->getAfterMethods(), + $iteration->getVariant()->getParameterSet()->getArrayCopy(), + $iteration->getVariant()->getWarmup() ?: 0, + $iteration->getIndex(), + $subjectMetadata->getTimeout(), + $iteration->getVariant()->getParameterSet()->getName() + ); + } + + public function getClassName(): string + { + return $this->className; + } + + public function getParameters(): array + { + return $this->parameters; + } + + public function getClassPath(): string + { + return $this->classPath; + } + + public function getIterationIndex(): int + { + return $this->iterationIndex; + } + + public function getWarmup(): int + { + return $this->warmup; + } + + public function getAfterMethods(): array + { + return $this->afterMethods; + } + + public function getBeforeMethods(): array + { + return $this->beforeMethods; + } + + public function getRevolutions(): int + { + return $this->revolutions; + } + + public function getMethodName(): string + { + return $this->methodName; + } + + public function getTimeOut(): ?float + { + return $this->timeOut; + } + + public function getParameterSetName(): string + { + return $this->parameterSetName; + } +} diff --git a/lib/Executor/ExecutionResults.php b/lib/Executor/ExecutionResults.php new file mode 100644 index 000000000..9d15ba8ad --- /dev/null +++ b/lib/Executor/ExecutionResults.php @@ -0,0 +1,55 @@ + + */ +final class ExecutionResults implements IteratorAggregate, Countable +{ + /** + * @var array + */ + private $results; + + private function __construct(ResultInterface ...$results) + { + $this->results = $results; + } + + public static function fromResults(ResultInterface ...$results): self + { + return new self(...$results); + } + + public function add(ResultInterface $result): void + { + $this->results[] = $result; + } + + public static function new(): self + { + return new self(); + } + + /** + * @return ArrayIterator + */ + public function getIterator(): ArrayIterator + { + return new ArrayIterator($this->results); + } + + /** + * {@inheritDoc} + */ + public function count(): int + { + return count($this->results); + } +} diff --git a/lib/Executor/HealthCheck/AlwaysFineHealthCheck.php b/lib/Executor/HealthCheck/AlwaysFineHealthCheck.php new file mode 100644 index 000000000..0a48eedaf --- /dev/null +++ b/lib/Executor/HealthCheck/AlwaysFineHealthCheck.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Executor; + +interface HealthCheckInterface +{ + /** + * This method should throw an exception if the executor cannot be used in + * the current environment. + */ + public function healthCheck(): void; +} diff --git a/lib/Executor/Method/ErrorHandlingExecutorDecorator.php b/lib/Executor/Method/ErrorHandlingExecutorDecorator.php new file mode 100644 index 000000000..54fb0561b --- /dev/null +++ b/lib/Executor/Method/ErrorHandlingExecutorDecorator.php @@ -0,0 +1,34 @@ +executor = $executor; + } + + public function executeMethods(MethodExecutorContext $context, array $methods): void + { + try { + $this->executor->executeMethods($context, $methods); + } catch (Throwable $throwable) { + throw new ExecutionError(sprintf( + 'Could not execute method(s) "%s" on "%s"', + implode('", "', $methods), + $context->getBenchmarkClass() + ), 0, $throwable); + } + } +} diff --git a/lib/Executor/Method/LocalMethodExecutor.php b/lib/Executor/Method/LocalMethodExecutor.php new file mode 100644 index 000000000..f5da319e1 --- /dev/null +++ b/lib/Executor/Method/LocalMethodExecutor.php @@ -0,0 +1,36 @@ + $methods + */ + public function executeMethods(MethodExecutorContext $context, array $methods): void + { + $className = $context->getBenchmarkClass(); + + if (!class_exists($className)) { + throw new RuntimeException(sprintf( + 'Class "%s" does not exist', $className + )); + } + + $class = new $className; + + foreach ($methods as $method) { + if (!method_exists($class, $method)) { + throw new RuntimeException(sprintf( + 'Method "%s" on class "%s" does not exist', $method, $className + )); + } + + $class->$method(); + } + } +} diff --git a/lib/Executor/Method/RemoteMethodExecutor.php b/lib/Executor/Method/RemoteMethodExecutor.php new file mode 100644 index 000000000..4c87239a2 --- /dev/null +++ b/lib/Executor/Method/RemoteMethodExecutor.php @@ -0,0 +1,35 @@ +launcher = $launcher; + } + + /** + * {@inheritdoc} + */ + public function executeMethods(MethodExecutorContext $context, array $methods): void + { + $tokens = [ + 'class' => $context->getBenchmarkClass(), + 'file' => $context->getBenchmarkPath(), + 'methods' => var_export($methods, true), + ]; + + $payload = $this->launcher->payload(__DIR__ . '/template/execute_static_methods.template', $tokens); + $payload->launch(); + } +} diff --git a/lib/Executor/Method/template/execute_static_methods.template b/lib/Executor/Method/template/execute_static_methods.template new file mode 100644 index 000000000..3879ffebc --- /dev/null +++ b/lib/Executor/Method/template/execute_static_methods.template @@ -0,0 +1,27 @@ +benchmarkPath = $benchmarkPath; + $this->benchmarkClass = $benchmarkClass; + } + + public static function fromBenchmarkMetadata(BenchmarkMetadata $benchmarkMetadata): self + { + return new self($benchmarkMetadata->getPath(), $benchmarkMetadata->getClass()); + } + + public function getBenchmarkClass(): string + { + return $this->benchmarkClass; + } + + public function getBenchmarkPath(): string + { + return $this->benchmarkPath; + } +} diff --git a/lib/Executor/MethodExecutorInterface.php b/lib/Executor/MethodExecutorInterface.php new file mode 100644 index 000000000..78a6970cc --- /dev/null +++ b/lib/Executor/MethodExecutorInterface.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Executor; + +interface MethodExecutorInterface +{ + public function executeMethods(MethodExecutorContext $context, array $methods): void; +} diff --git a/lib/Expression/Ast/ArgumentListNode.php b/lib/Expression/Ast/ArgumentListNode.php new file mode 100644 index 000000000..8eb3fd86b --- /dev/null +++ b/lib/Expression/Ast/ArgumentListNode.php @@ -0,0 +1,7 @@ +left = $left; + $this->operator = $operator; + $this->right = $right; + } + + public function left(): Node + { + return $this->left; + } + + public function operator(): string + { + return $this->operator; + } + + public function right(): Node + { + return $this->right; + } +} diff --git a/lib/Expression/Ast/BooleanNode.php b/lib/Expression/Ast/BooleanNode.php new file mode 100644 index 000000000..a9f35b93b --- /dev/null +++ b/lib/Expression/Ast/BooleanNode.php @@ -0,0 +1,21 @@ +value = $value; + } + + public function value(): bool + { + return $this->value; + } +} diff --git a/lib/Expression/Ast/ComparisonNode.php b/lib/Expression/Ast/ComparisonNode.php new file mode 100644 index 000000000..bdfdbcf75 --- /dev/null +++ b/lib/Expression/Ast/ComparisonNode.php @@ -0,0 +1,7 @@ +left = $left; + $this->right = $right; + } + + public function left(): Node + { + return $this->left; + } + + public function right(): Node + { + return $this->right; + } + + public function nodes() + { + $nodes = [$this->left()]; + + if ($this->right instanceof ConcatNode) { + $nodes = array_merge($nodes, $this->right->nodes()); + } else { + $nodes[] = $this->right; + } + + return $nodes; + } +} diff --git a/lib/Expression/Ast/ConcatenatedNode.php b/lib/Expression/Ast/ConcatenatedNode.php new file mode 100644 index 000000000..a587e3c87 --- /dev/null +++ b/lib/Expression/Ast/ConcatenatedNode.php @@ -0,0 +1,34 @@ +left = $left; + $this->right = $right; + } + + public function left(): Node + { + return $this->left; + } + + public function right(): Node + { + return $this->right; + } +} diff --git a/lib/Expression/Ast/DelimitedListNode.php b/lib/Expression/Ast/DelimitedListNode.php new file mode 100644 index 000000000..189c7620d --- /dev/null +++ b/lib/Expression/Ast/DelimitedListNode.php @@ -0,0 +1,41 @@ +nodes = $nodes; + } + + /** + * @return Node[] + */ + public function value(): array + { + return $this->nodes; + } + + /** + * @return mixed[] + */ + public function phpValues(): array + { + return array_map(function (PhpValue $node) { + if ($node instanceof DelimitedListNode) { + return $node->phpValues(); + } + + return $node->value(); + }, $this->nodes); + } +} diff --git a/lib/Expression/Ast/DisplayAsNode.php b/lib/Expression/Ast/DisplayAsNode.php new file mode 100644 index 000000000..dd25aea1e --- /dev/null +++ b/lib/Expression/Ast/DisplayAsNode.php @@ -0,0 +1,60 @@ +as = $as; + $this->value = $value; + $this->precision = $precision; + } + + public function node(): Node + { + return $this->value; + } + + public function as(): UnitNode + { + return $this->as; + } + + /** + * {@inheritDoc} + */ + public function value() + { + if (!$this->value instanceof NumberNode) { + throw new EvaluationError($this, sprintf( + 'Expected display at value to have been evaluated to a number node, but its a "%s"', + get_class($this->value) + )); + } + + return $this->value->value(); + } + + public function precision(): ?Node + { + return $this->precision; + } +} diff --git a/lib/Expression/Ast/DisplayAsTimeNode.php b/lib/Expression/Ast/DisplayAsTimeNode.php new file mode 100644 index 000000000..51918e87e --- /dev/null +++ b/lib/Expression/Ast/DisplayAsTimeNode.php @@ -0,0 +1,22 @@ +mode = $mode; + } + + public function mode(): ?Node + { + return $this->mode; + } +} diff --git a/lib/Expression/Ast/FloatNode.php b/lib/Expression/Ast/FloatNode.php new file mode 100644 index 000000000..8cc4f6200 --- /dev/null +++ b/lib/Expression/Ast/FloatNode.php @@ -0,0 +1,21 @@ +number = $number; + } + + public function value(): float + { + return $this->number; + } +} diff --git a/lib/Expression/Ast/FunctionNode.php b/lib/Expression/Ast/FunctionNode.php new file mode 100644 index 000000000..41a7c95f2 --- /dev/null +++ b/lib/Expression/Ast/FunctionNode.php @@ -0,0 +1,32 @@ +name = $name; + $this->arguments = $arguments; + } + + public function args(): ?ArgumentListNode + { + return $this->arguments; + } + + public function name(): string + { + return $this->name; + } +} diff --git a/lib/Expression/Ast/IntegerNode.php b/lib/Expression/Ast/IntegerNode.php new file mode 100644 index 000000000..fd06148b0 --- /dev/null +++ b/lib/Expression/Ast/IntegerNode.php @@ -0,0 +1,21 @@ +value = $value; + } + + public function value(): int + { + return $this->value; + } +} diff --git a/lib/Expression/Ast/ListNode.php b/lib/Expression/Ast/ListNode.php new file mode 100644 index 000000000..42c91d2e6 --- /dev/null +++ b/lib/Expression/Ast/ListNode.php @@ -0,0 +1,20 @@ + $values + */ + public static function fromValues(array $values): self + { + return new self(array_map(function ($value) { + if (is_array($value)) { + return ListNode::fromValues($value); + } + + return PhpValueFactory::fromValue($value); + }, $values)); + } +} diff --git a/lib/Expression/Ast/LogicalOperatorNode.php b/lib/Expression/Ast/LogicalOperatorNode.php new file mode 100644 index 000000000..21cd17f1a --- /dev/null +++ b/lib/Expression/Ast/LogicalOperatorNode.php @@ -0,0 +1,7 @@ +segments = $segments; + } + + public function segments(): array + { + return $this->segments; + } +} diff --git a/lib/Expression/Ast/ParenthesisNode.php b/lib/Expression/Ast/ParenthesisNode.php new file mode 100644 index 000000000..324510bc6 --- /dev/null +++ b/lib/Expression/Ast/ParenthesisNode.php @@ -0,0 +1,21 @@ +expression = $expression; + } + + public function expression(): Node + { + return $this->expression; + } +} diff --git a/lib/Expression/Ast/PercentDifferenceNode.php b/lib/Expression/Ast/PercentDifferenceNode.php new file mode 100644 index 000000000..f0725c347 --- /dev/null +++ b/lib/Expression/Ast/PercentDifferenceNode.php @@ -0,0 +1,40 @@ +percentage = $percentage; + $this->tolerance = $tolerance; + } + + public function percentage(): float + { + return $this->percentage; + } + + /** + * {@inheritDoc} + */ + public function value() + { + return $this->percentage; + } + + public function tolerance(): float + { + return $this->tolerance; + } +} diff --git a/lib/Expression/Ast/PercentageNode.php b/lib/Expression/Ast/PercentageNode.php new file mode 100644 index 000000000..cb2cd929f --- /dev/null +++ b/lib/Expression/Ast/PercentageNode.php @@ -0,0 +1,37 @@ +value = $value; + } + + public function valueNode(): Node + { + return $this->value; + } + + public function value(): float + { + $value = $this->value; + + if (!$value instanceof FloatNode && !$value instanceof IntegerNode) { + throw new EvaluationError($this, sprintf( + 'Percentage node has not been evaluated, its value is a "%s"', + get_class($this->value) + )); + } + + return (float)$value->value(); + } +} diff --git a/lib/Expression/Ast/PhpValue.php b/lib/Expression/Ast/PhpValue.php new file mode 100644 index 000000000..19689424d --- /dev/null +++ b/lib/Expression/Ast/PhpValue.php @@ -0,0 +1,11 @@ +string = $string; + } + + /** + * {@inheritDoc} + */ + public function value() + { + return $this->string; + } +} diff --git a/lib/Expression/Ast/TolerableNode.php b/lib/Expression/Ast/TolerableNode.php new file mode 100644 index 000000000..9c84d0a71 --- /dev/null +++ b/lib/Expression/Ast/TolerableNode.php @@ -0,0 +1,32 @@ +value = $value; + $this->tolerance = $tolerance; + } + + public function tolerance(): Node + { + return $this->tolerance; + } + + public function value(): Node + { + return $this->value; + } +} diff --git a/lib/Expression/Ast/ToleratedTrue.php b/lib/Expression/Ast/ToleratedTrue.php new file mode 100644 index 000000000..bc9465c9a --- /dev/null +++ b/lib/Expression/Ast/ToleratedTrue.php @@ -0,0 +1,14 @@ +unit = $unit; + } + + public function unit(): Node + { + return $this->unit; + } +} diff --git a/lib/Expression/Ast/ValueWithUnitNode.php b/lib/Expression/Ast/ValueWithUnitNode.php new file mode 100644 index 000000000..80b161684 --- /dev/null +++ b/lib/Expression/Ast/ValueWithUnitNode.php @@ -0,0 +1,31 @@ +left = $left; + $this->unit = $unit; + } + + public function left(): Node + { + return $this->left; + } + + public function unit(): UnitNode + { + return $this->unit; + } +} diff --git a/lib/Expression/ColorMap.php b/lib/Expression/ColorMap.php new file mode 100644 index 000000000..ebd893731 --- /dev/null +++ b/lib/Expression/ColorMap.php @@ -0,0 +1,16 @@ +, string|Closure(T):string> + */ + public function colors(): array; +} diff --git a/lib/Expression/Evaluator.php b/lib/Expression/Evaluator.php new file mode 100644 index 000000000..ab5e36fd3 --- /dev/null +++ b/lib/Expression/Evaluator.php @@ -0,0 +1,23 @@ + $expectedType + * @param parameters $params + * + * @return T + */ + public function evaluateType(Node $node, string $expectedType, array $params): Node; + + /** + * @param parameters $params + */ + public function evaluate(Node $node, array $params): Node; +} diff --git a/lib/Expression/Evaluator/MainEvaluator.php b/lib/Expression/Evaluator/MainEvaluator.php new file mode 100644 index 000000000..5abd94bb0 --- /dev/null +++ b/lib/Expression/Evaluator/MainEvaluator.php @@ -0,0 +1,52 @@ +evaluators = $evaluators; + } + + /** + * @template T of Node + * + * @param class-string $expectedType + * @param parameters $params + * + * @return T + */ + public function evaluateType(Node $node, string $expectedType, array $params): Node + { + $evaluated = $this->evaluate($node, $params); + + if ($evaluated instanceof $expectedType) { + return $evaluated; + } + + throw new EvaluationError($node, sprintf( + 'Expected "%s" but got "%s"', $expectedType, get_class($evaluated) + )); + } + + /** + * @param parameters $params + */ + public function evaluate(Node $node, array $params): Node + { + return $this->evaluators->evaluate($this, $node, $params); + } +} diff --git a/lib/Expression/Evaluator/PrettyErrorEvaluator.php b/lib/Expression/Evaluator/PrettyErrorEvaluator.php new file mode 100644 index 000000000..491a512fc --- /dev/null +++ b/lib/Expression/Evaluator/PrettyErrorEvaluator.php @@ -0,0 +1,79 @@ +innerEvaluator = $innerEvaluator; + $this->printer = $printer; + $this->underlineFactory = $underlineFactory; + } + + /** + * {@inheritDoc} + */ + public function evaluateType(Node $node, string $expectedType, array $params): Node + { + try { + return $this->innerEvaluator->evaluateType($node, $expectedType, $params); + } catch (EvaluationError $error) { + throw $this->prettyError($node, $error, $params); + } + } + + /** + * {@inheritDoc} + */ + public function evaluate(Node $node, array $params): Node + { + try { + return $this->innerEvaluator->evaluate($node, $params); + } catch (EvaluationError $error) { + throw $this->prettyError($node, $error, $params); + } + } + + /** + * @param parameters $params + */ + private function prettyError(Node $rootNode, EvaluationError $error, array $params): EvaluationError + { + try { + return new EvaluationError($error->node(), implode(PHP_EOL, [ + sprintf('%s:', $error->getMessage()), + '', + ' ' . $this->printer->print($rootNode), + ' ' . $this->underlineFactory->underline($error->node())->print($rootNode), + ]), $error); + } catch (PrinterError $printError) { + throw $error; + } + } +} diff --git a/lib/Expression/Exception/EvaluationError.php b/lib/Expression/Exception/EvaluationError.php new file mode 100644 index 000000000..55d15541b --- /dev/null +++ b/lib/Expression/Exception/EvaluationError.php @@ -0,0 +1,26 @@ +node = $node; + parent::__construct($message, 0, $previous); + } + + public function node(): Node + { + return $this->node; + } +} diff --git a/lib/Expression/Exception/ExpressionError.php b/lib/Expression/Exception/ExpressionError.php new file mode 100644 index 000000000..e88cd33e5 --- /dev/null +++ b/lib/Expression/Exception/ExpressionError.php @@ -0,0 +1,19 @@ +toString(); + $center = (int)$target->start() + ($target->length() / 2); + + foreach ($tokens as $token) { + if ($token === $target) { + $underline = str_repeat('-', $token->start()) . str_repeat('^', $token->length()); + + break; + } + } + + return new self(implode( + "\n", + [ + '', + $message . ':', + '', + ' ' . TextTruncate::centered($expr, $center, '…', $length), + ' ' . TextTruncate::centered($underline, $center, ' ', $length), + ] + )); + } +} diff --git a/lib/Expression/ExpressionFunctions.php b/lib/Expression/ExpressionFunctions.php new file mode 100644 index 000000000..c1f5387b8 --- /dev/null +++ b/lib/Expression/ExpressionFunctions.php @@ -0,0 +1,60 @@ + + */ + private $functionMap = []; + + /** + * @param array $functionMap + */ + public function __construct(array $functionMap) + { + foreach ($functionMap as $name => $callable) { + $this->add($name, $callable); + } + } + + /** + * @return string[] + */ + public function names(): array + { + return array_keys($this->functionMap); + } + + private function add(string $name, callable $callable): void + { + $this->functionMap[$name] = $callable; + } + + public function execute(string $functionName, array $args): Node + { + if (!isset($this->functionMap[$functionName])) { + throw new RuntimeException(sprintf( + 'Unknown function "%s", known functions "%s"', + $functionName, + implode('", "', array_keys($this->functionMap)) + )); + } + + $function = $this->functionMap[$functionName]; + $evaluated = $function(...$args); + + if (!$evaluated instanceof Node) { + throw new RuntimeException(sprintf( + 'Function "%s" must return a Node, got "%s"', + $functionName, gettype($evaluated) + )); + } + + return $evaluated; + } +} diff --git a/lib/Expression/ExpressionLanguage.php b/lib/Expression/ExpressionLanguage.php new file mode 100644 index 000000000..aa95987d6 --- /dev/null +++ b/lib/Expression/ExpressionLanguage.php @@ -0,0 +1,10 @@ + + */ + private $cache = []; + + /** + * @var ExpressionLanguage + */ + private $inner; + + public function __construct(ExpressionLanguage $inner) + { + $this->inner = $inner; + } + + public function parse(string $expression): Node + { + if (isset($this->cache[$expression])) { + return $this->cache[$expression]; + } + + $this->cache[$expression] = $this->inner->parse($expression); + + return $this->cache[$expression]; + } +} diff --git a/lib/Expression/ExpressionLanguage/RealExpressionLanguage.php b/lib/Expression/ExpressionLanguage/RealExpressionLanguage.php new file mode 100644 index 000000000..0e5bd1efa --- /dev/null +++ b/lib/Expression/ExpressionLanguage/RealExpressionLanguage.php @@ -0,0 +1,31 @@ +lexer = $lexer; + $this->parser = $parser; + } + + public function parse(string $expression): Node + { + return $this->parser->parse($this->lexer->lex($expression)); + } +} diff --git a/lib/Expression/Func/CoalesceFunction.php b/lib/Expression/Func/CoalesceFunction.php new file mode 100644 index 000000000..803780f98 --- /dev/null +++ b/lib/Expression/Func/CoalesceFunction.php @@ -0,0 +1,22 @@ +value(); + $first = reset($values); + + if (!$first) { + throw new EvaluationError($list, 'List is empty, cannot get first'); + } + + return $first; + } +} diff --git a/lib/Expression/Func/FormatFunction.php b/lib/Expression/Func/FormatFunction.php new file mode 100644 index 000000000..13e16f626 --- /dev/null +++ b/lib/Expression/Func/FormatFunction.php @@ -0,0 +1,30 @@ +value(), ...array_map(function (PhpValue $value) { + return $value->value(); + }, $values)); + + if (!is_string($formatted)) { + $error = error_get_last(); + $message = isset($error['message']) ? $error['message'] : 'could not format string'; + $message = str_replace('sprintf', 'format', $message); + + throw new RuntimeException($message); + } + + return new StringNode($formatted); + } +} diff --git a/lib/Expression/Func/JoinFunction.php b/lib/Expression/Func/JoinFunction.php new file mode 100644 index 000000000..fed8dcec7 --- /dev/null +++ b/lib/Expression/Func/JoinFunction.php @@ -0,0 +1,19 @@ +value(), + $values->phpValues() + ) + ); + } +} diff --git a/lib/Expression/Func/MaxFunction.php b/lib/Expression/Func/MaxFunction.php new file mode 100644 index 000000000..51cbd2503 --- /dev/null +++ b/lib/Expression/Func/MaxFunction.php @@ -0,0 +1,24 @@ +phpValues()); + + if (!is_float($result) && !is_int($result)) { + throw new RuntimeException( + 'Could not evaluate max' + ); + } + + return PhpValueFactory::fromValue($result); + } +} diff --git a/lib/Expression/Func/MeanFunction.php b/lib/Expression/Func/MeanFunction.php new file mode 100644 index 000000000..8165bc76d --- /dev/null +++ b/lib/Expression/Func/MeanFunction.php @@ -0,0 +1,16 @@ +phpValues())); + } +} diff --git a/lib/Expression/Func/MinFunction.php b/lib/Expression/Func/MinFunction.php new file mode 100644 index 000000000..111d1fd56 --- /dev/null +++ b/lib/Expression/Func/MinFunction.php @@ -0,0 +1,24 @@ +phpValues()); + + if (!is_float($result) && !is_int($result)) { + throw new RuntimeException( + 'Could not evaluate min' + ); + } + + return PhpValueFactory::fromValue($result); + } +} diff --git a/lib/Expression/Func/ModeFunction.php b/lib/Expression/Func/ModeFunction.php new file mode 100644 index 000000000..2b93ed6c0 --- /dev/null +++ b/lib/Expression/Func/ModeFunction.php @@ -0,0 +1,16 @@ +phpValues(), $space ? $space->value() : 512)); + } +} diff --git a/lib/Expression/Func/PercentDifferenceFunction.php b/lib/Expression/Func/PercentDifferenceFunction.php new file mode 100644 index 000000000..2e851bc00 --- /dev/null +++ b/lib/Expression/Func/PercentDifferenceFunction.php @@ -0,0 +1,18 @@ +value(), + $value2->value() + ), $tolerance ? $tolerance->value() : 0); + } +} diff --git a/lib/Expression/Func/RStDevFunction.php b/lib/Expression/Func/RStDevFunction.php new file mode 100644 index 000000000..06cf75065 --- /dev/null +++ b/lib/Expression/Func/RStDevFunction.php @@ -0,0 +1,19 @@ +phpValues(), $sample ? $sample->value() : false)) + ); + } +} diff --git a/lib/Expression/Func/StDevFunction.php b/lib/Expression/Func/StDevFunction.php new file mode 100644 index 000000000..50f9fdd14 --- /dev/null +++ b/lib/Expression/Func/StDevFunction.php @@ -0,0 +1,16 @@ +phpValues(), $sample ? $sample->value() : false)); + } +} diff --git a/lib/Expression/Func/VarianceFunction.php b/lib/Expression/Func/VarianceFunction.php new file mode 100644 index 000000000..968a3a7de --- /dev/null +++ b/lib/Expression/Func/VarianceFunction.php @@ -0,0 +1,16 @@ +phpValues(), $sample ? $sample->value() : false)); + } +} diff --git a/lib/Expression/InfixParselet.php b/lib/Expression/InfixParselet.php new file mode 100644 index 000000000..97d093eba --- /dev/null +++ b/lib/Expression/InfixParselet.php @@ -0,0 +1,12 @@ + Token::T_TOLERANCE, + '.' => Token::T_DOT, + '(' => Token::T_OPEN_PAREN, + ')' => Token::T_CLOSE_PAREN, + '[' => Token::T_OPEN_LIST, + ']' => Token::T_CLOSE_LIST, + ',' => Token::T_COMMA, + 'as' => Token::T_AS, + '+' => Token::T_PLUS, + '-' => Token::T_MINUS, + '*' => Token::T_MULTIPLY, + '%' => Token::T_PERCENTAGE, + '/' => Token::T_DIVIDE, + '>=' => Token::T_GTE, + '<=' => Token::T_LTE, + '>' => Token::T_GT, + '=' => Token::T_EQUALS, + '<' => Token::T_LT, + '~' => Token::T_TILDE, + 'and' => Token::T_LOGICAL_OR, + 'true' => Token::T_BOOLEAN, + 'false' => Token::T_BOOLEAN, + 'or' => Token::T_LOGICAL_AND, + 'ops/' => Token::T_THROUGHPUT, + ' ' => Token::T_WHITESPACE, + 'null' => Token::T_NULL, + ]; + + const PATTERNS = [ + self::PATTERN_NUMBER, // numbers + self::PATTERN_FUNCTION, + self::PATTERN_NAME, + self::PATTERN_STRING, + '\.', + ]; + + /** + */ + public function __construct( + array $unitNames = [] + ) { + $this->pattern = sprintf( + '{(%s)|(%s)|\n}iu', + implode(')|(', array_map(function (string $value) { + return preg_quote($value); + }, array_keys(self::TOKEN_VALUE_MAP))), + implode(')|(', self::PATTERNS) + ); + $this->unitNames = $unitNames; + } + + public function lex(string $expression): Tokens + { + $chunks = (array)preg_split( + $this->pattern, + $expression, + -1, + PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE + ); + $tokens = []; + + $prevToken = new Token(Token::T_EOF, '', 0); + + foreach ($chunks as $chunk) { + [ $value, $offset ] = $chunk; + $prevToken = new Token( + $this->resolveType($value, $prevToken), + $value, + $offset + ); + $tokens[] = $prevToken; + $prevChunk = $chunk; + } + + return new Tokens($tokens); + } + + /** + * @param mixed $value + */ + protected function resolveType($value, Token $prevToken): string + { + $type = Token::T_NONE; + + if (array_key_exists($value, self::TOKEN_VALUE_MAP)) { + return self::TOKEN_VALUE_MAP[$value]; + } + + switch (true) { + case (is_numeric($value)): + if (strpos((string)$value, '.') !== false || stripos((string)$value, 'e') !== false) { + return Token::T_FLOAT; + } + + return Token::T_INTEGER; + case ($value[0] === '"'): + return Token::T_STRING; + + case ($prevToken->type !== Token::T_DOT && in_array($value, $this->unitNames, true)): + return Token::T_UNIT; + + case (preg_match('{'. self::PATTERN_FUNCTION. '}', $value)): + return Token::T_FUNCTION; + + case (preg_match('{'. self::PATTERN_NAME. '}', $value)): + return Token::T_NAME; + + } + + return $type; + } +} diff --git a/lib/Expression/NodeEvaluator.php b/lib/Expression/NodeEvaluator.php new file mode 100644 index 000000000..c918e2a58 --- /dev/null +++ b/lib/Expression/NodeEvaluator.php @@ -0,0 +1,13 @@ +evaluateType($node, PhpValue::class, $params); + }, $node->value()) + ); + } +} diff --git a/lib/Expression/NodeEvaluator/ArithmeticOperatorEvaluator.php b/lib/Expression/NodeEvaluator/ArithmeticOperatorEvaluator.php new file mode 100644 index 000000000..fcc276607 --- /dev/null +++ b/lib/Expression/NodeEvaluator/ArithmeticOperatorEvaluator.php @@ -0,0 +1,56 @@ +evaluateType($node->left(), NumberNode::class, $params); + $rightValue = $evaluator->evaluateType($node->right(), NumberNode::class, $params); + + $value = $this->evaluateNode($node, $leftValue->value(), $rightValue->value()); + + return PhpValueFactory::fromValue($value); + } + + /** + * @param int|float $leftValue + * @param int|float $rightValue + * + * @return int|float + */ + private function evaluateNode(ArithmeticOperatorNode $node, $leftValue, $rightValue) + { + switch ($node->operator()) { + case '+': + return $leftValue + $rightValue; + case '*': + return $leftValue * $rightValue; + case '/': + return $leftValue / $rightValue; + case '-': + return $leftValue - $rightValue; + } + + throw new EvaluationError($node, sprintf( + 'Unknown operator "%s"', + $node->operator() + )); + } +} diff --git a/lib/Expression/NodeEvaluator/ComparisonEvaluator.php b/lib/Expression/NodeEvaluator/ComparisonEvaluator.php new file mode 100644 index 000000000..2ade0446a --- /dev/null +++ b/lib/Expression/NodeEvaluator/ComparisonEvaluator.php @@ -0,0 +1,87 @@ +left(); + $rightNode = $node->right(); + + $leftValue = $evaluator->evaluateType($node->left(), PhpValue::class, $params); + $rightValue = $evaluator->evaluate($node->right(), $params); + + if ($rightValue instanceof TolerableNode) { + $toleranceValue = $evaluator->evaluateType($rightValue->tolerance(), NumberValue::class, $params); + $rightValue = $evaluator->evaluateType($rightValue->value(), NumberValue::class, $params); + + if (FloatNumber::isWithin( + $leftValue->value(), + $rightValue->value() - $toleranceValue->value(), + $rightValue->value() + $toleranceValue->value() + )) { + return new ToleratedTrue(); + } + } + + $rightValue = $evaluator->evaluateType($rightValue, PhpValue::class, $params); + + return new BooleanNode($this->evaluateNode($node, $leftValue->value(), $rightValue->value())); + } + + /** + * @param string|int|float $leftValue + * @param string|int|float $rightValue + */ + private function evaluateNode(ComparisonNode $node, $leftValue, $rightValue): bool + { + if ($node->operator() == '=') { + return $leftValue == $rightValue; + } + + if (!is_numeric($leftValue) || !is_numeric($rightValue)) { + throw new EvaluationError($node, sprintf( + 'Unsupported operator "%s" when comparing "%s" and "%s"', + $node->operator(), + gettype($leftValue), + gettype($rightValue) + )); + } + + switch ($node->operator()) { + case '<': + return $leftValue < $rightValue; + case '<=': + return $leftValue <= $rightValue; + case '>': + return $leftValue > $rightValue; + case '>=': + return $leftValue >= $rightValue; + } + + throw new EvaluationError($node, sprintf( + 'Unknown comparison operator "%s"', + $node->operator() + )); + } +} diff --git a/lib/Expression/NodeEvaluator/ConcatEvaluator.php b/lib/Expression/NodeEvaluator/ConcatEvaluator.php new file mode 100644 index 000000000..326a9ede2 --- /dev/null +++ b/lib/Expression/NodeEvaluator/ConcatEvaluator.php @@ -0,0 +1,33 @@ +value(), + (string)$right->value() + ]), $left, $right); + })( + $evaluator->evaluateType($node->left(), PhpValue::class, $params), + $evaluator->evaluateType($node->right(), PhpValue::class, $params) + ); + } +} diff --git a/lib/Expression/NodeEvaluator/DisplayAsEvaluator.php b/lib/Expression/NodeEvaluator/DisplayAsEvaluator.php new file mode 100644 index 000000000..96df2b6be --- /dev/null +++ b/lib/Expression/NodeEvaluator/DisplayAsEvaluator.php @@ -0,0 +1,36 @@ +evaluateType($node->node(), NumberNode::class, $params); + $unit = new UnitNode($evaluator->evaluateType($node->as()->unit(), StringNode::class, $params)); + + $precision = $node->precision(); + + if ($precision) { + $precision = $evaluator->evaluateType($node->precision(), PhpValue::class, $params); + } + + return new DisplayAsNode($value, $unit, $precision); + } +} diff --git a/lib/Expression/NodeEvaluator/FunctionEvaluator.php b/lib/Expression/NodeEvaluator/FunctionEvaluator.php new file mode 100644 index 000000000..63cd0af9d --- /dev/null +++ b/lib/Expression/NodeEvaluator/FunctionEvaluator.php @@ -0,0 +1,75 @@ +functions = $functions; + } + + /** + * @param parameters $params + */ + public function evaluate(Evaluator $evaluator, Node $node, array $params): ?Node + { + if (!$node instanceof FunctionNode) { + return null; + } + + try { + return $this->functions->execute( + $node->name(), + array_map(function (Node $node) use ($evaluator, $params) { + return $evaluator->evaluateType($node, PhpValue::class, $params); + }, $this->args($node->args())) + ); + } catch (Throwable $throwable) { + throw new EvaluationError($node, $throwable->getMessage(), $throwable); + } + } + + /** + * @return array + */ + private function args(?ArgumentListNode $args) + { + if (null === $args) { + return []; + } + + return $args->value(); + } + + /** + * @return mixed + */ + private function resolveScalarValues(PhpValue $node) + { + $value = $node->value(); + + if (is_array($value)) { + return array_map(function (PhpValue $value) { + return $this->resolveScalarValues($value); + }, $value); + } + + return $value; + } +} diff --git a/lib/Expression/NodeEvaluator/ListEvaluator.php b/lib/Expression/NodeEvaluator/ListEvaluator.php new file mode 100644 index 000000000..e7b932268 --- /dev/null +++ b/lib/Expression/NodeEvaluator/ListEvaluator.php @@ -0,0 +1,25 @@ +evaluate($node, $params); + }, $node->value())); + } +} diff --git a/lib/Expression/NodeEvaluator/LogicalOperatorEvaluator.php b/lib/Expression/NodeEvaluator/LogicalOperatorEvaluator.php new file mode 100644 index 000000000..3912b43b3 --- /dev/null +++ b/lib/Expression/NodeEvaluator/LogicalOperatorEvaluator.php @@ -0,0 +1,54 @@ +evaluateType( + $node->left(), + PhpValue::class, + $params + ); + $rightValue = $evaluator->evaluateType( + $node->right(), + PhpValue::class, + $params + ); + + $value = $this->evaluateNode($node, $leftValue->value(), $rightValue->value()); + + return new BooleanNode($value); + } + + private function evaluateNode(LogicalOperatorNode $node, bool $leftValue, bool $rightValue): bool + { + switch ($node->operator()) { + case 'or': + return $leftValue || $rightValue; + case 'and': + return $leftValue && $rightValue; + } + + throw new EvaluationError($node, sprintf( + 'Unknown operator "%s"', + $node->operator() + )); + } +} diff --git a/lib/Expression/NodeEvaluator/MemoisedNodeEvaluator.php b/lib/Expression/NodeEvaluator/MemoisedNodeEvaluator.php new file mode 100644 index 000000000..f934bab78 --- /dev/null +++ b/lib/Expression/NodeEvaluator/MemoisedNodeEvaluator.php @@ -0,0 +1,47 @@ + + */ + private $cache = []; + + /** + * @var NodeEvaluator + */ + private $innerEvaluator; + + /** + */ + public function __construct(NodeEvaluator $innerEvaluator) + { + $this->innerEvaluator = $innerEvaluator; + } + + /** + * @param parameters $params + */ + public function evaluate(Evaluator $evaluator, Node $node, array $params): ?Node + { + try { + $hash = serialize($node) . serialize($params); + } catch (\Exception $exception) { + return $this->innerEvaluator->evaluate($evaluator, $node, $params); + } + + if (isset($this->cache[$hash])) { + return $this->cache[$hash]; + } + + $this->cache[$hash] = $this->innerEvaluator->evaluate($evaluator, $node, $params); + + return $this->cache[$hash]; + } +} diff --git a/lib/Expression/NodeEvaluator/ParameterEvaluator.php b/lib/Expression/NodeEvaluator/ParameterEvaluator.php new file mode 100644 index 000000000..06a5e3bea --- /dev/null +++ b/lib/Expression/NodeEvaluator/ParameterEvaluator.php @@ -0,0 +1,97 @@ +segments(), $params); + + if (is_numeric($value)) { + return PhpValueFactory::fromValue($value); + } + + if (is_array($value)) { + return ListNode::fromValues($value); + } + + if (is_string($value)) { + return new StringNode($value); + } + + if (is_null($value)) { + return new NullNode(); + } + + throw new EvaluationError($node, sprintf( + 'Do not know how to interpret value "%s"', gettype($value) + )); + } + + /** + * @return mixed + * + * @param array|object|scalar $container + * @param array $segments + */ + private static function resolvePropertyAccess(Node $node, array $segments, $container) + { + $segment = array_shift($segments); + $value = self::valueFromContainer($node, $container, $segment); + + if (count($segments) === 0) { + return $value; + } + + return self::resolvePropertyAccess($node, $segments, $value); + } + + /** + * @return int|float|object|array + * + * @param array|object|scalar $container + */ + private static function valueFromContainer(Node $node, $container, string $segment) + { + if (is_array($container)) { + if (!array_key_exists($segment, $container)) { + throw new EvaluationError($node, sprintf( + 'Array does not have key "%s", it has keys: "%s"', + $segment, + implode('", "', array_keys($container)) + )); + } + + return $container[$segment]; + } + + if (is_object($container) && method_exists($container, $segment)) { + return $container->$segment(); + } + + throw new EvaluationError($node, sprintf( + 'Could not access "%s" on "%s"', + $segment, + is_object($container) ? get_class($container) : gettype($container) + )); + } +} diff --git a/lib/Expression/NodeEvaluator/ParenthesisEvaluator.php b/lib/Expression/NodeEvaluator/ParenthesisEvaluator.php new file mode 100644 index 000000000..d5e701880 --- /dev/null +++ b/lib/Expression/NodeEvaluator/ParenthesisEvaluator.php @@ -0,0 +1,23 @@ +evaluate($node->expression(), $params); + } +} diff --git a/lib/Expression/NodeEvaluator/PhpValueEvaluator.php b/lib/Expression/NodeEvaluator/PhpValueEvaluator.php new file mode 100644 index 000000000..7e8406bbe --- /dev/null +++ b/lib/Expression/NodeEvaluator/PhpValueEvaluator.php @@ -0,0 +1,23 @@ +tolerance(); + $context = $evaluator->evaluateType($node->value(), NumberValue::class, $params); + + if ($toleranceNode instanceof PercentageNode) { + $amount = $evaluator->evaluateType($toleranceNode->valueNode(), NumberValue::class, $params); + $tolerance = new FloatNode( + ($context->value() / 100) * $amount->value() + ); + } else { + $tolerance = $evaluator->evaluate($toleranceNode, $params); + } + + if ($context instanceof DisplayAsNode) { + $tolerance = new DisplayAsNode($tolerance, $context->as()); + } + + return new TolerableNode( + $context, + $tolerance + ); + } +} diff --git a/lib/Expression/NodeEvaluator/ValueWithUnitEvaluator.php b/lib/Expression/NodeEvaluator/ValueWithUnitEvaluator.php new file mode 100644 index 000000000..7f1b83fcb --- /dev/null +++ b/lib/Expression/NodeEvaluator/ValueWithUnitEvaluator.php @@ -0,0 +1,58 @@ +evaluateType($node->left(), NumberNode::class, $params); + $unit = $evaluator->evaluateType($node->unit()->unit(), StringNode::class, $params)->value(); + + if (TimeUnit::isTimeUnit($unit)) { + return PhpValueFactory::fromValue($this->timeUnit($value->value(), $unit)); + } + + if (MemoryUnit::isMemoryUnit($unit)) { + return PhpValueFactory::fromValue($this->memoryUnit($value->value(), $unit)); + } + + throw new EvaluationError($node, sprintf( + 'Do not know how to evaluate unit "%s"', $unit + )); + } + + /** + * Return time in microseconds + */ + private function timeUnit(float $value, string $unit): float + { + return TimeUnit::convert($value, $unit, TimeUnit::MICROSECONDS, TimeUnit::MODE_TIME); + } + + /** + * Return memory in bytes + */ + private function memoryUnit(float $value, string $unit): float + { + return MemoryUnit::convertTo($value, $unit, MemoryUnit::BYTES); + } +} diff --git a/lib/Expression/NodeEvaluators.php b/lib/Expression/NodeEvaluators.php new file mode 100644 index 000000000..2ad42d7ce --- /dev/null +++ b/lib/Expression/NodeEvaluators.php @@ -0,0 +1,43 @@ +evaluators = $evaluators; + } + + /** + * @param parameters $params + */ + public function evaluate(Evaluator $evaluator, Node $node, array $params): Node + { + foreach ($this->evaluators as $nodeEvaluator) { + $evaluated = $nodeEvaluator->evaluate($evaluator, $node, $params); + + if (null === $evaluated) { + continue; + } + + return $evaluated; + } + + throw new EvaluationError($node, sprintf( + 'Could not find evaluator for node of type "%s"', get_class($node) + )); + } +} diff --git a/lib/Expression/NodePrinter.php b/lib/Expression/NodePrinter.php new file mode 100644 index 000000000..61577a44c --- /dev/null +++ b/lib/Expression/NodePrinter.php @@ -0,0 +1,12 @@ +value() as $expression) { + $out[] = $printer->print($expression); + } + + return implode(', ', $out); + } +} diff --git a/lib/Expression/NodePrinter/BinaryOperatorPrinter.php b/lib/Expression/NodePrinter/BinaryOperatorPrinter.php new file mode 100644 index 000000000..49316658e --- /dev/null +++ b/lib/Expression/NodePrinter/BinaryOperatorPrinter.php @@ -0,0 +1,25 @@ +print($node->left()), + $node->operator(), + $printer->print($node->right()) + ); + } +} diff --git a/lib/Expression/NodePrinter/BooleanPrinter.php b/lib/Expression/NodePrinter/BooleanPrinter.php new file mode 100644 index 000000000..258085cbc --- /dev/null +++ b/lib/Expression/NodePrinter/BooleanPrinter.php @@ -0,0 +1,20 @@ +value() ? 'true':'false'; + } +} diff --git a/lib/Expression/NodePrinter/ComparisonPrinter.php b/lib/Expression/NodePrinter/ComparisonPrinter.php new file mode 100644 index 000000000..08e73a648 --- /dev/null +++ b/lib/Expression/NodePrinter/ComparisonPrinter.php @@ -0,0 +1,25 @@ +print($node->left()), + $node->operator(), + $printer->print($node->right()) + ); + } +} diff --git a/lib/Expression/NodePrinter/ConcatPrinter.php b/lib/Expression/NodePrinter/ConcatPrinter.php new file mode 100644 index 000000000..a7cd2e206 --- /dev/null +++ b/lib/Expression/NodePrinter/ConcatPrinter.php @@ -0,0 +1,26 @@ +print($node->left()), + $printer->print($node->right()) + ]); + } +} diff --git a/lib/Expression/NodePrinter/ConcatenatedNodePrinter.php b/lib/Expression/NodePrinter/ConcatenatedNodePrinter.php new file mode 100644 index 000000000..cfee3c93a --- /dev/null +++ b/lib/Expression/NodePrinter/ConcatenatedNodePrinter.php @@ -0,0 +1,26 @@ +print($node->left()), + $printer->print($node->right()) + ]); + } +} diff --git a/lib/Expression/NodePrinter/DisplayAsPrinter.php b/lib/Expression/NodePrinter/DisplayAsPrinter.php new file mode 100644 index 000000000..d261b649c --- /dev/null +++ b/lib/Expression/NodePrinter/DisplayAsPrinter.php @@ -0,0 +1,156 @@ +timeUnit = $timeUnit; + } + + public function print(Printer $printer, Node $node): ?string + { + if (!$node instanceof DisplayAsNode) { + return null; + } + + $value = $node->node(); + $unitNode = $node->as(); + + $unit = $unitNode->unit(); + + if (!$unit instanceof StringNode) { + throw new PrinterError(sprintf('Unit must evaluate to string, got "%s"', get_class($unit))); + } + $unit = $unit->value(); + $mode = $this->resolveMode($node); + + if (!$value instanceof PhpValue) { + return sprintf('%s as %s', $printer->print($value), $unit); + } + + if (TimeUnit::isTimeUnit($unit)) { + return $this->timeUnit( + $value->value(), + $unit, + $this->resolvePrecision($node->precision()), + $printer->print( + new UnitNode(new StringNode($this->timeUnit->getDestSuffix( + $this->timeUnit->resolveDestUnit($unit, $value->value()), + $mode + ))) + ), + $mode + ); + } + + if (MemoryUnit::isMemoryUnit($unit)) { + $unit = MemoryUnit::resolveSuitableUnit($unit, $value->value()); + + return $this->memoryUnit( + $value->value(), + $unit, + $printer->print(new UnitNode( + new StringNode(MemoryUnit::suffixFor($unit)) + )) + ); + } + + throw new PrinterError(sprintf( + 'Do not know how to display unit "%s"', $unit + )); + } + + /** + * @return string[] + */ + public static function supportedUnitNames(): array + { + return array_merge( + TimeUnit::supportedUnitNames(), + MemoryUnit::supportedUnitNames() + ); + } + + private function timeUnit(float $value, ?string $unit, ?int $precision, string $prettyUnit, ?string $mode): string + { + return sprintf('%s%s', number_format( + $this->timeUnit->toDestUnit( + $value, + $this->timeUnit->resolveDestUnit($unit, $value), + $mode + ), + $precision ?: $this->timeUnit->getPrecision() + ), $prettyUnit); + } + + /** + * Return memory in bytes + */ + private function memoryUnit(float $value, string $unit, string $prettyUnit): string + { + return sprintf( + '%s%s', + number_format(MemoryUnit::convertTo($value, MemoryUnit::BYTES, $unit), $unit === MemoryUnit::BYTES ? 0 : 3), + $prettyUnit + ); + } + + private function resolvePrecision(?Node $node): ?int + { + if ($node instanceof IntegerNode) { + return $node->value(); + } + + return null; + } + + private function resolveMode(DisplayAsNode $node): ?string + { + if (!$node instanceof DisplayAsTimeNode) { + return null; + } + + $mode = $node->mode(); + + if (!$mode) { + return null; + } + + if ($mode instanceof NullNode) { + return null; + } + + if (!$mode instanceof StringNode) { + throw new PrinterError(sprintf('Time mode must evaluate to string, got "%s"', get_class($mode))); + } + + return $mode->value(); + } +} diff --git a/lib/Expression/NodePrinter/FunctionPrinter.php b/lib/Expression/NodePrinter/FunctionPrinter.php new file mode 100644 index 000000000..485910366 --- /dev/null +++ b/lib/Expression/NodePrinter/FunctionPrinter.php @@ -0,0 +1,20 @@ +name(), $printer->print($node->args())); + } +} diff --git a/lib/Expression/NodePrinter/HighlightingNodePrinter.php b/lib/Expression/NodePrinter/HighlightingNodePrinter.php new file mode 100644 index 000000000..b2682b55f --- /dev/null +++ b/lib/Expression/NodePrinter/HighlightingNodePrinter.php @@ -0,0 +1,58 @@ + + */ + private $colorMap; + + /** + * @param ColorMap $colorMap + */ + public function __construct(NodePrinter $nodePrinter, ColorMap $colorMap) + { + $this->nodePrinter = $nodePrinter; + $this->colorMap = $colorMap; + } + + /** + * {@inheritDoc} + */ + public function print(Printer $printer, Node $node): ?string + { + $printed = $this->nodePrinter->print($printer, $node); + + $map = $this->colorMap->colors(); + + foreach ($map as $nodeFqn => $color) { + if (!$node instanceof $nodeFqn) { + continue; + } + + if (is_callable($color)) { + $color = $color($node); + } + + return sprintf( + '<%s>%s', + $color, + $printed + ); + } + + return $printed; + } +} diff --git a/lib/Expression/NodePrinter/ListPrinter.php b/lib/Expression/NodePrinter/ListPrinter.php new file mode 100644 index 000000000..ef837b5d8 --- /dev/null +++ b/lib/Expression/NodePrinter/ListPrinter.php @@ -0,0 +1,26 @@ +value() as $expression) { + $out[] = $printer->print($expression); + } + + return '[' . implode(', ', $out) . ']'; + } +} diff --git a/lib/Expression/NodePrinter/NullPrinter.php b/lib/Expression/NodePrinter/NullPrinter.php new file mode 100644 index 000000000..16269d9eb --- /dev/null +++ b/lib/Expression/NodePrinter/NullPrinter.php @@ -0,0 +1,20 @@ +precision = $precision; + } + + public function print(Printer $printer, Node $node): ?string + { + if (!$node instanceof NumberNode) { + return null; + } + + return (string)round($node->value(), $this->precision); + } +} diff --git a/lib/Expression/NodePrinter/ParameterPrinter.php b/lib/Expression/NodePrinter/ParameterPrinter.php new file mode 100644 index 000000000..f29231831 --- /dev/null +++ b/lib/Expression/NodePrinter/ParameterPrinter.php @@ -0,0 +1,22 @@ +segments()); + } +} diff --git a/lib/Expression/NodePrinter/ParenthesisPrinter.php b/lib/Expression/NodePrinter/ParenthesisPrinter.php new file mode 100644 index 000000000..978f30035 --- /dev/null +++ b/lib/Expression/NodePrinter/ParenthesisPrinter.php @@ -0,0 +1,20 @@ +print($node->expression())); + } +} diff --git a/lib/Expression/NodePrinter/PercentageDifferencePrinter.php b/lib/Expression/NodePrinter/PercentageDifferencePrinter.php new file mode 100644 index 000000000..328c0b200 --- /dev/null +++ b/lib/Expression/NodePrinter/PercentageDifferencePrinter.php @@ -0,0 +1,30 @@ +percentage() > 0 ? '+' : ''; + + return sprintf( + '%s%.2f%%', + $prefix, + $printer->print(new FloatNode($node->percentage())) + ); + } +} diff --git a/lib/Expression/NodePrinter/PercentagePrinter.php b/lib/Expression/NodePrinter/PercentagePrinter.php new file mode 100644 index 000000000..1b14ae050 --- /dev/null +++ b/lib/Expression/NodePrinter/PercentagePrinter.php @@ -0,0 +1,26 @@ +print($node->valueNode()), + $printer->print(new UnitNode(new StringNode('%'))) + ); + } +} diff --git a/lib/Expression/NodePrinter/RelativeDeviationPrinter.php b/lib/Expression/NodePrinter/RelativeDeviationPrinter.php new file mode 100644 index 000000000..f513a12de --- /dev/null +++ b/lib/Expression/NodePrinter/RelativeDeviationPrinter.php @@ -0,0 +1,26 @@ +print(new UnitNode(new StringNode('±'))), + $node->value() + ); + } +} diff --git a/lib/Expression/NodePrinter/StringPrinter.php b/lib/Expression/NodePrinter/StringPrinter.php new file mode 100644 index 000000000..1d964cf9a --- /dev/null +++ b/lib/Expression/NodePrinter/StringPrinter.php @@ -0,0 +1,20 @@ +value(); + } +} diff --git a/lib/Expression/NodePrinter/TolerablePrinter.php b/lib/Expression/NodePrinter/TolerablePrinter.php new file mode 100644 index 000000000..6d87fb54d --- /dev/null +++ b/lib/Expression/NodePrinter/TolerablePrinter.php @@ -0,0 +1,31 @@ +print($node->value()), + $printer->print($node->tolerance()) + ); + } +} diff --git a/lib/Expression/NodePrinter/UnitPrinter.php b/lib/Expression/NodePrinter/UnitPrinter.php new file mode 100644 index 000000000..5e7c568c3 --- /dev/null +++ b/lib/Expression/NodePrinter/UnitPrinter.php @@ -0,0 +1,20 @@ +print($node->unit()); + } +} diff --git a/lib/Expression/NodePrinter/ValueWithUnitPrinter.php b/lib/Expression/NodePrinter/ValueWithUnitPrinter.php new file mode 100644 index 000000000..7f38c8aa7 --- /dev/null +++ b/lib/Expression/NodePrinter/ValueWithUnitPrinter.php @@ -0,0 +1,24 @@ +print($node->left()), + $printer->print($node->unit()) + ); + } +} diff --git a/lib/Expression/NodePrinters.php b/lib/Expression/NodePrinters.php new file mode 100644 index 000000000..6c84c4f17 --- /dev/null +++ b/lib/Expression/NodePrinters.php @@ -0,0 +1,42 @@ +printers = $printers; + } + + /** + */ + public function print(Printer $printer, Node $node): string + { + foreach ($this->printers as $nodePrinter) { + $output = $nodePrinter->print($printer, $node); + + if (null === $output) { + continue; + } + + return $output; + } + + throw new PrinterError(sprintf( + 'Could not find printer for node of class "%s"', + get_class($node) + )); + } +} diff --git a/lib/Expression/Parselet.php b/lib/Expression/Parselet.php new file mode 100644 index 000000000..c24930fda --- /dev/null +++ b/lib/Expression/Parselet.php @@ -0,0 +1,8 @@ +tokenType = $tokenType; + $this->precedence = $precedence; + } + + public function tokenType(): string + { + return $this->tokenType; + } + + public function parse(Parser $parser, Node $left, Tokens $tokens): Node + { + $binaryOperator = $tokens->chomp(); + $right = $parser->parseExpression($tokens, $this->precedence); + + return new ArithmeticOperatorNode($left, $binaryOperator->value, $right); + } + + public function precedence(): int + { + return $this->precedence; + } +} diff --git a/lib/Expression/Parselet/BooleanParselet.php b/lib/Expression/Parselet/BooleanParselet.php new file mode 100644 index 000000000..0f3c39704 --- /dev/null +++ b/lib/Expression/Parselet/BooleanParselet.php @@ -0,0 +1,29 @@ +chomp(); + + if ($token->value === 'true') { + return new BooleanNode(true); + } + + return new BooleanNode(false); + } +} diff --git a/lib/Expression/Parselet/ComparisonParselet.php b/lib/Expression/Parselet/ComparisonParselet.php new file mode 100644 index 000000000..dce7be235 --- /dev/null +++ b/lib/Expression/Parselet/ComparisonParselet.php @@ -0,0 +1,45 @@ +tokenType = $tokenType; + $this->precedence = $precedence; + } + + public function tokenType(): string + { + return $this->tokenType; + } + + public function parse(Parser $parser, Node $left, Tokens $tokens): Node + { + $binaryOperator = $tokens->chomp(); + $right = $parser->parseExpression($tokens, $this->precedence); + + return new ComparisonNode($left, $binaryOperator->value, $right); + } + + public function precedence(): int + { + return $this->precedence; + } +} diff --git a/lib/Expression/Parselet/ConcatParselet.php b/lib/Expression/Parselet/ConcatParselet.php new file mode 100644 index 000000000..84b155fa4 --- /dev/null +++ b/lib/Expression/Parselet/ConcatParselet.php @@ -0,0 +1,32 @@ +chomp(); + $right = $parser->parseExpression($tokens); + + return new ConcatNode($left, $right); + } + + public function precedence(): int + { + return Precedence::CONCAT; + } +} diff --git a/lib/Expression/Parselet/DisplayAsParselet.php b/lib/Expression/Parselet/DisplayAsParselet.php new file mode 100644 index 000000000..360f57eff --- /dev/null +++ b/lib/Expression/Parselet/DisplayAsParselet.php @@ -0,0 +1,63 @@ +chomp(); + + return new DisplayAsNode( + $left, + new UnitNode($this->resolveUnit($tokens, $parser)), + $this->resolvePrecision($tokens, $parser) + ); + } + + public function precedence(): int + { + return Precedence::AS; + } + + private function resolveUnit(Tokens $tokens, Parser $parser): Node + { + if ($tokens->current()->type === Token::T_UNIT) { + return new StringNode($tokens->chomp()->value); + } + + return $parser->parseExpression($tokens, Precedence::AS); + } + + private function resolvePrecision(Tokens $tokens, Parser $parser): ?Node + { + if ($tokens->current()->type !== Token::T_NAME) { + return null; + } + + if ($tokens->current()->value !== self::T_VAL_PRECISION) { + return null; + } + + $tokens->chomp(); + + return $parser->parseExpression($tokens, Precedence::AS); + } +} diff --git a/lib/Expression/Parselet/FloatParselet.php b/lib/Expression/Parselet/FloatParselet.php new file mode 100644 index 000000000..398664b1c --- /dev/null +++ b/lib/Expression/Parselet/FloatParselet.php @@ -0,0 +1,23 @@ +chomp()->value); + } +} diff --git a/lib/Expression/Parselet/FunctionParselet.php b/lib/Expression/Parselet/FunctionParselet.php new file mode 100644 index 000000000..ed004e0ca --- /dev/null +++ b/lib/Expression/Parselet/FunctionParselet.php @@ -0,0 +1,44 @@ +chomp(); + + if ($tokens->current()->type === Token::T_CLOSE_PAREN) { + $arguments = null; + } else { + $arguments = $parser->parseList($tokens); + $arguments = $this->resolveArguments($arguments); + } + + $tokens->chomp(Token::T_CLOSE_PAREN); + + return new FunctionNode(rtrim($functionToken->value, '('), $arguments); + } + + private function resolveArguments(Node $arguments): ArgumentListNode + { + if ($arguments instanceof ArgumentListNode) { + return $arguments; + } + + return new ArgumentListNode([$arguments]); + } +} diff --git a/lib/Expression/Parselet/IntegerParselet.php b/lib/Expression/Parselet/IntegerParselet.php new file mode 100644 index 000000000..b28e8a86f --- /dev/null +++ b/lib/Expression/Parselet/IntegerParselet.php @@ -0,0 +1,23 @@ +chomp()->value); + } +} diff --git a/lib/Expression/Parselet/ListParselet.php b/lib/Expression/Parselet/ListParselet.php new file mode 100644 index 000000000..7c5a17a92 --- /dev/null +++ b/lib/Expression/Parselet/ListParselet.php @@ -0,0 +1,41 @@ +chomp(); + + if ($tokens->current()->type === Token::T_CLOSE_LIST) { + $tokens->chomp(Token::T_CLOSE_LIST); + + return new ListNode([]); + } + + $list = $parser->parseList($tokens); + $tokens->chomp(Token::T_CLOSE_LIST); + + if ($list instanceof ArgumentListNode) { + $values = $list->value(); + } else { + $values = [ $list ]; + } + + return new ListNode($values); + } +} diff --git a/lib/Expression/Parselet/LogicalOperatorParselet.php b/lib/Expression/Parselet/LogicalOperatorParselet.php new file mode 100644 index 000000000..2e8af156a --- /dev/null +++ b/lib/Expression/Parselet/LogicalOperatorParselet.php @@ -0,0 +1,45 @@ +tokenType = $tokenType; + $this->precedence = $precedence; + } + + public function tokenType(): string + { + return $this->tokenType; + } + + public function parse(Parser $parser, Node $left, Tokens $tokens): Node + { + $binaryOperator = $tokens->chomp(); + $right = $parser->parseExpression($tokens, $this->precedence); + + return new LogicalOperatorNode($left, $binaryOperator->value, $right); + } + + public function precedence(): int + { + return $this->precedence; + } +} diff --git a/lib/Expression/Parselet/NullParselet.php b/lib/Expression/Parselet/NullParselet.php new file mode 100644 index 000000000..41e0025ca --- /dev/null +++ b/lib/Expression/Parselet/NullParselet.php @@ -0,0 +1,25 @@ +chomp(); + + return new NullNode(); + } +} diff --git a/lib/Expression/Parselet/ParameterParselet.php b/lib/Expression/Parselet/ParameterParselet.php new file mode 100644 index 000000000..dfcc61edb --- /dev/null +++ b/lib/Expression/Parselet/ParameterParselet.php @@ -0,0 +1,30 @@ +chomp()->value]; + + while ($tokens->current()->type === Token::T_DOT) { + $dot = $tokens->chomp(); + $segments[] = $tokens->chomp(Token::T_NAME)->value; + } + + return new ParameterNode($segments); + } +} diff --git a/lib/Expression/Parselet/ParenthesisParselet.php b/lib/Expression/Parselet/ParenthesisParselet.php new file mode 100644 index 000000000..5afa127d5 --- /dev/null +++ b/lib/Expression/Parselet/ParenthesisParselet.php @@ -0,0 +1,27 @@ +chomp(); + $expression = $parser->parseList($tokens); + $tokens->chomp(Token::T_CLOSE_PAREN); + + return new ParenthesisNode($expression); + } +} diff --git a/lib/Expression/Parselet/PercentageParselet.php b/lib/Expression/Parselet/PercentageParselet.php new file mode 100644 index 000000000..372f88086 --- /dev/null +++ b/lib/Expression/Parselet/PercentageParselet.php @@ -0,0 +1,24 @@ +chomp(); + + return new PercentageNode($left); + } +} diff --git a/lib/Expression/Parselet/StringParselet.php b/lib/Expression/Parselet/StringParselet.php new file mode 100644 index 000000000..554c50eba --- /dev/null +++ b/lib/Expression/Parselet/StringParselet.php @@ -0,0 +1,23 @@ +chomp()->value, '"')); + } +} diff --git a/lib/Expression/Parselet/TolerableParselet.php b/lib/Expression/Parselet/TolerableParselet.php new file mode 100644 index 000000000..14b875487 --- /dev/null +++ b/lib/Expression/Parselet/TolerableParselet.php @@ -0,0 +1,31 @@ +chomp(); + + return new TolerableNode($left, $parser->parseExpression($tokens, 100)); + } + + public function precedence(): int + { + return Precedence::TOLERANCE; + } +} diff --git a/lib/Expression/Parselet/ValueWithUnitParselet.php b/lib/Expression/Parselet/ValueWithUnitParselet.php new file mode 100644 index 000000000..5eb89223f --- /dev/null +++ b/lib/Expression/Parselet/ValueWithUnitParselet.php @@ -0,0 +1,24 @@ +chomp()->value))); + } +} diff --git a/lib/Expression/Parselets.php b/lib/Expression/Parselets.php new file mode 100644 index 000000000..cb972b774 --- /dev/null +++ b/lib/Expression/Parselets.php @@ -0,0 +1,93 @@ + + */ + private $parselets; + + /** + * @var string + */ + private $type; + + /** + * @param T[] $parselets + */ + private function __construct(array $parselets, string $type) + { + foreach ($parselets as $parselet) { + $this->parselets[$parselet->tokenType()] = $parselet; + } + + $this->type = $type; + } + + /** + * @param PrefixParselet[] $parselets + * + * @return self + */ + public static function fromPrefixParselets(array $parselets): self + { + return new self($parselets, 'prefix'); + } + + /** + * @param InfixParselet[] $parselets + * + * @return self + */ + public static function fromInfixParselets(array $parselets): self + { + return new self($parselets, 'infix'); + } + + /** + * @param SuffixParselet[] $parselets + * + * @return self + */ + public static function fromSuffixParselets(array $parselets): self + { + return new self($parselets, 'suffix'); + } + + /** + * @return T + */ + public function forToken(Token $token): Parselet + { + $parselet = $this->forTokenOrNull($token); + + if (null === $parselet) { + throw new ParseletNotFound(sprintf( + 'No %s parslet for token type "%s" registered', + $this->type, + $token->type + )); + } + + return $parselet; + } + + /** + * @return T|null + */ + public function forTokenOrNull(Token $token): ?Parselet + { + if (!isset($this->parselets[$token->type])) { + return null; + } + + return $this->parselets[$token->type]; + } +} diff --git a/lib/Expression/Parser.php b/lib/Expression/Parser.php new file mode 100644 index 000000000..48567ba41 --- /dev/null +++ b/lib/Expression/Parser.php @@ -0,0 +1,121 @@ + + */ + private $prefixParselets; + + /** + * @var Parselets + */ + private $infixParselets; + + /** + * @var Tokens + */ + private $tokens; + + /** + * @var Parselets + */ + private $suffixParselets; + + /** + * @param Parselets $prefixParselets + * @param Parselets $infixParselets + */ + public function __construct( + Parselets $prefixParselets, + Parselets $infixParselets, + Parselets $suffixParselets + ) { + $this->prefixParselets = $prefixParselets; + $this->infixParselets = $infixParselets; + $this->suffixParselets = $suffixParselets; + } + + public function parse(Tokens $tokens): Node + { + $tokens = $tokens->withoutWhitespace(); + $node = $this->parseList($tokens); + + if ($tokens->hasMore()) { + throw SyntaxError::forToken( + $tokens, + $tokens->current(), + sprintf('Unexpected "%s" at end of expression', $tokens->current()->type) + ); + } + + return $node; + } + + public function parseList(Tokens $tokens): Node + { + $expression = $this->parseExpression($tokens); + + $list = [$expression]; + + while ($tokens->current()->type === Token::T_COMMA) { + $tokens->chomp(); + $list[] = $this->parseExpression($tokens); + } + + if (count($list) > 1) { + return new ArgumentListNode($list); + } + + return $expression; + } + + public function parseExpression(Tokens $tokens, int $precedence = 0): Node + { + $token = $tokens->current(); + + try { + $left = $this->prefixParselets->forToken($token)->parse($this, $tokens); + } catch (ParseletNotFound $notFound) { + throw SyntaxError::forToken($tokens, $token, sprintf( + 'Could not find parselet for "%s" token', + $token->type + )); + } + + if (Token::T_EOF === $tokens->current()->type) { + return $left; + } + + $suffixParser = $this->suffixParselets->forTokenOrNull($tokens->current()); + + if ($suffixParser instanceof SuffixParselet) { + $left = $suffixParser->parse($left, $tokens); + } + + while ($precedence < $this->infixPrecedence($tokens->current())) { + $infixParselet = $this->infixParselets->forToken($tokens->current()); + $left = $infixParselet->parse($this, $left, $tokens); + } + + return $left; + } + + private function infixPrecedence(Token $token): int + { + $infixParser = $this->infixParselets->forTokenOrNull($token); + + if (!$infixParser) { + return 0; + } + + return $infixParser->precedence(); + } +} diff --git a/lib/Expression/Precedence.php b/lib/Expression/Precedence.php new file mode 100644 index 000000000..893ba3173 --- /dev/null +++ b/lib/Expression/Precedence.php @@ -0,0 +1,16 @@ +print($node); + }, $node->value()))); + } + + return (string)$node->value(); + } +} diff --git a/lib/Expression/Printer/EvaluatingPrinter.php b/lib/Expression/Printer/EvaluatingPrinter.php new file mode 100644 index 000000000..074e45a48 --- /dev/null +++ b/lib/Expression/Printer/EvaluatingPrinter.php @@ -0,0 +1,73 @@ +printers = $printers; + $this->nodeClasses = $nodeClasses; + $this->evaluator = $evaluator; + $this->params = $params; + } + + /** + * @param parameters $params + */ + public function withParams(array $params): self + { + return new self($this->printers, $this->evaluator, $this->nodeClasses, $params); + } + + public function print(Node $node): string + { + if (!$this->shouldEvaluate($node)) { + return $this->printers->print($this, $node); + } + + $node = $this->evaluator->evaluate($node, $this->params); + + return $this->printers->print($this, $node); + } + + private function shouldEvaluate(Node $node): bool + { + foreach ($this->nodeClasses as $nodeClass) { + if ($node instanceof $nodeClass) { + return true; + } + } + + return false; + } +} diff --git a/lib/Expression/Printer/NormalizingPrinter.php b/lib/Expression/Printer/NormalizingPrinter.php new file mode 100644 index 000000000..315f165ec --- /dev/null +++ b/lib/Expression/Printer/NormalizingPrinter.php @@ -0,0 +1,25 @@ +printers = $printers; + } + + public function print(Node $node): string + { + return $this->printers->print($this, $node); + } +} diff --git a/lib/Expression/Printer/UnderlinePrinter.php b/lib/Expression/Printer/UnderlinePrinter.php new file mode 100644 index 000000000..b13acc50e --- /dev/null +++ b/lib/Expression/Printer/UnderlinePrinter.php @@ -0,0 +1,37 @@ +printers = $printers; + $this->targetNode = $targetNode; + } + + public function print(Node $node): string + { + $printed = $this->printers->print($this, $node); + + if ($node !== $this->targetNode) { + return preg_replace('{[^-]}', ' ', $printed); + } + + return str_repeat('-', mb_strlen($printed)); + } +} diff --git a/lib/Expression/Printer/UnderlinePrinterFactory.php b/lib/Expression/Printer/UnderlinePrinterFactory.php new file mode 100644 index 000000000..d336a781c --- /dev/null +++ b/lib/Expression/Printer/UnderlinePrinterFactory.php @@ -0,0 +1,24 @@ +printers = $printers; + } + + public function underline(Node $targetNode): UnderlinePrinter + { + return new UnderlinePrinter($this->printers, $targetNode); + } +} diff --git a/lib/Expression/SuffixParselet.php b/lib/Expression/SuffixParselet.php new file mode 100644 index 000000000..838764f09 --- /dev/null +++ b/lib/Expression/SuffixParselet.php @@ -0,0 +1,10 @@ + + */ +class ArrayTheme implements ColorMap +{ + /** + * @var array, string|Closure(T):string> + */ + private $map; + + /** + * @param array, string|Closure(T):string> $map + */ + public function __construct(array $map) + { + $this->map = $map; + } + + /** + * {@inheritDoc} + */ + public function colors(): array + { + return $this->map; + } +} diff --git a/lib/Expression/Theme/EightColorTheme.php b/lib/Expression/Theme/EightColorTheme.php new file mode 100644 index 000000000..fa2e83914 --- /dev/null +++ b/lib/Expression/Theme/EightColorTheme.php @@ -0,0 +1,55 @@ + + */ +class EightColorTheme implements ColorMap +{ + /** + * @template T + * + * @return array, string|Closure(T):string> + */ + public function colors(): array + { + /** @phpstan-ignore-next-line */ + return [ + FunctionNode::class => 'fg=green', + ParenthesisNode::class => 'fg=red', + PercentDifferenceNode::class => function (Node $node) { + assert($node instanceof PercentDifferenceNode); + + if (abs($node->percentage()) <= $node->tolerance()) { + return 'fg=white'; + } + + return $node->percentage() > 0 ? 'fg=red' : 'fg=green'; + }, + DisplayAsNode::class => 'fg=cyan', + ParameterNode::class => 'fg=white', + BooleanNode::class => function (Node $node): string { + assert($node instanceof BooleanNode); + + return $node->value() ? 'fg=blue' : 'fg=red'; + }, + ToleratedTrue::class => 'fg=blue', + ArithmeticOperatorNode::class => 'fg=yellow', + ComparisonNode::class => 'fg=yellow', + ]; + } +} diff --git a/lib/Expression/Theme/SolarizedTheme.php b/lib/Expression/Theme/SolarizedTheme.php new file mode 100644 index 000000000..f3b4440fa --- /dev/null +++ b/lib/Expression/Theme/SolarizedTheme.php @@ -0,0 +1,147 @@ + + */ +class SolarizedTheme implements ColorMap +{ + private const BASE03 = '#002b36'; + private const BASE02 = '#073642'; + private const BASE01 = '#586e75'; + private const BASE00 = '#657b83'; + private const BASE0 = '#839496'; + private const BASE1 = '#93a1a1'; + private const BASE2 = '#eee8d5'; + private const BASE3 = '#fdf6e3'; + private const YELLOW = '#b58900'; + private const ORANGE = '#cb4b16'; + private const RED = '#dc322f'; + private const MAGENTA = '#d33682'; + private const VIOLET = '#6c71c4'; + private const BLUE = '#268bd2'; + private const CYAN = '#2aa198'; + private const GREEN = '#859900'; + + /** + * @var Gradient|null + */ + private $gradient; + + /** + * @var string + */ + private $base0; + + /** + * @var string + */ + private $base1; + + /** + * @var string + */ + private $base2; + + /** + * @var string + */ + private $base3; + + /** + * @var string + */ + private $neutral; + + public function __construct(bool $light = true) + { + if ($light) { + $this->base0 = self::BASE00; + $this->base1 = self::BASE01; + $this->base2 = self::BASE02; + $this->base3 = self::BASE03; + $this->neutral = '#222222'; + + return; + } + + $this->neutral = '#aaaaaa'; + $this->base0 = self::BASE0; + $this->base1 = self::BASE1; + $this->base2 = self::BASE2; + $this->base3 = self::BASE3; + } + + /** + * @template T + * + * @return array, string|Closure(T):string> + */ + public function colors(): array + { + + /** @phpstan-ignore-next-line */ + return [ + UnitNode::class => 'fg='. self::BASE1, + FunctionNode::class => 'fg=' . self::GREEN, + ParenthesisNode::class => 'fg=' . self::RED, + PercentDifferenceNode::class => function (Node $node) { + assert($node instanceof PercentDifferenceNode); + + return 'fg=#' . $this->gradient()->colorAtPercentile(((int)$node->percentage() + 100) / 2)->toHex(); + }, + RelativeDeviationNode::class => function (Node $node) { + assert($node instanceof RelativeDeviationNode); + + return 'fg=#' . $this->gradient()->colorAtPercentile( + 50 + (((int)$node->value())) + )->toHex(); + }, + DisplayAsNode::class => 'fg=default', + ParameterNode::class => 'fg=' .self::ORANGE, + BooleanNode::class => function (Node $node): string { + assert($node instanceof BooleanNode); + + return $node->value() ? 'fg=' . self::BLUE : 'fg=' . self::RED; + }, + ToleratedTrue::class => 'fg=' . self::BLUE, + ArithmeticOperatorNode::class => 'fg=' . self::YELLOW, + ComparisonNode::class => 'fg=' . self::YELLOW, + ]; + } + + private function gradient(): Gradient + { + if (!$this->gradient) { + $this->gradient = Gradient::start( + Color::fromHex(self::GREEN) + )->to( + Color::fromHex('#aaaaaa'), 100 + )->to( + Color::fromHex('#ff0000'), 100 + ); + } + + return $this->gradient; + } +} diff --git a/lib/Expression/Theme/Util/Color.php b/lib/Expression/Theme/Util/Color.php new file mode 100644 index 000000000..19a2151ad --- /dev/null +++ b/lib/Expression/Theme/Util/Color.php @@ -0,0 +1,69 @@ +red = $red; + $this->green = $green; + $this->blue = $blue; + } + + public static function fromHex(string $hex): self + { + $hexRgb = sscanf(ltrim($hex, '#'), '%02s%02s%02s'); + + if (!is_array($hexRgb)) { + throw new RuntimeException(sprintf( + 'Could not parse hex color "%s"', $hex + )); + } + + return new self(...array_map(function (string $hex): int { + return (int)hexdec($hex); + }, $hexRgb)); + } + + /** + * @return array{int,int,int} + */ + public function toTuple(): array + { + return [ + $this->red, + $this->green, + $this->blue + ]; + } + + public function toHex(): string + { + return implode('', array_map(function (int $number) { + return sprintf('%02s', dechex($number)); + }, $this->toTuple())); + } + + public static function fromRgb(int $red, int $green, int $blue): self + { + return new self($red, $green, $blue); + } +} diff --git a/lib/Expression/Theme/Util/Gradient.php b/lib/Expression/Theme/Util/Gradient.php new file mode 100644 index 000000000..787181f96 --- /dev/null +++ b/lib/Expression/Theme/Util/Gradient.php @@ -0,0 +1,82 @@ +colors = $colors; + } + + /** + * @return Color[] + */ + public function toArray(): array + { + return $this->colors; + } + + public static function start(Color $start): self + { + return new self($start); + } + + public function end(): Color + { + return $this->colors[count($this->colors) - 1]; + } + + public function to(Color $end, int $steps): self + { + if ($steps <= 0) { + throw new RuntimeException(sprintf( + 'Number of steps must be more than zero, got "%s"', $steps + )); + } + + $gradient = []; + $start = $this->end()->toTuple(); + $end = $end->toTuple(); + + for ($i = 0; $i <= 2; $i++) { + $cStart = $start[$i]; + $cEnd = $end[$i]; + + $step = abs($cStart - $cEnd) / $steps; + + if ($step === 0) { + $gradient[$i] = array_fill(0, $steps + 1, $cStart); + + continue; + } + $gradient[$i] = range($cStart, $cEnd, $step); + } + + $colors = array_merge($this->colors, array_map(function (int $r, int $g, int $b) { + return Color::fromRgb($r,$g,$b); + }, ...$gradient)); + + // remove the start color as it's already present + array_shift($colors); + + return new self(...$colors); + } + + public function colorAtPercentile(int $percentile): Color + { + $percentile = $percentile < 0 ? 0 : min(100, abs($percentile)); + + $offset = round((count($this->colors) - 1) / 100 * $percentile); + + return $this->colors[$offset]; + } +} diff --git a/lib/Expression/Token.php b/lib/Expression/Token.php new file mode 100644 index 000000000..d6aead6db --- /dev/null +++ b/lib/Expression/Token.php @@ -0,0 +1,80 @@ +type = $type; + $this->value = $value; + $this->offset = $offset; + } + + public function length(): int + { + return strlen($this->value); + } + + public function start(): int + { + return $this->offset; + } + + public function end(): int + { + return $this->offset + $this->length(); + } +} diff --git a/lib/Expression/Tokens.php b/lib/Expression/Tokens.php new file mode 100644 index 000000000..8d54e9fbe --- /dev/null +++ b/lib/Expression/Tokens.php @@ -0,0 +1,120 @@ + + */ +final class Tokens implements IteratorAggregate, Countable +{ + /** + * @var Token[] + */ + private $tokens; + + /** + * @var int + */ + private $position = 0; + + /** + * @param Token[] $tokens + */ + public function __construct(array $tokens) + { + $this->tokens = $tokens; + } + + /** + * @return Token[] + */ + public function toArray(): array + { + return $this->tokens; + } + + /** + * @return ArrayIterator + */ + public function getIterator(): ArrayIterator + { + return new ArrayIterator($this->tokens); + } + + /** + * Return the current token and move the position ahead. + */ + public function chomp(?string $type = null): ?Token + { + $previous = $this->previous(); + $token = $this->atPosition($this->position++); + + if (null !== $type && $token->type !== $type) { + throw SyntaxError::forToken($this, $previous, sprintf( + 'Expected type "%s" after, got "%s"', + $type, + $token->type + )); + } + + return $token; + } + + public function count(): int + { + return count($this->tokens); + } + + public function toString(): string + { + $last = $this->tokens[count($this->tokens) - 1]; + + if (!$last instanceof Token) { + return ''; + } + + $out = str_repeat(' ', $last->end()); + + foreach ($this as $token) { + $out = substr_replace($out, $token->value, $token->start(), $token->length()); + } + + return $out; + } + + private function atPosition(int $position): Token + { + if (isset($this->tokens[$position])) { + return $this->tokens[$position]; + } + + return new Token(Token::T_EOF, '', $position); + } + + public function current(): Token + { + return $this->atPosition($this->position); + } + + public function previous(): Token + { + return $this->atPosition($this->position - 1); + } + + public function hasMore(): bool + { + return $this->position < count($this->tokens); + } + + public function withoutWhitespace(): self + { + return new self(array_values(array_filter($this->tokens, function (Token $token) { + return $token->type !== Token::T_WHITESPACE; + }))); + } +} diff --git a/lib/Extension/ConsoleExtension.php b/lib/Extension/ConsoleExtension.php new file mode 100644 index 000000000..0baad3d57 --- /dev/null +++ b/lib/Extension/ConsoleExtension.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Extension; + +use PhpBench\Compat\SymfonyOptionsResolverCompat; +use PhpBench\DependencyInjection\Container; +use PhpBench\DependencyInjection\ExtensionInterface; +use RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class ConsoleExtension implements ExtensionInterface +{ + public const PARAM_ANSI = 'console.ansi'; + public const PARAM_ERROR_STREAM = 'console.error_stream'; + public const PARAM_OUTPUT_STREAM = 'console.output_stream'; + public const PARAM_DISABLE_OUTPUT = 'console.disable_output'; + + public const TAG_CONSOLE_COMMAND = 'console.command'; + + public const SERVICE_OUTPUT_ERR = 'console.stream_err'; + public const SERVICE_OUTPUT_STD = 'console.stream_std'; + + public function configure(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + + self::PARAM_ANSI => true, + self::PARAM_DISABLE_OUTPUT => false, + self::PARAM_OUTPUT_STREAM => 'php://stdout', + self::PARAM_ERROR_STREAM => 'php://stderr', + ]); + + $resolver->setAllowedTypes(self::PARAM_ANSI, ['bool']); + $resolver->setAllowedTypes(self::PARAM_DISABLE_OUTPUT, ['bool']); + $resolver->setAllowedTypes(self::PARAM_ERROR_STREAM, ['string']); + $resolver->setAllowedTypes(self::PARAM_OUTPUT_STREAM, ['string']); + SymfonyOptionsResolverCompat::setInfos($resolver, [ + self::PARAM_ANSI => 'Enable or disable ANSI control characters (e.g. console colors)', + self::PARAM_OUTPUT_STREAM => 'Change the normal output stream - the output stream used for reports', + self::PARAM_ERROR_STREAM => 'Change the error output stream - the output stream used for diagnostics (e.g. progress loggers use this stream)', + self::PARAM_DISABLE_OUTPUT => 'Disable output from both STDOUT and STDERR', + ]); + } + + public function load(Container $container): void + { + $container->register(self::SERVICE_OUTPUT_STD, function (Container $container) { + return $this->createOutput($container, self::PARAM_OUTPUT_STREAM); + }); + + $container->register(self::SERVICE_OUTPUT_ERR, function (Container $container) { + return $this->createOutput($container, self::PARAM_ERROR_STREAM); + }); + + $container->register(InputInterface::class, function (Container $container) { + return new ArgvInput(); + }); + } + + private function createOutput(Container $container, string $type): OutputInterface + { + if ($container->getParameter(self::PARAM_DISABLE_OUTPUT)) { + return new NullOutput(); + } + + $output = (function (string $name): OutputInterface { + $resource = fopen($name, 'w'); + + if (false === $resource) { + throw new RuntimeException(sprintf( + 'Could not open stream "%s"', + $name + )); + } + + return new StreamOutput($resource); + })($container->getParameter($type)); + + if (false === $container->getParameter(self::PARAM_ANSI)) { + $output->setDecorated(false); + } + + $output->getFormatter()->setStyle('success', new OutputFormatterStyle('black', 'green', [])); + $output->getFormatter()->setStyle('baseline', new OutputFormatterStyle('cyan', null, [])); + $output->getFormatter()->setStyle('result-neutral', new OutputFormatterStyle('cyan', null, [])); + $output->getFormatter()->setStyle('result-good', new OutputFormatterStyle('green', null, [])); + $output->getFormatter()->setStyle('result-none', new OutputFormatterStyle(null, null, [])); + $output->getFormatter()->setStyle('result-failure', new OutputFormatterStyle('white', 'red', [])); + $output->getFormatter()->setStyle('title', new OutputFormatterStyle('white', null, ['bold'])); + $output->getFormatter()->setStyle('subtitle', new OutputFormatterStyle('white', null, [])); + $output->getFormatter()->setStyle('description', new OutputFormatterStyle(null, null, [])); + + return $output; + } +} diff --git a/lib/Extension/CoreExtension.php b/lib/Extension/CoreExtension.php new file mode 100644 index 000000000..eda7f1b7e --- /dev/null +++ b/lib/Extension/CoreExtension.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Extension; + +use Humbug\SelfUpdate\Updater; +use PhpBench\Compat\SymfonyOptionsResolverCompat; +use PhpBench\Console\Application; +use PhpBench\Console\Command\Handler\TimeUnitHandler; +use PhpBench\Console\Command\SelfUpdateCommand; +use PhpBench\DependencyInjection\Container; +use PhpBench\DependencyInjection\ExtensionInterface; +use PhpBench\Json\JsonDecoder; +use PhpBench\Logger\ConsoleLogger; +use PhpBench\Util\TimeUnit; +use Psr\Log\LoggerInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class CoreExtension implements ExtensionInterface +{ + public const PARAM_EXTENSIONS = 'core.extensions'; + public const PARAM_PROFILES = 'core.profiles'; + + public const PARAM_CONFIG_PATH = 'core.config_path'; + public const PARAM_DEBUG = 'core.debug'; + public const PARAM_WORKING_DIR = 'core.working_dir'; + + public const PARAM_TIME_UNIT = 'core.time_unit'; + + public function configure(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + self::PARAM_DEBUG => false, + self::PARAM_EXTENSIONS => [], + self::PARAM_WORKING_DIR => getcwd(), + self::PARAM_CONFIG_PATH => null, + self::PARAM_PROFILES => [], + ]); + + $resolver->setAllowedTypes(self::PARAM_PROFILES, ['array']); + $resolver->setAllowedTypes(self::PARAM_DEBUG, ['bool']); + $resolver->setAllowedTypes(self::PARAM_CONFIG_PATH, ['string', 'null']); + $resolver->setAllowedTypes(self::PARAM_EXTENSIONS, ['array']); + $resolver->setAllowedTypes(self::PARAM_WORKING_DIR, ['string']); + SymfonyOptionsResolverCompat::setInfos($resolver, [ + self::PARAM_PROFILES => <<<'EOT' +Alternative configurations:: + + { + "core.profiles": { + "php8": { + "runner.php_bin": "/bin/php8" + } + } + } + +The named configuration will be merged with the default configuration, and can be used via: + +.. code-block:: bash + + $ phpbench run --profile=php8 +EOT + , + self::PARAM_DEBUG => 'If enabled output debug messages (e.g. the commands being executed when running benchamrks). Same as ``-vvv``', + self::PARAM_EXTENSIONS => 'List of additional extensions to enable', + self::PARAM_CONFIG_PATH => 'Alternative path to a PHPBench configuration file (default is ``phpbench.json``', + self::PARAM_WORKING_DIR => 'Working directory to use', + ]); + } + + public function load(Container $container): void + { + $container->register(Application::class, function (Container $container) { + $application = new Application(); + + foreach (array_keys($container->getServiceIdsForTag(ConsoleExtension::TAG_CONSOLE_COMMAND)) as $serviceId) { + $command = $container->get($serviceId); + $application->add($command); + } + + return $application; + }); + + $container->register(LoggerInterface::class, function (Container $container) { + return new ConsoleLogger( + $container->get(ConsoleExtension::SERVICE_OUTPUT_ERR), + $container->getParameter(self::PARAM_DEBUG) + ); + }); + + $container->register(TimeUnit::class, function (Container $container) { + return new TimeUnit(TimeUnit::MICROSECONDS, TimeUnit::MICROSECONDS); + }); + + $this->registerJson($container); + $this->registerCommands($container); + } + + private function registerJson(Container $container): void + { + $container->register(JsonDecoder::class, function (Container $container) { + return new JsonDecoder(); + }); + } + + private function registerCommands(Container $container): void + { + $container->register(TimeUnitHandler::class, function (Container $container) { + return new TimeUnitHandler( + $container->get(TimeUnit::class) + ); + }); + + if (class_exists(Updater::class) && class_exists(\Phar::class) && \Phar::running()) { + $container->register(SelfUpdateCommand::class, function (Container $container) { + return new SelfUpdateCommand(); + }, [ + ConsoleExtension::TAG_CONSOLE_COMMAND => [] + ]); + } + } +} diff --git a/lib/Extension/DevelopmentExtension.php b/lib/Extension/DevelopmentExtension.php new file mode 100644 index 000000000..07b932d60 --- /dev/null +++ b/lib/Extension/DevelopmentExtension.php @@ -0,0 +1,33 @@ +register(ConfigReferenceCommand::class, function (Container $container) { + return new ConfigReferenceCommand(new ConfigDumper( + $container->getExtensionClasses() + ), $container->get(ConsoleExtension::SERVICE_OUTPUT_STD)); + }, [ + ConsoleExtension::TAG_CONSOLE_COMMAND => [] + ]); + } + + /** + * {@inheritDoc} + */ + public function configure(OptionsResolver $resolver): void + { + } +} diff --git a/lib/Extension/ExpressionExtension.php b/lib/Extension/ExpressionExtension.php new file mode 100644 index 000000000..7715c0b1a --- /dev/null +++ b/lib/Extension/ExpressionExtension.php @@ -0,0 +1,351 @@ +register(EvaluateCommand::class, function (Container $container) { + return new EvaluateCommand( + $container->get(Evaluator::class), + $container->get(Lexer::class), + $container->get(Parser::class), + $container->get(Printer::class), + $container->get(EvaluatingPrinter::class), + $container->get(ConsoleExtension::SERVICE_OUTPUT_STD) + ); + }, [ + ConsoleExtension::TAG_CONSOLE_COMMAND => [] + ]); + $container->register(Parser::class, function (Container $container) { + return new Parser( + Parselets::fromPrefixParselets([ + new ListParselet(), + new FunctionParselet(), + new IntegerParselet(), + new FloatParselet(), + new ParenthesisParselet(), + new BooleanParselet(), + new StringParselet(), + new ParameterParselet(), + new NullParselet(), + ]), + Parselets::fromInfixParselets([ + new LogicalOperatorParselet(Token::T_LOGICAL_OR, Precedence::LOGICAL_OR), + new LogicalOperatorParselet(Token::T_LOGICAL_AND, Precedence::LOGICAL_AND), + new ArithmeticOperatorParselet(Token::T_PLUS, Precedence::SUM), + new ArithmeticOperatorParselet(Token::T_MINUS, Precedence::SUM), + new ArithmeticOperatorParselet(Token::T_MULTIPLY, Precedence::PRODUCT), + new ArithmeticOperatorParselet(Token::T_DIVIDE, Precedence::PRODUCT), + + new ComparisonParselet(Token::T_LT, Precedence::COMPARISON), + new ComparisonParselet(Token::T_LTE, Precedence::COMPARISON), + new ComparisonParselet(Token::T_EQUALS, Precedence::COMPARISON_EQUALITY), + new ComparisonParselet(Token::T_GT, Precedence::COMPARISON), + new ComparisonParselet(Token::T_GTE, Precedence::COMPARISON), + new TolerableParselet(), + new DisplayAsParselet(), + new ConcatParselet(), + ]), + Parselets::fromSuffixParselets([ + new ValueWithUnitParselet(), + new PercentageParselet(), + ]) + ); + }); + + $container->register(NodeEvaluator::class, function (Container $container) { + $evaluators = new NodeEvaluators([ + new ArgumentListEvaluator(), + new ArithmeticOperatorEvaluator(), + new LogicalOperatorEvaluator(), + new FunctionEvaluator($container->get(ExpressionFunctions::class)), + new ListEvaluator(), + new ValueWithUnitEvaluator(), + new ParenthesisEvaluator(), + new ComparisonEvaluator(), + new TolerableEvaluator(), + new DisplayAsEvaluator(), + new ParameterEvaluator(), + new ConcatEvaluator(), + new PhpValueEvaluator(), + ]); + + return $evaluators; + }); + + $container->register(Evaluator::class, function (Container $container) { + return new PrettyErrorEvaluator( + new MainEvaluator(new MemoisedNodeEvaluator($container->get(NodeEvaluator::class))), + $container->get(self::SERVICE_PLAIN_PRINTER), + new UnderlinePrinterFactory($container->get(NodePrinters::class)) + ); + }); + + $container->register(NodePrinter::class, function (Container $container) { + if ($container->getParameter(self::PARAM_SYNTAX_HIGHLIGHTING)) { + return $container->get(HighlightingNodePrinter::class); + } + + return $container->get(NodePrinters::class); + }); + + $container->register(HighlightingNodePrinter::class, function (Container $container) { + return new HighlightingNodePrinter( + $container->get(NodePrinters::class), + $container->get(ColorMap::class) + ); + }); + + $container->register(ColorMap::class, function (Container $container) { + $themes = []; + $selected = $container->getParameter(self::PARAM_THEME); + + foreach ($container->getServiceIdsForTag(self::TAG_THEME) as $serviceId => $attrs) { + $name = $attrs['name']; + + if ($name === $selected) { + return $container->get($serviceId); + } + $themes[] = $name; + } + + throw new RuntimeException(sprintf( + 'Unknown theme "%s", known themes: "%s"', + $selected, implode('", "', $themes) + )); + }); + + $container->register(SolarizedTheme::class, function (Container $container) { + // this theme uses true colors which is only supported in SF5 + return class_exists(NullOutputFormatter::class) ? new SolarizedTheme(false) : new EightColorTheme(); + }, [ + self::TAG_THEME => [ + 'name' => self::THEME_SOLARIZED, + ], + ]); + + $container->register(EightColorTheme::class, function (Container $container) { + return new EightColorTheme(); + }, [ + self::TAG_THEME => [ + 'name' => self::THEME_BASIC, + ], + ]); + + $container->register(NodePrinters::class, function (Container $container) { + return new NodePrinters([ + new ConcatenatedNodePrinter(), + new ArgumentListPrinter(), + new RelativeDeviationPrinter(), + new UnitPrinter(), + new NumberPrinter(), + new BinaryOperatorPrinter(), + new ComparisonPrinter(), + new BooleanPrinter(), + new FunctionPrinter(), + new ListPrinter(), + new ParenthesisPrinter(), + new TolerablePrinter(), + new PercentagePrinter(), + new ValueWithUnitPrinter(), + new DisplayAsPrinter($container->get(TimeUnit::class)), + new ParameterPrinter(), + new StringPrinter(), + new ConcatPrinter(), + new PercentageDifferencePrinter(), + new NullPrinter(), + ]); + }); + + $container->register(Printer::class, function (Container $container) { + return new NormalizingPrinter($container->get(NodePrinter::class)); + }); + + $container->register(self::SERVICE_PLAIN_PRINTER, function (Container $container) { + return new NormalizingPrinter($container->get(NodePrinters::class)); + }); + $container->register(self::SERVICE_BARE_PRINTER, function (Container $container) { + return new BareValuePrinter(); + }); + + $container->register(EvaluatingPrinter::class, function (Container $container) { + return new EvaluatingPrinter( + $container->get(NodePrinter::class), + $container->get(Evaluator::class), + [ + TolerableNode::class, + FunctionNode::class, + ArithmeticOperatorNode::class, + ParenthesisNode::class, + DisplayAsNode::class, + ParameterNode::class, + ] + ); + }); + + $container->register(ExpressionFunctions::class, function () { + return new ExpressionFunctions([ + 'mode' => new ModeFunction(), + 'mean' => new MeanFunction(), + 'min' => new MinFunction(), + 'max' => new MaxFunction(), + 'stdev' => new StDevFunction(), + 'rstdev' => new RStDevFunction(), + 'variance' => new VarianceFunction(), + 'percent_diff' => new PercentDifferenceFunction(), + 'format' => new FormatFunction(), + 'join' => new JoinFunction(), + 'first' => new FirstFunction(), + 'coalesce' => new CoalesceFunction(), + 'display_as_time' => new DisplayAsTimeFunction(), + ]); + }); + + $container->register(Lexer::class, function (Container $container) { + return new Lexer(DisplayAsPrinter::supportedUnitNames()); + }); + + $container->register(ExpressionLanguage::class, function (Container $container) { + return new MemoisedExpressionLanguage( + new RealExpressionLanguage($container->get(Lexer::class), $container->get(Parser::class)) + ); + }); + } + + /** + * {@inheritDoc} + */ + public function configure(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + self::PARAM_SYNTAX_HIGHLIGHTING => true, + self::PARAM_THEME => self::THEME_SOLARIZED, + ]); + $resolver->setAllowedTypes(self::PARAM_SYNTAX_HIGHLIGHTING, 'bool'); + $resolver->setAllowedTypes(self::PARAM_THEME, 'string'); + SymfonyOptionsResolverCompat::setInfos($resolver, [ + self::PARAM_SYNTAX_HIGHLIGHTING => 'Enable syntax highlighting', + self::PARAM_THEME => 'Select a theme to use', + ]); + } +} diff --git a/lib/Extension/ReportExtension.php b/lib/Extension/ReportExtension.php new file mode 100644 index 000000000..a9bfbdded --- /dev/null +++ b/lib/Extension/ReportExtension.php @@ -0,0 +1,185 @@ +setDefaults([ + self::PARAM_REPORTS => [], + self::PARAM_OUTPUTS => [], + ]); + + $resolver->setAllowedTypes(self::PARAM_REPORTS, ['array']); + $resolver->setAllowedTypes(self::PARAM_OUTPUTS, ['array']); + SymfonyOptionsResolverCompat::setInfos($resolver, [ + self::PARAM_REPORTS => 'Report generator configurations, see :doc:`report-generators`', + self::PARAM_OUTPUTS => 'Report renderer configurations, see :doc:`report-renderers`', + ]); + } + + public function load(Container $container): void + { + $container->register(ReportManager::class, function (Container $container) { + return new ReportManager( + $container->get(self::SERVICE_REGISTRY_GENERATOR), + $container->get(self::SERVICE_REGISTRY_RENDERER) + ); + }); + + $this->registerCommands($container); + $this->registerRegistries($container); + $this->registerReportGenerators($container); + $this->registerReportRenderers($container); + } + + private function registerJson(Container $container): void + { + $container->register(JsonDecoder::class, function (Container $container) { + return new JsonDecoder(); + }); + } + + private function registerCommands(Container $container): void + { + $container->register(ReportHandler::class, function (Container $container) { + return new ReportHandler( + $container->get(ReportManager::class) + ); + }); + + $container->register(ReportCommand::class, function (Container $container) { + return new ReportCommand( + $container->get(ReportHandler::class), + $container->get(TimeUnitHandler::class), + $container->get(SuiteCollectionHandler::class), + $container->get(DumpHandler::class) + ); + }, [ + ConsoleExtension::TAG_CONSOLE_COMMAND => [] + ]); + + $container->register(ShowCommand::class, function (Container $container) { + return new ShowCommand( + $container->get(StorageExtension::SERVICE_REGISTRY_DRIVER), + $container->get(ReportHandler::class), + $container->get(TimeUnitHandler::class), + $container->get(DumpHandler::class), + $container->get(UuidResolver::class) + ); + }, [ + ConsoleExtension::TAG_CONSOLE_COMMAND => [] + ]); + } + + private function registerRegistries(Container $container): void + { + foreach (['generator' => self::PARAM_REPORTS, 'renderer' => self::PARAM_OUTPUTS] as $registryType => $optionName) { + $container->register('report.registry_' . $registryType, function (Container $container) use ($registryType, $optionName) { + $registry = new ConfigurableRegistry( + $registryType, + $container, + $container->get(JsonDecoder::class) + ); + + foreach ($container->getServiceIdsForTag('report.' . $registryType) as $serviceId => $attributes) { + $registry->registerService($attributes['name'], $serviceId); + } + + $configs = array_merge( + require(__DIR__ . '/config/report/' . $registryType . 's.php'), + $container->getParameter($optionName) + ); + + foreach ($configs as $name => $config) { + $registry->setConfig($name, $config); + } + + return $registry; + }); + } + } + + private function registerReportGenerators(Container $container): void + { + $container->register(ExpressionGenerator::class, function (Container $container) { + return new ExpressionGenerator( + $container->get(ExpressionLanguage::class), + $container->get(Evaluator::class), + $container->get(EvaluatingPrinter::class), + new SuiteCollectionTransformer(), + $container->get(LoggerInterface::class) + ); + }, [self::TAG_REPORT_GENERATOR => ['name' => 'expression']]); + $container->register(EnvGenerator::class, function (Container $container) { + return new EnvGenerator(); + }, [self::TAG_REPORT_GENERATOR => ['name' => 'env']]); + $container->register(BareGenerator::class, function (Container $container) { + return new BareGenerator(new SuiteCollectionTransformer()); + }, [self::TAG_REPORT_GENERATOR => ['name' => 'bare']]); + $container->register(OutputTestGenerator::class, function (Container $container) { + return new OutputTestGenerator(); + }, [self::TAG_REPORT_GENERATOR => ['name' => 'output_test']]); + $container->register(CompositeGenerator::class, function (Container $container) { + return new CompositeGenerator( + $container->get(ReportManager::class) + ); + }, [ + self::TAG_REPORT_GENERATOR => ['name' => 'composite'] + ]); + } + + private function registerReportRenderers(Container $container): void + { + $container->register(ConsoleRenderer::class, function (Container $container) { + return new ConsoleRenderer( + $container->get(ConsoleExtension::SERVICE_OUTPUT_STD), + $container->get(Printer::class) + ); + }, [self::TAG_REPORT_RENDERER => ['name' => 'console']]); + $container->register(DelimitedRenderer::class, function (Container $container) { + return new DelimitedRenderer( + $container->get(ConsoleExtension::SERVICE_OUTPUT_STD), + $container->get(ExpressionExtension::SERVICE_BARE_PRINTER) + ); + }, [self::TAG_REPORT_RENDERER => ['name' => 'delimited']]); + } +} diff --git a/lib/Extension/RunnerExtension.php b/lib/Extension/RunnerExtension.php new file mode 100644 index 000000000..99a7fca89 --- /dev/null +++ b/lib/Extension/RunnerExtension.php @@ -0,0 +1,643 @@ +setDefaults([ + self::PARAM_ANNOTATIONS => true, + self::PARAM_ANNOTATION_IMPORT_USE => false, + self::PARAM_ATTRIBUTES => true, + self::PARAM_BOOTSTRAP => null, + self::PARAM_ENABLED_PROVIDERS => [ + self::ENV_PROVIDER_SAMPLER, + self::ENV_PROVIDER_GIT, + self::ENV_PROVIDER_OPCACHE, + self::ENV_PROVIDER_PHP, + self::ENV_PROVIDER_UNAME, + self::ENV_PROVIDER_UNIX_SYSLOAD, + ], + self::PARAM_ENV_SAMPLERS => ['nothing', 'md5', 'file_rw'], + self::PARAM_ENV_SAMPLER_CALLABLES => [], + self::PARAM_EXECUTORS => [], + self::PARAM_PATH => null, + self::PARAM_PHP_BINARY => null, + self::PARAM_PHP_CONFIG => [], + self::PARAM_PHP_DISABLE_INI => false, + self::PARAM_PHP_WRAPPER => null, + self::PARAM_PROGRESS => 'verbose', + self::PARAM_PROGRESS_SUMMARY_FORMAT => VariantSummaryFormatter::DEFAULT_FORMAT, + self::PARAM_PROGRESS_SUMMARY_BASELINE_FORMAT => VariantSummaryFormatter::BASELINE_FORMAT, + self::PARAM_REMOTE_SCRIPT_PATH => null, + self::PARAM_REMOTE_SCRIPT_REMOVE => true, + self::PARAM_RUNNER_ASSERT => null, + self::PARAM_RUNNER_EXECUTOR => null, + self::PARAM_RUNNER_FORMAT => null, + self::PARAM_RUNNER_ITERATIONS => null, + self::PARAM_RUNNER_OUTPUT_MODE => null, + self::PARAM_RUNNER_OUTPUT_TIME_UNIT => null, + self::PARAM_RUNNER_RETRY_THRESHOLD => null, + self::PARAM_RUNNER_REVS => null, + self::PARAM_RUNNER_TIMEOUT => null, + self::PARAM_RUNNER_WARMUP => null, + self::PARAM_SUBJECT_PATTERN => '^bench', + ]); + + $resolver->setAllowedTypes(self::PARAM_ANNOTATIONS, ['bool']); + $resolver->setAllowedTypes(self::PARAM_ANNOTATION_IMPORT_USE, ['bool']); + $resolver->setAllowedTypes(self::PARAM_ATTRIBUTES, ['bool']); + $resolver->setAllowedTypes(self::PARAM_BOOTSTRAP, ['string', 'null']); + $resolver->setAllowedTypes(self::PARAM_ENABLED_PROVIDERS, ['array']); + $resolver->setAllowedTypes(self::PARAM_ENV_SAMPLERS, ['array']); + $resolver->setAllowedTypes(self::PARAM_ENV_SAMPLER_CALLABLES, ['array']); + $resolver->setAllowedTypes(self::PARAM_EXECUTORS, ['array']); + $resolver->setAllowedTypes(self::PARAM_PATH, ['string', 'array', 'null']); + $resolver->setAllowedTypes(self::PARAM_PHP_BINARY, ['string', 'null']); + $resolver->setAllowedTypes(self::PARAM_PHP_CONFIG, ['array']); + $resolver->setAllowedTypes(self::PARAM_PHP_DISABLE_INI, ['bool']); + $resolver->setAllowedTypes(self::PARAM_PHP_WRAPPER, ['string', 'null']); + $resolver->setAllowedTypes(self::PARAM_PROGRESS, ['string']); + $resolver->setAllowedTypes(self::PARAM_PROGRESS_SUMMARY_BASELINE_FORMAT, ['string']); + $resolver->setAllowedTypes(self::PARAM_PROGRESS_SUMMARY_FORMAT, ['string']); + $resolver->setAllowedTypes(self::PARAM_REMOTE_SCRIPT_PATH, ['string', 'null']); + $resolver->setAllowedTypes(self::PARAM_REMOTE_SCRIPT_REMOVE, ['bool']); + $resolver->setAllowedTypes(self::PARAM_RUNNER_ASSERT, ['null', 'string', 'array']); + $resolver->setAllowedTypes(self::PARAM_RUNNER_EXECUTOR, ['null', 'string']); + $resolver->setAllowedTypes(self::PARAM_RUNNER_FORMAT, ['null', 'string']); + $resolver->setAllowedTypes(self::PARAM_RUNNER_ITERATIONS, ['null', 'int', 'array']); + $resolver->setAllowedTypes(self::PARAM_RUNNER_OUTPUT_MODE, ['null', 'string']); + $resolver->setAllowedTypes(self::PARAM_RUNNER_OUTPUT_TIME_UNIT, ['null', 'string']); + $resolver->setAllowedTypes(self::PARAM_RUNNER_RETRY_THRESHOLD, ['null', 'int', 'float']); + $resolver->setAllowedTypes(self::PARAM_RUNNER_REVS, ['null', 'int', 'array']); + $resolver->setAllowedTypes(self::PARAM_RUNNER_TIMEOUT, ['null', 'float', 'int']); + $resolver->setAllowedTypes(self::PARAM_RUNNER_WARMUP, ['null', 'int', 'array']); + $resolver->setAllowedTypes(self::PARAM_SUBJECT_PATTERN, ['string']); + + SymfonyOptionsResolverCompat::setInfos($resolver, [ + self::PARAM_ANNOTATIONS => 'Read metadata from annotations', + self::PARAM_ANNOTATION_IMPORT_USE => 'Require that annotations be imported before use', + self::PARAM_ATTRIBUTES => 'Read metadata from PHP 8 attributes', + self::PARAM_BOOTSTRAP => 'Path to bootstrap (e.g. ``vendor/autoload.php``)', + self::PARAM_ENABLED_PROVIDERS => 'Select which environment samplers to use', + self::PARAM_ENV_SAMPLERS => 'Environment baselines (not to be confused with baseline comparisons when running benchmarks) are small benchmarks which run to sample the speed of the system (e.g. file I/O, computation etc). This setting enables or disables these baselines', + self::PARAM_ENV_SAMPLER_CALLABLES => 'Map of baseline callables (adds you to register a new environemntal baseline)', + self::PARAM_EXECUTORS => 'Add new executor configurations', + self::PARAM_PATH => 'Path or paths to the benchmarks', + self::PARAM_PHP_BINARY => 'Specify a PHP binary to use when executing out-of-band benchmarks, e.g. ``/usr/bin/php6``, defaults to the version of PHP used to invoke PHPBench', + self::PARAM_PHP_CONFIG => 'Map of PHP ini settings to use when executing out-of-band benchmarks', + self::PARAM_PHP_DISABLE_INI => 'Disable reading the default PHP configuration', + self::PARAM_PHP_WRAPPER => 'Wrap the PHP binary with this command (e.g. ``blackfire run``)', + self::PARAM_PROGRESS => 'Default progress logger to use', + self::PARAM_PROGRESS_SUMMARY_FORMAT => 'Expression used to render the summary text default progress loggers', + self::PARAM_PROGRESS_SUMMARY_BASELINE_FORMAT => 'When the a comparison benchmark is referenced, alternative expression used to render the summary text default progress loggers', + self::PARAM_REMOTE_SCRIPT_PATH => 'PHPBench generates a PHP file for out-of-band benchmarks which is executed, this setting specifies the path to this file. When NULL a file in the systems temporary directory will be used', + self::PARAM_REMOTE_SCRIPT_REMOVE => 'If the generated file should be removed after it has been executed (useful for debugging)', + self::PARAM_RUNNER_ASSERT => 'Default :ref:`metadata_assertions`', + self::PARAM_RUNNER_EXECUTOR => 'Default executor', + self::PARAM_RUNNER_FORMAT => 'Default :ref:`metadata_format`', + self::PARAM_RUNNER_ITERATIONS => 'Default :ref:`metadata_iterations`', + self::PARAM_RUNNER_OUTPUT_MODE => 'Default :ref:`output mode `', + self::PARAM_RUNNER_OUTPUT_TIME_UNIT => 'Default :ref:`time unit `', + self::PARAM_RUNNER_RETRY_THRESHOLD => 'Default :ref:`metadata_retry_threshold`', + self::PARAM_RUNNER_REVS => 'Default number of :ref:`metadata_revolutions`', + self::PARAM_RUNNER_TIMEOUT => 'Default :ref:`metadata_timeout`', + self::PARAM_RUNNER_WARMUP => 'Default :ref:`metadata_warmup`', + self::PARAM_SUBJECT_PATTERN => 'Subject prefix to use when finding benchmarks', + ]); + } + + /** + * {@inheritDoc} + */ + public function load(Container $container): void + { + $this->relativizeConfigPath($container); + $this->registerBenchmark($container); + $this->registerCommands($container); + $this->registerProgressLoggers($container); + $this->registerExecutors($container); + $this->registerEnvironment($container); + $this->registerAsserters($container); + $this->registerMetadata($container); + } + + public function registerEnvironment(Container $container): void + { + $container->register(Provider\Uname::class, function (Container $container) { + return new Provider\Uname(); + }, [self::TAG_ENV_PROVIDER => [ + 'name' => self::ENV_PROVIDER_UNAME, + ]]); + + $container->register(Provider\Php::class, function (Container $container) { + return new Provider\Php( + $container->get(Launcher::class) + ); + }, [self::TAG_ENV_PROVIDER => [ + 'name' => self::ENV_PROVIDER_PHP, + ]]); + + $container->register(Provider\Opcache::class, function (Container $container) { + return new Provider\Opcache( + $container->get(Launcher::class) + ); + }, [self::TAG_ENV_PROVIDER => [ + 'name' => self::ENV_PROVIDER_OPCACHE, + ]]); + + $container->register(Provider\UnixSysload::class, function (Container $container) { + return new Provider\UnixSysload(); + }, [self::TAG_ENV_PROVIDER => [ + 'name' => self::ENV_PROVIDER_UNIX_SYSLOAD, + ]]); + + $container->register(Provider\Git::class, function (Container $container) { + return new Provider\Git($container->getParameter(CoreExtension::PARAM_WORKING_DIR)); + }, [self::TAG_ENV_PROVIDER => [ + 'name' => self::ENV_PROVIDER_GIT, + ]]); + + $container->register(Provider\Sampler::class, function (Container $container) { + return new Provider\Sampler( + $container->get(SamplerManager::class), + $container->getParameter(self::PARAM_ENV_SAMPLERS) + ); + }, [self::TAG_ENV_PROVIDER => [ + 'name' => self::ENV_PROVIDER_SAMPLER, + ]]); + + $container->register(Provider\TestProvider::class, function (Container $container) { + return new Provider\TestProvider(); + }, [self::TAG_ENV_PROVIDER => [ + 'name' => self::ENV_PROVIDER_TEST, + ]]); + + $container->register(Supplier::class, function (Container $container) { + $supplier = new Supplier(); + $enabledProviders = $container->getParameter(self::PARAM_ENABLED_PROVIDERS); + + foreach ($container->getServiceIdsForTag(self::TAG_ENV_PROVIDER) as $serviceId => $attributes) { + if (!isset($attributes['name'])) { + throw new RuntimeException(sprintf( + 'Env provider "%s" has no `name` attribute', $serviceId + )); + } + + if (!in_array($attributes['name'], $enabledProviders)) { + continue; + } + + $provider = $container->get($serviceId); + $supplier->addProvider($provider); + } + + return $supplier; + }); + } + + private function registerBenchmark(Container $container): void + { + $container->register(Runner::class, function (Container $container) { + return new Runner( + $container->get(self::SERVICE_REGISTRY_EXECUTOR), + $container->get(Supplier::class), + $container->get(AssertionProcessor::class), + $container->getParameter(self::PARAM_RUNNER_RETRY_THRESHOLD), + $container->getParameter(CoreExtension::PARAM_CONFIG_PATH) + ); + }); + + $container->register(RemoteExecutor::class . '.composite', function (Container $container) { + return new CompositeExecutor( + $container->get(RemoteExecutor::class), + new ErrorHandlingExecutorDecorator($container->get(RemoteMethodExecutor::class)) + ); + }, [self::TAG_EXECUTOR => ['name' => 'remote']]); + + $container->register(LocalExecutor::class . '.composite', function (Container $container) { + return new CompositeExecutor( + $container->get(LocalExecutor::class), + new ErrorHandlingExecutorDecorator($container->get(LocalMethodExecutor::class)) + ); + }, [self::TAG_EXECUTOR => ['name' => 'local']]); + + $container->register(MemoryCentricMicrotimeExecutor::class, function (Container $container) { + return new CompositeExecutor( + new MemoryCentricMicrotimeExecutor($container->get(Launcher::class)), + $container->get(RemoteMethodExecutor::class) + ); + }, [self::TAG_EXECUTOR => ['name' => 'memory_centric_microtime']]); + + $container->register(RemoteExecutor::class, function (Container $container) { + return new RemoteExecutor( + $container->get(Launcher::class) + ); + }); + + $container->register(LocalExecutor::class, function (Container $container) { + return new LocalExecutor( + $container->getParameter(self::PARAM_BOOTSTRAP) + ); + }); + + $container->register(RemoteMethodExecutor::class, function (Container $container) { + return new RemoteMethodExecutor( + $container->get(Launcher::class) + ); + }); + + $container->register(LocalMethodExecutor::class, function (Container $container) { + return new LocalMethodExecutor(); + }); + + $container->register(DebugExecutor::class, function (Container $container) { + return new DebugExecutor(); + }, [ + self::TAG_EXECUTOR => ['name' => 'debug'] + ]); + + $container->register(Finder::class, function (Container $container) { + return new Finder(); + }); + + $container->register(ProcessFactory::class, function (Container $container) { + return new ProcessFactory($container->get(LoggerInterface::class)); + }); + + $container->register(Launcher::class, function (Container $container) { + return new Launcher( + new PayloadFactory( + $container->get(ProcessFactory::class), + $container->getParameter(self::PARAM_REMOTE_SCRIPT_PATH), + $container->getParameter(self::PARAM_REMOTE_SCRIPT_REMOVE) + ), + new ExecutableFinder(), + $container->getParameter(self::PARAM_BOOTSTRAP) ? Path::makeAbsolute( + $container->getParameter(self::PARAM_BOOTSTRAP), + $container->getParameter(CoreExtension::PARAM_WORKING_DIR) + ) : null, + $container->hasParameter(self::PARAM_PHP_BINARY) ? $container->getParameter(self::PARAM_PHP_BINARY) : null, + $container->hasParameter(self::PARAM_PHP_CONFIG) ? $container->getParameter(self::PARAM_PHP_CONFIG) : null, + $container->hasParameter(self::PARAM_PHP_WRAPPER) ? $container->getParameter(self::PARAM_PHP_WRAPPER) : null, + $container->hasParameter(self::PARAM_PHP_DISABLE_INI) ? $container->getParameter(self::PARAM_PHP_DISABLE_INI) : false + ); + }); + + $container->register(RemoteReflector::class, function (Container $container) { + return new RemoteReflector($container->get(Launcher::class)); + }); + } + + private function registerCommands(Container $container): void + { + $container->register(RunnerHandler::class, function (Container $container) { + return new RunnerHandler( + $container->get(Runner::class), + $container->get(self::SERVICE_REGISTRY_LOGGER), + $container->get(BenchmarkFinder::class), + $container->getParameter(self::PARAM_PROGRESS), + $container->getParameter(self::PARAM_PATH) + ); + }); + + $container->register(RunCommand::class, function (Container $container) { + return new RunCommand( + $container->get(RunnerHandler::class), + $container->get(ReportHandler::class), + $container->get(SuiteCollectionHandler::class), + $container->get(TimeUnitHandler::class), + $container->get(DumpHandler::class), + $container->get(StorageExtension::SERVICE_REGISTRY_DRIVER) + ); + }, [ + ConsoleExtension::TAG_CONSOLE_COMMAND => [] + ]); + } + + private function registerProgressLoggers(Container $container): void + { + $container->register(VariantFormatter::class, function (Container $container) { + return new VariantSummaryFormatter( + $container->get(ExpressionLanguage::class), + $container->get(EvaluatingPrinter::class), + $container->get(ParameterProvider::class), + $container->getParameter(self::PARAM_PROGRESS_SUMMARY_FORMAT), + $container->getParameter(self::PARAM_PROGRESS_SUMMARY_BASELINE_FORMAT) + ); + }); + $container->register(self::SERVICE_REGISTRY_LOGGER, function (Container $container) { + $registry = new LoggerRegistry(); + + foreach ($container->getServiceIdsForTag(self::TAG_PROGRESS_LOGGER) as $serviceId => $attributes) { + $registry->addProgressLogger( + $attributes['name'], + $container->get($serviceId) + ); + } + + return $registry; + }); + + $container->register(DotsLogger::class, function (Container $container) { + return new DotsLogger( + $container->get(ConsoleExtension::SERVICE_OUTPUT_ERR), + $container->get(VariantFormatter::class), + $container->get(TimeUnit::class) + ); + }, [self::TAG_PROGRESS_LOGGER => ['name' => 'dots']]); + + $container->register(DotsLogger::class .'.show', function (Container $container) { + return new DotsLogger( + $container->get(ConsoleExtension::SERVICE_OUTPUT_ERR), + $container->get(VariantFormatter::class), + $container->get(TimeUnit::class), + true + ); + }, [self::TAG_PROGRESS_LOGGER => ['name' => 'classdots']]); + + $container->register(VerboseLogger::class, function (Container $container) { + return new VerboseLogger( + $container->get(ConsoleExtension::SERVICE_OUTPUT_ERR), + $container->get(VariantFormatter::class), + $container->get(TimeUnit::class) + ); + }, [self::TAG_PROGRESS_LOGGER => ['name' => 'verbose']]); + + $container->register(TravisLogger::class, function (Container $container) { + return new TravisLogger( + $container->get(ConsoleExtension::SERVICE_OUTPUT_ERR), + $container->get(VariantFormatter::class), + $container->get(TimeUnit::class) + ); + }, [self::TAG_PROGRESS_LOGGER => ['name' => 'plain']]); + + $container->register(NullLogger::class, function (Container $container) { + return new NullLogger(); + }, [self::TAG_PROGRESS_LOGGER => ['name' => 'none']]); + + $container->register(BlinkenLogger::class, function (Container $container) { + return new BlinkenLogger( + $container->get(ConsoleExtension::SERVICE_OUTPUT_ERR), + $container->get(VariantFormatter::class), + $container->get(TimeUnit::class) + ); + }, [self::TAG_PROGRESS_LOGGER => ['name' => 'blinken']]); + + $container->register(HistogramLogger::class, function (Container $container) { + return new HistogramLogger( + $container->get(ConsoleExtension::SERVICE_OUTPUT_ERR), + $container->get(VariantFormatter::class), + $container->get(TimeUnit::class) + ); + }, [self::TAG_PROGRESS_LOGGER => ['name' => 'histogram']]); + } + + + private function relativizeConfigPath(Container $container): void + { + $paths = (array)$container->getParameter(self::PARAM_PATH); + + if (empty($paths)) { + return; + } + + $container->setParameter(self::PARAM_PATH, array_map(function (string $path) use ($container) { + if (Path::isAbsolute($path)) { + return $path; + } + + return Path::join([ + dirname($container->getParameter(CoreExtension::PARAM_CONFIG_PATH)), + $path + ]); + }, $paths)); + } + + private function registerExecutors(Container $container): void + { + $container->register(self::SERVICE_REGISTRY_EXECUTOR, function (Container $container) { + $registry = new ConfigurableRegistry( + 'executor', + $container, + $container->get(JsonDecoder::class) + ); + + foreach ($container->getServiceIdsForTag(self::TAG_EXECUTOR) as $serviceId => $attributes) { + $registry->registerService($attributes['name'], $serviceId); + } + + $executorConfigs = array_merge( + require(__DIR__ . '/config/benchmark/executors.php'), + $container->getParameter(self::PARAM_EXECUTORS) + ); + + foreach ($executorConfigs as $name => $config) { + $registry->setConfig($name, $config); + } + + return $registry; + }); + } + + private function registerMetadata(Container $container): void + { + $container->register(AnnotationReader::class, function (Container $container) { + return new AnnotationReader($container->getParameter(self::PARAM_ANNOTATION_IMPORT_USE)); + }); + + $container->register(AnnotationDriver::class, function (Container $container) { + return new AnnotationDriver( + $container->get(RemoteReflector::class), + $container->getParameter(self::PARAM_SUBJECT_PATTERN), + $container->get(AnnotationReader::class) + ); + }); + + $container->register(AttributeDriver::class, function (Container $container) { + return new AttributeDriver( + $container->getParameter(self::PARAM_SUBJECT_PATTERN) + ); + }); + + $container->register(ChainDriver::class, function (Container $container) { + $drivers = []; + + if ($container->getParameter(self::PARAM_ANNOTATIONS)) { + $drivers[] = $container->get(AnnotationDriver::class); + } + + if ($container->getParameter(self::PARAM_ATTRIBUTES)) { + $drivers[] = $container->get(AttributeDriver::class); + } + + return new ChainDriver($drivers); + }); + + $container->register(MetadataFactory::class, function (Container $container) { + return new MetadataFactory( + $container->get(RemoteReflector::class), + $container->get(ConfigDriver::class) + ); + }); + + $container->register(ConfigDriver::class, function (Container $container) { + return new ConfigDriver( + $container->get(ChainDriver::class), + (array)$container->getParameter(self::PARAM_RUNNER_ASSERT), + $container->getParameter(self::PARAM_RUNNER_EXECUTOR), + $container->getParameter(self::PARAM_RUNNER_FORMAT), + (array)$container->getParameter(self::PARAM_RUNNER_ITERATIONS), + $container->getParameter(self::PARAM_RUNNER_OUTPUT_MODE), + $container->getParameter(self::PARAM_RUNNER_OUTPUT_TIME_UNIT), + (array)$container->getParameter(self::PARAM_RUNNER_REVS), + $container->getParameter(self::PARAM_RUNNER_TIMEOUT), + (array)$container->getParameter(self::PARAM_RUNNER_WARMUP), + (float)$container->getParameter(self::PARAM_RUNNER_RETRY_THRESHOLD) + ); + }); + + $container->register(BenchmarkFinder::class, function (Container $container) { + return new BenchmarkFinder( + $container->get(MetadataFactory::class), + $container->getParameter(CoreExtension::PARAM_WORKING_DIR) + ); + }); + + $container->register(SamplerManager::class, function (Container $container) { + $manager = new SamplerManager(); + $callables = array_merge([ + 'nothing' => '\PhpBench\Benchmark\Baseline\Baselines::nothing', + 'md5' => '\PhpBench\Benchmark\Baseline\Baselines::md5', + 'file_rw' => '\PhpBench\Benchmark\Baseline\Baselines::fwriteFread', + ], $container->getParameter(self::PARAM_ENV_SAMPLER_CALLABLES)); + + foreach ($callables as $name => $callable) { + $manager->addSamplerCallable($name, $callable); + } + + return $manager; + }); + } + + private function registerAsserters(Container $container): void + { + $container->register(AssertionProcessor::class, function (Container $container) { + return new AssertionProcessor( + $container->get(ExpressionLanguage::class), + $container->get(Evaluator::class), + $container->get(Printer::class), + $container->get(EvaluatingPrinter::class), + $container->get(ParameterProvider::class) + ); + }); + + $container->register(ParameterProvider::class, function () { + return new ParameterProvider(); + }); + } +} diff --git a/lib/Extension/StorageExtension.php b/lib/Extension/StorageExtension.php new file mode 100644 index 000000000..37f118fc0 --- /dev/null +++ b/lib/Extension/StorageExtension.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Extension; + +use PhpBench\Compat\SymfonyOptionsResolverCompat; +use PhpBench\Console\Command\Handler\DumpHandler; +use PhpBench\Console\Command\Handler\SuiteCollectionHandler; +use PhpBench\Console\Command\Handler\TimeUnitHandler; +use PhpBench\Console\Command\LogCommand; +use PhpBench\DependencyInjection\Container; +use PhpBench\DependencyInjection\ExtensionInterface; +use PhpBench\Json\JsonDecoder; +use PhpBench\Serializer\XmlDecoder; +use PhpBench\Serializer\XmlEncoder; +use PhpBench\Storage\Driver\Xml\XmlDriver; +use PhpBench\Storage\StorageRegistry; +use PhpBench\Storage\UuidResolver; +use PhpBench\Storage\UuidResolver\ChainResolver; +use PhpBench\Storage\UuidResolver\LatestResolver; +use PhpBench\Storage\UuidResolver\TagResolver; +use PhpBench\Util\TimeUnit; +use Symfony\Component\OptionsResolver\OptionsResolver; +use Webmozart\PathUtil\Path; + +class StorageExtension implements ExtensionInterface +{ + public const PARAM_STORAGE = 'storage.driver'; + public const PARAM_XML_STORAGE_PATH = 'storage.xml_storage_path'; + + public const SERVICE_REGISTRY_DRIVER = 'storage.driver_registry'; + + public const TAG_STORAGE_DRIVER = 'storage.driver'; + public const TAG_UUID_RESOLVER = 'storage.uuid_resolver'; + + public function configure(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + self::PARAM_STORAGE => 'xml', + self::PARAM_XML_STORAGE_PATH => '.phpbench/storage', + ]); + + $resolver->setAllowedTypes(self::PARAM_STORAGE, ['string']); + $resolver->setAllowedTypes(self::PARAM_XML_STORAGE_PATH, ['string']); + SymfonyOptionsResolverCompat::setInfos($resolver, [ + self::PARAM_STORAGE => 'Storage driver to use', + self::PARAM_XML_STORAGE_PATH => 'Path to store benchmark runs when they are stored with ``--store`` or ``--tag=foo``' + ]); + } + + public function load(Container $container): void + { + $this->registerCommands($container); + $this->registerSerializer($container); + $this->registerStorage($container); + } + + private function registerJson(Container $container): void + { + $container->register(JsonDecoder::class, function (Container $container) { + return new JsonDecoder(); + }); + } + + private function registerCommands(Container $container): void + { + $container->register(SuiteCollectionHandler::class, function (Container $container) { + return new SuiteCollectionHandler( + $container->get(XmlDecoder::class), + $container->get(self::SERVICE_REGISTRY_DRIVER), + $container->get(UuidResolver::class) + ); + }); + + $container->register(DumpHandler::class, function (Container $container) { + return new DumpHandler( + $container->get(XmlEncoder::class), + $container->get(ConsoleExtension::SERVICE_OUTPUT_STD) + ); + }); + + $container->register(LogCommand::class, function (Container $container) { + return new LogCommand( + $container->get(self::SERVICE_REGISTRY_DRIVER), + $container->get(TimeUnit::class), + $container->get(TimeUnitHandler::class), + null, + $container->get(ConsoleExtension::SERVICE_OUTPUT_STD) + ); + }, [ + ConsoleExtension::TAG_CONSOLE_COMMAND => [] + ]); + } + + private function registerSerializer(Container $container): void + { + $container->register(XmlEncoder::class, function (Container $container) { + return new XmlEncoder(); + }); + $container->register(XmlDecoder::class, function (Container $container) { + return new XmlDecoder(); + }); + } + + private function registerStorage(Container $container): void + { + $container->register(self::SERVICE_REGISTRY_DRIVER, function (Container $container) { + $registry = new StorageRegistry($container, $container->getParameter(self::PARAM_STORAGE)); + + foreach ($container->getServiceIdsForTag(self::TAG_STORAGE_DRIVER) as $serviceId => $attributes) { + $registry->registerService($attributes['name'], $serviceId); + } + + return $registry; + }); + $container->register(XmlDriver::class, function (Container $container) { + return new XmlDriver( + Path::makeAbsolute( + $container->getParameter(self::PARAM_XML_STORAGE_PATH), + $container->getParameter(CoreExtension::PARAM_WORKING_DIR) + ), + $container->get(XmlEncoder::class), + $container->get(XmlDecoder::class) + ); + }, [self::TAG_STORAGE_DRIVER => ['name' => 'xml']]); + + $container->register(UuidResolver::class, function (Container $container) { + $resolvers = []; + + foreach (array_keys($container->getServiceIdsForTag(self::TAG_UUID_RESOLVER)) as $serviceId) { + $resolvers[] = $container->get($serviceId); + } + + return new UuidResolver(new ChainResolver($resolvers)); + }); + + $container->register(LatestResolver::class, function (Container $container) { + return new LatestResolver( + $container->get(self::SERVICE_REGISTRY_DRIVER) + ); + }, [self::TAG_UUID_RESOLVER => []]); + + $container->register(TagResolver::class, function (Container $container) { + return new TagResolver( + $container->get(self::SERVICE_REGISTRY_DRIVER) + ); + }, [self::TAG_UUID_RESOLVER => []]); + } +} diff --git a/lib/Extension/config/benchmark/executors.php b/lib/Extension/config/benchmark/executors.php new file mode 100644 index 000000000..3f8311bca --- /dev/null +++ b/lib/Extension/config/benchmark/executors.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +return [ + 'debug_macro' => [ + 'executor' => 'debug', + 'times' => [1000000, 200000], + 'spread' => [50000, -12345, 1000], + ], +]; diff --git a/lib/Extension/config/class/main.json b/lib/Extension/config/class/main.json new file mode 100644 index 000000000..a1309527b --- /dev/null +++ b/lib/Extension/config/class/main.json @@ -0,0 +1,42 @@ +{ + "z-value": [ + [ "number", { "decimal_places": 2 } ], + [ "balance", { } ], + [ "printf", { "format": "%sσ" } ] + ], + "integer": [ + [ "number", { "decimal_places": 0 } ] + ], + "percentage": [ + [ "printf", { "format": "%.2f%%" } ] + ], + "invert_on_throughput": [ + [ "invert_on_throughput", { "mode": "{{ output_mode }}" } ] + ], + "deviation": [ + [ "number", { "decimal_places": 2 }], + [ "balance", {} ], + [ "printf", { "format": "%s%%" } ] + ], + "diff": [ + [ "number", { "decimal_places": 2 }], + [ "printf", { "format": "%sx" } ] + ], + "mem": [ + [ "number" ], + [ "printf", { "format": "%sb" }] + ], + "time_sd": [ + [ "time", { "mode": "time", "unit": "{{ output_time_unit }}" }] + ], + "timeunit": [ + [ "time", { "precision": "{{ output_time_precision }}", "unit": "{{ output_time_unit }}", "mode": "{{ output_mode }}", "resolve": ["mode", "unit"] }] + ], + "microseconds": [ + [ "time", { "precision": "0", "unit": "microseconds" }] + ], + "seconds": [ + [ "time", { "precision": "0", "unit": "seconds" }] + ] +} + diff --git a/lib/Extension/config/report/generators.php b/lib/Extension/config/report/generators.php new file mode 100644 index 000000000..c8d4b521d --- /dev/null +++ b/lib/Extension/config/report/generators.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +return [ + 'bare' => [ + 'generator' => 'bare', + ], + 'aggregate' => [ + 'generator' => 'expression', + 'cols' => [ + 'benchmark', + 'subject', + 'set', + 'revs', + 'its', + 'mem_peak', + 'mode', + 'rstdev', + ] + ], + 'default' => [ + 'generator' => 'expression', + 'cols' => [ + 'iter' => 'first(iteration_index)', + 'benchmark' => 'first(subject_name)', + 'subject' => 'first(subject_name)', + 'set' => 'first(variant_name)', + 'revs' => 'first(variant_revs)', + 'mem_peak' => 'first(result_mem_peak) as bytes', + 'time_avg' => 'display_as_time(first(result_time_avg), first(subject_time_unit))', + 'comp_z_value' => 'format("%+.2fσ", first(result_comp_z_value))', + 'comp_deviation' => 'format("%+.2f%%", first(result_comp_deviation))', + ], + 'aggregate' => ['benchmark_class', 'subject_name', 'variant_name', 'iteration_index'], + ], + 'memory' => [ + 'generator' => 'expression', + 'aggregate' => ['benchmark_class', 'subject_name', 'variant_name', 'iteration_index'], + 'cols' => [ + 'iter' => 'first(iteration_index)', + 'benchmark' => 'first(subject_name)', + 'subject' => 'first(subject_name)', + 'set' => 'first(variant_name)', + 'revs' => 'first(variant_revs)', + 'mem_final' => 'first(result_mem_final) as bytes', + 'mem_real' => 'first(result_mem_real) as bytes', + 'mem_peak' => 'first(result_mem_peak) as bytes', + ], + ] +]; diff --git a/lib/Extension/config/report/renderers.php b/lib/Extension/config/report/renderers.php new file mode 100644 index 000000000..f018a6eac --- /dev/null +++ b/lib/Extension/config/report/renderers.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +return [ + 'csv' => [ + 'renderer' => 'delimited', + 'delimiter' => ',', + ], +]; diff --git a/lib/Functional/Functional.php b/lib/Functional/Functional.php new file mode 100644 index 000000000..97a559db3 --- /dev/null +++ b/lib/Functional/Functional.php @@ -0,0 +1,49 @@ + $element) { + $groupKey = $callback($element, $index, $collection); + + if (!isset($groups[$groupKey])) { + $groups[$groupKey] = []; + } + + $groups[$groupKey][$index] = $element; + } + + return $groups; + } + + public static function map(iterable $collection, callable $callback): iterable + { + $aggregation = []; + + foreach ($collection as $index => $element) { + $aggregation[$index] = $callback($element, $index, $collection); + } + + return $aggregation; + } + + /** + */ + public static function reduceLeft(iterable $collection, callable $callback, $initial = null) + { + foreach ($collection as $index => $value) { + $initial = $callback($value, $index, $collection, $initial); + } + + return $initial; + } +} diff --git a/lib/Json/JsonDecoder.php b/lib/Json/JsonDecoder.php new file mode 100644 index 000000000..c2311a617 --- /dev/null +++ b/lib/Json/JsonDecoder.php @@ -0,0 +1,166 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Json; + +use Seld\JsonLint\JsonParser; + +/** + * Decodes JSON to an array. + * + * Accepts non-strict "JSON": + * + * - {this: ["is": {where: "sparta"}]} # quoteless keys are allowed + * - this: ["is": {where: "sparta"}] # enclosing braces can be omitted + * + * Lints the JSON using the JsonParser. + */ +class JsonDecoder +{ + /** + * @var JsonParser + */ + private $parser; + + public function __construct() + { + $this->parser = new JsonParser(); + } + + /** + * Normalize, parse and decode the given JSON(ish) encoded string into + * an array. + * + */ + public function decode(string $jsonString): array + { + $jsonString = $this->normalize($jsonString); + $this->parser->parse($jsonString); + + return json_decode($jsonString, true); + } + + /** + * Allow "non-strict" JSON - i.e. if no quotes are provided then try and + * add them. + */ + private function normalize($jsonString) + { + if (!is_string($jsonString)) { + throw new \InvalidArgumentException(sprintf( + 'Expected a string, got "%s"', + gettype($jsonString) + )); + } + $chars = str_split($jsonString); + $inRight = $inQuote = $inFakeQuote = false; + $fakeQuoteStart = null; + + if (empty($chars)) { + return; + } + + if ($chars[0] !== '{') { + array_unshift($chars, '{'); + $chars[] = '}'; + } + + for ($index = 0; $index < count($chars); $index++) { + $char = $chars[$index]; + $prevChar = isset($chars[$index - 1]) ? $chars[$index - 1] : null; + + if (!$inQuote && $prevChar == ':') { + $inRight = true; + } + + if (!$inQuote && preg_match('{\{,}', $char)) { + $inRight = false; + } + + // detect start of unquoted string + if (!$inQuote && preg_match('{[\$a-zA-Z0-9]}', $char)) { + array_splice($chars, $index, 0, '"'); + $fakeQuoteStart = $index; + $index++; + $inQuote = $inFakeQuote = true; + + continue; + } + + // if we added a "fake" quote, look for the end of the unquoted string + if ($inFakeQuote && preg_match('{[\s:\}\],]}', $char)) { + + // if we are on the left side, then "]" is OK. + if (!$inRight && $char === ']') { + continue; + } + + if ($inRight) { + + // extract the right hand value + $string = implode('', array_slice($chars, $fakeQuoteStart + 1, $index - 1 - $fakeQuoteStart)); + + // if it is a number, then we don't quote it + if (is_numeric($string)) { + unset($chars[$fakeQuoteStart]); + $chars = array_values($chars); + $inQuote = $inFakeQuote = false; + $index--; + + continue; + } + + // if it is a boolean, then we don't quote it + if (in_array($string, ['true', 'false'])) { + unset($chars[$fakeQuoteStart]); + $chars = array_values($chars); + $inQuote = $inFakeQuote = false; + $index--; + + continue; + } + } + + // add the ending quote + array_splice($chars, $index, 0, '"'); + $index++; + $inQuote = $inFakeQuote = false; + + continue; + } + + // enter standard quote mode + if (!$inQuote && $char === '"') { + $inQuote = true; + + continue; + } + + // if we are in quote mode and encounter a closing quote, and the last character + // was not the escape character + if ($inQuote && $char === '"' && $prevChar !== '\\') { + $inQuote = $inFakeQuote = false; + + continue; + } + } + + // if we were in a fake quote and hit the end, then add the closing quote + if ($inFakeQuote) { + $chars[] = '"'; + } + + $normalized = implode('', $chars); + + return $normalized; + } +} diff --git a/lib/Logger/ConsoleLogger.php b/lib/Logger/ConsoleLogger.php new file mode 100644 index 000000000..8eff935d5 --- /dev/null +++ b/lib/Logger/ConsoleLogger.php @@ -0,0 +1,54 @@ +enable = $enable; + $this->output = $output; + } + + /** + * {@inheritDoc} + */ + public function log($level, $message, array $context = []): void + { + $decoration = 'fg=cyan'; + + switch ($level) { + case LogLevel::DEBUG: + case LogLevel::INFO: + case LogLevel::NOTICE: + if (!$this->enable) { + return; + } + + break; + case LogLevel::ERROR: + case LogLevel::WARNING: + case LogLevel::CRITICAL: + case LogLevel::EMERGENCY: + case LogLevel::ALERT: + $decoration = 'bg=red;fg=white'; + + break; + } + $this->output->writeln(sprintf("[<%s>%s] %s\n", $decoration, strtoupper($level), $message)); + } +} diff --git a/lib/Math/Distribution.php b/lib/Math/Distribution.php new file mode 100644 index 000000000..f902f4b7f --- /dev/null +++ b/lib/Math/Distribution.php @@ -0,0 +1,190 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Math; + +use ArrayAccess; +use IteratorAggregate; + +/** + * Represents a population of samples. + * + * Lazily Provides summary statistics, also traversable. + * + * @implements IteratorAggregate, ArrayAccess + */ +class Distribution implements IteratorAggregate, ArrayAccess +{ + private $samples = []; + private $stats = []; + private $closures = []; + + public function __construct(array $samples, array $stats = []) + { + if (count($samples) < 1) { + throw new \LogicException( + 'Cannot create a distribution with zero samples.' + ); + } + + $this->samples = $samples; + $this->closures = [ + 'min' => function () { + return min($this->samples); + }, + 'max' => function () { + return max($this->samples); + }, + 'sum' => function () { + return array_sum($this->samples); + }, + 'stdev' => function () { + return Statistics::stdev($this->samples); + }, + 'mean' => function () { + return Statistics::mean($this->samples); + }, + 'mode' => function () { + return Statistics::kdeMode($this->samples); + }, + 'variance' => function () { + return Statistics::variance($this->samples); + }, + 'rstdev' => function () { + $mean = $this->getMean(); + + return $mean ? $this->getStdev() / $mean * 100 : 0; + }, + ]; + + if ($diff = array_diff(array_keys($stats), array_keys($this->closures))) { + throw new \RuntimeException(sprintf( + 'Unknown pre-computed stat(s) encountered: "%s"', + implode('", "', $diff) + )); + } + + $this->stats = $stats; + } + + public function getMin() + { + return $this->getStat('min'); + } + + public function getMax() + { + return $this->getStat('max'); + } + + public function getSum() + { + return $this->getStat('sum'); + } + + public function getStdev() + { + return $this->getStat('stdev'); + } + + public function getMean() + { + return $this->getStat('mean'); + } + + public function getMode() + { + return $this->getStat('mode'); + } + + public function getRstdev() + { + return $this->getStat('rstdev'); + } + + public function getVariance() + { + return $this->getStat('variance'); + } + + public function getIterator(): \ArrayIterator + { + foreach ($this->closures as $name => $callback) { + if (!array_key_exists($name, $this->stats)) { + $this->stats[$name] = $callback(); + } + } + + return new \ArrayIterator($this->stats); + } + + public function getStats(): array + { + $stats = []; + + foreach (array_keys($this->closures) as $name) { + $stats[$name] = $this->getStat($name); + } + + return $stats; + } + + private function getStat($name) + { + if (isset($this->stats[$name])) { + return $this->stats[$name]; + } + + if (!isset($this->closures[$name])) { + throw new \InvalidArgumentException(sprintf( + 'Unknown stat "%s", known stats: "%s"', + $name, implode('", "', array_keys($this->closures)) + )); + } + + $this->stats[$name] = $this->closures[$name]($this->samples, $this); + + return $this->stats[$name]; + } + + /** + * {@inheritdoc} + */ + public function offsetExists($offset): bool + { + return isset($this->stats[$offset]); + } + + /** + * {@inheritdoc} + */ + public function offsetGet($offset) + { + return $this->getStat($offset); + } + + /** + * {@inheritdoc} + */ + public function offsetSet($offset, $value): void + { + throw new \BadMethodCallException('Distribution is read-only'); + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($offset): void + { + throw new \BadMethodCallException('Distribution is read-only'); + } +} diff --git a/lib/Math/FloatNumber.php b/lib/Math/FloatNumber.php new file mode 100644 index 000000000..c1fe84ab5 --- /dev/null +++ b/lib/Math/FloatNumber.php @@ -0,0 +1,34 @@ + $right; + } +} diff --git a/lib/Math/Kde.php b/lib/Math/Kde.php new file mode 100644 index 000000000..a73230be1 --- /dev/null +++ b/lib/Math/Kde.php @@ -0,0 +1,248 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Math; + +/** + * This class was ported from the Python scipy package. + * + * https://github.com/scipy/scipy/blob/master/scipy/stats/kde.py + */ + +/** + * Define classes for (uni/multi)-variate kernel density estimation. + * + * Currently, only Gaussian kernels are implemented. + * + * Original Python version by: Robert Kern + * + * Date: 2004-08-09 + * + * Modified: 2005-02-10 by Robert Kern. + * Contributed to Scipy + * 2005-10-07 by Robert Kern. + * Some fixes to match the new scipy_core + * + * Copyright 2004-2005 by Enthought, Inc. + * Copyright 2003-2013 SciPy Developers. + * All rights reserved. + */ +class Kde +{ + /** + * @var \Closure + */ + private $coVarianceFactor; + + /** + * @var array + */ + private $dataset; + + /** + * @var float + */ + private $factor; + + /** + * @var float + */ + private $_dataInvCov; + + /** + * @var float + */ + private $_dataCovariance; + + /** + * @var float + */ + private $invCov; + + private $covariance; + + private $normFactor; + + /** + * Representation of a kernel-density estimate using Gaussian kernels. + * + * Kernel density estimation is a way to estimate the probability density + * function (PDF) of a random variable in a non-parametric way. + * + * `gaussian_kde` works for both uni-variate and multi-variate data. It + * includes automatic bandwidth determination. The estimation works best for + * a unimodal distribution; bimodal or multi-modal distributions tend to be + * oversmoothed. + * + * Note: + * + * Bandwidth selection strongly influences the estimate obtained from the KDE + * (much more so than the actual shape of the kernel). Bandwidth selection + * can be done by a "rule of thumb", by cross-validation, by "plug-in + * methods" or by other means; see [3]_, [4]_ for reviews. `gaussian_kde` + * uses a rule of thumb, the default is Scott's Rule. + * Scott's Rule [1]_, implemented as `scotts_factor`, is:: + * n**(-1./(d+4)), + * with ``n`` the number of data points and ``d`` the number of dimensions. + * Silverman's Rule [2]_, implemented as `silverman_factor`, is:: + * (n * (d + 2) / 4.)**(-1. / (d + 4)). + * Good general descriptions of kernel density estimation can be found in [1]_ + * and [2]_, the mathematics for this multi-dimensional implementation can be + * found in [1]_. + * + * References + * + * .. [1] D.W. Scott, "Multivariate Density Estimation: Theory, Practice, and + * Visualization", John Wiley & Sons, New York, Chicester, 1992. + * .. [2] B.W. Silverman, "Density Estimation for Statistics and Data + * Analysis", Vol. 26, Monographs on Statistics and Applied Probability, + * Chapman and Hall, London, 1986. + * + * @param array $dataset Array of univariate data points. + * @param string $bwMethod : Either "scott", "silverman" or an explicit (float) value. + */ + public function __construct(array $dataset, string $bwMethod = null) + { + $this->dataset = $dataset; + + if (count($this->dataset) <= 1) { + throw new \OutOfBoundsException('`dataset` input should have multiple elements.'); + } + + $this->setBandwidth($bwMethod); + } + + /** + * Evaluate the estimated pdf on a set of points. + * + * @param array $points 1-D array of points on to which we will map the kde + */ + public function evaluate(array $points): array + { + $count = count($this->dataset); + + $bigger = count($points) > $count; + + if ($bigger) { + $range = $count - 1; + } else { + $range = count($points) - 1; + } + + $result = array_fill(0, count($points), 0); + + // loop over points + foreach (range(0, $range) as $i) { + if ($bigger) { + $dataValue = $this->dataset[$i]; + $diff = array_map(function ($point) use ($dataValue) { + return $dataValue - $point; + }, $points); + } else { + $diff = array_map(function ($v) use ($points, $i) { + return $v - $points[$i]; + }, $this->dataset); + } + + // dot product (consider dedicated function) + $invCov = $this->invCov; + $tDiff = array_map(function ($v) use ($invCov) { + return $invCov * $v; + }, $diff); + + // multiply the two arrays + $multiplied = []; + + foreach ($diff as $index => $value) { + $multiplied[$index] = $diff[$index] * $tDiff[$index]; + } + + // numpy sum does nothing with our 2d array in PHP + // $energy = array_sum(diff * tdiff, axis=0) / 2.0 + $energy = array_map(function ($v) { + return exp(-($v / 2)); + }, $multiplied); + + if ($bigger) { + $sum = $result; + + foreach ($sum as $index => $value) { + $sum[$index] = $sum[$index] + $energy[$index]; + } + $result = $sum; + } else { + $result[$i] = array_sum($energy); + } + } + + $result = array_map(function ($v) { + return $v / $this->normFactor; + }, $result); + + return $result; + } + + /** + * Compute the estimator bandwidth with given method. + * + * The new bandwidth calculated after a call to `setBandwidth` is used + * for subsequent evaluations of the estimated density. + * + * @param string $bwMethod Either "scott" or "silverman" + */ + public function setBandwidth(string $bwMethod = null): void + { + if ($bwMethod == 'scott' || null === $bwMethod) { + $this->coVarianceFactor = function () { + return pow(count($this->dataset), -1. / (5)); + }; + } elseif ($bwMethod == 'silverman') { + $this->coVarianceFactor = function () { + return pow(count($this->dataset) * (3.0) / 4.0, -1. / (5)); + }; + } elseif (is_numeric($bwMethod)) { + $this->coVarianceFactor = function () use ($bwMethod) { + return $bwMethod; + }; + } else { + throw new \InvalidArgumentException(sprintf( + 'Unknown bandwidth method "%s"', + $bwMethod + )); + } + + $this->computeCovariance(); + } + + /** + * Computes the covariance matrix for each Gaussian kernel using + * coVarianceFactor(). + */ + private function computeCovariance(): void + { + $factorCallable = $this->coVarianceFactor; + $this->factor = $factorCallable(); + + // Cache covariance and inverse covariance of the data + if (null === $this->_dataInvCov) { + // original used the numpy.cov function. + $this->_dataCovariance = pow(Statistics::stdev($this->dataset, true), 2); + + //$this->_dataInvCov = 1/ linalg.inv($this->_dataCovariance) + $this->_dataInvCov = 1 / $this->_dataCovariance; + } + + $this->covariance = $this->_dataCovariance * pow($this->factor, 2); + $this->invCov = $this->_dataInvCov / pow($this->factor, 2); + $this->normFactor = sqrt(2 * M_PI * $this->covariance) * count($this->dataset); + } +} diff --git a/lib/Math/Statistics.php b/lib/Math/Statistics.php new file mode 100644 index 000000000..6cc914936 --- /dev/null +++ b/lib/Math/Statistics.php @@ -0,0 +1,223 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Math; + +/** + * Static class containing functions related to statistics. + */ +class Statistics +{ + /** + * Return the standard deviation of a given population. + * + */ + public static function stdev(array $values, bool $sample = false): float + { + $variance = self::variance($values, $sample); + + return \sqrt($variance); + } + + /** + * Return the variance for a given population. + * + * + * @return float + */ + public static function variance(array $values, bool $sample = false) + { + $average = self::mean($values); + $sum = 0; + + foreach ($values as $value) { + $diff = pow($value - $average, 2); + $sum += $diff; + } + + if (count($values) === 0) { + return 0; + } + + $variance = $sum / (count($values) - ($sample ? 1 : 0)); + + return $variance; + } + + /** + * Return the mean (average) value of the given values. + * + * + */ + public static function mean(array $values) + { + if (empty($values)) { + return 0; + } + + $sum = array_sum($values); + + if (0 == $sum) { + return 0; + } + + $count = count($values); + + return $sum / $count; + } + + /** + * Return the mode using the kernel density estimator using the normal + * distribution. + * + * The mode is the point in the kernel density estimate with the highest + * frequency, i.e. the time which correlates with the highest peak. + * + * If there are two or more modes (i.e. bimodal, trimodal, etc) then we + * could take the average of these modes. + * + * NOTE: If the kde estimate of the population is multi-modal (it has two + * points with exactly the same value) then the mean mode is returned. This + * is potentially misleading, but When benchmarking this should be a very + * rare occurance. + * + * @param string $bandwidth + */ + public static function kdeMode(array $population, int $space = 512, string $bandwidth = null): float + { + if (count($population) === 1) { + return current($population); + } + + if (count($population) === 0) { + return 0.0; + } + + if (min($population) == max($population)) { + return min($population); + } + + $kde = new Kde($population, $bandwidth); + $space = self::linspace(min($population), max($population), $space, true); + $dist = $kde->evaluate($space); + + $maxKeys = array_keys($dist, max($dist)); + $modes = []; + + foreach ($maxKeys as $maxKey) { + $modes[] = $space[$maxKey]; + } + + $mode = array_sum($modes) / count($modes); + + return $mode; + } + + /** + * Return an array populated with $num numbers from $min to $max. + * + * + * @return float[] + */ + public static function linspace(float $min, float $max, int $num = 50, bool $endpoint = true): array + { + $range = $max - $min; + + if ($max == $min) { + throw new \InvalidArgumentException(sprintf( + 'Min and max cannot be the same number: %s', $max + )); + } + + $unit = $range / ($endpoint ? $num - 1 : $num); + $space = []; + + for ($value = $min; $value <= $max; $value += $unit) { + $space[] = $value; + } + + if ($endpoint === false) { + array_pop($space); + } + + return $space; + } + + /** + * Generate a histogram. + * + * Note this is not a great function, and should not be relied upon + * for serious use. + * + * For a better implementation copy: + * http://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.histogram.html + * + * @param float $lowerBound + * @param float $upperBound + */ + public static function histogram(array $values, int $steps = 10, float $lowerBound = null, float $upperBound = null): array + { + $min = $lowerBound ?: min($values); + $max = $upperBound ?: max($values); + + $range = $max - $min; + + $step = $range / $steps; + $steps++; // add one extra step to catch the max value + + $histogram = []; + + $floor = $min; + + for ($i = 0; $i < $steps; $i++) { + $ceil = $floor + $step; + + if (!isset($histogram[(string) $floor])) { + $histogram[(string) $floor] = 0; + } + + foreach ($values as $value) { + if ($value >= $floor && $value < $ceil) { + $histogram[(string) $floor]++; + } + } + + $floor += $step; + $ceil += $step; + } + + return $histogram; + } + + public static function percentageDifference(float $value1, float $value2): float + { + if ($value1 == 0 && $value2 == 0) { + return 0; + } + + if ($value1 == 0) { + return INF; + } + + return (($value2 / $value1) - 1) * 100; + } + + /** + * @param (int|float)[] $values + */ + public static function rstdev(array $values, bool $sample = false): float + { + $mean = self::mean($values); + + return $mean ? self::stdev($values, $sample) / $mean * 100 : 0; + } +} diff --git a/lib/Model/Benchmark.php b/lib/Model/Benchmark.php new file mode 100644 index 000000000..c85b63c73 --- /dev/null +++ b/lib/Model/Benchmark.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Model; + +use \ArrayIterator; +use PhpBench\Benchmark\Metadata\SubjectMetadata; + +/** + * Benchmark metadata class. + * + * @implements \IteratorAggregate + */ +class Benchmark implements \IteratorAggregate +{ + /** + * @var string + */ + private $class; + + /** + * @var Subject[] + */ + private $subjects = []; + + /** + * @var Suite + */ + private $suite; + + /** + */ + public function __construct(Suite $suite, string $class) + { + $this->suite = $suite; + $this->class = $class; + } + + public function createSubjectFromMetadataAndExecutor(SubjectMetadata $metadata, ResolvedExecutor $executor): Subject + { + $subject = new Subject($this, $metadata->getName()); + $subject->setGroups($metadata->getGroups() ?: []); + $subject->setSleep($metadata->getSleep() ?: 0); + $subject->setRetryThreshold($metadata->getRetryThreshold()); + $subject->setOutputTimeUnit($metadata->getOutputTimeUnit()); + $subject->setOutputTimePrecision($metadata->getOutputTimePrecision()); + $subject->setOutputMode($metadata->getOutputMode()); + $subject->setExecutor($executor); + $subject->setFormat($metadata->getFormat()); + + $this->subjects[] = $subject; + + return $subject; + } + + /** + * Create and add a subject. + * + */ + public function createSubject(string $name): Subject + { + $subject = new Subject($this, $name); + $this->subjects[$name] = $subject; + + return $subject; + } + + /** + * Get the subject metadata instances for this benchmark metadata. + * + * @return array + */ + public function getSubjects(): array + { + return $this->subjects; + } + + /** + * Return the benchmark class. + */ + public function getClass(): string + { + return $this->class; + } + + public function getName(): string + { + $parts = explode('\\', $this->class); + end($parts); + + return current($parts); + } + + /** + * Return the suite to which this benchmark belongs. + */ + public function getSuite(): Suite + { + return $this->suite; + } + + /** + * @return ArrayIterator + */ + public function getIterator(): ArrayIterator + { + return new ArrayIterator($this->subjects); + } + + public function getSubject(string $subjectName): ?Subject + { + return $this->subjects[$subjectName] ?? null; + } +} diff --git a/lib/Model/Error.php b/lib/Model/Error.php new file mode 100644 index 000000000..c75ea41a8 --- /dev/null +++ b/lib/Model/Error.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Model; + +use Throwable; + +/** + * Represents an Error. Typically this is + * a serializable representation of an Exception. + */ +class Error +{ + private $message; + private $code; + private $line; + private $file; + private $class; + private $trace; + + public function __construct( + $message, + $class, + $code, + $file, + $line, + $trace + ) { + $this->message = $message; + $this->class = $class; + $this->line = $line; + $this->file = $file; + $this->code = $code; + $this->trace = $trace; + } + + public static function fromException(Throwable $exception): Error + { + return new self( + $exception->getMessage(), + get_class($exception), + $exception->getCode(), + $exception->getFile(), + $exception->getLine(), + $exception->getTraceAsString() + ); + } + + public function getCode() + { + return $this->code; + } + + public function getLine() + { + return $this->line; + } + + public function getFile() + { + return $this->file; + } + + public function getMessage() + { + return $this->message; + } + + public function getClass() + { + return $this->class; + } + + public function getTrace() + { + return $this->trace; + } +} diff --git a/lib/Model/ErrorStack.php b/lib/Model/ErrorStack.php new file mode 100644 index 000000000..48c36c10a --- /dev/null +++ b/lib/Model/ErrorStack.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Model; + +use Countable; +use IteratorAggregate; + +/** + * Essentially this class represents a single exception (the "top" + * exception) and any parent exceptions of it. + * + * It is also linked to the variant which encountered the error. + * + * @implements IteratorAggregate + */ +class ErrorStack implements IteratorAggregate, Countable +{ + /** + * @var Error[] + */ + private $errors; + + /** + * @var Variant + */ + private $variant; + + /** + * @param Error[] $errors + */ + public function __construct(Variant $variant, array $errors) + { + $this->variant = $variant; + $this->errors = $errors; + } + + public function getVariant(): Variant + { + return $this->variant; + } + + public function getErrors(): array + { + return $this->errors; + } + + public function getTop() + { + return reset($this->errors); + } + + public function getIterator(): \ArrayIterator + { + return new \ArrayIterator($this->errors); + } + + /** + * {@inheritDoc} + */ + public function count(): int + { + return count($this->errors); + } +} diff --git a/lib/Model/Iteration.php b/lib/Model/Iteration.php new file mode 100644 index 000000000..648f2d755 --- /dev/null +++ b/lib/Model/Iteration.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Model; + +/** + * Represents the data required to execute a single iteration. + */ +class Iteration extends ResultCollection +{ + private $variant; + private $index; + + /** + */ + public function __construct( + int $index, + Variant $variant, + array $results = [] + ) { + $this->index = $index; + $this->variant = $variant; + parent::__construct($results); + } + + /** + * Return the Variant that this + * iteration belongs to. + */ + public function getVariant(): Variant + { + return $this->variant; + } + + /** + * Return the index of this iteration. + */ + public function getIndex(): int + { + return $this->index; + } +} diff --git a/lib/Model/ParameterSet.php b/lib/Model/ParameterSet.php new file mode 100644 index 000000000..f4b8e2f7e --- /dev/null +++ b/lib/Model/ParameterSet.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Model; + +use ArrayObject; + +/** + * @extends ArrayObject + */ +class ParameterSet extends ArrayObject +{ + /** + * @var string + */ + private $name; + + /** + * @param array $parameters + */ + public function __construct(string $name, array $parameters = []) + { + $this->name = $name; + parent::__construct($parameters); + } + + /** + * @param array $parameters + */ + public static function create(string $name, array $parameters): self + { + return new self($name, $parameters); + } + + public function getName(): string + { + return (string) $this->name; + } + + /** + * @deprecated use getName instead + */ + public function getIndex(): string + { + return $this->name; + } +} diff --git a/lib/Model/ResolvedExecutor.php b/lib/Model/ResolvedExecutor.php new file mode 100644 index 000000000..27cd9c311 --- /dev/null +++ b/lib/Model/ResolvedExecutor.php @@ -0,0 +1,39 @@ +name = $name; + $this->config = $config; + } + + public function getName(): string + { + return $this->name; + } + + public function getConfig(): Config + { + return $this->config; + } + + public static function fromNameAndConfig(string $name, Config $executorConfig): ResolvedExecutor + { + return new self($name, $executorConfig); + } +} diff --git a/lib/Model/Result/ComputedResult.php b/lib/Model/Result/ComputedResult.php new file mode 100644 index 000000000..18d3b693d --- /dev/null +++ b/lib/Model/Result/ComputedResult.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Model\Result; + +use PhpBench\Model\ResultInterface; + +/** + * Metrics calculated relative to the iteration set of the iteration to which + * this result belongs. + */ +class ComputedResult implements ResultInterface +{ + private $zValue; + private $deviation; + + /** + * {@inheritdoc} + */ + public static function fromArray(array $values): ResultInterface + { + return new self( + (float) $values['z_value'], + (float) $values['deviation'] + ); + } + + /** + */ + public function __construct(float $zValue, float $deviation) + { + $this->zValue = $zValue; + $this->deviation = $deviation; + } + + /** + * {@inheritdoc} + */ + public function getMetrics(): array + { + return [ + 'z_value' => $this->zValue, + 'deviation' => $this->deviation, + ]; + } + + /** + * {@inheritdoc} + */ + public function getKey(): string + { + return 'comp'; + } + + /** + * Return the ZValue - the number of standard + * deviations away from the mean of the iteration + * set to which the iteration of this result belongs. + */ + public function getZValue(): float + { + return $this->zValue; + } + + /** + * Return the percentage deviation from the mean of the + * iteration set of the iteration to which this result + * belongs. + */ + public function getDeviation(): float + { + return $this->deviation; + } +} diff --git a/lib/Model/Result/MemoryResult.php b/lib/Model/Result/MemoryResult.php new file mode 100644 index 000000000..13ecf4e5c --- /dev/null +++ b/lib/Model/Result/MemoryResult.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Model\Result; + +use PhpBench\Model\ResultInterface; + +/** + * Represents the memory reported at the end of the benchmark script. + */ +class MemoryResult implements ResultInterface +{ + private $real; + private $peak; + private $final; + + /** + * {@inheritdoc} + */ + public static function fromArray(array $values): ResultInterface + { + return new self( + (int) $values['peak'], + (int) $values['real'], + (int) $values['final'] + ); + } + + /** + */ + public function __construct(int $peak, int $real, int $final) + { + $this->peak = $peak; + $this->real = $real; + $this->final = $final; + } + + /** + * Return peak memory usage as gathered by + * `memory_get_peak_usage`. + * + * @see http://php.net/manual/en/function.memory-get-peak-usage.php + */ + public function getPeak(): int + { + return $this->peak; + } + + /** + * Return real memory usage at the end of the script + * as gathered by `memory_get_usage(true)`. + * + * @see http://php.net/manual/en/function.memory-get-usage.php + */ + public function getReal(): int + { + return $this->real; + } + + /** + * Get memory usage at the end of the script. + * + * @see http://php.net/manual/en/function.memory-get-usage.php + */ + public function getFinal(): int + { + return $this->final; + } + + /** + * {@inheritdoc} + */ + public function getMetrics(): array + { + return [ + 'peak' => $this->peak, + 'real' => $this->real, + 'final' => $this->final, + ]; + } + + /** + * {@inheritdoc} + */ + public function getKey(): string + { + return 'mem'; + } +} diff --git a/lib/Model/Result/RejectionCountResult.php b/lib/Model/Result/RejectionCountResult.php new file mode 100644 index 000000000..ac885e3c6 --- /dev/null +++ b/lib/Model/Result/RejectionCountResult.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Model\Result; + +use Assert\Assertion; +use PhpBench\Model\ResultInterface; + +class RejectionCountResult implements ResultInterface +{ + private $rejectCount; + + /** + * {@inheritdoc} + */ + public static function fromArray(array $values): ResultInterface + { + return new self( + (int) $values['count'] + ); + } + + public function __construct(int $rejectCount) + { + Assertion::greaterOrEqualThan($rejectCount, 0, 'Rejection count must be greater or equal to 0'); + $this->rejectCount = $rejectCount; + } + + public function getRejectCount(): int + { + return $this->rejectCount; + } + + /** + * {@inheritdoc} + */ + public function getMetrics(): array + { + return [ + 'count' => $this->rejectCount, + ]; + } + + public function getKey(): string + { + return 'reject'; + } +} diff --git a/lib/Model/Result/TimeResult.php b/lib/Model/Result/TimeResult.php new file mode 100644 index 000000000..1f88d0c1f --- /dev/null +++ b/lib/Model/Result/TimeResult.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Model\Result; + +use Assert\Assertion; +use PhpBench\Model\ResultInterface; + +/** + * Represents the net time taken by a single iteration (all revolutions). + */ +class TimeResult implements ResultInterface +{ + /** + * @var int + */ + private $netTime; + + /** + * @var int + */ + private $revs; + + + public function __construct(int $netTime, int $revs = 1) + { + Assertion::greaterOrEqualThan($netTime, 0, 'Time cannot be less than 0, got %s'); + + $this->netTime = $netTime; + $this->revs = $revs; + } + + public static function fromArray(array $values): ResultInterface + { + return new self( + (int) $values['net'], + array_key_exists('revs', $values) ? $values['revs'] : 1 + ); + } + + /** + * Return the net-time for this iteration. + */ + public function getNet(): int + { + return $this->netTime; + } + + /** + * Return the time for the given number of revolutions. + * + * + * @throws \OutOfBoundsException If revs <= 0 + * + * @return float + */ + public function getRevTime(int $revs) + { + Assertion::greaterThan($revs, 0, 'Revolutions must be more than 0, got %s'); + + return $this->netTime / $revs; + } + + /** + * {@inheritdoc} + */ + public function getMetrics(): array + { + return [ + 'net' => $this->netTime, + 'revs' => $this->revs, + 'avg' => $this->netTime / $this->revs, + ]; + } + + /** + * {@inheritdoc} + */ + public function getKey(): string + { + return 'time'; + } +} diff --git a/lib/Model/ResultCollection.php b/lib/Model/ResultCollection.php new file mode 100644 index 000000000..1418a1bc4 --- /dev/null +++ b/lib/Model/ResultCollection.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Model; + +/** + * Represents the result of a single iteration executed by an executor. + */ +class ResultCollection +{ + private $results = []; + + /** + * @param ResultInterface[] $results + */ + public function __construct(array $results = []) + { + foreach ($results as $result) { + $this->setResult($result); + } + } + + /** + * Add a result to the collection. + * + * Only one result per class is permitted. + * + */ + public function setResult(ResultInterface $result): void + { + $class = get_class($result); + $this->results[$class] = $result; + } + + /** + * Return true if there is a result for the given class name. + * + */ + public function hasResult(string $class): bool + { + return isset($this->results[$class]); + } + + /** + * Return the result of the given class, throw an exception + * if it does not exist. + * + * + * @throws \RuntimeException + */ + public function getResult(string $class): ResultInterface + { + if (!isset($this->results[$class])) { + throw new \RuntimeException(sprintf( + 'Result of class "%s" has not been set', + $class + )); + } + + return $this->results[$class]; + } + + /** + * Return the named metric for the given result class. + * + * + * @throws \InvalidArgumentException + * + */ + public function getMetric(string $class, string $metric) + { + $metrics = $this->getResult($class)->getMetrics(); + + if (!isset($metrics[$metric])) { + throw new \InvalidArgumentException(sprintf( + 'Unknown metric "%s" for result class "%s". Available metrics: "%s"', + $metric, $class, implode('", "', array_keys($metrics)) + )); + } + + return $metrics[$metric]; + } + + /** + * Return the named metric or the default value if the *result class* has + * not been set. + * + * If the metric does not exist but the class *does* exist then there is + * clearly a problem and we should allow an error to be thrown. + * + * + */ + public function getMetricOrDefault(string $class, string $metric, $default = null) + { + if (false === $this->hasResult($class)) { + return $default; + } + + return $this->getMetric($class, $metric); + } + + /** + * Return all results. + * + * @return ResultInterface[] + */ + public function getResults(): ?array + { + return $this->results; + } +} diff --git a/lib/Model/ResultInterface.php b/lib/Model/ResultInterface.php new file mode 100644 index 000000000..cb57b2210 --- /dev/null +++ b/lib/Model/ResultInterface.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Model; + +/** + * Marker interface for iteration results. + * + * Implementations should reflect a particular domain of information, e.g. + * time, memory, xdebug.. + */ +interface ResultInterface +{ + /** + * Return a new instance based using the given array values. + * + * @param array $values + */ + public static function fromArray(array $values): ResultInterface; + + /** + * Return a key value set representing the metrics in this result. + * + * This set will be used to serialize the results, f.e. as attributes in an + * XML element. + * + * ``` + * [ + * 'stat_1' => 1234, + * 'stat_2' => 5678, + * ] + * ``` + * + * If the key of this result is "foo" then the set might be serialized as: + * + * ``` + * + * ``` + * + * @retrun array + */ + public function getMetrics(); + + /** + * Return a short key which should represent this result, f.e. `time`. + * This key must be unique in the set of all result classes. + */ + public function getKey(): string; +} diff --git a/lib/Model/Subject.php b/lib/Model/Subject.php new file mode 100644 index 000000000..27d2522d6 --- /dev/null +++ b/lib/Model/Subject.php @@ -0,0 +1,238 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Model; + +use PhpBench\Util\TimeUnit; + +/** + * Subject representation. + * + * It represents the result rather than the details of + * how to create that result. + */ +class Subject +{ + /** + * @var Benchmark + */ + private $benchmark; + + /** + * @var string + */ + private $name; + + /** + * @var string[] + */ + private $groups = []; + + /** + * @var int + */ + private $sleep = 0; + + /** + * @var float|null + */ + private $retryThreshold; + + /** + * @var string + */ + private $outputTimeUnit = TimeUnit::MICROSECONDS; + + /** + * @var int|null + */ + private $outputTimePrecision = null; + + /** + * @var string + */ + private $outputMode = TimeUnit::MODE_TIME; + + /** + * @var Variant[] + */ + private $variants = []; + + /** + * @var int + */ + private $index = 0; + + /** + * @var ResolvedExecutor + */ + private $executor; + + /** + * @var string + */ + private $format; + + /** + */ + public function __construct(Benchmark $benchmark, string $name) + { + $this->benchmark = $benchmark; + $this->name = $name; + + $this->index = count($benchmark->getSubjects()); + } + + /** + * Return the method name of this subject. + */ + public function getName(): string + { + return $this->name; + } + + /** + * Create and add a new variant based on this subject. + * + */ + public function createVariant(ParameterSet $parameterSet, int $revolutions, int $warmup, array $computedStats = []): Variant + { + $variant = new Variant( + $this, + $parameterSet, + $revolutions, + $warmup, + $computedStats + ); + $this->variants[$parameterSet->getName()] = $variant; + + return $variant; + } + + /** + * @return Variant[] + */ + public function getVariants(): array + { + return $this->variants; + } + + /** + * Return the (containing) benchmark for this subject. + */ + public function getBenchmark(): Benchmark + { + return $this->benchmark; + } + + public function getGroups(): array + { + return $this->groups; + } + + public function inGroups(array $groups): bool + { + return 0 !== count(array_intersect($this->groups, $groups)); + } + + public function setGroups(array $groups): void + { + $this->groups = $groups; + } + + public function getSleep(): int + { + return $this->sleep; + } + + public function setSleep(int $sleep): void + { + $this->sleep = $sleep; + } + + public function getOutputTimeUnit(): ?string + { + return $this->outputTimeUnit; + } + + public function setOutputTimeUnit(?string $outputTimeUnit): void + { + $this->outputTimeUnit = $outputTimeUnit; + } + + public function getOutputTimePrecision(): ?int + { + return $this->outputTimePrecision; + } + + public function setOutputTimePrecision(?int $outputTimePrecision): void + { + $this->outputTimePrecision = $outputTimePrecision; + } + + public function getOutputMode(): ?string + { + return $this->outputMode; + } + + public function setOutputMode(?string $outputMode): void + { + $this->outputMode = $outputMode; + } + + public function getRetryThreshold(): ?float + { + return $this->retryThreshold; + } + + public function setRetryThreshold(?float $retryThreshold): void + { + $this->retryThreshold = $retryThreshold; + } + + public function getIndex(): int + { + return $this->index; + } + + public function getExecutor(): ResolvedExecutor + { + return $this->executor; + } + + public function setExecutor(ResolvedExecutor $executor): void + { + $this->executor = $executor; + } + + public function remove(Variant $target): void + { + $this->variants = array_filter($this->variants, function (Variant $variant) use ($target) { + return $variant !== $target; + }); + } + + public function getVariant(string $parameterSetName): ?Variant + { + return $this->variants[$parameterSetName] ?? null; + } + + public function setFormat(?string $format): void + { + $this->format = $format; + } + + public function getFormat(): ?string + { + return $this->format; + } +} diff --git a/lib/Model/Suite.php b/lib/Model/Suite.php new file mode 100644 index 000000000..756510809 --- /dev/null +++ b/lib/Model/Suite.php @@ -0,0 +1,279 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Model; + +use ArrayIterator; +use IteratorAggregate; +use PhpBench\Assertion\VariantAssertionResults; +use PhpBench\Environment\Information; + +/** + * Represents a Suite. + * + * This is the base of the object graph created by the Runner. + * + * @implements IteratorAggregate + */ +class Suite implements IteratorAggregate +{ + private $tag; + private $date; + private $configPath; + private $envInformations = []; + private $benchmarks = []; + private $uuid; + + /** + * __construct. + * + * @param string $tag + * @param string $configPath + * @param Information[] $envInformations + */ + public function __construct( + ?string $tag, + \DateTime $date, + ?string $configPath = null, + array $benchmarks = [], + array $envInformations = [], + $uuid = null + ) { + $this->tag = $tag ? new Tag($tag) : null; + $this->date = $date; + $this->configPath = $configPath; + $this->envInformations = $envInformations; + $this->benchmarks = $benchmarks; + $this->uuid = $uuid; + } + + /** + * @return array + */ + public function getBenchmarks(): array + { + return $this->benchmarks; + } + + public function getBenchmark(string $class): ?Benchmark + { + return $this->benchmarks[$class] ?? null; + } + + /** + * Create and add a benchmark. + * + */ + public function createBenchmark(string $class): Benchmark + { + $benchmark = new Benchmark($this, $class); + $this->benchmarks[$class] = $benchmark; + + return $benchmark; + } + + /** + * @return ArrayIterator + */ + public function getIterator(): ArrayIterator + { + return new ArrayIterator($this->benchmarks); + } + + public function getTag(): ?Tag + { + return $this->tag; + } + + public function getDate(): \DateTime + { + return $this->date; + } + + public function getConfigPath(): ?string + { + return $this->configPath; + } + + public function getSummary(): Summary + { + return new Summary($this); + } + + public function getIterations(): array + { + $iterations = []; + + foreach ($this->getVariants() as $variant) { + foreach ($variant as $iteration) { + $iterations[] = $iteration; + } + } + + return $iterations; + } + + /** + * @return Subject[] + */ + public function getSubjects(): array + { + $subjects = []; + + foreach ($this->getBenchmarks() as $benchmark) { + foreach ($benchmark->getSubjects() as $subject) { + $subjects[] = $subject; + } + } + + return $subjects; + } + + /** + * @return array + */ + public function getVariants(): array + { + $variants = []; + + foreach ($this->getSubjects() as $subject) { + foreach ($subject->getVariants() as $variant) { + $variants[] = $variant; + } + } + + return $variants; + } + + public function getErrorStacks(): array + { + $errorStacks = []; + + foreach ($this->getVariants() as $variant) { + if (false === $variant->hasErrorStack()) { + continue; + } + + $errorStacks[] = $variant->getErrorStack(); + } + + return $errorStacks; + } + + /** + * @return VariantAssertionResults[] + */ + public function getFailures(): array + { + $failures = []; + + /** @var Variant $variant */ + foreach ($this->getVariants() as $variant) { + if (0 === $variant->getAssertionResults()->failures()->count()) { + continue; + } + + $failures[] = $variant->getAssertionResults()->failures(); + } + + return $failures; + } + + /** + * @param Information[] $envInformations + */ + public function setEnvInformations(iterable $envInformations): void + { + foreach ($envInformations as $envInformation) { + $this->addEnvInformation($envInformation); + } + } + + public function addEnvInformation(Information $information): void + { + $this->envInformations[$information->getName()] = $information; + } + + /** + * @return Information[] + */ + public function getEnvInformations(): array + { + return $this->envInformations; + } + + /** + * The uuid uniquely identifies this suite. + * + * The uuid is determined by the storage driver, and may be empty + * only when dynamically generating reports on-the-fly. + * + */ + public function getUuid(): ?string + { + return $this->uuid; + } + + /** + * Generate a universally unique identifier. + * + * The first 7 characters are the year month and in hex, the rest is a + * truncated sha1 string encoding the environmental information, the + * microtime and the configuration path. + */ + public function generateUuid(): void + { + $serialized = serialize($this->envInformations); + $this->uuid = dechex((int)$this->getDate()->format('Ymd')) . substr(sha1(implode([ + microtime(), + $serialized, + $this->configPath, + ])), 0, -7); + } + + public function mergeBaselines(SuiteCollection $suiteCollection): self + { + foreach ($suiteCollection->getSuites() as $baselineSuite) { + foreach ($this->getVariants() as $variant) { + $subject = $variant->getSubject(); + $benchmark = $subject->getBenchmark(); + + $baselineVariant = $baselineSuite->findVariant( + $benchmark->getClass(), + $subject->getName(), + $variant->getParameterSet()->getName() + ); + + if (!$baselineVariant) { + continue; + } + + $variant->attachBaseline($baselineVariant); + } + } + + return $this; + } + + public function findVariant(string $benchmarkClass, string $subjectName, string $variantName): ?Variant + { + if (!$benchmark = $this->getBenchmark($benchmarkClass)) { + return null; + } + + if (!$subject = $benchmark->getSubject($subjectName)) { + return null; + } + + return $subject->getVariant($variantName); + } +} diff --git a/lib/Model/SuiteCollection.php b/lib/Model/SuiteCollection.php new file mode 100644 index 000000000..7e68f6fb1 --- /dev/null +++ b/lib/Model/SuiteCollection.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Model; + +use ArrayIterator; +use IteratorAggregate; + +/** + * @implements IteratorAggregate + */ +class SuiteCollection implements IteratorAggregate +{ + /** + * @var Suite[] + */ + private $suites; + + /** + * @param Suite[] $suites + */ + public function __construct(array $suites = []) + { + $this->suites = $suites; + } + + /** + * Return the suites. + * + * @return Suite[] + */ + public function getSuites(): array + { + return $this->suites; + } + + /** + * Add a suite to the collection. + * + */ + public function addSuite(Suite $suite): void + { + $this->suites[] = $suite; + } + + /** + * Merge another collection into this one. + */ + public function mergeCollection(SuiteCollection $collection): self + { + foreach ($collection->getSuites() as $suite) { + $this->addSuite($suite); + } + + return $this; + } + + /** + * @return ArrayIterator + */ + public function getIterator(): ArrayIterator + { + return new \ArrayIterator($this->suites); + } + + public function findBaselineForVariant(Variant $variant): ?Variant + { + foreach (array_reverse($this->suites) as $suite) { + if (!$variant = $suite->findVariant( + $variant->getSubject()->getBenchmark()->getClass(), + $variant->getSubject()->getName(), + $variant->getParameterSet()->getName() + )) { + continue; + } + + return $variant; + } + + return null; + } +} diff --git a/lib/Model/Summary.php b/lib/Model/Summary.php new file mode 100644 index 000000000..e77ebf177 --- /dev/null +++ b/lib/Model/Summary.php @@ -0,0 +1,186 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Model; + +use PhpBench\Math\Statistics; + +/** + * Provides summary statistics for the entires suite. + */ +class Summary +{ + private $nbSubjects = 0; + private $nbIterations = 0; + private $nbRejects = 0; + private $nbRevolutions = 0; + private $nbFailures = 0; + private $nbAssertions = 0; + private $nbErrors = 0; + private $stats = [ + 'stdev' => [], + 'mean' => [], + 'mode' => [], + 'rstdev' => [], + 'variance' => [], + 'min' => [], + 'max' => [], + 'sum' => [], + ]; + private $errorStacks = []; + + /** + * @var bool + */ + private $opCacheEnabled = false; + + /** + * @var bool + */ + private $xdebugEnabled = false; + + /** + * @var string|null + */ + private $phpVersion = null; + + /** + */ + public function __construct(Suite $suite) + { + foreach ($suite->getBenchmarks() as $benchmark) { + foreach ($benchmark->getSubjects() as $subject) { + $this->nbSubjects++; + + foreach ($subject->getVariants() as $variant) { + $this->nbIterations += count($variant); + $this->nbRevolutions += $variant->getRevolutions(); + $this->nbFailures += $variant->getAssertionResults()->failures()->count(); + $this->nbAssertions += $variant->getAssertionResults()->count(); + $this->nbErrors += $variant->getErrorStack()->count(); + $this->nbRejects += $variant->getRejectCount(); + + if ($variant->hasErrorStack()) { + $this->errorStacks[] = $variant->getErrorStack(); + + continue; + } + + foreach ($variant->getStats() as $name => $value) { + $this->stats[$name][] = $value; + } + } + } + } + + $env = $suite->getEnvInformations(); + + if (isset($env['opcache'])) { + $this->opCacheEnabled = (bool)($env['opcache']['enabled'] ?? false); + } + + if (isset($env['php'])) { + $this->xdebugEnabled = (bool)($env['php']['xdebug'] ?? false); + $this->phpVersion = $env['php']['version'] ?? null; + } + } + + public function getNbSubjects(): int + { + return $this->nbSubjects; + } + + public function getNbIterations(): int + { + return $this->nbIterations; + } + + public function getNbRejects(): int + { + return $this->nbRejects; + } + + public function getNbRevolutions(): int + { + return $this->nbRevolutions; + } + + public function getNbFailures(): int + { + return $this->nbFailures; + } + + public function getNbErrors(): int + { + return $this->nbErrors; + } + + public function getNbAssertions(): int + { + return $this->nbAssertions; + } + + public function getStats(): array + { + return $this->stats; + } + + public function getMinTime() + { + return $this->stats['min'] ? min($this->stats['min']) : 0; + } + + public function getMaxTime() + { + return $this->stats['max'] ? min($this->stats['max']) : 0; + } + + public function getMeanTime() + { + return Statistics::mean($this->stats['mean']); + } + + public function getModeTime() + { + return Statistics::mean($this->stats['mode']); + } + + public function getTotalTime() + { + return array_sum($this->stats['sum']); + } + + public function getMeanStDev() + { + return Statistics::mean($this->stats['stdev']); + } + + public function getMeanRelStDev() + { + return Statistics::mean($this->stats['rstdev']); + } + + public function getOpcacheEnabled(): bool + { + return $this->opCacheEnabled; + } + + public function getXdebugEnabled(): bool + { + return $this->xdebugEnabled; + } + + public function getPhpVersion(): ?string + { + return $this->phpVersion; + } +} diff --git a/lib/Model/Tag.php b/lib/Model/Tag.php new file mode 100644 index 000000000..948e50596 --- /dev/null +++ b/lib/Model/Tag.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Model; + +use PhpBench\Storage\Exception\InvalidTagException; + +final class Tag +{ + public const REGEX_PATTERN = '\\w+'; + + /** + * @var string + */ + private $tag; + + public function __construct(string $tag) + { + if (!preg_match(sprintf('/^%s$/', self::REGEX_PATTERN), $tag)) { + throw new InvalidTagException(sprintf( + 'Tag mast be non-empty string of alphanumeric characters and _, got "%s"', + $tag + )); + } + $this->tag = strtolower($tag); + } + + public function __toString(): string + { + return $this->tag; + } +} diff --git a/lib/Model/Variant.php b/lib/Model/Variant.php new file mode 100644 index 000000000..ddb707147 --- /dev/null +++ b/lib/Model/Variant.php @@ -0,0 +1,458 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Model; + +use ArrayAccess; +use ArrayIterator; +use Countable; +use Exception; +use IteratorAggregate; +use PhpBench\Assertion\AssertionResult; +use PhpBench\Assertion\VariantAssertionResults; +use PhpBench\Math\Distribution; +use PhpBench\Math\Statistics; +use PhpBench\Model\Result\ComputedResult; +use PhpBench\Model\Result\TimeResult; +use RuntimeException; + +/** + * Stores Iterations and calculates the deviations and rejection + * status for each based on the given rejection threshold. + * + * @implements IteratorAggregate + * @implements ArrayAccess + */ +class Variant implements IteratorAggregate, ArrayAccess, Countable +{ + /** + * @var Subject + */ + private $subject; + + /** + * @var ParameterSet + */ + private $parameterSet; + + /** + * @var Iteration[] + */ + private $iterations = []; + + /** + * @var Iteration[] + */ + private $rejects = []; + + /** + * @var ErrorStack + */ + private $errorStack; + + /** + * @var Distribution + */ + private $stats; + + /** + * @var array + */ + private $computedStats; + + /** + * @var bool + */ + private $computed = false; + + /** + * @var int + */ + private $revolutions; + + /** + * @var int + */ + private $warmup; + + /** + * @var Variant|null + */ + private $baseline; + + /** + * @var VariantAssertionResults + */ + private $assertionResults; + + public function __construct( + Subject $subject, + ParameterSet $parameterSet, + int $revolutions, + int $warmup, + array $computedStats = [] + ) { + $this->subject = $subject; + $this->parameterSet = $parameterSet; + $this->revolutions = $revolutions; + $this->warmup = $warmup; + $this->computedStats = $computedStats; + $this->assertionResults = new VariantAssertionResults($this, []); + } + + /** + * Generate $nbIterations and add them to the variant. + * + */ + public function spawnIterations(int $nbIterations): void + { + for ($index = 0; $index < $nbIterations; $index++) { + $this->iterations[] = new Iteration($index, $this); + } + } + + /** + * Create and add a new iteration. + * + * @param array $results + */ + public function createIteration(array $results = []): Iteration + { + $index = count($this->iterations); + $iteration = new Iteration($index, $this, $results); + $this->iterations[] = $iteration; + + return $iteration; + } + + /** + * Return the iteration at the given index. + * + * @return Iteration + */ + public function getIteration($index): ?Iteration + { + return $this->iterations[$index] ?? null; + } + + /** + * Add an iteration. + * + */ + public function addIteration(Iteration $iteration): void + { + $this->iterations[] = $iteration; + } + + /** + * @return ArrayIterator + */ + public function getIterator(): ArrayIterator + { + return new ArrayIterator($this->iterations); + } + + /** + * Return result values by class and metric name. + * + * e.g. + * + * ``` + * $variant->getMetricValues(ComputedResult::class, 'z_value'); + * ``` + * + * @return mixed[] + */ + public function getMetricValues(string $resultClass, string $metricName): array + { + $values = []; + + foreach ($this->iterations as $iteration) { + if ($iteration->hasResult($resultClass)) { + $values[] = $iteration->getMetric($resultClass, $metricName); + } + } + + return $values; + } + + /** + * Return the average metric values by revolution. + * + * @return mixed[] + */ + public function getMetricValuesByRev(string $resultClass, string $metric): array + { + return array_map(function ($value) { + return $value / $this->getRevolutions(); + }, $this->getMetricValues($resultClass, $metric)); + } + + public function resetAssertionResults(): void + { + $this->assertionResults = new VariantAssertionResults($this, []); + } + + /** + * Calculate and set the deviation from the mean time for each iteration. If + * the deviation is greater than the rejection threshold, then mark the iteration as + * rejected. + */ + public function computeStats(): void + { + $this->rejects = []; + $revs = $this->getRevolutions(); + + if (0 === count($this->iterations)) { + return; + } + + $times = $this->getMetricValuesByRev(TimeResult::class, 'net'); + $retryThreshold = $this->getSubject()->getRetryThreshold(); + + $this->stats = new Distribution($times, $this->computedStats); + + foreach ($this->iterations as $iteration) { + $timeResult = $iteration->getResult(TimeResult::class); + assert($timeResult instanceof TimeResult); + // deviation is the percentage different of the value from the mean of the set. + if ($this->stats->getMean() > 0) { + $deviation = 100 / $this->stats->getMean() * ( + ( + $timeResult->getRevTime($iteration->getVariant()->getRevolutions()) + ) - $this->stats->getMean() + ); + } else { + $deviation = 0; + } + + // the Z-Value represents the number of standard deviations this + // value is away from the mean. + $revTime = $timeResult->getRevTime($revs); + $zValue = $this->stats->getStdev() ? ($revTime - $this->stats->getMean()) / $this->stats->getStdev() : 0; + + if (null !== $retryThreshold) { + if (abs($deviation) >= $retryThreshold) { + $this->rejects[] = $iteration; + } + } + + $iteration->setResult(new ComputedResult($zValue, $deviation)); + } + + $this->computed = true; + } + + /** + * Return the number of rejected iterations. + * + */ + public function getRejectCount(): int + { + return count($this->rejects); + } + + /** + * Return all rejected iterations. + * + * @return Iteration[] + */ + public function getRejects(): array + { + return $this->rejects; + } + + /** + * Return statistics about this iteration collection. + * + * See self::$stats. + * + * TODO: Rename to getDistribution + * + */ + public function getStats(): Distribution + { + if (null !== $this->errorStack) { + throw new RuntimeException(sprintf( + 'Cannot retrieve stats when an exception was encountered ([%s] %s)', + $this->errorStack->getTop()->getClass(), + $this->errorStack->getTop()->getMessage() + )); + } + + if (false === $this->computed) { + throw new RuntimeException( + 'No statistics have yet been computed for this iteration set (::computeStats should be called)' + ); + } + + return $this->stats; + } + + /** + * Return true if the collection has been computed (i.e. stats have been s + * set and rejects identified). + */ + public function isComputed(): bool + { + return $this->computed; + } + + /** + * Return the parameter set. + */ + public function getParameterSet(): ParameterSet + { + return $this->parameterSet; + } + + /** + * Return the subject metadata. + */ + public function getSubject(): Subject + { + return $this->subject; + } + + /** + * Return true if any of the iterations in this set encountered + * an error. + */ + public function hasErrorStack(): bool + { + return null !== $this->errorStack; + } + + /** + * Should be called when rebuiling the object graph. + */ + public function getErrorStack(): ErrorStack + { + if (null === $this->errorStack) { + return new ErrorStack($this, []); + } + + return $this->errorStack; + } + + /** + * Create an error stack from an Exception. + * + * Should be called when an Exception is encountered during + * the execution of any of the iteration processes. + * + * After an exception is encountered the results from this iteration + * set are invalid. + * + */ + public function setException(Exception $exception): void + { + $errors = []; + + do { + $errors[] = Error::fromException($exception); + } while ($exception = $exception->getPrevious()); + + $this->errorStack = new ErrorStack($this, $errors); + } + + /** + * Create and set the error stack from a list of Error instances. + * + * @param Error[] $errors + */ + public function createErrorStack(array $errors): void + { + $this->errorStack = new ErrorStack($this, $errors); + } + + /** + * Return the number of revolutions for this variant. + */ + public function getRevolutions(): int + { + return $this->revolutions; + } + + /** + * Return the number of warmup revolutions. + */ + public function getWarmup(): int + { + return $this->warmup; + } + + /** + * Return all the iterations. + * + * @return Iteration[] + */ + public function getIterations(): array + { + return $this->iterations; + } + + /** + * Return number of iterations. + */ + public function count(): int + { + return count($this->iterations); + } + + public function offsetGet($offset): ?Iteration + { + return $this->getIteration($offset); + } + + public function offsetSet($offset, $value): void + { + throw new \InvalidArgumentException( + 'Iteration collections are immutable' + ); + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($offset): void + { + throw new \InvalidArgumentException( + 'Iteration collections are immutable' + ); + } + + public function offsetExists($offset): bool + { + return array_key_exists($offset, $this->iterations); + } + + public function attachBaseline(Variant $baselineVariant): void + { + $this->baseline = $baselineVariant; + } + + public function getBaseline(): ?Variant + { + return $this->baseline; + } + + public function addAssertionResult(AssertionResult $result): void + { + $this->assertionResults->add($result); + } + + public function getAssertionResults(): VariantAssertionResults + { + return $this->assertionResults; + } +} diff --git a/lib/PhpBench.php b/lib/PhpBench.php new file mode 100644 index 000000000..b0431846d --- /dev/null +++ b/lib/PhpBench.php @@ -0,0 +1,256 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench; + +use PhpBench\Console\Application; +use PhpBench\DependencyInjection\Container; +use PhpBench\Exception\ConfigurationPreProcessingError; +use PhpBench\Extension\ConsoleExtension; +use PhpBench\Extension\CoreExtension; +use PhpBench\Extension\ExpressionExtension; +use PhpBench\Extension\ReportExtension; +use PhpBench\Extension\RunnerExtension; +use PhpBench\Extension\StorageExtension; +use PhpBench\Extensions\XDebug\XDebugExtension; +use PhpBench\Json\JsonDecoder; +use Seld\JsonLint\JsonParser; +use Seld\JsonLint\ParsingException; +use function set_error_handler; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Throwable; +use Webmozart\PathUtil\Path; + +class PhpBench +{ + // PHPBench version: @git_tag@ will be replaced by box. + const VERSION = '@git_tag@'; + + // URL to phar and version file for self-updating + const PHAR_URL = 'https://phpbench.github.io/phpbench/phpbench.phar'; + const PHAR_VERSION_URL = 'https://phpbench.github.io/phpbench/phpbench.phar.version'; + + public static function run(?InputInterface $input = null, ?OutputInterface $output = null): void + { + $input = $input ?: new ArgvInput(); + self::registerErrorHandler(); + + $container = self::loadContainer($input); + $container->get(Application::class)->run( + $input, + $output ?? $container->get(ConsoleExtension::SERVICE_OUTPUT_ERR) + ); + } + + public static function loadContainer(InputInterface $input, ?string $cwd = null): Container + { + $config = self::loadConfig($input, $cwd ?: getcwd()); + + $extensions = array_merge([ + CoreExtension::class, + RunnerExtension::class, + ReportExtension::class, + ExpressionExtension::class, + StorageExtension::class, + XDebugExtension::class, + ConsoleExtension::class, + ], $config[CoreExtension::PARAM_EXTENSIONS]); + + $container = new Container(array_unique($extensions), $config); + $container->init(); + + return $container; + } + + /** + * @return array + */ + private static function loadConfig(InputInterface $input, string $cwd): array + { + $configPaths = []; + $extensions = []; + $configOverride = []; + $profile = null; + $argBootstrap = null; + + if ($value = $input->getParameterOption(['--working-dir'])) { + $cwd = Path::makeAbsolute($value, getcwd()); + } + + if ($configFile = $input->getParameterOption(['--config'])) { + if (!file_exists($configFile)) { + echo sprintf('Config file "%s" does not exist', $configFile) . PHP_EOL; + + exit(1); + } + + $configFile = Path::makeAbsolute($configFile, $cwd); + $configPaths = [$configFile]; + } + + if ($value = $input->getParameterOption(['--bootstrap', '-b='])) { + $argBootstrap = $value; + $configOverride[RunnerExtension::PARAM_BOOTSTRAP] = $value; + } + + if ($input->getParameterOption(['--no-ansi'])) { + $configOverride[ConsoleExtension::PARAM_ANSI] = false; + } + + if ($value = $input->getParameterOption(['--extension'])) { + $extensions[] = $value; + } + + if ($value = $input->getParameterOption(['--php-binary'])) { + $configOverride[RunnerExtension::PARAM_PHP_BINARY] = $value; + } + + if ($value = $input->getParameterOption(['--php-wrapper'])) { + $configOverride[RunnerExtension::PARAM_PHP_WRAPPER] = $value; + } + + if ($value = $input->getParameterOption(['--php-config'])) { + $jsonParser = new JsonDecoder(); + $value = $jsonParser->decode($value); + $configOverride[RunnerExtension::PARAM_PHP_CONFIG] = $value; + } + + if ($input->hasParameterOption(['--php-disable-ini'])) { + $configOverride[RunnerExtension::PARAM_PHP_DISABLE_INI] = true; + } + + if ($value = $input->getParameterOption(['--profile'])) { + $profile = $value; + } + + if ($value = $input->getParameterOption(['--theme'])) { + $configOverride['expression.theme'] = $value; + } + + if ($input->hasParameterOption(['-vvv'])) { + $configOverride[CoreExtension::PARAM_DEBUG] = true; + } + + if (empty($configPaths)) { + $configPaths = [ + $cwd . '/phpbench.json', + $cwd . '/phpbench.json.dist', + ]; + } + + $config = [ + CoreExtension::PARAM_EXTENSIONS => [], + RunnerExtension::PARAM_BOOTSTRAP => null, + CoreExtension::PARAM_WORKING_DIR => $cwd, + ]; + + foreach ($configPaths as $configPath) { + if (!file_exists($configPath)) { + continue; + } + + $configRaw = (string)file_get_contents($configPath); + + try { + $parser = new JsonParser(); + $parser->parse($configRaw); + } catch (ParsingException $e) { + echo 'Error parsing config file:' . PHP_EOL . PHP_EOL; + echo $e->getMessage(); + + exit(1); + } + + $config = array_merge( + $config, + json_decode($configRaw, true) + ); + $config[CoreExtension::PARAM_CONFIG_PATH] = $configPath; + + break; + } + + $config = array_merge( + $config, + $configOverride + ); + + if ($configFile && !$argBootstrap && $config[RunnerExtension::PARAM_BOOTSTRAP]) { + $config[RunnerExtension::PARAM_BOOTSTRAP] = Path::makeAbsolute($config[RunnerExtension::PARAM_BOOTSTRAP], dirname($configFile)); + } elseif ($config[RunnerExtension::PARAM_BOOTSTRAP]) { + $config[RunnerExtension::PARAM_BOOTSTRAP] = Path::makeAbsolute($config[RunnerExtension::PARAM_BOOTSTRAP], $cwd); + } + + if (null !== $profile) { + $config = self::mergeProfile($config, $profile); + } + unset($config[CoreExtension::PARAM_PROFILES]); + + // add any manually specified extensions + foreach ($extensions as $extension) { + $config[CoreExtension::PARAM_EXTENSIONS][] = $extension; + } + + return $config; + } + + private static function mergeProfile(array $config, string $profile): array + { + if (!isset($config[CoreExtension::PARAM_PROFILES][$profile])) { + throw new ConfigurationPreProcessingError(sprintf( + 'Unknown profile "%s" specified, defined profiles: "%s"', + $profile, implode('", "', array_keys($config[CoreExtension::PARAM_PROFILES] ?? [])) + )); + } + + return array_merge($config, $config[CoreExtension::PARAM_PROFILES][$profile]); + } + + private static function registerErrorHandler(): void + { + $input = new ArgvInput(); + $output = (new ConsoleOutput())->getErrorOutput(); + + $format = new SymfonyStyle($input, $output); + set_error_handler(function ( + int $code, + string $message, + string $file, + int $line, + ?array $context = null + ) use ($format): ?bool { + $format->error(sprintf( + '%s in %s:%s', + $message, + $file, + $line + )); + + exit(255); + }, E_USER_ERROR); + + set_exception_handler(function (Throwable $throwable) use ($format, $input): void { + $format->text(sprintf('%s:%s', $throwable->getFile(), $throwable->getLine())); + $format->error($throwable->getMessage()); + + if ($input->hasParameterOption(['-v', '-vv', '-vvv'])) { + $format->block($throwable->getTraceAsString()); + } + + exit(255); + }); + } +} diff --git a/lib/Progress/Logger/AnsiLogger.php b/lib/Progress/Logger/AnsiLogger.php new file mode 100644 index 000000000..f856c46d7 --- /dev/null +++ b/lib/Progress/Logger/AnsiLogger.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Progress\Logger; + +use PhpBench\Model\Variant; + +abstract class AnsiLogger extends PhpBenchLogger +{ + protected function renderCollectionStatus(Variant $variant): void + { + $this->output->write(PHP_EOL); + $this->output->write("\x1B[0J"); // clear the line + $this->output->write(PHP_EOL); + $this->output->write(sprintf( + 'subject %s with %s iteration(s) of %s rev(s),', + sprintf('%s', $variant->getSubject()->getName()), + count($variant), + $variant->getRevolutions() + )); + $this->output->write(PHP_EOL); + $this->output->write(sprintf( + 'parameter set %s', + $variant->getParameterSet()->getName() + )); + $this->output->write(PHP_EOL); + $this->output->write("\x1B[". 4 . 'A'); // put the cursor back to the line with the measurements + $this->output->write("\x1B[0G"); // put the cursor back at col 0 + } +} diff --git a/lib/Progress/Logger/BlinkenLogger.php b/lib/Progress/Logger/BlinkenLogger.php new file mode 100644 index 000000000..8db07dccc --- /dev/null +++ b/lib/Progress/Logger/BlinkenLogger.php @@ -0,0 +1,234 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Progress\Logger; + +use PhpBench\Model\Benchmark; +use PhpBench\Model\Iteration; +use PhpBench\Model\Suite; +use PhpBench\Model\Variant; + +class BlinkenLogger extends AnsiLogger +{ + /** + * Number of measurements to show per row. + */ + const NUMBER_COLS = 10; + + const INDENT = 4; + + /** + * Track rejected iterations. + * + * @var bool[] + */ + private $rejects = []; + + /** + * Current number of rows in the time display. + * + * @var int + */ + private $currentLine = 0; + + /** + * Column width. + * + * @var int + */ + private $colWidth = 6; + private $firstTime = true; + + /** + * {@inheritdoc} + */ + public function endSuite(Suite $suite): void + { + $this->output->write(PHP_EOL); + parent::endSuite($suite); + } + + /** + * {@inheritdoc} + */ + public function benchmarkStart(Benchmark $benchmark): void + { + if (false === $this->firstTime) { + $this->output->write(PHP_EOL); + } + $this->firstTime = true; + $this->output->write(sprintf('%s', $benchmark->getClass())); + + $subjectNames = []; + + foreach ($benchmark->getSubjects() as $index => $subject) { + $subjectNames[] = sprintf('#%s %s', $index, $subject->getName()); + } + + $this->output->write(sprintf(' (%s)', implode(', ', $subjectNames))); + $this->output->write(PHP_EOL); + $this->output->write(PHP_EOL); + } + + /** + * {@inheritdoc} + */ + public function variantStart(Variant $variant): void + { + $this->drawIterations($variant, $this->rejects, 'error'); + $this->renderCollectionStatus($variant); + $this->resetLinePosition(); // put cursor at starting ypos ready for iteration times + } + + /** + * {@inheritdoc} + */ + public function variantEnd(Variant $variant): void + { + $this->resetLinePosition(); + $this->drawIterations($variant, [], null); + + if ($variant->hasErrorStack()) { + $this->output->write(' ERROR'); + $this->output->write("\x1B[0J"); // clear the rest of the line + $this->output->write(PHP_EOL); + + return; + } + + $this->rejects = []; + + foreach ($variant->getRejects() as $reject) { + $this->rejects[$reject->getIndex()] = true; + } + + if ($this->rejects) { + $this->resetLinePosition(); + + return; + } + + $this->output->write(' ' . $this->formatIterationsShortSummary($variant)); + $this->output->write(PHP_EOL); + } + + /** + * {@inheritdoc} + */ + public function iterationEnd(Iteration $iteration): void + { + $time = $this->formatIterationTime($iteration); + $this->output->write(sprintf( + "\x1B[" . $this->getXPos($iteration) . 'G%s', + $time + )); + } + + /** + * {@inheritdoc} + */ + public function iterationStart(Iteration $iteration): void + { + if ($this->currentLine != $yPos = $this->getYPos($iteration)) { + $downMovement = $yPos - $this->currentLine; + $this->output->write("\x1B[" . $downMovement . 'B'); + $this->currentLine = (int)$yPos; + } + + $time = $this->formatIterationTime($iteration); + + $this->output->write(sprintf( + "\x1B[" . $this->getXPos($iteration) . 'G%s', + $time + )); + } + + /** + * {@inheritdoc} + */ + protected function formatIterationTime(Iteration $iteration): string + { + $time = sprintf('%-' . $this->colWidth . 's', parent::formatIterationTime($iteration)); + + if (strlen(trim($time)) >= $this->colWidth) { + $this->colWidth += 2; + $this->resetLinePosition(); + $this->drawIterations($iteration->getVariant(), $this->rejects, 'error'); + $this->resetLinePosition(); + } + + return $time; + } + + private function drawIterations(Variant $variant, array $specials, $tag): void + { + $this->output->write("\x1B[2K"); // clear line + + $timeUnit = $variant->getSubject()->getOutputTimeUnit(); + $outputMode = $variant->getSubject()->getOutputMode(); + $lines = []; + $line = sprintf('%-' . self::INDENT . 's', '#' . $variant->getSubject()->getIndex()); + $nbIterations = $variant->count(); + + for ($index = 0; $index < $nbIterations; $index++) { + $iteration = $variant->getIteration($index); + + $displayTime = $this->formatIterationTime($iteration); + + if (isset($specials[$iteration->getIndex()])) { + $displayTime = sprintf('<%s>%' . $this->colWidth . 's', $tag, $displayTime, $tag); + } + + $line .= $displayTime; + + if ($index > 0 && $index < $nbIterations - 1 && ($index + 1) % self::NUMBER_COLS == 0) { + $lines[] = $line; + $line = str_repeat(' ', self::INDENT); + } + } + + $lines[] = $line; + $this->currentLine = count($lines) - 1; + + $output = trim(implode(PHP_EOL, $lines)); + $output .= sprintf( + ' (%s)', + $this->timeUnit->getDestSuffix( + $this->timeUnit->resolveDestUnit($timeUnit), + $this->timeUnit->resolveMode($outputMode) + ) + ); + + $this->output->write(sprintf("%s\x1B[0J", $output)); // clear rest of the line + } + + private function getXPos(Iteration $iteration): int + { + return self::INDENT + ($iteration->getIndex() % self::NUMBER_COLS) * $this->colWidth + 1; + } + + private function getYPos(Iteration $iteration): float + { + return floor($iteration->getIndex() / self::NUMBER_COLS); + } + + private function resetLinePosition(): void + { + if ($this->currentLine) { + $this->output->write("\x1B[" . $this->currentLine . 'A'); // reset cursor Y pos + } + $this->currentLine = 0; + + $xPos = 0; + $this->output->write("\x1B[" . $xPos . 'G'); + } +} diff --git a/lib/Progress/Logger/DotsLogger.php b/lib/Progress/Logger/DotsLogger.php new file mode 100644 index 000000000..5ae35e1f1 --- /dev/null +++ b/lib/Progress/Logger/DotsLogger.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Progress\Logger; + +use PhpBench\Model\Benchmark; +use PhpBench\Model\Iteration; +use PhpBench\Model\Suite; +use PhpBench\Model\Variant; +use PhpBench\Progress\VariantFormatter; +use PhpBench\Util\TimeUnit; +use Symfony\Component\Console\Output\OutputInterface; + +class DotsLogger extends PhpBenchLogger +{ + /** + * @var bool + */ + private $showBench; + + /** + * @var string + */ + private $buffer; + + /** + * @var bool + */ + private $firstTime = true; + + public function __construct( + OutputInterface $output, + VariantFormatter $formatter, + TimeUnit $timeUnit, + bool $showBench = false + ) { + parent::__construct($output, $formatter, $timeUnit); + $this->showBench = $showBench; + } + + public function benchmarkStart(Benchmark $benchmark): void + { + if ($this->showBench) { + // do not output a line break on the first run + if (false === $this->firstTime) { + $this->output->writeln(''); + } + $this->firstTime = false; + + $this->output->writeln($benchmark->getClass()); + } + } + + public function variantEnd(Variant $variant): void + { + // do not show reject runs + if ($variant->getRejectCount() > 0) { + return; + } + + $dot = '.'; + + if ($variant->getAssertionResults()->hasFailures()) { + $dot = 'F'; + } + + if ($variant->hasErrorStack()) { + $dot = 'E'; + } + + $this->buffer .= $dot; + $this->output->write(sprintf( + "\x0D%s ", + $this->buffer + )); + } + + public function iterationStart(Iteration $iteration): void + { + $state = $iteration->getIndex() % 4; + $states = [ + 0 => '|', + 1 => '/', + 2 => '-', + 3 => '\\', + ]; + + $this->output->write(sprintf( + "\x0D%s%s", + $this->buffer, + $states[$state] + )); + } + + public function endSuite(Suite $suite): void + { + $this->output->write(PHP_EOL . PHP_EOL); + parent::endSuite($suite); + } +} diff --git a/lib/Progress/Logger/HistogramLogger.php b/lib/Progress/Logger/HistogramLogger.php new file mode 100644 index 000000000..2c3887c4c --- /dev/null +++ b/lib/Progress/Logger/HistogramLogger.php @@ -0,0 +1,193 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Progress\Logger; + +use PhpBench\Math\Distribution; +use PhpBench\Math\Statistics; +use PhpBench\Model\Benchmark; +use PhpBench\Model\Iteration; +use PhpBench\Model\Result\ComputedResult; +use PhpBench\Model\Suite; +use PhpBench\Model\Variant; + +class HistogramLogger extends AnsiLogger +{ + private $rows = 1; + private $blocks = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█']; + + /** + * {@inheritdoc} + */ + public function endSuite(Suite $suite): void + { + $this->output->write(PHP_EOL); + parent::endSuite($suite); + } + + /** + * {@inheritdoc} + */ + public function benchmarkStart(Benchmark $benchmark): void + { + $this->output->write(PHP_EOL); + $this->output->write(sprintf('%s', $benchmark->getClass())); + $subjectNames = []; + + foreach ($benchmark->getSubjects() as $subject) { + $subjectNames[] = sprintf('#%s %s', $subject->getIndex(), $subject->getName()); + } + + $this->output->write(sprintf(' (%s)', implode(', ', $subjectNames))); + $this->output->write(PHP_EOL); + $this->output->write("\x1B[2K"); + $this->output->write(PHP_EOL); + } + + /** + * {@inheritdoc} + */ + public function variantStart(Variant $variant): void + { + $this->drawIterations($variant); + $this->output->write("\x1B[1A"); // move cursor up + $this->output->write(PHP_EOL); + $this->renderCollectionStatus($variant); + $this->output->write("\x1B[1A"); // move cursor up + } + + /** + * {@inheritdoc} + */ + public function variantEnd(Variant $variant): void + { + $this->drawIterations($variant); + + if ($variant->hasErrorStack()) { + $this->output->write(' ERROR'); + $this->output->write("\x1B[0J"); // clear the rest of the line + $this->output->write(PHP_EOL); + + return; + } + + if ($variant->getRejectCount() > 0) { + $this->output->write("\x1B[1A"); // move cursor up + $this->output->write("\x1B[0G"); + + return; + } + } + + /** + * {@inheritdoc} + */ + public function iterationStart(Iteration $iteration): void + { + $this->output->write(PHP_EOL); + $this->output->write(PHP_EOL); + $this->output->write(sprintf( + 'it%3d/%-3d (rej %s)', + $iteration->getIndex(), + $iteration->getVariant()->count(), + $iteration->getVariant()->getRejectCount() + )); + $this->output->write("\x1B[2A"); + $this->output->write("\x1B[0G"); + } + + private function drawBlocks($freqs): void + { + $steps = 7; + $resolution = $this->rows * $steps; + $max = max($freqs); + $blocks = []; + + for ($row = 1; $row <= $this->rows; $row++) { + $blocks[$row] = []; + + foreach ($freqs as &$freq) { + if (null === $freq || 0 === $freq) { + $blocks[$row][] = ' '; + + continue; + } + + $scale = 1 / $max * $freq; + $value = $resolution * $scale; + + $lowerLimit = $resolution - ($steps * $row); + $upperLimit = $lowerLimit + $steps; + + if ($value >= $lowerLimit && $value < $upperLimit) { + $blockIndex = $value % $steps; + + $blocks[$row][] = $this->blocks[$blockIndex]; + } elseif ($value < $lowerLimit) { + $blocks[$row][] = ' '; + } else { + $blocks[$row][] = $this->blocks[7]; + } + } + } + + $output = []; + + foreach ($blocks as $blockRow) { + $output[] = implode('', $blockRow); + } + + $output = implode(sprintf( + "\x1B[%sD\x1B[1B", + count($blocks[1]) + ), $output); + + $this->output->write($output); + } + + private function drawIterations(Variant $variant): void + { + $subject = $variant->getSubject(); + $this->output->write("\x1B[2K"); // clear the whole line + $this->output->write(PHP_EOL); + $this->output->write("\x1B[2K"); // clear the whole line + $this->output->write("\x1B[1A"); + + $sigma = 2; + $bins = 16; + + if ($variant->isComputed()) { + $times = $variant->getMetricValues(ComputedResult::class, 'z_value'); + $stats = $variant->getStats(); + $freqs = Statistics::histogram($times, $bins, -$sigma, $sigma); + } else { + $stats = new Distribution([0]); + $freqs = array_fill(0, $bins + 1, null); + } + + $this->output->write(sprintf( + '#%-2d (σ = %s ) -%sσ [', + $subject->getIndex(), + $this->timeUnit->format($stats->getStdev()), + $sigma + )); + $this->drawBlocks($freqs); + + $this->output->write(sprintf( + '] +%sσ %s', + $sigma, + $variant->isComputed() ? $this->formatIterationsShortSummary($variant) : '' + )); + + $this->output->write(PHP_EOL); + } +} diff --git a/lib/Progress/Logger/NullLogger.php b/lib/Progress/Logger/NullLogger.php new file mode 100644 index 000000000..8bd342f02 --- /dev/null +++ b/lib/Progress/Logger/NullLogger.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Progress\Logger; + +use PhpBench\Model\Benchmark; +use PhpBench\Model\Iteration; +use PhpBench\Model\Subject; +use PhpBench\Model\Suite; +use PhpBench\Model\Variant; +use PhpBench\Progress\LoggerInterface; + +class NullLogger implements LoggerInterface +{ + /** + * {@inheritdoc} + */ + public function benchmarkStart(Benchmark $benchmark): void + { + } + + /** + * {@inheritdoc} + */ + public function benchmarkEnd(Benchmark $benchmark): void + { + } + + /** + * {@inheritdoc} + */ + public function subjectStart(Subject $subject): void + { + } + + /** + * {@inheritdoc} + */ + public function subjectEnd(Subject $subject): void + { + } + + /** + * {@inheritdoc} + */ + public function iterationStart(Iteration $iteration): void + { + } + + /** + * {@inheritdoc} + */ + public function iterationEnd(Iteration $iteration): void + { + } + + /** + * {@inheritdoc} + */ + public function variantStart(Variant $variant): void + { + } + + /** + * {@inheritdoc} + */ + public function variantEnd(Variant $variant): void + { + } + + /** + * {@inheritdoc} + */ + public function retryStart(int $rejectionCount): void + { + } + + /** + * {@inheritdoc} + */ + public function startSuite(Suite $suite): void + { + } + + /** + * {@inheritdoc} + */ + public function endSuite(Suite $suite): void + { + } +} diff --git a/lib/Progress/Logger/PhpBenchLogger.php b/lib/Progress/Logger/PhpBenchLogger.php new file mode 100644 index 000000000..2d5678dcc --- /dev/null +++ b/lib/Progress/Logger/PhpBenchLogger.php @@ -0,0 +1,201 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Progress\Logger; + +use PhpBench\Model\Iteration; +use PhpBench\Model\Result\TimeResult; +use PhpBench\Model\Suite; +use PhpBench\Model\Summary; +use PhpBench\Model\Variant; +use PhpBench\PhpBench; +use PhpBench\Progress\VariantFormatter; +use PhpBench\Util\TimeUnit; +use Symfony\Component\Console\Output\OutputInterface; + +abstract class PhpBenchLogger extends NullLogger +{ + /** + * @var TimeUnit + */ + protected $timeUnit; + + /** + * @var OutputInterface + */ + public $output; + + /** + * @var VariantFormatter + */ + private $formatter; + + public function __construct( + OutputInterface $output, + VariantFormatter $formatter, + TimeUnit $timeUnit = null + ) { + $this->timeUnit = $timeUnit; + $this->output = $output; + $this->formatter = $formatter; + } + + public function startSuite(Suite $suite): void + { + $this->output->writeln('PHPBench ' . PhpBench::VERSION . ' running benchmarks...'); + + if ($configPath = $suite->getConfigPath()) { + $this->output->writeln(sprintf('with configuration file: %s', $configPath)); + } + + $summary = $suite->getSummary(); + $this->output->writeln(sprintf( + 'with PHP version %s, xdebug %s, opcache %s', + $summary->getPhpVersion() ?? '', + $summary->getXdebugEnabled() ? '✔' : '❌', + $summary->getOpcacheEnabled() ? '✔' : '❌' + )); + + $this->output->writeln(''); + } + + public function endSuite(Suite $suite): void + { + $summary = $suite->getSummary(); + + $this->listErrors($suite); + $this->listFailures($suite); + + $this->output->writeln((function (Summary $summary, string $message) { + if ($summary->getNbFailures() || $summary->getNbErrors()) { + return sprintf('%s', $message); + } + + if ($summary->getNbAssertions()) { + return sprintf('%s', $message); + } + + return $message; + })($suite->getSummary(), sprintf( + 'Subjects: %s, Assertions: %s, Failures: %s, Errors: %s', + number_format($summary->getNbSubjects()), + number_format($summary->getNbAssertions()), + number_format($summary->getNbFailures()), + number_format($summary->getNbErrors()) + ))); + } + + private function listErrors(Suite $suite): void + { + $errorStacks = $suite->getErrorStacks(); + + if (empty($errorStacks)) { + return; + } + + $this->output->write(PHP_EOL); + $this->output->writeln(sprintf('%d subjects encountered errors:', count($errorStacks))); + $this->output->write(PHP_EOL); + + foreach ($errorStacks as $errorStack) { + $this->output->writeln(sprintf( + '%s::%s', + $errorStack->getVariant()->getSubject()->getBenchmark()->getClass(), + $errorStack->getVariant()->getSubject()->getName() + )); + $this->output->write(PHP_EOL); + + foreach ($errorStack as $error) { + $this->output->writeln(sprintf( + " %s %s\n\n %s\n", + $error->getClass(), + str_replace("\n", "\n ", $error->getMessage()), + str_replace("\n", "\n ", $error->getTrace()) + )); + } + } + } + + private function listFailures(Suite $suite): void + { + $variantFailures = $suite->getFailures(); + + if (empty($variantFailures)) { + return; + } + + $this->output->writeln(sprintf('%d variants failed:', count($variantFailures))); + $this->output->write(PHP_EOL); + + foreach ($variantFailures as $variantFailure) { + $this->output->writeln(sprintf( + ' ✘ %s::%s # %s', + $variantFailure->getVariant()->getSubject()->getBenchmark()->getClass(), + $variantFailure->getVariant()->getSubject()->getName(), + $variantFailure->getVariant()->getParameterSet()->getName() + )); + $this->output->write(PHP_EOL); + + foreach ($variantFailure as $index => $failure) { + $this->output->writeln(sprintf(' %s) %s', $index + 1, str_replace("\n", "\n ", $failure->getMessage()))); + } + $this->output->write(PHP_EOL); + } + } + + public function formatIterationsFullSummary(Variant $variant): string + { + return $this->formatter->formatVariant($variant); + } + + public function formatIterationsShortSummary(Variant $variant): string + { + return $this->formatter->formatVariant($variant); + } + + protected function formatIterationTime(Iteration $iteration): string + { + $subject = $iteration->getVariant()->getSubject(); + $timeUnit = $subject->getOutputTimeUnit(); + $outputMode = $subject->getOutputMode(); + + $time = 0; + + if ($iteration->hasResult(TimeResult::class)) { + $timeResult = $iteration->getResult(TimeResult::class); + assert($timeResult instanceof TimeResult); + $time = $timeResult->getRevTime($iteration->getVariant()->getRevolutions()); + } + + return number_format( + $this->timeUnit->toDestUnit( + $time, + $this->timeUnit->resolveDestUnit($timeUnit), + $this->timeUnit->resolveMode($outputMode) + ), + $this->timeUnit->resolvePrecision($subject->getOutputTimePrecision()) + ); + } + + protected function formatVariantName(Variant $variant): string + { + if (count($variant->getSubject()->getVariants()) > 1) { + return sprintf( + '%s # %s', + $variant->getSubject()->getName(), + $variant->getParameterSet()->getName() + ); + } + + return $variant->getSubject()->getName(); + } +} diff --git a/lib/Progress/Logger/TravisLogger.php b/lib/Progress/Logger/TravisLogger.php new file mode 100644 index 000000000..b5bd89572 --- /dev/null +++ b/lib/Progress/Logger/TravisLogger.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Progress\Logger; + +use PhpBench\Model\Benchmark; +use PhpBench\Model\Suite; +use PhpBench\Model\Variant; + +class TravisLogger extends PhpBenchLogger +{ + private $firstTime = true; + /** + * {@inheritdoc} + */ + public function benchmarkStart(Benchmark $benchmark): void + { + if (false === $this->firstTime) { + $this->output->write(PHP_EOL); + } + $this->firstTime = false; + $this->output->writeln(sprintf('%s', $benchmark->getClass())); + $this->output->write(PHP_EOL); + } + + /** + * {@inheritdoc} + */ + public function variantEnd(Variant $variant): void + { + if ($variant->getRejectCount() > 0) { + return; + } + + $subject = $variant->getSubject(); + + if ($variant->hasErrorStack()) { + $this->output->writeln(sprintf( + ' t%-30s ERROR', + $subject->getName() + )); + + return; + } + + $this->output->writeln(sprintf( + " %-40.39s I%s %s", + $this->formatVariantName($variant), + $variant->count(), + $this->formatIterationsFullSummary($variant) + )); + } + + public function endSuite(Suite $suite): void + { + $this->output->write(PHP_EOL); + parent::endSuite($suite); + } +} diff --git a/lib/Progress/Logger/VerboseLogger.php b/lib/Progress/Logger/VerboseLogger.php new file mode 100644 index 000000000..f3c36f4da --- /dev/null +++ b/lib/Progress/Logger/VerboseLogger.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Progress\Logger; + +use PhpBench\Model\Benchmark; +use PhpBench\Model\Iteration; +use PhpBench\Model\Variant; + +class VerboseLogger extends PhpBenchLogger +{ + /** + * @var int + */ + private $rejectionCount = 0; + + /** + * @var string + */ + private $paramSetIndex; + + /** + * {@inheritdoc} + */ + public function benchmarkStart(Benchmark $benchmark): void + { + $this->output->writeln(sprintf('%s', $benchmark->getClass())); + $this->output->write(PHP_EOL); + } + + /** + * {@inheritdoc} + */ + public function benchmarkEnd(Benchmark $benchmark): void + { + $this->output->write(PHP_EOL); + } + + /** + * {@inheritdoc} + */ + public function iterationStart(Iteration $iteration): void + { + $this->output->write(sprintf( + "\x1B[0G %'.-40.39s%sI%s ", + $this->formatVariantName($iteration->getVariant()), + $this->rejectionCount ? 'R' . $this->rejectionCount . ' ' : '', + $iteration->getIndex() + )); + } + + /** + * {@inheritdoc} + */ + public function variantStart(Variant $variant): void + { + $this->paramSetIndex = $variant->getParameterSet()->getIndex(); + } + + /** + * {@inheritdoc} + */ + public function variantEnd(Variant $variant): void + { + if ($variant->hasErrorStack()) { + $this->output->write(sprintf( + "\x1B[0G %-30sERROR", + $variant->getSubject()->getName() + )); + $this->output->write(PHP_EOL); + + return; + } + + $this->output->write(sprintf( + "%s %s", + $this->resolveAssertionStatus($variant), + $this->formatIterationsFullSummary($variant) + )); + $this->output->write(PHP_EOL); + } + + /** + * {@inheritdoc} + */ + public function retryStart(int $rejectionCount): void + { + $this->rejectionCount = $rejectionCount; + $this->output->write("\x1B[1F\x1B[0K"); + } + + private function resolveAssertionStatus(Variant $variant): string + { + $results = $variant->getAssertionResults(); + + if (!$results->count()) { + return '-'; + } + + return $results->hasFailures() ? '✘' : '✔'; + } +} diff --git a/lib/Progress/LoggerInterface.php b/lib/Progress/LoggerInterface.php new file mode 100644 index 000000000..cca901118 --- /dev/null +++ b/lib/Progress/LoggerInterface.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Progress; + +use PhpBench\Model\Benchmark; +use PhpBench\Model\Iteration; +use PhpBench\Model\Subject; +use PhpBench\Model\Suite; +use PhpBench\Model\Variant; + +interface LoggerInterface +{ + /** + * Log the end of a benchmark. + * + */ + public function benchmarkEnd(Benchmark $benchmark); + + /** + * Log the start of a benchmark. + * + */ + public function benchmarkStart(Benchmark $benchmark); + + /** + * Log the end of a benchmarking subject. + * + */ + public function subjectEnd(Subject $subject); + + /** + * Log the end of a benchmarking subject. + * + */ + public function subjectStart(Subject $subject); + + /** + * Log the end of an an iteration run. + * + * Errors should be checked using Variant->hasException() + * + */ + public function variantEnd(Variant $variant); + + /** + * Log the start of an iteration run. + * + */ + public function variantStart(Variant $variant); + + /** + * Log the end of an iteration. + * + */ + public function iterationEnd(Iteration $iteration); + + /** + * Log the start of an iteration. + * + */ + public function iterationStart(Iteration $iteration); + + /** + * Log the number of retries to be made. + * + */ + public function retryStart(int $rejectionCount); + + /** + * Called at the start of the suite run. + * + */ + public function startSuite(Suite $suite); + + /** + * Called at the end of the suite run. + * + */ + public function endSuite(Suite $suite); +} diff --git a/lib/Progress/LoggerRegistry.php b/lib/Progress/LoggerRegistry.php new file mode 100644 index 000000000..d9d5f9a84 --- /dev/null +++ b/lib/Progress/LoggerRegistry.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Progress; + +class LoggerRegistry +{ + private $progressLoggers; + + public function addProgressLogger($name, LoggerInterface $progressLogger): void + { + $this->progressLoggers[$name] = $progressLogger; + } + + public function getProgressLogger($name) + { + if (!isset($this->progressLoggers[$name])) { + throw new \InvalidArgumentException(sprintf( + 'No progress logger with name "%s" has been registered, known progress loggers: "%s"', + $name, implode('", "', array_keys($this->progressLoggers)) + )); + } + + return $this->progressLoggers[$name]; + } +} diff --git a/lib/Progress/VariantFormatter.php b/lib/Progress/VariantFormatter.php new file mode 100644 index 000000000..a0264030a --- /dev/null +++ b/lib/Progress/VariantFormatter.php @@ -0,0 +1,10 @@ +format = $format; + $this->baselineFormat = $baselineFormat; + $this->parser = $parser; + $this->printer = $printer; + $this->paramProvider = $paramProvider; + } + + public function formatVariant(Variant $variant): string + { + $data = $this->paramProvider->provideFor($variant); + $subjectFormat = $variant->getSubject()->getFormat(); + + $node = $variant->getBaseline() ? + $this->parser->parse($subjectFormat ?? $this->baselineFormat) : + $this->parser->parse($subjectFormat ?? $this->format); + + return $this->printer->withParams($data)->print($node); + } +} diff --git a/lib/Reflection/ReflectionAttribute.php b/lib/Reflection/ReflectionAttribute.php new file mode 100644 index 000000000..e9312e6ab --- /dev/null +++ b/lib/Reflection/ReflectionAttribute.php @@ -0,0 +1,39 @@ +name = $name; + $this->args = $args; + } + + /** + * @return ?object + */ + public function instantiate() + { + return (function (string $name) { + if (!class_exists($name)) { + return null; + } + + return new $name(...$this->args); + })($this->name); + } +} diff --git a/lib/Reflection/ReflectionClass.php b/lib/Reflection/ReflectionClass.php new file mode 100644 index 000000000..e8538504f --- /dev/null +++ b/lib/Reflection/ReflectionClass.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Reflection; + +class ReflectionClass +{ + public $attributes = []; + public $path; + public $interfaces = []; + public $class; + public $namespace; + public $abstract = false; + public $comment; + public $methods = []; + + public function __construct(?string $path = null, ?string $class = null) + { + $this->path = $path; + $this->class = $class; + } +} diff --git a/lib/Reflection/ReflectionHierarchy.php b/lib/Reflection/ReflectionHierarchy.php new file mode 100644 index 000000000..b0f0806f8 --- /dev/null +++ b/lib/Reflection/ReflectionHierarchy.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Reflection; + +use IteratorAggregate; + +/** + * Contains a reflected class (the "top" class) and all it's ancestors. + * + * @implements IteratorAggregate + */ +class ReflectionHierarchy implements IteratorAggregate +{ + /** + * @var ReflectionClass[] + */ + private $reflectionClasses; + + /** + * @param ReflectionClass[] $reflectionClasses + */ + public function __construct(array $reflectionClasses = []) + { + $this->reflectionClasses = $reflectionClasses; + } + + /** + * Add a reflection class. + * + */ + public function addReflectionClass(ReflectionClass $reflectionClass): void + { + $this->reflectionClasses[] = $reflectionClass; + } + + public function getIterator() + { + return new \ArrayObject($this->reflectionClasses); + } + + /** + * Return the "top" class. + * + * @throws \InvalidArgumentException + */ + public function getTop(): ReflectionClass + { + if (!isset($this->reflectionClasses[0])) { + throw new \InvalidArgumentException( + 'Cannot get top reflection class, reflection hierarchy is empty.' + ); + } + + return $this->reflectionClasses[0]; + } + + /** + * Return true if the class hierarchy contains the named method. + * + */ + public function hasMethod(string $name): bool + { + foreach ($this->reflectionClasses as $reflectionClass) { + if (isset($reflectionClass->methods[$name])) { + return true; + } + } + + return false; + } + + /** + * Return true if the class hierarchy contains the named static method. + * + */ + public function hasStaticMethod(string $name): bool + { + foreach ($this->reflectionClasses as $reflectionClass) { + if (isset($reflectionClass->methods[$name])) { + $method = $reflectionClass->methods[$name]; + + if ($method->isStatic) { + return true; + } + + break; + } + } + + return false; + } + + /** + * Return true if there are no reflection classes here. + */ + public function isEmpty(): bool + { + return 0 === count($this->reflectionClasses); + } +} diff --git a/lib/Reflection/ReflectionMethod.php b/lib/Reflection/ReflectionMethod.php new file mode 100644 index 000000000..e157306b2 --- /dev/null +++ b/lib/Reflection/ReflectionMethod.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Reflection; + +class ReflectionMethod +{ + /** + * @var string + */ + public $class; + + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $comment; + + /** + * @var bool + */ + public $isStatic; + + /** + * @var ReflectionClass + */ + public $reflectionClass; + + public $attributes = []; +} diff --git a/lib/Reflection/ReflectorInterface.php b/lib/Reflection/ReflectorInterface.php new file mode 100644 index 000000000..94802d134 --- /dev/null +++ b/lib/Reflection/ReflectorInterface.php @@ -0,0 +1,23 @@ + $paramProviders + * + * @return array> + */ + public function getParameterSets(string $file, array $paramProviders): array; +} diff --git a/lib/Reflection/RemoteReflector.php b/lib/Reflection/RemoteReflector.php new file mode 100644 index 000000000..ef998df2f --- /dev/null +++ b/lib/Reflection/RemoteReflector.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Reflection; + +use function array_filter; +use PhpBench\Remote\Launcher; + +/** + * Reflector for remote classes. + */ +class RemoteReflector implements ReflectorInterface +{ + /** + * @var Launcher + */ + private $launcher; + + /** + */ + public function __construct( + Launcher $launcher + ) { + $this->launcher = $launcher; + } + + /** + * Return an array of ReflectionClass instances for the given file. The + * first ReflectionClass is the class contained in the given file (there + * may be only one) additional ReflectionClass instances are the ancestors + * of this first class. + * + */ + public function reflect(string $file): ReflectionHierarchy + { + $classFqn = $this->getClassNameFromFile($file); + $hierarchy = new ReflectionHierarchy(); + + if (null === $classFqn) { + return $hierarchy; + } + + $classHierarchy = $this->launcher->payload(__DIR__ . '/template/reflector.template', [ + 'file' => $file, + 'class' => $classFqn, + ])->launch(); + + foreach ($classHierarchy as $classInfo) { + $reflectionClass = new ReflectionClass(); + $reflectionClass->class = $classInfo['class']; + $reflectionClass->abstract = $classInfo['abstract']; + $reflectionClass->comment = $classInfo['comment']; + $reflectionClass->interfaces = $classInfo['interfaces']; + $reflectionClass->path = $file; + $reflectionClass->namespace = $classInfo['namespace']; + $reflectionClass->attributes = $this->resolveAttributes($classInfo['attributes']); + + foreach ($classInfo['methods'] as $methodInfo) { + $reflectionMethod = new ReflectionMethod(); + $reflectionMethod->reflectionClass = $reflectionClass; + $reflectionMethod->class = $classInfo['class']; + $reflectionMethod->name = $methodInfo['name']; + $reflectionMethod->isStatic = $methodInfo['static']; + $reflectionMethod->comment = $methodInfo['comment']; + $attributes = $methodInfo['attributes']; + $reflectionMethod->attributes = $this->resolveAttributes($attributes); + $reflectionClass->methods[$reflectionMethod->name] = $reflectionMethod; + } + $hierarchy->addReflectionClass($reflectionClass); + } + + return $hierarchy; + } + + /** + * Return the parameter sets for the benchmark container in the given file. + * + * @param string[] $paramProviders + */ + public function getParameterSets(string $file, array $paramProviders): array + { + $parameterSets = $this->launcher->payload(__DIR__ . '/template/parameter_set_extractor.template', [ + 'file' => $file, + 'class' => $this->getClassNameFromFile($file), + 'paramProviders' => var_export($paramProviders, true), + ])->launch(); + + // validate parameters + $parameters = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($parameterSets)); + iterator_apply($parameters, function (\Iterator $iterator): void { + $parameter = $iterator->current(); + + if (!is_scalar($parameter) && isset($parameter)) { + throw new \InvalidArgumentException(sprintf( + 'Parameter values must be scalar. Got "%s"', + is_object($parameter) ? get_class($parameter) : gettype($parameter) + )); + } + }, [$parameters]); + + return $parameterSets; + } + + /** + * Return the class name from a file. + * + * Taken from http://stackoverflow.com/questions/7153000/get-class-name-from-file + */ + private function getClassNameFromFile(string $file): ?string + { + $fp = fopen($file, 'r'); + + $class = $namespace = $buffer = ''; + $i = 0; + + while (!$class) { + if (feof($fp)) { + break; + } + + // Read entire lines to prevent keyword truncation + for ($line = 0; $line <= 20; $line++) { + $buffer .= fgets($fp); + } + $tokens = @\token_get_all($buffer); + + if (strpos($buffer, '{') === false) { + continue; + } + + for (; $i < count($tokens); $i++) { + if ($tokens[$i][0] === T_NAMESPACE) { + for ($j = $i + 1; $j < count($tokens); $j++) { + $tokenId = $tokens[$j][0]; + + if ($tokenId === T_STRING || $tokenId === 314) { + $namespace .= '\\' . $tokens[$j][1]; + } elseif ($tokens[$j] === '{' || $tokens[$j] === ';') { + break; + } + } + } + + if ($tokens[$i][0] === T_CLASS) { + for ($j = $i + 1; $j < count($tokens); $j++) { + if ($tokens[$j][0] === T_STRING) { + $class = $tokens[$i + 2][1]; + + break 2; + } + } + } + } + } + + if (!trim($class)) { + return null; + } + + return $namespace . '\\' . $class; + } + + /** + * @param array $attributes + * + * @return object[] + */ + private function resolveAttributes(array $attributes): array + { + return array_filter(array_map(function (array $attr) { + return ( + new ReflectionAttribute($attr['name'], $attr['args']) + )->instantiate() ?: false; + }, $attributes)); + } +} diff --git a/lib/Reflection/template/parameter_set_extractor.template b/lib/Reflection/template/parameter_set_extractor.template new file mode 100644 index 000000000..0868de2f0 --- /dev/null +++ b/lib/Reflection/template/parameter_set_extractor.template @@ -0,0 +1,54 @@ +$paramProvider()); + continue; + } + + if (is_callable($paramProvider)) { + $parameterSets[] = normalize_parameter_set($paramProvider()); + continue; + } + + throw new \RuntimeException(sprintf( + 'Class has no method "%s" and it is not a callable', $paramProvider + )); +} + +ob_end_clean(); + +echo serialize($parameterSets); +exit(0); diff --git a/lib/Reflection/template/reflector.template b/lib/Reflection/template/reflector.template new file mode 100644 index 000000000..6f7be4cc5 --- /dev/null +++ b/lib/Reflection/template/reflector.template @@ -0,0 +1,85 @@ + $class, + 'namespace' => $refl->getNamespaceName(), + 'abstract' => $refl->isAbstract(), + 'methods' => array(), + 'interfaces' => array(), + 'comment' => $refl->getDocComment() + ); + + foreach ($refl->getMethods() as $methodRefl) { + $comment = $methodRefl->getDocComment(); + $metadata['methods'][$methodRefl->getName()] = array( + 'class' => $class, + 'name' => $methodRefl->getName(), + 'comment' => $comment, + 'static' => $methodRefl->isStatic(), + 'attributes' => resolve_method_attributes($methodRefl), + ); + } + + foreach ($refl->getInterfaces() as $interfaceRefl) { + $metadata['interfaces'][] = $interfaceRefl->getName(); + } + + $metadata['attributes'] = resolve_class_attributes($refl); + + $metadatas[] = $metadata; +} + +function resolve_class_attributes(ReflectionClass $reflection): array +{ + if (PHP_VERSION_ID < 80000) { + return []; + } + + return array_map(function (ReflectionAttribute $attribute) { + return [ + 'name' => $attribute->getName(), + 'args' => $attribute->getArguments(), + ]; + }, $reflection->getAttributes()); +} + +function resolve_method_attributes(ReflectionMethod $reflection): array +{ + if (PHP_VERSION_ID < 80000) { + return []; + } + + return array_map(function (ReflectionAttribute $attribute) { + return [ + 'name' => $attribute->getName(), + 'args' => $attribute->getArguments(), + ]; + }, $reflection->getAttributes()); +} + +// in the future we should return also the parent classes +echo serialize($metadatas); +exit(0); diff --git a/lib/Registry/Config.php b/lib/Registry/Config.php new file mode 100644 index 000000000..1edbae601 --- /dev/null +++ b/lib/Registry/Config.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Registry; + +use ArrayObject; + +/** + * Configuration storage. + * Throws exceptions when accessing undefined offsets. + * + * @extends ArrayObject + */ +class Config extends ArrayObject +{ + /** + * All names must satisfy this regex. + */ + const NAME_REGEX = '{^[0-9a-zA-Z_-]+$}'; + + private $name; + + public function __construct($name, array $config) + { + if (!preg_match(self::NAME_REGEX, $name)) { + throw new \InvalidArgumentException(sprintf( + 'Configuration names may only contain alpha-numeric characters, _ and -. Got "%s"', + $name + )); + } + $this->name = $name; + parent::__construct($config); + } + + public function offsetGet($offset) + { + if (!$this->offsetExists($offset)) { + throw new \InvalidArgumentException(sprintf( + 'Configuration offset "%s" does not exist. Known offsets: "%s"', + $offset, + implode('", "', array_keys($this->getArrayCopy())) + )); + } + + return parent::offsetGet($offset); + } + + public function getName() + { + return $this->name; + } +} diff --git a/lib/Registry/ConfigurableRegistry.php b/lib/Registry/ConfigurableRegistry.php new file mode 100644 index 000000000..5a4867869 --- /dev/null +++ b/lib/Registry/ConfigurableRegistry.php @@ -0,0 +1,193 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Registry; + +use PhpBench\DependencyInjection\Container; +use PhpBench\Json\JsonDecoder; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * Registry that adds configuration capabilities to the service + * registry class. + * + * TODO: Coupling configuration to the registry is convenient but the two are + * effectively unrelated and could be decoupled. + * + * ``` + * $config = $reg->getConfig('foobar'); + * ``` + * + * @template T of object + * @extends Registry + */ +class ConfigurableRegistry extends Registry +{ + private $configs = []; + private $jsonDecoder; + + /** + * @var array + */ + private $resolvedConfigs; + + public function __construct( + $serviceType, + Container $container, + JsonDecoder $jsonDecoder + ) { + parent::__construct($serviceType, $container); + $this->jsonDecoder = $jsonDecoder; + } + + /** + * Return the named configuration. + * + * @param string|array $name + */ + public function getConfig($name): Config + { + if (is_array($name)) { + $config = $name; + $name = uniqid(); + $this->setConfig($name, $config); + } + + $name = trim($name); + $name = $this->processRawCliConfig($name); + + if (!isset($this->configs[$name]) && $this->hasService($name)) { + $this->setConfig($name, [ + $this->serviceType => $name, + ]); + } + + if (!isset($this->configs[$name])) { + throw new \InvalidArgumentException(sprintf( + 'No %s configuration or service named "%s" exists. Known configurations: "%s", known services: "%s"', + $this->serviceType, + $name, + implode('", "', array_keys($this->configs)), + implode('", "', array_keys($this->services)) + )); + } + + if (!isset($this->resolvedConfigs[$name])) { + $this->resolveConfig($name); + } + + return $this->resolvedConfigs[$name]; + } + + /** + * @return string[] + */ + public function getConfigNames(): array + { + return array_keys($this->configs); + } + + /** + * Set a named configuration. + * + * Note that all configurations must be associated with a named service + * via a configuration key equal to the configuration service type of this registry. + * + */ + public function setConfig(string $name, array $config): void + { + if (isset($this->configs[$name])) { + throw new \InvalidArgumentException(sprintf( + '%s config "%s" already exists.', + $this->serviceType, + $name + )); + } + + $this->configs[$name] = $config; + } + + /** + * Recursively merge configs (having the "extends" key) which extend + * another report. + * + */ + private function resolveConfig(string $name): void + { + $config = $this->configs[$name]; + + if (isset($config['extends'])) { + $extended = $this->getConfig($config['extends']); + + if (isset($config[$this->serviceType]) && ($extended[$this->serviceType] != $config[$this->serviceType])) { + throw new \InvalidArgumentException(sprintf( + '%s configuration for service "%s" cannot extend configuration for different service "%s"', + $this->serviceType, + $config[$this->serviceType], + $extended[$this->serviceType] + )); + } + + unset($config['extends']); + $config = array_merge( + $extended->getArrayCopy(), + $config + ); + } + + if (!isset($config[$this->serviceType])) { + throw new \InvalidArgumentException(sprintf( + '%s configuration must EITHER indicate its target %s service with the "%s" key or extend an existing configuration with the "extends" key.', + $this->serviceType, + $this->serviceType, + $this->serviceType + )); + } + + /** @var RegistrableInterface $service */ + $service = $this->getService($config[$this->serviceType]); + + $options = new OptionsResolver(); + $options->setRequired([$this->serviceType]); + $service->configure($options); + $config = $options->resolve($config); + + $this->resolvedConfigs[$name] = new Config($name, $config); + } + + /** + * Process raw configuration as received from the CLI, for example:. + * + * ```` + * {"generator": "table", "sort": ["time"]} + * ```` + * + * Or simply the name of a pre-configured configuration to use: + * + * ```` + * table + * ```` + * + */ + private function processRawCliConfig(string $rawConfig): string + { + if (preg_match(Config::NAME_REGEX, $rawConfig)) { + return $rawConfig; + } + + $config = $this->jsonDecoder->decode($rawConfig); + $configName = uniqid(); + $this->setConfig($configName, $config); + + return $configName; + } +} diff --git a/lib/Registry/RegistrableInterface.php b/lib/Registry/RegistrableInterface.php new file mode 100644 index 000000000..5386a8314 --- /dev/null +++ b/lib/Registry/RegistrableInterface.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Registry; + +use Symfony\Component\OptionsResolver\OptionsResolver; + +interface RegistrableInterface +{ + /*** + * Configure options. + * + * @return OptionsResolver $options + */ + public function configure(OptionsResolver $options): void; +} diff --git a/lib/Registry/Registry.php b/lib/Registry/Registry.php new file mode 100644 index 000000000..743649147 --- /dev/null +++ b/lib/Registry/Registry.php @@ -0,0 +1,151 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Registry; + +use Psr\Container\ContainerInterface; + +/** + * Service and configuration registry. + * + * Lazily instantiates tagged services which are associated with a name + * and stores configurations which are relevant to these services. + * + * ``` + * $reg->getService($config['renderer']); + * $reg->render($something, $config); + * ``` + * + * @template T of object + */ +class Registry +{ + protected $serviceType; + + /** + * @var array + */ + protected $services = []; + + /** + * @var array + */ + private $serviceMap = []; + + /** + * @var ContainerInterface + */ + private $container; + + /** + * @var string + */ + private $defaultService; + + public function __construct( + $serviceType, + ContainerInterface $container, + $defaultService = null + ) { + $this->serviceType = $serviceType; + $this->container = $container; + $this->defaultService = $defaultService; + } + + /** + * Register a service ID with against the given name. + * + */ + public function registerService(string $name, string $serviceId): void + { + if (isset($this->serviceMap[$name])) { + throw new \InvalidArgumentException(sprintf( + '%s service "%s" is already registered', + $this->serviceType, $name + )); + } + + $this->serviceMap[$name] = $serviceId; + $this->services[$name] = null; + } + + /** + * Directly set a named service. + * + * @param T $object + */ + public function setService(string $name, object $object): void + { + if (isset($this->services[$name])) { + throw new \InvalidArgumentException(sprintf( + '%s service "%s" already exists.', + $this->serviceType, + $name + )); + } + + $this->services[$name] = $object; + } + + /** + * Return the named service, lazily creating it from the container + * if it has not yet been accessed. + * + * @param string $name + * + * @return T + */ + public function getService(string $name = null): object + { + $name = $name ?: $this->defaultService; + + if (!$name) { + throw new \RuntimeException(sprintf( + 'You must configure a default %s service, registered %s services: "%s"', + $this->serviceType, $this->serviceType, + implode('", "', array_keys($this->services)) + )); + } + + if (isset($this->services[$name])) { + return $this->services[$name]; + } + + $this->assertServiceExists($name); + $this->services[$name] = $this->container->get($this->serviceMap[$name]); + + return $this->services[$name]; + } + + public function hasService(string $name): bool + { + return array_key_exists($name, $this->services); + } + + public function getServiceNames(): array + { + return array_keys($this->services); + } + + private function assertServiceExists(string $name): void + { + if (!array_key_exists($name, $this->services)) { + throw new \InvalidArgumentException(sprintf( + '%s service "%s" does not exist. Registered %s services: "%s"', + $this->serviceType, + $name, + $this->serviceType, + implode('", "', array_keys($this->services)) + )); + } + } +} diff --git a/lib/Remote/Exception/ScriptErrorException.php b/lib/Remote/Exception/ScriptErrorException.php new file mode 100644 index 000000000..be2cd6280 --- /dev/null +++ b/lib/Remote/Exception/ScriptErrorException.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Remote\Exception; + +/** + * Thrown in the case of an error in the remote script. + */ +class ScriptErrorException extends \RuntimeException +{ +} diff --git a/lib/Remote/IniStringBuilder.php b/lib/Remote/IniStringBuilder.php new file mode 100644 index 000000000..8fef22f6e --- /dev/null +++ b/lib/Remote/IniStringBuilder.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Remote; + +class IniStringBuilder +{ + public function build(array $config): string + { + $string = []; + + foreach ($config as $key => $values) { + $values = (array) $values; + + foreach ($values as $value) { + $string[] = sprintf('-d%s=%s', $key, $value); + } + } + + return implode(' ', $string); + } +} diff --git a/lib/Remote/Launcher.php b/lib/Remote/Launcher.php new file mode 100644 index 000000000..b17fc2b20 --- /dev/null +++ b/lib/Remote/Launcher.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Remote; + +use Symfony\Component\Process\ExecutableFinder; +use Symfony\Component\Process\PhpExecutableFinder; + +/** + * Build and execute tokenized scripts in separate processes. + * The scripts should return a JSON encoded string. + */ +class Launcher +{ + /** + * @var string + */ + private $bootstrap; + + /** + * @var PayloadFactory + */ + private $payloadFactory; + + /** + * @var string + */ + private $phpBinary; + + /** + * @var array + */ + private $phpConfig; + + /** + * @var string + */ + private $phpWrapper; + + /** + * @var PayloadFactory + */ + private $factory; + + /** + * @var ExecutableFinder + */ + private $finder; + + /** + * @var bool + */ + private $phpDisableIni; + + public function __construct( + PayloadFactory $factory = null, + ExecutableFinder $finder = null, + ?string $bootstrap = null, + ?string $phpBinary = null, + array $phpConfig = [], + ?string $phpWrapper = null, + bool $phpDisableIni = false + ) { + $this->bootstrap = $bootstrap; + $this->payloadFactory = $factory ?: new PayloadFactory(); + $this->phpBinary = $phpBinary; + $this->phpConfig = $phpConfig; + $this->phpWrapper = $phpWrapper; + $this->finder = $finder ?: new ExecutableFinder(); + $this->factory = $factory; + $this->phpDisableIni = $phpDisableIni; + } + + public function payload($template, array $tokens = [], ?float $timeout = null): Payload + { + $tokens['bootstrap'] = ''; + + if (null !== $this->bootstrap) { + if (!file_exists($this->bootstrap)) { + throw new \InvalidArgumentException(sprintf( + 'Bootstrap file "%s" does not exist.', + $this->bootstrap + )); + } + $tokens['bootstrap'] = $this->bootstrap; + } + + $phpBinary = $this->resolvePhpBinary(); + + $payload = $this->payloadFactory->create($template, $tokens, $phpBinary, $timeout); + + if ($this->phpWrapper) { + $payload->setWrapper($this->phpWrapper); + } + + if ($this->phpConfig) { + $payload->mergePhpConfig($this->phpConfig); + } + + if (true === $this->phpDisableIni) { + $payload->disableIni(); + } + + return $payload; + } + + private function resolvePhpBinary() + { + // if no php binary, use the PhpExecutableFinder (generally will + // resolve to PHP_BINARY) + if (!$this->phpBinary) { + $finder = new PhpExecutableFinder(); + + return $finder->find(); + } + + // if the php binary is absolute, fine. + if (substr($this->phpBinary, 0, 1) === '/') { + return $this->phpBinary; + } + + // otherwise try and find it in PATH etc. + /** @var string|null $phpBinary */ + $phpBinary = $this->finder->find($this->phpBinary); + + if (null === $phpBinary) { + throw new \InvalidArgumentException(sprintf( + 'Could not find PHP binary "%s"', $this->phpBinary + )); + } + + return $phpBinary; + } +} diff --git a/lib/Remote/Payload.php b/lib/Remote/Payload.php new file mode 100644 index 000000000..d2d3d7071 --- /dev/null +++ b/lib/Remote/Payload.php @@ -0,0 +1,273 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Remote; + +use PhpBench\Remote\Exception\ScriptErrorException; +use RuntimeException; +use Symfony\Component\Process\Process; + +/** + * Class representing the context from which a script can be generated and executed by a PHP binary. + */ +class Payload +{ + const FLAG_DISABLE_INI = '-n'; + + /** + * Path to script template. + */ + private $template; + + /** + * Wrapper for PHP binary, e.g. "blackfire". + */ + private $wrapper; + + /** + * Associative array of PHP INI settings. + */ + private $phpConfig = []; + + /** + * Path to PHP binary. + */ + private $phpPath; + + /** + * Tokens to substitute in the script template. + */ + private $tokens = []; + + /** + * Symfony Process instance. + */ + private $process; + + /** + * @var bool + */ + private $disableIni = false; + + /** + * @var IniStringBuilder + */ + private $iniStringBuilder; + + /** + * @var ProcessFactory + */ + private $processFactory; + + /** + * @var float + */ + private $timeout; + + /** + * @var string + */ + private $scriptPath; + + /** + * @var bool + */ + private $scriptRemove; + + /** + * Create a new Payload object with the given script template. + * The template must be the path to a script template. + * + * @param array $tokens + */ + public function __construct( + string $template, + array $tokens = [], + ?string $phpPath = PHP_BINARY, + ?float $timeout = null, + ProcessFactory $processFactory = null, + string $scriptPath = null, + bool $scriptRemove = false + ) { + $this->setPhpPath($phpPath); + $this->template = $template; + $this->tokens = $tokens; + $this->processFactory = $processFactory ?: new ProcessFactory(); + $this->iniStringBuilder = new IniStringBuilder(); + $this->timeout = $timeout; + $this->phpPath = $phpPath; + $this->scriptPath = $scriptPath; + $this->scriptRemove = $scriptRemove; + } + + public function setWrapper($wrapper): void + { + $this->wrapper = $wrapper; + } + + public function mergePhpConfig(array $phpConfig): void + { + $this->phpConfig = array_merge( + $this->phpConfig, + $phpConfig + ); + } + + public function setPhpPath($phpPath): void + { + $this->phpPath = escapeshellarg($phpPath); + } + + public function disableIni(): void + { + $this->disableIni = true; + } + + public function launch(): array + { + $script = $this->readFile(); + $script = $this->replaceTokens($script); + $scriptPath = $this->writeTempFile($script); + $commandLine = $this->buildCommandLine($scriptPath); + + $process = $this->processFactory->create($commandLine, $this->timeout); + $process->run(); + + $this->removeTmpFile($scriptPath); + + if (false === $process->isSuccessful()) { + throw new ScriptErrorException(sprintf( + '%s%s', + $process->getErrorOutput(), + $process->getOutput() + )); + } + + return $this->decodeResults($process); + } + + private function getIniString(): string + { + if (empty($this->phpConfig)) { + return ''; + } + + return $this->iniStringBuilder->build($this->phpConfig); + } + + private function replaceTokens(string $templateBody): string + { + $tokenSubs = []; + + foreach ($this->tokens as $key => $value) { + $tokenSubs['{{ ' . $key . ' }}'] = $value; + } + + return str_replace( + array_keys($tokenSubs), + array_values($tokenSubs), + $templateBody + ); + } + + private function readFile(): string + { + if (!file_exists($this->template)) { + throw new \RuntimeException(sprintf( + 'Could not find script template "%s"', + $this->template + )); + } + + return file_get_contents($this->template); + } + + private function writeTempFile(string $script): string + { + $scriptPath = $this->scriptPath ? + $this->scriptPath . '/' . basename($this->template) : + tempnam(sys_get_temp_dir(), 'PhpBench'); + + if (false === $scriptPath) { + throw new RuntimeException( + 'Could not generate temporary script name' + ); + } + + (function (string $directory): void { + if (file_exists($directory)) { + return; + } + + if (@mkdir($directory, 0744)) { + return; + } + + throw new RuntimeException(sprintf( + 'Could not create directory "%s"', $directory + )); + })(dirname($scriptPath)); + + file_put_contents($scriptPath, $script); + + return $scriptPath; + } + + private function buildCommandLine(string $scriptPath): string + { + $arguments = []; + + if ($this->wrapper) { + $arguments[] = $this->wrapper; + } + + $arguments[] = $this->phpPath; + + if (true === $this->disableIni) { + $arguments[] = self::FLAG_DISABLE_INI; + } + + $arguments[] = $this->getIniString(); + $arguments[] = escapeshellarg($scriptPath); + + return implode(' ', $arguments); + } + + private function removeTmpFile(string $scriptPath): void + { + if (!$this->scriptRemove) { + return; + } + + unlink($scriptPath); + } + + /** + * @return array + */ + private function decodeResults(Process $process): array + { + $output = $process->getOutput(); + + $result = @unserialize($output); + + if (is_array($result)) { + return $result; + } + + throw new \RuntimeException(sprintf( + 'Script "%s" did not return an array, got: %s', + $this->template, + $output + )); + } +} diff --git a/lib/Remote/PayloadFactory.php b/lib/Remote/PayloadFactory.php new file mode 100644 index 000000000..382d08f84 --- /dev/null +++ b/lib/Remote/PayloadFactory.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Remote; + +class PayloadFactory +{ + /** + * @var string|null + */ + private $remoteScriptPath; + + /** + * @var bool + */ + private $remoteScriptRemove; + + /** + * @var ProcessFactory + */ + private $processFactory; + + public function __construct(?ProcessFactory $processFactory = null, ?string $remoteScriptPath = null, bool $remoteScriptRemove = false) + { + $this->remoteScriptPath = $remoteScriptPath; + $this->remoteScriptRemove = $remoteScriptRemove; + $this->processFactory = $processFactory ?: new ProcessFactory(); + } + + /** + * @param array $tokens + */ + public function create(string $template, array $tokens = [], ?string $phpBinary = null, ?float $timeout = null): Payload + { + return new Payload( + $template, + $tokens, + $phpBinary, + $timeout, + $this->processFactory, + $this->remoteScriptPath, + $this->remoteScriptRemove + ); + } +} diff --git a/lib/Remote/ProcessFactory.php b/lib/Remote/ProcessFactory.php new file mode 100644 index 000000000..4f3d7247a --- /dev/null +++ b/lib/Remote/ProcessFactory.php @@ -0,0 +1,28 @@ +logger = $logger ?: new NullLogger(); + } + + public function create(string $commandLine, ?float $timeout = null): Process + { + $this->logger->debug(sprintf('Spawning process: %s', $commandLine)); + + return Process::fromShellCommandline($commandLine) + ->setTimeout($timeout); + } +} diff --git a/lib/Report/Generator/BareGenerator.php b/lib/Report/Generator/BareGenerator.php new file mode 100644 index 000000000..154e96be4 --- /dev/null +++ b/lib/Report/Generator/BareGenerator.php @@ -0,0 +1,71 @@ +transformer = $transformer; + } + + /** + * {@inheritDoc} + */ + public function configure(OptionsResolver $options): void + { + $options->setDefaults([ + self::PARAM_VERTICAL => false, + ]); + $options->setAllowedTypes(self::PARAM_VERTICAL, ['bool']); + } + + /** + * {@inheritDoc} + */ + public function generate(SuiteCollection $collection, Config $config): Reports + { + if ($config[self::PARAM_VERTICAL]) { + return Reports::fromReport( + Report::fromTables(array_map(function (array $table) { + return Table::fromRowArray(array_map(function ($key, $value) { + return [ + 'field' => PhpValueFactory::fromValue($key), + 'value' => PhpValueFactory::fromValue($value), + ]; + }, array_keys($table), array_values($table))); + }, $this->transformer->suiteToTable($collection))) + ); + } + + return Reports::fromReport( + Report::fromTable( + Table::fromRowArray( + array_map(function (array $row) { + return array_map(function ($value) { + return PhpValueFactory::fromValue($value); + }, $row); + }, $this->transformer->suiteToTable($collection)) + ) + ) + ); + } +} diff --git a/lib/Report/Generator/CompositeGenerator.php b/lib/Report/Generator/CompositeGenerator.php new file mode 100644 index 000000000..fc00fc6eb --- /dev/null +++ b/lib/Report/Generator/CompositeGenerator.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Report\Generator; + +use PhpBench\Model\SuiteCollection; +use PhpBench\Registry\Config; +use PhpBench\Report\GeneratorInterface; +use PhpBench\Report\Model\Reports; +use PhpBench\Report\ReportManager; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * Report generator which is a composite of other named reports. + */ +class CompositeGenerator implements GeneratorInterface +{ + /** + * @var ReportManager + */ + private $reportManager; + + /** + */ + public function __construct(ReportManager $reportManager) + { + $this->reportManager = $reportManager; + } + + /** + * {@inheritdoc} + */ + public function configure(OptionsResolver $options): void + { + $options->setRequired(['reports']); + $options->setAllowedTypes('reports', 'array'); + } + + /** + * {@inheritdoc} + */ + public function generate(SuiteCollection $collection, Config $config): Reports + { + return $this->reportManager->generateReports($collection, $config['reports']); + } +} diff --git a/lib/Report/Generator/EnvGenerator.php b/lib/Report/Generator/EnvGenerator.php new file mode 100644 index 000000000..7610253d3 --- /dev/null +++ b/lib/Report/Generator/EnvGenerator.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Report\Generator; + +use PhpBench\Expression\Ast\PhpValueFactory; +use PhpBench\Expression\Ast\StringNode; +use PhpBench\Model\SuiteCollection; +use PhpBench\Registry\Config; +use PhpBench\Report\GeneratorInterface; +use PhpBench\Report\Model\Report; +use PhpBench\Report\Model\Reports; +use PhpBench\Report\Model\Table; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * Report generator for environmental information. + * + * NOTE: The Table report generator could probably be improved to be able to incorporate + * this report somehow. + */ +class EnvGenerator implements GeneratorInterface +{ + /** + * {@inheritdoc} + */ + public function configure(OptionsResolver $options): void + { + $options->setDefaults([ + 'title' => null, + 'description' => null, + ]); + } + + /** + * {@inheritdoc} + */ + public function generate(SuiteCollection $suiteCollection, Config $config): Reports + { + $tables = []; + + foreach ($suiteCollection as $suite) { + $title = sprintf( + 'Suite #%s %s', $suite->getUuid(), $suite->getDate()->format('Y-m-d H:i:s') + ); + + $rows = []; + + foreach ($suite->getEnvInformations() as $envInformation) { + foreach ($envInformation as $key => $value) { + $rows[] = [ + 'provider' => new StringNode($envInformation->getName()), + 'key' => new StringNode($key), + 'value' => PhpValueFactory::fromValue($value) + ]; + } + } + $tables[] = Table::fromRowArray($rows, $title); + } + + return Reports::fromReport( + Report::fromTables( + $tables, + isset($config['title']) ? $config['title'] : null, + isset($config['description']) ? $config['description'] : null + ) + ); + } +} diff --git a/lib/Report/Generator/ExpressionGenerator.php b/lib/Report/Generator/ExpressionGenerator.php new file mode 100644 index 000000000..b3fc204eb --- /dev/null +++ b/lib/Report/Generator/ExpressionGenerator.php @@ -0,0 +1,341 @@ +parser = $parser; + $this->evaluator = $evaluator; + $this->printer = $printer; + $this->logger = $logger; + $this->transformer = $transformer; + } + + /** + * {@inheritDoc} + */ + public function configure(OptionsResolver $options): void + { + $formatTime = function (string $expr) { + return sprintf(<<<'EOT' +display_as_time( + %s, + coalesce( + first(subject_time_unit), + "microseconds" + ), + first(subject_time_precision), + first(subject_time_mode)) +EOT + , $expr); + }; + + $options->setDefaults([ + self::PARAM_TITLE => null, + self::PARAM_DESCRIPTION => null, + self::PARAM_COLS => null, + self::PARAM_EXPRESSIONS => [], + self::PARAM_BASELINE_EXPRESSIONS => [], + self::PARAM_AGGREGATE => ['suite_tag', 'benchmark_class', 'subject_name', 'variant_name'], + self::PARAM_BREAK => [], + self::PARAM_INCLUDE_BASELINE => false, + ]); + + $options->setAllowedTypes(self::PARAM_TITLE, ['null', 'string']); + $options->setAllowedTypes(self::PARAM_DESCRIPTION, ['null', 'string']); + $options->setAllowedTypes(self::PARAM_COLS, ['array', 'null']); + $options->setAllowedTypes(self::PARAM_EXPRESSIONS, 'array'); + $options->setAllowedTypes(self::PARAM_BASELINE_EXPRESSIONS, 'array'); + $options->setAllowedTypes(self::PARAM_AGGREGATE, 'array'); + $options->setAllowedTypes(self::PARAM_BREAK, 'array'); + $options->setAllowedTypes(self::PARAM_INCLUDE_BASELINE, 'bool'); + $options->setNormalizer(self::PARAM_EXPRESSIONS, function (Options $options, array $expressions) use ($formatTime) { + return array_merge([ + 'tag' => 'first(suite_tag)', + 'benchmark' => 'first(benchmark_name)', + 'subject' => 'first(subject_name)', + 'set' => 'first(variant_name)', + 'revs' => 'first(variant_revs)', + 'its' => 'first(variant_iterations)', + 'mem_peak' => 'max(result_mem_peak) as memory', + 'best' => $formatTime('min(result_time_avg)'), + 'mode' => $formatTime('mode(result_time_avg)'), + 'mean' => $formatTime('mean(result_time_avg)'), + 'worst' => $formatTime('max(result_time_avg)'), + 'stdev' => $formatTime('stdev(result_time_avg)'), + 'rstdev' => 'rstdev(result_time_avg)', + ], $expressions); + }); + $options->setNormalizer(self::PARAM_BASELINE_EXPRESSIONS, function (Options $options, array $expressions) use ($formatTime) { + return array_merge([ + 'best' => $formatTime('min(result_time_avg)'), + 'worst' => $formatTime('max(result_time_avg)'), + 'mode' => $formatTime('mode(result_time_avg)') . ' ~" "~ percent_diff(mode(baseline_time_avg), mode(result_time_avg), rstdev(result_time_avg))', + 'mem_peak' => '(first(baseline_mem_peak) as memory) ~ " " ~ percent_diff(first(baseline_mem_peak), first(result_mem_peak))', + 'rstdev' => 'rstdev(result_time_avg) ~ " " ~ percent_diff(rstdev(baseline_time_avg), rstdev(result_time_avg))', + ], $expressions); + }); + $options->setNormalizer(self::PARAM_COLS, function (Options $options, ?array $cols) { + if (null !== $cols) { + return $cols; + } + + return array_keys($options[self::PARAM_EXPRESSIONS]); + }); + } + + /** + * {@inheritDoc} + */ + public function generate(SuiteCollection $collection, Config $config): Reports + { + $expressionMap = $this->resolveExpressionMap($config); + $baselineExpressionMap = $this->resolveBaselineExpressionMap($config, array_keys($expressionMap)); + + $table = $this->transformer->suiteToTable($collection, $config[self::PARAM_INCLUDE_BASELINE]); + $table = $this->aggregate($table, $config[self::PARAM_AGGREGATE]); + $table = iterator_to_array($this->evaluate($table, $expressionMap, $baselineExpressionMap)); + $tables = $this->partition($table, $config[self::PARAM_BREAK]); + + return $this->generateReports($tables, $config); + } + + /** + * @param array $table + * @param string[] $aggregateCols + * + * @return array + */ + private function aggregate(array $table, array $aggregateCols): array + { + $aggregated = []; + + foreach ($table as $row) { + $hash = implode('-', array_map(function (string $key) use ($row) { + if (!array_key_exists($key, $row)) { + throw new RuntimeException(sprintf( + 'Cannot aggregate: field "%s" does not exist, know fields: "%s"', + $key, implode('", "', array_keys($row)) + )); + } + + return $row[$key]; + }, $aggregateCols)); + + $aggregated[$hash] = (function () use ($row, $hash, $aggregated) { + if (!isset($aggregated[$hash])) { + return array_map(function ($value) { + if (is_array($value)) { + return $value; + } + + return [$value]; + }, $row); + } + + return array_combine(array_keys($aggregated[$hash]), array_map(function ($aggValue, $value) { + return array_merge((array)$aggValue, (array)$value); + }, $aggregated[$hash], $row)); + })(); + } + + return $aggregated; + } + + /** + * @param array $table + * + * @return Generator> + */ + private function evaluate(array $table, array $exprMap, array $baselineExprMap): Generator + { + foreach ($table as $row) { + $evaledRow = []; + + foreach (($row[SuiteCollectionTransformer::COL_HAS_BASELINE][0] ? array_merge($exprMap, $baselineExprMap) : $exprMap) as $name => $expr) { + try { + $evaledRow[$name] = $this->evaluator->evaluate($this->parser->parse($expr), $row); + } catch (EvaluationError $e) { + $evaledRow[$name] = new StringNode('ERR'); + $this->logger->error(sprintf( + 'Expression error (column "%s"): %s', $name, $e->getMessage() + )); + } + } + + yield $evaledRow; + } + } + + /** + * @param array>> $tables + */ + private function generateReports(array $tables, Config $config): Reports + { + return Reports::fromReport(Report::fromTables( + array_map(function (array $table, string $title) { + return Table::fromRowArray($table, $title); + }, $tables, array_keys($tables)), + isset($config[self::PARAM_TITLE]) ? $config[self::PARAM_TITLE] : null, + isset($config[self::PARAM_DESCRIPTION]) ? $config[self::PARAM_DESCRIPTION] : null + )); + } + + /** + * @param array> $table + * @param string[] $breakCols + * + * @return array>> + */ + private function partition(array $table, array $breakCols): array + { + $partitioned = []; + + foreach ($table as $key => $row) { + $hash = implode('-', array_map(function (string $key) use ($row) { + if (!array_key_exists($key, $row)) { + throw new RuntimeException(sprintf( + 'Cannot partition table: column "%s" does not exist, known columns: "%s"', + $key, + implode('", "', array_keys($row)) + )); + } + + $value = $row[$key]; + + if (!$value instanceof StringNode) { + throw new RuntimeException(sprintf( + 'Partition value for "%s" must be a string, got "%s"', + $key, get_class($value) + )); + } + + return $value->value(); + }, $breakCols)); + + foreach ($breakCols as $col) { + unset($row[$col]); + } + + if (!isset($partitioned[$hash])) { + $partitioned[$hash] = []; + } + + $partitioned[$hash][] = $row; + } + + return $partitioned; + } + + /** + * @return array + */ + private function resolveExpressionMap(Config $config): array + { + $expressions = $config[self::PARAM_EXPRESSIONS]; + $map = []; + + foreach ($config[self::PARAM_COLS] as $key => $expr) { + if (is_int($key) || null === $expr) { + $expr = null === $expr ? $key : $expr; + + if (!isset($expressions[$expr])) { + throw new RuntimeException(sprintf( + 'No expression with name "%s" is available, available expressions: "%s"', + $expr, + implode('", "', array_keys($expressions)) + )); + } + $map[(string)$expr] = (string)$expressions[$expr]; + + continue; + } + $map[(string)$key] = (string)$expr; + } + + return $map; + } + + /** + * @param string[] $visibleCols + * + * @return array + */ + private function resolveBaselineExpressionMap(Config $config, array $visibleCols): array + { + $map = []; + + foreach ($config[self::PARAM_BASELINE_EXPRESSIONS] as $name => $baselineExpression) { + if (!in_array($name, $visibleCols)) { + continue; + } + $map[(string)$name] = (string)$baselineExpression; + } + + return $map; + } +} diff --git a/lib/Report/Generator/OutputTestGenerator.php b/lib/Report/Generator/OutputTestGenerator.php new file mode 100644 index 000000000..be56b1003 --- /dev/null +++ b/lib/Report/Generator/OutputTestGenerator.php @@ -0,0 +1,66 @@ + new StringNode('one'), + 'int' => new IntegerNode(123), + 'float' => new FloatNode(12.2), + 'bool' => new ListNode([new BooleanNode(true), new BooleanNode(false)]), + 'null' => new NullNode(), + 'parameter' => new ParameterNode(['foo', 'bar']), + ] + ], 'Values'), + Table::fromRowArray([ + (function (array $percents): array { + return (array)array_combine($percents, array_map(function (float $percent) { + return new PercentDifferenceNode($percent); + }, $percents)); + })(range(-100, 100, 10)), + ], 'Percent Difference'), + Table::fromRowArray([ + [ + 'time' => new DisplayAsNode(new IntegerNode(10000), new UnitNode(new StringNode('ms')), new IntegerNode(1)) + ] + ], 'Time'), + ], + 'Output Test Report', + 'This report demonstrates output' + )); + } +} diff --git a/lib/Report/GeneratorInterface.php b/lib/Report/GeneratorInterface.php new file mode 100644 index 000000000..354cb9eef --- /dev/null +++ b/lib/Report/GeneratorInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Report; + +use PhpBench\Dom\Document; +use PhpBench\Model\SuiteCollection; +use PhpBench\Registry\Config; +use PhpBench\Registry\RegistrableInterface; +use PhpBench\Report\Model\Report; +use PhpBench\Report\Model\Reports; + +interface GeneratorInterface extends RegistrableInterface +{ + /** + * Generate the report document from the suite result document. + * + */ + public function generate(SuiteCollection $collection, Config $config): Reports; +} diff --git a/lib/Report/Model/Report.php b/lib/Report/Model/Report.php new file mode 100644 index 000000000..93c7a25e0 --- /dev/null +++ b/lib/Report/Model/Report.php @@ -0,0 +1,62 @@ +tables = $tables; + $this->title = $title; + $this->description = $description; + } + + /** + * @param Table[] $tables + */ + public static function fromTables(array $tables, ?string $title = null, ?string $description = null): self + { + return new self($tables, $title, $description); + } + + public static function fromTable(Table $table, ?string $title = null, ?string $description = null): self + { + return new self([$table], $title, $description); + } + + public function title(): ?string + { + return $this->title; + } + + public function description(): ?string + { + return $this->description; + } + + /** + * @return Table[] + */ + public function tables(): array + { + return $this->tables; + } +} diff --git a/lib/Report/Model/Reports.php b/lib/Report/Model/Reports.php new file mode 100644 index 000000000..a3eb67a36 --- /dev/null +++ b/lib/Report/Model/Reports.php @@ -0,0 +1,66 @@ + + */ +final class Reports implements IteratorAggregate +{ + /** + * @var Report[] + */ + private $reports; + + /** + * @param Report[] $reports + */ + private function __construct(array $reports) + { + $this->reports = $reports; + } + + public static function fromReport(Report $report): self + { + return new self([$report]); + } + + public static function fromReports(Report ...$reports): self + { + return new self($reports); + } + + public static function empty(): self + { + return new self([]); + } + + public function merge(Reports $reports): self + { + return new self(array_merge($this->reports, $reports->reports)); + } + + public function getIterator() + { + return new ArrayIterator($this->reports); + } + + /** + * @return Table[] + */ + public function tables(): array + { + $tables = []; + + foreach ($this as $report) { + foreach ($report->tables() as $table) { + $tables[] = $table; + } + } + + return $tables; + } +} diff --git a/lib/Report/Model/Table.php b/lib/Report/Model/Table.php new file mode 100644 index 000000000..ba4dc52c3 --- /dev/null +++ b/lib/Report/Model/Table.php @@ -0,0 +1,74 @@ + + */ +final class Table implements IteratorAggregate +{ + /** + * @var TableRow[] + */ + private $rows; + + /** + * @var string|null + */ + private $title; + + /** + * @param TableRow[] $rows + */ + private function __construct(array $rows, ?string $title) + { + $this->rows = $rows; + $this->title = $title; + } + + /** + * @param array> $rows + */ + public static function fromRowArray(array $rows, ?string $title = null): self + { + return new self(array_map(function (array $row) { + return TableRow::fromArray($row); + }, $rows), $title); + } + + public function title(): ?string + { + return $this->title; + } + + /** + * {@inheritDoc} + */ + public function getIterator() + { + return new ArrayIterator($this->rows); + } + + /** + * @return string[] + */ + public function columnNames(): array + { + foreach ($this->rows as $row) { + return $row->keys(); + } + + return []; + } + + /** + * @return TableRow[] + */ + public function rows(): array + { + return $this->rows; + } +} diff --git a/lib/Report/Model/TableRow.php b/lib/Report/Model/TableRow.php new file mode 100644 index 000000000..c5b3df9a2 --- /dev/null +++ b/lib/Report/Model/TableRow.php @@ -0,0 +1,54 @@ + + */ +final class TableRow implements IteratorAggregate +{ + /** + * @var array + */ + private $cells; + + /** + * @param array $cells + */ + private function __construct(array $cells) + { + $this->cells = $cells; + } + + public static function fromArray(array $row): self + { + return new self(array_map(function (Node $node) { + return $node; + }, $row)); + } + + /** + * {@inheritDoc} + */ + public function getIterator() + { + return new ArrayIterator($this->cells); + } + + /** + * @return string[] + */ + public function keys(): array + { + return array_keys($this->cells); + } + + public function cells(): array + { + return $this->cells; + } +} diff --git a/lib/Report/Renderer/ConsoleRenderer.php b/lib/Report/Renderer/ConsoleRenderer.php new file mode 100644 index 000000000..3d838fbc7 --- /dev/null +++ b/lib/Report/Renderer/ConsoleRenderer.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Report\Renderer; + +use PhpBench\Expression\Ast\Node; +use PhpBench\Expression\Printer; +use PhpBench\Registry\Config; +use PhpBench\Report\Model\Reports; +use PhpBench\Report\Model\Table as PhpBenchTable; +use PhpBench\Report\Model\TableRow; +use PhpBench\Report\RendererInterface; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class ConsoleRenderer implements RendererInterface +{ + /** + * @var OutputInterface + */ + private $output; + + /** + * @var Printer + */ + private $printer; + + public function __construct(OutputInterface $output, Printer $printer) + { + $this->output = $output; + $this->printer = $printer; + } + + /** + * Render the table. + * + */ + public function render(Reports $reports, Config $config): void + { + foreach ($reports as $report) { + if ($title = $report->title()) { + $this->output->writeln(sprintf('%s', $title)); + $this->output->writeln(sprintf('%s', str_repeat('=', strlen($title)))); + $this->output->write(PHP_EOL); + } + + if ($description = $report->description()) { + $this->output->writeln(sprintf('%s', $title)); + $this->output->writeln(sprintf('%s', $description)); + $this->output->writeln(''); + } + + foreach ($report->tables() as $table) { + $this->output->writeln(sprintf('%s', $table->title())); + $this->renderTableElement($table, $config); + } + } + } + + protected function renderTableElement(PhpBenchTable $table, Config $config): void + { + $rows = []; + + $consoleTable = new Table($this->output); + $consoleTable->setStyle($config['table_style']); + $consoleTable->setHeaders($table->columnNames()); + $consoleTable->setRows($this->buildRows($table)); + $consoleTable->render(); + $this->output->writeln(''); + } + + /** + * {@inheritdoc} + */ + public function configure(OptionsResolver $options): void + { + $options->setDefaults([ + 'table_style' => 'default', + ]); + $options->setAllowedTypes('table_style', ['string']); + } + + private function buildRows(PhpBenchTable $table): array + { + return array_map(function (TableRow $row) { + return array_map(function (Node $node) { + return $this->printer->print($node); + }, $row->cells()); + }, $table->rows()); + } +} diff --git a/lib/Report/Renderer/DelimitedRenderer.php b/lib/Report/Renderer/DelimitedRenderer.php new file mode 100644 index 000000000..e7a24f7c8 --- /dev/null +++ b/lib/Report/Renderer/DelimitedRenderer.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Report\Renderer; + +use PhpBench\Expression\Printer; +use PhpBench\Registry\Config; +use PhpBench\Report\Model\Reports; +use PhpBench\Report\Model\Table; +use PhpBench\Report\RendererInterface; +use RuntimeException; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * Renders the report as a delimited list. + */ +class DelimitedRenderer implements RendererInterface +{ + /** + * @var OutputInterface + */ + private $output; + + /** + * @var Printer + */ + private $printer; + + public function __construct(OutputInterface $output, Printer $printer) + { + $this->output = $output; + $this->printer = $printer; + } + + /** + * Render the table. + * + */ + public function render(Reports $reports, Config $config): void + { + foreach ($reports->tables() as $table) { + $this->renderTable($table, $config); + } + } + + protected function renderTable(Table $table, $config): void + { + $rows = []; + + if (true === $config['header']) { + $rows[] = $table->columnNames(); + } + + foreach ($table as $tableRow) { + $row = []; + + foreach ($tableRow as $name => $node) { + $row[$name] = $this->printer->print($node); + } + + $rows[] = $row; + } + + $fname = $config['file'] ?: 'php://temp'; + $pointer = fopen($fname, 'w+'); + + if (false === $pointer) { + throw new RuntimeException(sprintf( + 'Could not open file "%s"', $fname + )); + } + + foreach ($rows as $row) { + // use fputcsv to handle escaping + fputcsv( + $pointer, + $row, + $config['delimiter'] + ); + } + + rewind($pointer); + + $contents = stream_get_contents($pointer); + + if (false === $contents) { + throw new RuntimeException(sprintf( + 'Could not read stream "%s"', $fname + )); + } + + $this->output->write($contents); + fclose($pointer); + + if ($config['file']) { + $this->output->writeln('Dumped delimited file:'); + $this->output->writeln($config['file']); + } + } + + /** + * {@inheritdoc} + */ + public function configure(OptionsResolver $options): void + { + $options->setDefaults([ + 'delimiter' => "\t", + 'file' => null, + 'header' => true, + ]); + } +} diff --git a/lib/Report/RendererInterface.php b/lib/Report/RendererInterface.php new file mode 100644 index 000000000..69c491c08 --- /dev/null +++ b/lib/Report/RendererInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Report; + +use PhpBench\Dom\Document; +use PhpBench\Registry\Config; +use PhpBench\Registry\RegistrableInterface; +use PhpBench\Report\Model\Reports; + +/** + * Implementors render the DOM Document generated by a generator to some visual medium. + * + * This might be either direct output (e.g. to the console, streaming markup) or to a file. + * + * Example implementors might be XsltRenderer, ConsoleRenderer, etc + */ +interface RendererInterface extends RegistrableInterface +{ + public function render(Reports $report, Config $config); +} diff --git a/lib/Report/ReportManager.php b/lib/Report/ReportManager.php new file mode 100644 index 000000000..a66e7e8c7 --- /dev/null +++ b/lib/Report/ReportManager.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Report; + +use PhpBench\Model\SuiteCollection; +use PhpBench\Registry\Config; +use PhpBench\Registry\ConfigurableRegistry; +use PhpBench\Report\Model\Reports; + +/** + * Manage report configuration and generation. + */ +class ReportManager +{ + /** + * @var ConfigurableRegistry + */ + private $generatorRegistry; + + /** + * @var ConfigurableRegistry + */ + private $rendererRegistry; + + /** + * @param ConfigurableRegistry $generatorRegistry + * @param ConfigurableRegistry $rendererRegistry + */ + public function __construct( + ConfigurableRegistry $generatorRegistry, + ConfigurableRegistry $rendererRegistry + ) { + $this->generatorRegistry = $generatorRegistry; + $this->rendererRegistry = $rendererRegistry; + } + + /** + * @param string[] $reportNames + */ + public function validateReportNames(array $reportNames): void + { + foreach ($reportNames as $reportName) { + $this->generatorRegistry->getConfig($reportName); + } + } + + /** + * @param string[] $reportNames + * @param string[] $outputNames + */ + public function renderReports(SuiteCollection $collection, array $reportNames, array $outputNames): void + { + $reports = $this->generateReports($collection, $reportNames); + + foreach ($outputNames as $outputName) { + $outputConfig = $this->rendererRegistry->getConfig($outputName); + $renderer = $this->rendererRegistry->getService($outputConfig['renderer']); + assert($renderer instanceof RendererInterface); + + $renderer->render($reports, $outputConfig); + } + } + + /** + * @param string[] $reportNames + */ + public function generateReports(SuiteCollection $collection, array $reportNames): Reports + { + $reportConfigs = (array)array_combine($reportNames, array_map(function (string $reportName): Config { + return $this->generatorRegistry->getConfig($reportName); + }, $reportNames)); + + $reports = Reports::empty(); + + foreach ($reportConfigs as $reportName => $reportConfig) { + assert($reportConfig instanceof Config); + + $generatorName = $reportConfig['generator']; + $generator = $this->generatorRegistry->getService($generatorName); + + $reports = $reports->merge($generator->generate($collection, $reportConfig)); + } + + return $reports; + } +} diff --git a/lib/Report/Transform/SuiteCollectionTransformer.php b/lib/Report/Transform/SuiteCollectionTransformer.php new file mode 100644 index 000000000..04aa8997b --- /dev/null +++ b/lib/Report/Transform/SuiteCollectionTransformer.php @@ -0,0 +1,157 @@ +> + */ + public function suiteToTable(SuiteCollection $collection, bool $includeBaseline = false): array + { + return $this->normalize(iterator_to_array($this->reportData($collection, $includeBaseline))); + } + + /** + * @return Generator> + */ + private function reportData(SuiteCollection $collection, bool $includeBaseline): Generator + { + foreach ($collection as $suite) { + assert($suite instanceof Suite); + + foreach ($suite->getSubjects() as $subject) { + foreach ($subject->getVariants() as $variant) { + $nbIterations = (function (Variant $variant, ?Variant $baseline) { + if (null === $baseline) { + return count($variant->getIterations()); + } + + return max(count($variant->getIterations()), count($baseline->getIterations())); + })($variant, $variant->getBaseline()); + + for ($itNum = 0; $itNum < $nbIterations; $itNum++) { + $iteration = $variant->getIteration($itNum); + $baseline = $variant->getBaseline(); + $baselineIteration = $baseline ? $baseline->getIteration($itNum) : null; + + if ($includeBaseline && $baseline) { + yield array_merge( + [ + self::COL_HAS_BASELINE => false + ], + $this->createRow($baseline->getSubject(), $baseline, $baseline->getSubject()->getBenchmark()->getSuite(), $itNum), + $this->resultData($baselineIteration, 'result') + ); + } + + yield array_merge( + [ + self::COL_HAS_BASELINE => $baseline ? true : false + ], + $this->createRow($subject, $variant, $suite, $itNum), + $this->resultData($iteration, 'result'), + $this->resultData($baselineIteration, 'baseline') + ); + } + } + } + } + } + + /** + * @return array + */ + private function resultData(?Iteration $iteration, string $prefix = 'result'): array + { + if (null === $iteration) { + return []; + } + + $data = []; + + foreach ($iteration->getResults() as $result) { + foreach ($result->getMetrics() as $key => $value) { + $data[sprintf('%s_%s_%s', $prefix, $result->getKey(), $key)] = $value; + } + } + + return $data; + } + + + /** + * @param array> $table + * + * @return array> + */ + private function normalize(array $table): array + { + $cols = []; + + foreach ($table as $row) { + foreach ($row as $key => $value) { + if (!isset($cols[$key])) { + $cols[$key] = null; + } + } + } + + foreach ($table as &$row) { + $row = array_merge($cols, $row); + } + + return $table; + } + + /** + * @return array + */ + private function createRow(Subject $subject, Variant $variant, Suite $suite, int $itNum): array + { + return array_merge([ + 'benchmark_name' => $subject->getBenchmark()->getName(), + 'benchmark_class' => $subject->getBenchmark()->getClass(), + 'subject_name' => $subject->getName(), + 'subject_groups' => $subject->getGroups(), + 'subject_time_unit' => $subject->getOutputTimeUnit(), + 'subject_time_precision' => $subject->getOutputTimePrecision(), + 'subject_time_mode' => $subject->getOutputMode(), + 'variant_name' => $variant->getParameterSet()->getName(), + 'variant_params' => $variant->getParameterSet()->getArrayCopy(), + 'variant_revs' => $variant->getRevolutions(), + 'variant_iterations' => count($variant->getIterations()), + 'suite_tag' => $suite->getTag() ? $suite->getTag()->__toString() : '', + 'suite_date' => $suite->getDate()->format('Y-m-d'), + 'suite_time' => $suite->getDate()->format('H:i:s'), + 'iteration_index' => $itNum, + ], $this->envData($suite)); + } + + /** + * @return array + */ + private function envData(Suite $suite): array + { + $data = []; + + foreach ($suite->getEnvInformations() as $info) { + $name = $info->getName(); + + foreach ($info as $key => $value) { + $data[sprintf('env_%s_%s', $name, $key)] = $value; + } + } + + return $data; + } +} diff --git a/lib/Serializer/XmlDecoder.php b/lib/Serializer/XmlDecoder.php new file mode 100644 index 000000000..c3b2c4149 --- /dev/null +++ b/lib/Serializer/XmlDecoder.php @@ -0,0 +1,312 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Serializer; + +use PhpBench\Assertion\AssertionResult; +use PhpBench\Dom\Document; +use PhpBench\Dom\Element; +use PhpBench\Environment\Information; +use PhpBench\Model\Benchmark; +use PhpBench\Model\Error; +use PhpBench\Model\ParameterSet; +use PhpBench\Model\ResolvedExecutor; +use PhpBench\Model\Subject; +use PhpBench\Model\Suite; +use PhpBench\Model\SuiteCollection; +use PhpBench\Model\Variant; +use PhpBench\PhpBench; +use PhpBench\Registry\Config; + +/** + * Encodes the Suite object graph into an XML document. + */ +class XmlDecoder +{ + /** + * Decode a PHPBench XML document into a SuiteCollection. + * + */ + public function decode(Document $document): SuiteCollection + { + $suites = []; + + foreach ($document->query('//suite') as $suiteEl) { + $suites[] = $this->processSuite($suiteEl); + } + + return new SuiteCollection($suites); + } + + /** + * Return a SuiteCollection from a number of PHPBench xml files. + * + * @param string[] $files + */ + public function decodeFiles(array $files): SuiteCollection + { + // combine into one document. + // + $suiteDocument = new Document('phpbench'); + $rootEl = $suiteDocument->createRoot('phpbench'); + + foreach ($files as $file) { + $fileDom = new Document(); + $fileDom->load($file); + + foreach ($fileDom->query('./suite') as $suiteEl) { + $importedEl = $suiteDocument->importNode($suiteEl, true); + $rootEl->appendChild($importedEl); + } + } + + return $this->decode($suiteDocument); + } + + private function processSuite(Element $suiteEl): Suite + { + $suite = new Suite( + $suiteEl->getAttribute('tag'), + new \DateTime($suiteEl->getAttribute('date')), + $suiteEl->getAttribute('config-path'), + [], + [], + $suiteEl->getAttribute('uuid') + ); + + $informations = []; + + foreach ($suiteEl->query('./env/*') as $envEl) { + assert($envEl instanceof Element); + $name = $envEl->nodeName; + $info = []; + + if ($envEl->childNodes->count()) { + foreach ($envEl->childNodes as $value) { + if (!$value instanceof Element) { + continue; + } + $info[$value->getAttribute('name')] = $this->resolveEnvType($value->getAttribute('type'), $value->nodeValue); + } + } else { // legacy format + foreach ($envEl->attributes as $iName => $valueAttr) { + $info[$iName] = $valueAttr->nodeValue; + } + } + + $informations[$name] = new Information($name, $info); + } + + $resultClasses = []; + + foreach ($suiteEl->query('//result') as $resultEl) { + $class = $resultEl->getAttribute('class'); + + if (!class_exists($class)) { + throw new \RuntimeException(sprintf( + 'XML file defines a non-existing result class "%s" - maybe you are missing an extension?', + $class + )); + } + + $resultClasses[$resultEl->getAttribute('key')] = $class; + } + + $suite->setEnvInformations($informations); + + foreach ($suiteEl->query('./benchmark') as $benchmarkEl) { + $benchmark = $suite->createBenchmark( + $benchmarkEl->getAttribute('class') + ); + + $this->processBenchmark($benchmark, $benchmarkEl, $resultClasses); + } + + return $suite; + } + + private function processBenchmark(Benchmark $benchmark, Element $benchmarkEl, array $resultClasses): void + { + foreach ($benchmarkEl->query('./subject') as $subjectEl) { + $subject = $benchmark->createSubject($subjectEl->getAttribute('name')); + $this->processSubject($subject, $subjectEl, $resultClasses); + } + } + + private function processSubject(Subject $subject, Element $subjectEl, array $resultClasses): void + { + $groups = []; + + foreach ($subjectEl->query('./group') as $groupEl) { + $groups[] = $groupEl->getAttribute('name'); + } + $subject->setGroups($groups); + + foreach ($subjectEl->query('./executor') as $executorEl) { + $subject->setExecutor(ResolvedExecutor::fromNameAndConfig($executorEl->getAttribute('name'), new Config('asd', $this->getParameters($executorEl)))); + + break; + } + + // TODO: These attributes should be on the subject, see + // https://github.com/phpbench/phpbench/issues/307 + foreach ($subjectEl->query('./variant') as $variantEl) { + $subject->setSleep((int)$variantEl->getAttribute('sleep')); + $subject->setOutputTimeUnit($variantEl->getAttribute('output-time-unit')); + $subject->setOutputTimePrecision((int)$variantEl->getAttribute('output-time-precision')); + $subject->setOutputMode($variantEl->getAttribute('output-mode')); + $subject->setRetryThreshold((float)$variantEl->getAttribute('retry-threshold')); + + break; + } + + foreach ($subjectEl->query('./variant') as $index => $variantEl) { + $parameterSet = new ParameterSet('0', []); + + foreach ($variantEl->query('./parameter-set') as $parameterSetEl) { + $name = $parameterSetEl->getAttribute('name'); + $parameters = $this->getParameters($parameterSetEl); + $parameterSet = new ParameterSet($name, $parameters); + + break; + } + $stats = $this->getComputedStats($variantEl); + $variant = $subject->createVariant($parameterSet, (int)$variantEl->getAttribute('revs'), (int)$variantEl->getAttribute('warmup'), $stats); + $this->processVariant($variant, $variantEl, $resultClasses); + } + } + + private function getComputedStats(Element $element): array + { + $stats = []; + + foreach ($element->query('./stats') as $statsEl) { + foreach ($statsEl->attributes as $key => $attribute) { + $stats[$key] = (float)$attribute->nodeValue; + } + } + + return $stats; + } + + private function getParameters(Element $element): array + { + $parameters = []; + + foreach ($element->query('./parameter') as $parameterEl) { + $name = $parameterEl->getAttribute('name'); + + if ($parameterEl->getAttribute('type') === 'collection') { + $parameters[$name] = $this->getParameters($parameterEl); + + continue; + } + + if ($parameterEl->getAttribute('xsi:nil') === 'true') { + $parameters[$name] = null; + + continue; + } + + $parameters[$name] = $parameterEl->getAttribute('value'); + } + + return $parameters; + } + + private function processVariant(Variant $variant, Element $variantEl, array $resultClasses): void + { + $errorEls = $variantEl->query('.//error'); + + if ($errorEls->length) { + $errors = []; + + foreach ($errorEls as $errorEl) { + $error = new Error( + $errorEl->nodeValue, + $errorEl->getAttribute('exception-class'), + $errorEl->getAttribute('code'), + $errorEl->getAttribute('file'), + $errorEl->getAttribute('line'), + '' // we don't serialize the trace.. + ); + $errors[] = $error; + } + $variant->createErrorStack($errors); + + return; + } + + $failureEls = $variantEl->query('.//failure'); + + if ($failureEls->length) { + $failures = []; + + foreach ($failureEls as $failureEl) { + $variant->addAssertionResult(AssertionResult::fail($failureEl->nodeValue)); + } + } + + foreach ($variantEl->query('./iteration') as $iterationEl) { + $results = []; + + foreach ($iterationEl->attributes as $attributeEl) { + $name = $attributeEl->name; + + if (false === strpos($name, '-')) { + throw new \RuntimeException(sprintf( + 'Expected attribute name to have a result key prefix, got "%s".', + $name + )); + } + + $prefix = substr($name, 0, strpos($name, '-')); + + if (!isset($resultClasses[$prefix])) { + throw new \RuntimeException(sprintf( + 'No result class was provided with key "%s" for attribute "%s"', + $prefix, $name + )); + } + + $suffix = substr($name, strpos($name, '-') + 1); + $results[$prefix][str_replace('-', '_', $suffix)] = $attributeEl->value; + } + + $iteration = $variant->createIteration(); + + foreach ($results as $resultKey => $resultData) { + $iteration->setResult(call_user_func_array([ + $resultClasses[$resultKey], + 'fromArray', + ], [$resultData])); + } + } + + // TODO: Serialize statistics .. + $variant->computeStats(); + } + + /** + * @return mixed + * + * @param mixed $value + */ + private function resolveEnvType(string $type, $value) + { + if ($type === 'boolean') { + return (bool)$value; + } + + return $value; + } +} diff --git a/lib/Serializer/XmlEncoder.php b/lib/Serializer/XmlEncoder.php new file mode 100644 index 000000000..1ad44e231 --- /dev/null +++ b/lib/Serializer/XmlEncoder.php @@ -0,0 +1,252 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Serializer; + +use PhpBench\Dom\Document; +use PhpBench\Dom\Element; +use PhpBench\Model\Benchmark; +use PhpBench\Model\ResolvedExecutor; +use PhpBench\Model\Subject; +use PhpBench\Model\Suite; +use PhpBench\Model\SuiteCollection; +use PhpBench\Model\Variant; +use PhpBench\PhpBench; +use PhpBench\Util\TimeUnit; + +/** + * Encodes the Suite object graph into an XML document. + */ +class XmlEncoder +{ + /** + * Encode a Suite object into a XML document. + * + */ + public function encode(SuiteCollection $suiteCollection): Document + { + $dom = new Document(); + + $rootEl = $dom->createRoot('phpbench'); + $rootEl->setAttribute('version', PhpBench::VERSION); + $rootEl->setAttributeNS( + 'http://www.w3.org/2000/xmlns/', + 'xmlns:xsi', + 'http://www.w3.org/2001/XMLSchema-instance' + ); + + foreach ($suiteCollection->getSuites() as $suite) { + $suiteEl = $rootEl->appendElement('suite'); + $suiteEl->setAttribute('tag', $suite->getTag()); + + // @deprecated context is deprecated and replaced by `tag`, to be + // removed in version 1.0 + $suiteEl->setAttribute('context', $suite->getTag()); + $suiteEl->setAttribute('date', $suite->getDate()->format('c')); + $suiteEl->setAttribute('config-path', $suite->getConfigPath()); + $suiteEl->setAttribute('uuid', $suite->getUuid()); + + $envEl = $suiteEl->appendElement('env'); + + foreach ($suite->getEnvInformations() as $information) { + $infoEl = $envEl->appendElement($information->getName()); + + foreach ($information as $key => $value) { + $valueEl = $infoEl->appendElement('value'); + $valueEl->setAttribute('name', $key); + $valueEl->setAttribute('type', gettype($value)); + $valueEl->nodeValue = $value; + } + } + + foreach ($suite->getBenchmarks() as $benchmark) { + $this->processBenchmark($benchmark, $suiteEl); + } + } + + return $dom; + } + + private function processBenchmark(Benchmark $benchmark, Element $suiteEl): void + { + $benchmarkEl = $suiteEl->appendElement('benchmark'); + $benchmarkEl->setAttribute('class', $benchmark->getClass()); + + foreach ($benchmark->getSubjects() as $subject) { + $this->processSubject($subject, $benchmarkEl); + } + } + + private function processSubject(Subject $subject, Element $benchmarkEl): void + { + $subjectEl = $benchmarkEl->appendElement('subject'); + $subjectEl->setAttribute('name', $subject->getName()); + + $this->appendExecutor($subjectEl, $subject->getExecutor()); + + foreach ($subject->getGroups() as $group) { + $groupEl = $subjectEl->appendElement('group'); + $groupEl->setAttribute('name', $group); + } + + foreach ($subject->getVariants() as $variant) { + $this->processVariant($subject, $variant, $subjectEl); + } + } + + private function processVariant(Subject $subject, Variant $variant, Element $subjectEl): void + { + $variantEl = $subjectEl->appendElement('variant'); + + // TODO: These attributes should be on the subject, see + // https://github.com/phpbench/phpbench/issues/307 + $variantEl->setAttribute('sleep', (string)$subject->getSleep()); + $variantEl->setAttribute('output-time-unit', $subject->getOutputTimeUnit() ?: TimeUnit::MICROSECONDS); + $variantEl->setAttribute('output-time-precision', (string)$subject->getOutputTimePrecision()); + $variantEl->setAttribute('output-mode', $subject->getOutputMode() ?: TimeUnit::MODE_TIME); + $variantEl->setAttribute('revs', (string) $variant->getRevolutions()); + $variantEl->setAttribute('warmup', (string) $variant->getWarmup()); + $variantEl->setAttribute('retry-threshold', (string)$subject->getRetryThreshold()); + + $parameterSetEl = $variantEl->appendElement('parameter-set'); + $parameterSetEl->setAttribute('name', $variant->getParameterSet()->getName()); + + foreach ($variant->getParameterSet() as $name => $value) { + $this->createParameter($parameterSetEl, $name, $value); + } + + if ($variant->hasErrorStack()) { + $errorsEl = $variantEl->appendElement('errors'); + + foreach ($variant->getErrorStack() as $error) { + $errorEl = $errorsEl->appendElement('error', $error->getMessage()); + $errorEl->setAttribute('exception-class', $error->getClass()); + $errorEl->setAttribute('code', $error->getCode()); + $errorEl->setAttribute('file', $error->getFile()); + $errorEl->setAttribute('line', $error->getLine()); + } + + return; + } + + if ($variant->getAssertionResults()->failures()->count()) { + $failuresEl = $variantEl->appendElement('failures'); + + foreach ($variant->getAssertionResults()->failures() as $failure) { + $failureEl = $failuresEl->appendElement('failure', $failure->getMessage()); + } + } + + $resultClasses = []; + + foreach ($variant as $iteration) { + $iterationEl = $variantEl->appendElement('iteration'); + + foreach ($iteration->getResults() as $result) { + + // we need to store the class FQNs of the results for deserialization later. + if (!isset($resultClasses[$result->getKey()])) { + $resultClasses[$result->getKey()] = get_class($result); + } + + $prefix = $result->getKey(); + $metrics = $result->getMetrics(); + + foreach ($metrics as $key => $value) { + $iterationEl->setAttribute(sprintf( + '%s-%s', + $prefix, + str_replace('_', '-', $key) + ), $value); + } + } + } + + $statsEl = $variantEl->appendElement('stats'); + $this->buildStatsEl($variant, $statsEl); + + if ($variant->getBaseline()) { + $baselineEl = $variantEl->appendElement('baseline-stats'); + $this->buildStatsEl($variant->getBaseline(), $baselineEl); + } + + foreach ($resultClasses as $resultKey => $classFqn) { + if ($subjectEl->queryOne('ancestor::suite/result[@key="' . $resultKey . '"]')) { + continue; + } + + $resultEl = $subjectEl->queryOne('ancestor::suite')->appendElement('result'); + $resultEl->setAttribute('key', $resultKey); + $resultEl->setAttribute('class', $classFqn); + } + } + + private function createParameter($parentEl, $name, $value) + { + $parameterEl = $parentEl->appendElement('parameter'); + $parameterEl->setAttribute('name', $name); + + if (is_array($value)) { + $parameterEl->setAttribute('type', 'collection'); + + foreach ($value as $key => $element) { + $this->createParameter($parameterEl, $key, $element); + } + + return $parameterEl; + } + + if (is_null($value)) { + $parameterEl->setAttribute('xsi:nil', 'true'); + + return $parameterEl; + } + + if (is_scalar($value)) { + $parameterEl->setAttribute('value', $value); + + return $parameterEl; + } + + throw new \InvalidArgumentException(sprintf( + 'Parameters must be either scalars or arrays, got: %s', + is_object($value) ? get_class($value) : gettype($value) + )); + } + + private function appendExecutor(Element $subjectEl, ResolvedExecutor $executor = null): void + { + if (null === $executor) { + return; + } + + $executorEl = $subjectEl->appendElement('executor'); + $executorEl->setAttribute('name', $executor->getName()); + $subjectEl->appendChild($executorEl); + + foreach ($executor->getConfig() as $key => $value) { + $this->createParameter($executorEl, $key, $value); + } + } + + private function buildStatsEl(Variant $variant, Element $statsEl): void + { + $stats = $variant->getStats(); + $stats = iterator_to_array($stats); + // ensure same order (for testing) + ksort($stats); + + foreach ($stats as $statName => $statValue) { + $statsEl->setAttribute($statName, $statValue); + } + } +} diff --git a/lib/Storage/ArchiverInterface.php b/lib/Storage/ArchiverInterface.php new file mode 100644 index 000000000..395283b43 --- /dev/null +++ b/lib/Storage/ArchiverInterface.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Storage; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Archivers handle archiving and retoring the contents of the configured + * storage driver. + */ +interface ArchiverInterface +{ + /** + * Archive all suites in the configured storage driver. + * + * In the case that a given record already exists in the archive, + * then that record should be skipped. + * + * Progress should be written to the given console output class. + * + */ + public function archive(OutputInterface $output); + + /** + * Restore the archive to storage. If a given record exists in + * the storage, it should be skipped. + * + * Progress should be written to the given console output class. + */ + public function restore(OutputInterface $output); +} diff --git a/lib/Storage/Driver/Fake/FakeHistoryIterator.php b/lib/Storage/Driver/Fake/FakeHistoryIterator.php new file mode 100644 index 000000000..208e75391 --- /dev/null +++ b/lib/Storage/Driver/Fake/FakeHistoryIterator.php @@ -0,0 +1,59 @@ +entries = $entries; + } + + /** + * {@inheritDoc} + */ + public function current() + { + return current($this->entries); + } + + /** + * {@inheritDoc} + */ + public function next(): void + { + next($this->entries); + } + + /** + * {@inheritDoc} + */ + public function key() + { + return key($this->entries); + } + + /** + * {@inheritDoc} + */ + public function valid() + { + return current($this->entries); + } + + /** + * {@inheritDoc} + */ + public function rewind(): void + { + reset($this->entries); + } +} diff --git a/lib/Storage/Driver/Xml/HistoryIterator.php b/lib/Storage/Driver/Xml/HistoryIterator.php new file mode 100644 index 000000000..bb0e0e2e5 --- /dev/null +++ b/lib/Storage/Driver/Xml/HistoryIterator.php @@ -0,0 +1,262 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Storage\Driver\Xml; + +use PhpBench\Dom\Document; +use PhpBench\Serializer\XmlDecoder; +use PhpBench\Storage\HistoryEntry; +use PhpBench\Storage\HistoryIteratorInterface; + +/** + * XML file history iterator. + * + * This command will iterate over the suite collections created by the XML + * storage driver. + */ +class HistoryIterator implements HistoryIteratorInterface +{ + private $xmlDecoder; + private $path; + + private $years; + private $months; + private $days; + private $entries; + private $initialized; + + public function __construct( + XmlDecoder $xmlDecoder, + $path + ) { + $this->xmlDecoder = $xmlDecoder; + $this->path = $path; + } + + /** + * {@inheritdoc} + */ + public function current() + { + $this->init(); + + return $this->entries->current(); + } + + /** + * {@inheritdoc} + */ + public function next(): void + { + $this->init(); + + $this->entries->next(); + + if (!$this->entries->valid()) { + $this->days->next(); + + if (!$this->days->valid()) { + $this->months->next(); + + if (!$this->months->valid()) { + $this->years->next(); + + if ($this->years->valid()) { + $this->months = $this->getDirectoryIterator($this->years->current()); + } + } + + if ($this->months->valid()) { + $this->days = $this->getDirectoryIterator($this->months->current()); + } + } + + if ($this->days->valid()) { + $this->entries = $this->getEntryIterator(); + } + } + } + + /** + * {@inheritdoc} + */ + public function key(): string + { + $this->init(); + $key = sprintf( + '%s-%s-%s-%s', + $this->years->key(), + $this->months->key(), + $this->days->key(), + $this->entries->key() + ); + + return $key; + } + + /** + * {@inheritdoc} + */ + public function rewind(): void + { + $this->init(); + $this->years->rewind(); + $this->months->rewind(); + $this->days->rewind(); + $this->entries->rewind(); + } + + /** + * {@inheritdoc} + */ + public function valid() + { + $this->init(); + + return $this->entries->valid(); + } + + private function init(): void + { + if ($this->initialized) { + return; + } + + $this->initialized = true; + + if (file_exists($this->path)) { + $this->years = $this->getDirectoryIterator($this->path); + } else { + $this->years = new \ArrayIterator(); + } + + // create directory iterators for each part of the date sharding + // (2016/01/01/.xml). if there is not valid entries for the + // preceding shard, just create an empty array iterator. + if ($this->years->valid()) { + $this->months = $this->getDirectoryIterator($this->years->current()); + } else { + $this->months = new \ArrayIterator(); + } + + if ($this->months->valid()) { + $this->days = $this->getDirectoryIterator($this->months->current()); + } else { + $this->days = new \ArrayIterator(); + } + + if ($this->days->valid()) { + $this->entries = $this->getEntryIterator(); + } else { + $this->entries = new \ArrayIterator(); + } + } + + /** + * Return an iterator for the history entries. + * + * We hydrate all of the entries for the "current" day. + */ + private function getEntryIterator(): \ArrayIterator + { + $files = $this->days->current(); + $files = new \DirectoryIterator($this->days->current()); + $historyEntries = []; + + foreach ($files as $file) { + if (!$file->isFile()) { + continue; + } + + if ($file->getExtension() !== 'xml') { + continue; + } + + $historyEntries[] = $this->getHistoryEntry($file->getPathname()); + } + usort($historyEntries, function ($entry1, $entry2) { + return $entry2->getDate()->format('U') <=> $entry1->getDate()->format('U'); + }); + + return new \ArrayIterator($historyEntries); + } + + /** + * Hydrate and return the history entry for the given path. + * + * The summary *should* used pre-calculated values from the XML + * therefore reducing the normal overhead, however this code + * is still quite expensive as we are creating the entire object + * graph for each suite run. + * + */ + private function getHistoryEntry(string $path): HistoryEntry + { + $dom = new Document(); + $dom->load($path); + $collection = $this->xmlDecoder->decode($dom); + $suites = $collection->getSuites(); + $suite = reset($suites); + $envInformations = $suite->getEnvInformations(); + + $vcsBranch = null; + + if (isset($envInformations['vcs']['branch'])) { + $vcsBranch = $envInformations['vcs']['branch']; + } + + $summary = $suite->getSummary(); + $entry = new HistoryEntry( + $suite->getUuid(), + $suite->getDate(), + $suite->getTag(), + $vcsBranch, + $summary->getNbSubjects(), + $summary->getNbIterations(), + $summary->getNbRevolutions(), + $summary->getMinTime(), + $summary->getMaxTime(), + $summary->getMeanTime(), + $summary->getMeanRelStDev(), + $summary->getTotalTime() + ); + + return $entry; + } + + /** + * Return the iterator for a specific path (years, months, days). + * + * We sort by date in descending order. + */ + private function getDirectoryIterator($path): \ArrayIterator + { + $nodes = new \DirectoryIterator($path); + $dirs = []; + + foreach ($nodes as $dir) { + if (!$dir->isDir()) { + continue; + } + + if ($dir->isDot()) { + continue; + } + + $dirs[hexdec($dir->getFilename())] = $dir->getPathname(); + } + + krsort($dirs); + + return new \ArrayIterator($dirs); + } +} diff --git a/lib/Storage/Driver/Xml/XmlDriver.php b/lib/Storage/Driver/Xml/XmlDriver.php new file mode 100644 index 000000000..42da2add3 --- /dev/null +++ b/lib/Storage/Driver/Xml/XmlDriver.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Storage\Driver\Xml; + +use PhpBench\Dom\Document; +use PhpBench\Model\SuiteCollection; +use PhpBench\Serializer\XmlDecoder; +use PhpBench\Serializer\XmlEncoder; +use PhpBench\Storage\DriverInterface; +use Symfony\Component\Filesystem\Filesystem; + +/** + * XML storage driver. + * + * The collections are sharded by year, month and day in order that we can + * effectively sort them without hydrating all of the results. + */ +class XmlDriver implements DriverInterface +{ + const UUID_LENGTH = 40; + + private $path; + private $xmlEncoder; + private $xmlDecoder; + private $filesystem; + + public function __construct($path, XmlEncoder $xmlEncoder, XmlDecoder $xmlDecoder, Filesystem $filesystem = null) + { + $this->path = $path; + $this->xmlEncoder = $xmlEncoder; + $this->xmlDecoder = $xmlDecoder; + $this->filesystem = $filesystem ?: new Filesystem(); + } + + public function store(SuiteCollection $collection): ?string + { + foreach ($collection->getSuites() as $suite) { + $path = $this->getPath($suite->getUuid()); + + if (false === $this->filesystem->exists(dirname($path))) { + $this->filesystem->mkdir(dirname($path)); + } + + $collection = new SuiteCollection([$suite]); + $dom = $this->xmlEncoder->encode($collection); + $dom->save($path); + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function fetch(string $runId): SuiteCollection + { + if (!$this->has($runId)) { + throw new \InvalidArgumentException(sprintf( + 'Cannot find run with reference "%s"', $runId + )); + } + + $path = $this->getPath($runId); + + $dom = new Document(); + $dom->load($path); + $collection = $this->xmlDecoder->decode($dom); + + return $collection; + } + + /** + * {@inheritdoc} + */ + public function has($runId): bool + { + $path = $this->getPath($runId); + + if (null === $path) { + return false; + } + + return $this->filesystem->exists($path); + } + + /** + * {@inheritdoc} + */ + public function history(): \PhpBench\Storage\HistoryIteratorInterface + { + return new HistoryIterator($this->xmlDecoder, $this->path); + } + + private function getPath(string $uuid): ?string + { + if (strlen($uuid) !== self::UUID_LENGTH) { + return null; + } + + try { + $date = new \DateTime((string) hexdec(substr($uuid, 0, 7))); + } catch (\Exception $e) { + return null; + } + + return sprintf( + '%s/%s/%s/%s/%s.xml', + $this->path, + dechex((int) $date->format('Y')), + dechex((int) $date->format('m')), + dechex((int) $date->format('d')), + $uuid + ); + } +} diff --git a/lib/Storage/DriverInterface.php b/lib/Storage/DriverInterface.php new file mode 100644 index 000000000..f665c1b97 --- /dev/null +++ b/lib/Storage/DriverInterface.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Storage; + +use PhpBench\Model\SuiteCollection; + +/** + * Storage driver interface. + */ +interface DriverInterface +{ + /** + * Store the given SuiteCollection. + * + * Optionally return a message which should be displayed + * by the CLI interface after successful storage. + * + * + */ + public function store(SuiteCollection $collection): ?string; + + /** + * Return the suite collection with the given run ID. + * If no suite is found an exception will be thrown. + * + * @throws \InvalidArgumentException + */ + public function fetch(string $runId): SuiteCollection; + + /** + * Return true if the driver has the given run ID. + */ + public function has($runId); + + /** + * Return a history iterator of HistoryEntries in descending + * chronological order. + */ + public function history(): HistoryIteratorInterface; +} diff --git a/lib/Storage/Exception/InvalidTagException.php b/lib/Storage/Exception/InvalidTagException.php new file mode 100644 index 000000000..bf7521630 --- /dev/null +++ b/lib/Storage/Exception/InvalidTagException.php @@ -0,0 +1,9 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Storage; + +use PhpBench\Model\Tag; + +/** + * Represents a summary of a run. Used when listing the history. + */ +class HistoryEntry +{ + private $runId; + private $date; + private $tag; + private $branch; + + private $nbSubjects; + private $nbIterations; + private $nbRevolutions; + + private $minTime; + private $maxTime; + private $meanTime; + private $meanRelStDev; + private $totalTime; + + public function __construct( + $runId, + \DateTime $date, + $tag, + $branch, + $nbSubjects, + $nbIterations, + $nbRevolutions, + $minTime, + $maxTime, + $meanTime, + $meanRelStDev, + $totalTime + ) { + $this->runId = $runId; + $this->date = $date; + $this->tag = $tag; + $this->branch = $branch; + $this->nbSubjects = $nbSubjects; + $this->nbIterations = $nbIterations; + $this->nbRevolutions = $nbRevolutions; + $this->minTime = $minTime; + $this->maxTime = $maxTime; + $this->meanTime = $meanTime; + $this->meanRelStDev = $meanRelStDev; + $this->totalTime = $totalTime; + } + + public function getRunId() + { + return $this->runId; + } + + public function getDate(): \DateTime + { + return $this->date; + } + + public function getTag(): ?Tag + { + return $this->tag ? new Tag($this->tag) : null; + } + + public function getNbSubjects() + { + return $this->nbSubjects; + } + + public function getNbIterations() + { + return $this->nbIterations; + } + + public function getNbRevolutions() + { + return $this->nbRevolutions; + } + + public function getVcsBranch() + { + return $this->branch; + } + + public function getMinTime() + { + return $this->minTime; + } + + public function getMaxTime() + { + return $this->maxTime; + } + + public function getMeanTime() + { + return $this->meanTime; + } + + public function getMeanRelStDev() + { + return $this->meanRelStDev; + } + + public function getTotalTime() + { + return $this->totalTime; + } +} diff --git a/lib/Storage/HistoryIteratorInterface.php b/lib/Storage/HistoryIteratorInterface.php new file mode 100644 index 000000000..d3ce97d73 --- /dev/null +++ b/lib/Storage/HistoryIteratorInterface.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Storage; + +/** + * Iterator interface which must be implemented by storage drivers. + * + * Each element should be an instance of HistoryEntry. + */ +interface HistoryIteratorInterface extends \Iterator +{ +} diff --git a/lib/Storage/StorageRegistry.php b/lib/Storage/StorageRegistry.php new file mode 100644 index 000000000..6f73b7cc4 --- /dev/null +++ b/lib/Storage/StorageRegistry.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Storage; + +use PhpBench\Registry\Registry; +use Psr\Container\ContainerInterface; + +/** + * @method DriverInterface getService() + * @extends Registry + */ +class StorageRegistry extends Registry +{ + public function __construct(ContainerInterface $container, string $default) + { + parent::__construct('storage', $container, $default); + } +} diff --git a/lib/Storage/UuidResolver.php b/lib/Storage/UuidResolver.php new file mode 100644 index 000000000..fb9f48087 --- /dev/null +++ b/lib/Storage/UuidResolver.php @@ -0,0 +1,25 @@ +innerResolver = $innerResolver; + } + + public function resolve(string $ref): string + { + return $this->innerResolver->resolve($ref) ?? $ref; + } +} diff --git a/lib/Storage/UuidResolver/ChainResolver.php b/lib/Storage/UuidResolver/ChainResolver.php new file mode 100644 index 000000000..36d833e6c --- /dev/null +++ b/lib/Storage/UuidResolver/ChainResolver.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Storage\UuidResolver; + +use PhpBench\Storage\UuidResolverInterface; + +class ChainResolver implements UuidResolverInterface +{ + /** + * @var array + */ + private $resolvers = []; + + public function __construct(array $resolvers) + { + $this->resolvers = $resolvers; + } + + public function resolve(string $reference): ?string + { + /** @var UuidResolverInterface $resolver */ + foreach ($this->resolvers as $resolver) { + $ref = $resolver->resolve($reference); + + if (null === $ref) { + continue; + } + + return $ref; + } + + return null; + } +} diff --git a/lib/Storage/UuidResolver/LatestResolver.php b/lib/Storage/UuidResolver/LatestResolver.php new file mode 100644 index 000000000..5a70f71ba --- /dev/null +++ b/lib/Storage/UuidResolver/LatestResolver.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Storage\UuidResolver; + +use PhpBench\Registry\Registry; +use PhpBench\Storage\UuidResolverInterface; +use RuntimeException; + +class LatestResolver implements UuidResolverInterface +{ + const LATEST_KEYWORD = 'latest'; + + private $driverRegistry; + + public function __construct(Registry $driver) + { + $this->driverRegistry = $driver; + } + + public function resolve(string $ref): ?string + { + if (0 !== strpos($ref, self::LATEST_KEYWORD)) { + return null; + } + + if (strtolower($ref) === self::LATEST_KEYWORD) { + return $this->getLatestUuid(); + } + + if (preg_match('{' . self::LATEST_KEYWORD . '-([0-9]+)}', $ref, $matches)) { + return $this->getNthUuid($matches[1]); + } + + throw new RuntimeException(sprintf( + 'Could not resolve ref "%s"', $ref + )); + } + + private function getLatestUuid() + { + $history = $this->driverRegistry->getService()->history(); + + $current = $history->current(); + + if (!$current) { + throw new \InvalidArgumentException( + 'No history present, therefore cannot retrieve latest UUID' + ); + } + + return $current->getRunId(); + } + + private function getNthUuid($nth) + { + $history = $this->driverRegistry->getService()->history(); + $entry = $history->current(); + + for ($i = 0; $i <= $nth; $i++) { + $entry = $history->current(); + $history->next(); + } + + return $entry->getRunId(); + } +} diff --git a/lib/Storage/UuidResolver/TagResolver.php b/lib/Storage/UuidResolver/TagResolver.php new file mode 100644 index 000000000..e70235484 --- /dev/null +++ b/lib/Storage/UuidResolver/TagResolver.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Storage\UuidResolver; + +use PhpBench\Model\Tag; +use PhpBench\Storage\Exception\InvalidTagException; +use PhpBench\Storage\HistoryEntry; +use PhpBench\Storage\StorageRegistry; +use PhpBench\Storage\UuidResolverInterface; + +class TagResolver implements UuidResolverInterface +{ + /** + * @var StorageRegistry + */ + private $storageRegistry; + + public function __construct(StorageRegistry $storageRegistry) + { + $this->storageRegistry = $storageRegistry; + } + public function resolve(string $reference): ?string + { + $history = $this->storageRegistry->getService()->history(); + + list($offset, $tag) = $this->tagAndOffset($reference); + + $count = 0; + /** @var HistoryEntry $entry */ + foreach ($history as $entry) { + if ($tag->__toString() === strtolower($entry->getTag())) { + if ($count++ < $offset) { + continue; + } + + return $entry->getRunId(); + } + } + + return null; + } + + /** + * @return array{int, Tag} + */ + private function tagAndOffset(string $reference): array + { + if (!preg_match(sprintf('{^(%s)?-?([0-9]+)?$}', Tag::REGEX_PATTERN), $reference, $matches)) { + throw new InvalidTagException(sprintf( + 'Could not parse tag "%s"', $reference + )); + } + + $tag = $matches[1] ? new Tag($matches[1]) : null; + $offset = $matches[2] ?? 0; + + return [(int)$offset, $tag]; + } +} diff --git a/lib/Storage/UuidResolverInterface.php b/lib/Storage/UuidResolverInterface.php new file mode 100644 index 000000000..10aa3ef31 --- /dev/null +++ b/lib/Storage/UuidResolverInterface.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Storage; + +interface UuidResolverInterface +{ + public function resolve(string $reference): ?string; +} diff --git a/lib/SymfonyOptionsResolverCompat.php b/lib/SymfonyOptionsResolverCompat.php new file mode 100644 index 000000000..e69de29bb diff --git a/lib/Util/Cast.php b/lib/Util/Cast.php new file mode 100644 index 000000000..2c0098796 --- /dev/null +++ b/lib/Util/Cast.php @@ -0,0 +1,52 @@ + + */ + private static $multipliers = [ + self::BYTES => 1, + self::KILOBYTES => 1000, + self::MEGABYTES => 1000000, + self::GIGABYTES => 1000000000, + ]; + + private static $aliases = [ + 'b' => self::BYTES, + 'k' => self::KILOBYTES, + 'kb' => self::KILOBYTES, + 'mb' => self::MEGABYTES, + 'gb' => self::GIGABYTES + ]; + + private static $suffixes = [ + self::BYTES => 'b', + self::KILOBYTES => 'kb', + self::MEGABYTES => 'mb', + self::GIGABYTES => 'gb', + ]; + + /** + * @return string[] + */ + public static function supportedUnitNames(): array + { + return array_merge( + [ + self::AUTO, + ], + array_keys(self::$aliases), + array_keys(self::$multipliers) + ); + } + + public static function suffixFor(string $unit): string + { + $unit = self::resolveUnit($unit); + + return isset(self::$suffixes[$unit]) ? self::$suffixes[$unit] : $unit; + } + + public static function isMemoryUnit(string $unit): bool + { + return in_array($unit, self::supportedUnitNames()); + } + + public static function convertTo(float $value, string $fromUnit, string $toUnit): float + { + $fromUnit = self::resolveUnit($fromUnit); + $toUnit = self::resolveUnit($toUnit); + $byteValue = $value * self::$multipliers[$fromUnit]; + + return $byteValue / self::$multipliers[$toUnit]; + } + + public static function resolveSuitableUnit(?string $unit, ?float $value): string + { + if ($unit !== self::AUTO) { + return $unit; + } + + if (null === $value) { + return self::BYTES; + } + + if (($value / 1E9) >= 1) { + return self::GIGABYTES; + } + + if (($value / 1E6) >= 1) { + return self::MEGABYTES; + } + + if (($value / 1E3) >= 1) { + return self::KILOBYTES; + } + + return self::BYTES; + } + + private static function resolveUnit(string $unit): string + { + if (isset(self::$aliases[$unit])) { + $unit = self::$aliases[$unit]; + } + + if (!isset(self::$multipliers[$unit])) { + throw new RuntimeException(sprintf( + 'Unknown memory unit "%s", known memory units: "%s"', + $unit, implode('", "', array_keys(self::$multipliers)) + )); + } + + return $unit; + } +} diff --git a/lib/Util/TextTruncate.php b/lib/Util/TextTruncate.php new file mode 100644 index 000000000..177db6380 --- /dev/null +++ b/lib/Util/TextTruncate.php @@ -0,0 +1,34 @@ + $inputLn) { + return ''; + } + + $truncated = mb_substr($input, $tStart, $tEnd - $tStart); + + if ($tEnd < mb_strlen($input)) { + $truncated = $truncated . ' ' . $elipsis; + } + + if ($tStart > 0) { + $truncated = $elipsis . ' ' . $truncated; + } + + return $truncated; + } +} diff --git a/lib/Util/TimeUnit.php b/lib/Util/TimeUnit.php new file mode 100644 index 000000000..71939364e --- /dev/null +++ b/lib/Util/TimeUnit.php @@ -0,0 +1,416 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace PhpBench\Util; + +/** + * Utility class for representing and converting time units. + */ +class TimeUnit +{ + public const MILLISECOND = 'millisecond'; + public const MICROSECOND = 'microsecond'; + public const SECOND = 'second'; + public const MINUTE = 'minute'; + public const HOUR = 'hour'; + public const DAY = 'day'; + + public const MICROSECONDS = 'microseconds'; + public const MILLISECONDS = 'milliseconds'; + public const SECONDS = 'seconds'; + public const MINUTES = 'minutes'; + public const HOURS = 'hours'; + public const DAYS = 'days'; + + public const MODE_THROUGHPUT = 'throughput'; + public const MODE_TIME = 'time'; + public const AUTO = 'time'; + + /** + * @var array + */ + private static $map = [ + self::MICROSECONDS => 1, + self::MILLISECONDS => 1000, + self::SECONDS => 1000000, + self::MINUTES => 60000000, + self::HOURS => 3600000000, + self::DAYS => 86400000000, + ]; + + private static $aliases = [ + self::MICROSECOND => self::MICROSECONDS, + self::MILLISECOND => self::MILLISECONDS, + self::SECOND => self::SECONDS, + self::MINUTE => self::MINUTES, + self::HOUR => self::HOURS, + self::DAY => self::DAYS, + 'us' => self::MICROSECONDS, + 'ms' => self::MILLISECONDS, + 's' => self::SECONDS, + 'm' => self::MINUTES, + ]; + + /** + * @var array + */ + private static $suffixes = [ + self::MICROSECONDS => 'μs', + self::MILLISECONDS => 'ms', + self::SECONDS => 's', + self::MINUTES => 'm', + self::HOURS => 'h', + self::DAYS => 'd', + ]; + + /** + * @var string + */ + private $sourceUnit; + + /** + * @var string + */ + private $destUnit; + + /** + * @var bool + */ + private $overriddenDestUnit = false; + + /** + * @var bool + */ + private $overriddenMode = false; + + /** + * @var bool + */ + private $overriddenPrecision = false; + + /** + * @var string + */ + private $mode; + + /** + * @var int + */ + private $precision; + + public function __construct( + string $sourceUnit = self::MICROSECONDS, + string $destUnit = self::MICROSECONDS, + string $mode = self::MODE_TIME, + int $precision = 3 + ) { + $this->sourceUnit = $sourceUnit; + $this->destUnit = $destUnit; + $this->mode = $mode; + $this->precision = $precision; + } + + /** + * @return string[] + */ + public static function supportedUnitNames(): array + { + return array_merge( + [ + self::AUTO + ], + array_keys(self::$aliases), + array_keys(self::$map) + ); + } + + /** + * Convert instance value to given unit. + */ + public function toDestUnit(float $time, string $destUnit = null, string $mode = null) + { + return self::convert($time, $this->sourceUnit, $this->getDestUnit($destUnit), $this->getMode($mode)); + } + + /** + * Override the destination unit. + * + */ + public function overrideDestUnit(string $destUnit): void + { + $destUnit = self::normalizeUnit($destUnit); + $this->destUnit = $destUnit; + $this->overriddenDestUnit = true; + } + + /** + * Override the mode. + * + */ + public function overrideMode(string $mode): void + { + self::validateMode($mode); + $this->mode = $mode; + $this->overriddenMode = true; + } + + /** + * Override the precision. + * + */ + public function overridePrecision(int $precision): void + { + $this->precision = $precision; + $this->overriddenPrecision = true; + } + + public function getPrecision(): int + { + return $this->precision; + } + + /** + * Return the destination unit. + * + * @param string $unit + */ + public function getDestUnit(string $unit = null): string + { + // if a unit is given, use that + if ($unit) { + return $unit; + } + + // otherwise return the default + return $this->destUnit; + } + + /** + * Utility method, if the dest unit is overridden, return the overridden + * value. + * + * @return string + */ + public function resolveDestUnit($unit, float $value = null) + { + if ($unit === self::AUTO) { + $unit = self::resolveSuitableUnit($value); + } + + if ($this->overriddenDestUnit) { + return $this->destUnit; + } + + return $unit; + } + + /** + * Utility method, if the mode is overridden, return the overridden + * value. + * + * @return string + */ + public function resolveMode($mode) + { + if ($this->overriddenMode) { + return $this->mode; + } + + return $mode; + } + + /** + * Utility method, if the precision is overridden, return the overridden + * value. + */ + public function resolvePrecision($precision): ?int + { + if ($this->overriddenPrecision) { + return $this->precision; + } + + return $precision; + } + + /** + * Return the destination mode. + */ + public function getMode(string $mode = null): string + { + // if a mode is given, use that + if ($mode) { + return $mode; + } + + // otherwise return the default + return $this->mode; + } + + /** + * Return the destination unit suffix. + */ + public function getDestSuffix(string $unit = null, string $mode = null): string + { + return self::getSuffix($this->getDestUnit($unit), $this->getMode($mode)); + } + + /** + * Return a human readable representation of the unit including the suffix. + */ + public function format(float $time, string $unit = null, string $mode = null, int $precision = null, bool $suffix = true): string + { + $value = number_format( + $this->toDestUnit($time, $unit, $mode), + $precision !== null ? $precision : $this->precision + ); + + if (false === $suffix) { + return $value; + } + + $suffix = $this->getDestSuffix($unit, $mode); + + return $value . $suffix; + } + + /** + * Convert given time in given unit to given destination unit in given mode. + */ + public static function convert(float $time, string $unit, string $destUnit, string $mode) + { + self::validateMode($mode); + + if ($mode === self::MODE_TIME) { + return self::convertTo($time, $unit, $destUnit); + } + + return self::convertInto($time, $unit, $destUnit); + } + + /** + * Convert a given time INTO the given unit. That is, how many times the + * given time will fit into the the destination unit. i.e. `x` per unit. + */ + public static function convertInto(float $time, string $unit, string $destUnit) + { + if (!$time) { + return 0; + } + + $unit = self::normalizeUnit($unit); + $destUnit = self::normalizeUnit($destUnit); + + $destMultiplier = self::$map[$destUnit]; + $sourceMultiplier = self::$map[$unit]; + + $time = $destMultiplier / ($time * $sourceMultiplier); + + return $time; + } + + /** + * Convert the given time from the given unit to the given destination + * unit. + */ + public static function convertTo(float $time, string $unit, string $destUnit): float + { + $unit = self::normalizeUnit($unit); + $destUnit = self::normalizeUnit($destUnit); + + $destM = self::$map[$destUnit]; + $sourceM = self::$map[$unit]; + + $time = ($time * $sourceM) / $destM; + + return $time; + } + + /** + * Return the suffix for a given unit. + * + * @static + * + * @param string $mode + * + * @return string + */ + public static function getSuffix(string $unit, string $mode = null) + { + $unit = self::normalizeUnit($unit); + + $suffix = self::$suffixes[$unit]; + + if ($mode === self::MODE_THROUGHPUT) { + return sprintf('ops/%s', $suffix); + } + + return $suffix; + } + + public static function isTimeUnit(string $unit): bool + { + return in_array($unit, self::supportedUnitNames()); + } + + private static function validateMode(string $mode): void + { + $validModes = [self::MODE_THROUGHPUT, self::MODE_TIME]; + + if (!in_array($mode, $validModes)) { + throw new \InvalidArgumentException(sprintf( + 'Time mode must be one of "%s", got "%s"', + implode('", "', $validModes), $mode + )); + } + } + + public static function normalizeUnit(string $unit): string + { + if (isset(self::$aliases[$unit])) { + $unit = self::$aliases[$unit]; + } + + if (isset(self::$map[$unit])) { + return $unit; + } + + if ($unit === self::AUTO) { + return $unit; + } + + throw new \InvalidArgumentException(sprintf( + 'Invalid time unit "%s", available units: "%s"', + $unit, implode('", "', array_keys(self::$map)) + )); + } + + public static function resolveSuitableUnit(?float $value): string + { + if (null === $value) { + return self::MICROSECONDS; + } + + if (($value / 60E6) >= 1) { + return self::MINUTES; + } + + if (($value / 1E6) >= 1) { + return self::SECONDS; + } + + if (($value / 1E3) >= 1) { + return self::MILLISECONDS; + } + + return self::MICROSECONDS; + } +} diff --git a/phpbench.json.dist b/phpbench.json.dist new file mode 100644 index 000000000..b57eeda11 --- /dev/null +++ b/phpbench.json.dist @@ -0,0 +1,41 @@ +{ + "core.extensions": [ + "PhpBench\\Extension\\DevelopmentExtension" + ], + "runner.bootstrap": "vendor/autoload.php", + "runner.path": "tests/Benchmark", + "runner.php_config": { + "memory_limit": "1G" + }, + "runner.iterations": 10, + "runner.revs": 10, + "runner.time_unit": "milliseconds", + "runner.assert": "mode(variant.time.avg) as ms <= mode(baseline.time.avg) as ms +/- 5%", + "report.generators": { + "all": { + "generator": "composite", + "reports": [ + "env", + "default", + "aggregate" + ] + } + }, + "core.profiles": { + "examples": { + "core.extensions": [ + "PhpBench\\Examples\\Extension\\AcmeExtension" + ], + "runner.path": "examples/Benchmark", + "runner.php_disable_ini": true, + "runner.php_config": { + "extension": [ + "tokenizer.so", + "dom.so", + "json.so", + "iconv.so" + ] + } + } + } +} diff --git a/phpbench.phar b/phpbench.phar new file mode 100755 index 0000000000000000000000000000000000000000..7de3a592fd6d822b446e7460858a39d8579c0596 GIT binary patch literal 1189493 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
!8L&J95CBdG{THua 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