{"id":13616857,"url":"https://github.com/liuggio/fastest","last_synced_at":"2026-03-17T10:07:10.311Z","repository":{"id":20750182,"uuid":"24034649","full_name":"liuggio/fastest","owner":"liuggio","description":"Simple parallel testing execution... with some goodies for functional tests.","archived":false,"fork":false,"pushed_at":"2026-02-23T08:26:39.000Z","size":1852,"stargazers_count":483,"open_issues_count":18,"forks_count":71,"subscribers_count":8,"default_branch":"master","last_synced_at":"2026-02-23T16:52:38.910Z","etag":null,"topics":["hacktoberfest","hacktoberfest2020"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/liuggio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2014-09-14T22:06:02.000Z","updated_at":"2026-02-23T08:26:43.000Z","dependencies_parsed_at":"2024-03-15T10:32:16.183Z","dependency_job_id":"0eb1e998-4d7c-4539-9baf-fde7ca25a30b","html_url":"https://github.com/liuggio/fastest","commit_stats":{"total_commits":170,"total_committers":37,"mean_commits":4.594594594594595,"dds":0.7588235294117647,"last_synced_commit":"52b7768e806e25c7ee9ed4f11b1697c4f0b6410f"},"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"purl":"pkg:github/liuggio/fastest","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuggio%2Ffastest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuggio%2Ffastest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuggio%2Ffastest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuggio%2Ffastest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/liuggio","download_url":"https://codeload.github.com/liuggio/fastest/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuggio%2Ffastest/sbom","scorecard":{"id":593436,"data":{"date":"2025-08-11","repo":{"name":"github.com/liuggio/fastest","commit":"a3d9d23607fdf28cc7a539f5b7fcae867d79dbfc"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":3,"reason":"Found 9/24 approved changesets -- score normalized to 3","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 23 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-20T22:32:37.003Z","repository_id":20750182,"created_at":"2025-08-20T22:32:37.003Z","updated_at":"2025-08-20T22:32:37.003Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30622221,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-17T08:10:05.930Z","status":"ssl_error","status_checked_at":"2026-03-17T08:10:04.972Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hacktoberfest","hacktoberfest2020"],"created_at":"2024-08-01T20:01:34.154Z","updated_at":"2026-03-17T10:07:10.305Z","avatar_url":"https://github.com/liuggio.png","language":"PHP","funding_links":[],"categories":["PHP"],"sub_categories":[],"readme":"Fastest - simple parallel testing execution\n===========================================\n\n![example branch parameter](https://github.com/liuggio/fastest/actions/workflows/build.yml/badge.svg?branch=master)\n[![Latest Stable Version](https://poser.pugx.org/liuggio/fastest/v/stable.svg)](https://packagist.org/packages/liuggio/fastest) [![Latest Unstable Version](https://poser.pugx.org/liuggio/fastest/v/unstable.svg)](https://packagist.org/packages/liuggio/fastest)\n\n## Only one thing\n\n**Execute parallel commands, creating a Process for each Processor (with some goodies for functional tests).**\n\n``` bash\nfind tests/ -name \"*Test.php\" | ./vendor/liuggio/fastest/fastest \"vendor/phpunit/phpunit/phpunit -c app {};\"\n```\n\nFastest works with **any available testing tool**! It just executes it in parallel.\n\nIt is optimized for functional tests, giving an easy way to work with N databases in parallel.\n\n## Motto\n\n\u003e \"I had a problem,  \n\u003e  so I decided to use threads.  \n\u003e  tNwoowp rIo bhlaevmes.\n\n## Why\n\nWe were tired of not being able to run [paratest](https://github.com/brianium/paratest) with our project (big complex functional project).  \n[Parallel](https://github.com/grosser/parallel) is a great tool but not so nice for functional tests.  \nThere were no simple tool available for functional tests.\n\nOur old codebase run in 30 minutes, now in 7 minutes with 4 Processors.\n\n## Features\n\n1. Functional tests could use a database per processor using the environment variable.\n2. Tests are randomized by default.\n3. Is not coupled with PhpUnit you could run any command.\n3. Is developed in PHP with no dependencies.\n4. As input you could use a `phpunit.xml.dist` file or use pipe (see below).\n5. Includes a Behat extension to easily pipe scenarios into fastest.\n6. Increase Verbosity with -v option.\n7. Works with a installation in project or global mode\n\n## How\n\nIt creates N threads where N is the number of the core in the computer.  \nReally fast,\n100% written in PHP, inspired by [Parallel](https://github.com/grosser/parallel).\n\n## Usage\n\n### Configure paths to binaries\n\nExamples shown below use paths to binaries installed in the `vendor/` directory.\nYou can use symlinks in the `bin/` directory by defining the [`bin-dir` parameter of Composer](https://getcomposer.org/doc/06-config.md#bin-dir):\n\n```\ncomposer config \"bin-dir\" \"bin\"\n```\n\nThen you'll be able to call binaries in the `bin/` directory:\n\n- `bin/fastest` instead of `vendor/liuggio/fastest/fastest`\n- `bin/phpunit` instead of `vendor/phpunit/phpunit/phpunit`\n\n### Parallelize everything\n\n``` bash\nls | ./fastest \"echo slow operation on {}\" -vvv\n```\n#### Using the placeholders\n\n`{}` is the current test file.  \n`{p}` is the current processor number.  \n`{n}` is the unique number of the current test.\n`phpunit {}` is used as default command.\n\n### PHPUnit\n\n#### A. Using `ls`, list of folders as input **suggested**\n\n``` bash\nls -d test/* | ./vendor/liuggio/fastest/fastest \"vendor/phpunit/phpunit/phpunit {};\"\n```\n\n#### B. using `find`, list of php files as input\n\n``` bash\nfind tests/ -name \"*Test.php\" | ./vendor/liuggio/fastest/fastest  \"vendor/phpunit/phpunit/phpunit {};\"\n```\n\n#### C. Using `phpunit.xml.dist` as input\n\nYou can use the option `-x` and import the test suites from the `phpunit.xml.dist`\n\n`./vendor/liuggio/fastest/fastest -x phpunit.xml.dist \"vendor/phpunit/phpunit/phpunit {};\"`\n\nIf you use this option make sure the test-suites contains a lot of directories: **this feature should be improved, don't blame help instead.**\n\n### Functional tests and database\n\nInside your tests you could use the env. variables,  \nif you are running tests on a computer that has 4 core, `fastest` will create 4 threads in parallel,\nand inside your test you could use those variables to better identify the current process:\n\n``` php\necho getenv('ENV_TEST_CHANNEL');          // The number of the current channel that is using the current test eg.2\necho getenv('ENV_TEST_CHANNEL_READABLE'); // Name used to make the database name unique, is a readable name eg. test_2\necho getenv('ENV_TEST_CHANNELS_NUMBER');  // Max channel number on a system (the core number) eg. 4\necho getenv('ENV_TEST_ARGUMENT');         // The current running test eg. tests/UserFunctionalTest.php\necho getenv('ENV_TEST_INC_NUMBER');       // Unique number of the current test eg. 32\necho getenv('ENV_TEST_IS_FIRST_ON_CHANNEL'); // Is 1 if is the first test on its thread useful for clear cache.\n```\n\n### Setup the database `before`\n\nYou can also run a script per process **before** the tests, useful for init schema and fixtures loading.\n\n``` bash\nfind tests/ -name \"*Test.php\" | ./vendor/liuggio/fastest/fastest -b\"app/console doc:sch:create -e test\" \"vendor/phpunit/phpunit/phpunit {};\";\n```\n\n### Generate and merge code coverage (or junit files)\n\n``` bash\n# Install phpcov in order to merge the code coverage\ncomposer require --dev phpunit/phpcov\n# Create a directory where the coverage files will be put\nmkdir -p cov/fastest/\n# Generate as many files than tests, since {n} is an unique number for each test\nfind tests/ -name \"*Test.php\" | vendor/liuggio/fastest/fastest \"vendor/phpunit/phpunit/phpunit -c app {} --coverage-php cov/fastest/{n}.cov;\"\n# Merge the code coverage files\nphpcov merge cov/fastest/ --html cov/merge/fastest/\n```\n\nCode coverage will be available in the `cov/merge/fastest/` directory.\n\nThis can also be used for junit using an alternative library [phpunit-merger](https://github.com/Nimut/phpunit-merger).\n\n## Storage adapters\n\nIf you want to parallel functional tests, and if you have a machine with 4 CPUs, the best thing you could do is create a db foreach parallel process,\n`fastest` gives you the opportunity to work easily with Symfony.\n\nModifying the `config_test.yml` config file in Symfony, each functional test will look for a database called `\u003cdatabase_name\u003e_test_x` automatically (x is from 1 to CPUs number).\n\n### Doctrine DBAL\n\n`config_test.yml`\n``` yml\nparameters:\n    # Stubs\n    doctrine.dbal.connection_factory.class: Liuggio\\Fastest\\Doctrine\\DBAL\\ConnectionFactory\n```\n\n### Doctrine MongoDB Connection \n\n`config_test.yml`\n``` yml\nparameters:\n    # Stubs\n    doctrine_mongodb.odm.connection.class: Liuggio\\Fastest\\Doctrine\\MongoDB\\Connection\n```\n\n### SQLite databases\n\nSQLite databases don't have names. It's always 1 database per file. If SQLite driver is detected, instead switching the database name, database path will be changed. To make it work simply add `__DBNAME__` placeholder in your database path.\n\n`config_test.yml`\n``` yml\ndoctrine:\n    dbal:\n        driver:   pdo_sqlite\n        path:     \"%kernel.cache_dir%/__DBNAME__.db\"\n        \nparameters:\n    doctrine.dbal.connection_factory.class: Liuggio\\Fastest\\Doctrine\\DBAL\\ConnectionFactory\n```\n\nWhere `__DBNAME__` will be replaced with `ENV_TEST_CHANNEL_READABLE` value.\n\n### Behat.* extension\n\nA Behat extension is included that provides the ability for Behat to output a list of feature files or individual scenarios that would be executed without\nactually executing them. This list can be piped into fastest to run the scenarios in parallel.\n\nTo install the extension just add it to your `behat.yml` file:\n\n``` yaml\nextensions:\n    Liuggio\\Fastest\\Behat\\ListFeaturesExtension\\Extension: ~\n```\n\nfor Behat2:\n\n``` yaml\nextensions:\n    Liuggio\\Fastest\\Behat2\\ListFeaturesExtension\\Extension: ~\n```\n\nAfter this you will have two additional command line options: `--list-features` and `--list-scenarios`. The former will output a list of *.feature files\nand the later will output each scenario of each feature file, including its line number (e.g. /full/path/Features/myfeature.feature:lineNumber)\n\nThis will let you pipe the output directly into fastest to parallelize its execution:\n\n    /my/path/behat --list-scenarios | ./vendor/liuggio/fastest/fastest \"/my/path/behat {}\"\n\nWhile using `--list-scenarios` might be preferred over `--list-features` because it will give a more granular\nscenario-by-scenario output, allowing fastest to shuffle and balance individual tests in a better way...\nthis can lead to a problem merging junit output e.g.\n\n```\n  Scenario Outline: my scenario\n    Given some setup\n    When I do \u003cvariable\u003e\n    Then I see \u003cvariable\u003e\n\n  Examples:\n    | variable |\n    | a        |\n    | b        |\n```\n\nBoth of the separate junit xml files detail the testcase as name my `scenario #1` which confuses the merging logic.\nFor this reason `--list-features` might be safer.\n\nTake care with using `--tags` in the behat command piping into fastest to then use the same tag list within the second behat call.\nThis is because one may have a default tag set within its default profile in the `behat.yml` and this may result in scenarios\nnot being found and therefore not being executed.\n\n### About browser-based tests (Selenium, Mink, etc)\n\nWhen a browser is controlled remotely via PHPUnit, Behat or another test suite that is being used by Fastest, the browser makes requests\nback to the server. The problem is that when the server process the request it has no idea of which fastest channel called it, so there must\nbe a way to set this information before connecting to the database (in order to choose the correct database that corresponds to the channel).\n\nOne possible way is to implement the following steps:\n\n#### 1. Set a cookie, GET query parameter or HTTP header with the appropriate channel value\n\nWhen your test scenario begins, maybe at the authentication phase, set one of the following to the value of the environment variable `ENV_TEST_CHANNEL_READABLE`:\n\n* If it's a cookie or a GET query parameter name it ENV_TEST_CHANNEL_READABLE\n   * Beware that if you use the GET query parameter option and via automation you click on a link of the browser that doesn't have that query parameter, the request won't\n   have the query parameter the server won't know the channel to initialize.\n* If it's a HTTP header name it X-FASTEST-ENV-TEST-CHANNEL-READABLE and send it on every request to the server.\n\n#### 2. Configure the entry point of your application to set the environment variables for the request\n\nFor this is enough to add the following code before booting your application:\n\n    \\Liuggio\\Fastest\\Environment\\FastestEnvironment::setFromRequest();\n\nThis will detect the presence of the ENV_TEST_CHANNEL_READABLE value in any of the contexts mentioned in #1 and set the corresponding environment variable.\n\nFor example, in the case of the Symfony framework you may just add it in `web/app_dev.php` just before `require_once __DIR__.'/../app/AppKernel.php'`:\n\n``` php\n// ... code\n$loader = require_once __DIR__.'/../app/bootstrap.php.cache';\nDebug::enable();\n\n\\Liuggio\\Fastest\\Environment\\FastestEnvironment::setFromRequest();\n\nrequire_once __DIR__.'/../app/AppKernel.php';\n$kernel = new AppKernel('dev', true);\n// ... code\n```\n\n## Install\n\nIf you use Composer just run `composer require --dev 'liuggio/fastest:^1.6'`\n\nor simply add a dependency on liuggio/fastest to your project's composer.json file:\n\n\t{\n\t    \"require-dev\": {\n\t\t    \"liuggio/fastest\": \"^1.6\"\n\t    }\n\t}\n\nFor a system-wide installation via Composer, you can run:\n\n`composer global require \"liuggio/fastest=^1.6\"`\n\nMake sure you have `~/.composer/vendor/bin/` in your path,\nread more at [getcomposer.org](https://getcomposer.org/doc/00-intro.md#globally)\n\nIf you want to use it with phpunit you may want to install phpunit/phpunit as dependency.\n\n### Run this test with `fastest`\n\n```\nUsage:\n fastest [-p|--process=\"...\"] [-b|--before=\"...\"] [-x|--xml=\"...\"] [-o|--preserve-order] [--no-errors-summary] [execute]\n\nArguments:\n execute               Optional command to execute.\n\nOptions:\n --process (-p)        Number of parallel processes, default: available CPUs.\n --before (-b)         Execute a process before consuming the queue, it executes this command once per process, useful for init schema and load fixtures.\n --xml (-x)            Read input from a phpunit xml file from the '\u003ctestsuites\u003e' collection. Note: it is not used for consuming.\n --preserve-order (-o) Queue is randomized by default, with this option the queue is read preserving the order.\n --rerun-failed (-r)   Re-run failed test with before command if exists.\n --no-errors-summary   Do not display all errors after the test run. Useful with --vv because it already displays errors immediately after they happen.\n --no-progress         Do not display progress bar when running.\n --help (-h)           Display this help message.\n --quiet (-q)          Do not output any message.\n --verbose (-v|vv|vvv) Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug\n --version (-V)        Display this application version.\n --ansi                Force ANSI output.\n --no-ansi             Disable ANSI output.\n --no-interaction (-n) Do not ask any interactive question.\n\n```\n\ne.g. `./fastest -x phpunit.xml.dist -v \"bin/phpunit {}\"`\n\n### Known problems\n\nIf you're facing problems with unknown command errors, make sure your  [variables-order](http://us.php.net/manual/en/ini.core.php#ini.variables-order) `php.ini` setting contains `E`.\nIf not, your environment variables are not set, and commands that are in your `PATH` will not work.\n\n### Contribution\n\nPlease help with code, love, feedback and bug reporting.\n\nThanks to: \n \n- @giorrrgio for the mongoDB adapter\n- @diegosainz for the Behat2 adapter\n- @barryswaisland-eagleeye for some updated docs\n- you?\n\n\n### License [![License](https://poser.pugx.org/liuggio/fastest/license.svg)](https://packagist.org/packages/liuggio/fastest)\n\nRead [LICENSE](./LICENSE) for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliuggio%2Ffastest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fliuggio%2Ffastest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliuggio%2Ffastest/lists"}