Skip to content

Commit

Permalink
Merge pull request #18 from tienvx/add-queued-loop-path-reducer
Browse files Browse the repository at this point in the history
Add queued loop path reducer
  • Loading branch information
tienvx committed May 16, 2018
2 parents cf03f07 + 0b1897d commit deb88c7
Show file tree
Hide file tree
Showing 78 changed files with 1,668 additions and 681 deletions.
3 changes: 3 additions & 0 deletions .coveralls.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
coverage_clover: tests/logs/clover.xml
json_path: tests/logs/coveralls-upload.json
service_name: travis-ci
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
# Ignore files created during tests
/tests/app/var/cache/
/tests/app/var/logs/
/tests/logs/
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ php:
install:
- composer install

script: phpunit --verbose
script: phpunit --coverage-clover ./tests/logs/clover.xml

after_success:
- travis_retry php vendor/bin/php-coveralls
94 changes: 57 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,77 @@
# MbtBundle [![Build Status](https://travis-ci.org/tienvx/mbt-bundle.svg?branch=master)](https://travis-ci.org/tienvx/mbt-bundle)
# MBT Bundle [![Build Status][travis_badge]][travis_link] [![Coverage Status][coveralls_badge]][coveralls_link]

This Bundle provides ability to test your application using Model Based Testing
techique. Before testing your project, you need to create new **symfony project**
to load this bundle. Then by defining workflows (the way your system work) and
entities (the way to interact with your system) in that project, it will test
your project for you.
techique.

The major features are:
1. Automatically generate test cases when a task is created.
2. Automatically execute those test cases to test your application.
3. Automatically reduce reproduce path when a bug is found.
4. Automatically report the bug when the reproduce path is reduced.

All you have to do:
1. Define models to describe your application.
2. Define subjects to interact with your application.
3. Create tasks based on your need. e.g.:
1. Test the whole application to make sure there are no bugs in the application.
2. Test only models that have a tag to make sure the part of your application is still working while developing a feature.
3. Test a model to make sure a bug that has been fixed is not regressed.
4. Manage bugs that has been found. e.g. mark a bug has been fixed.

## Requirements

* PHP 7.1 / 7.2
* Symfony 4.1
* See also the `require` section of [composer.json](composer.json)

## Installation

### Step 1: Download the Bundle
### Step 1: Create symfony project

Open a command console, enter your project directory and execute the
following command to download the latest stable version of this bundle:
Before testing your application, you need to create new **symfony project**
to use this bundle:

```console
$ composer create-project symfony/skeleton my-project
```

### Step 2: Download the Bundle

Install lastest version of this bundle:

```console
$ composer require tienvx/mbt-bundle "1.0.x-dev"
```

This command requires you to have Composer installed globally, as explained
in the [installation chapter](https://getcomposer.org/doc/00-intro.md)
of the Composer documentation.
### Step 3: Create models and subjects

### Step 2: Enable the Bundle
Model is the way to describe part your application. Subject is
the way to tell this bundle to interact with your application.

Then, enable the bundle by adding it to the list of registered bundles
in the `app/AppKernel.php` file of your project:
## Documentation

```php
<?php
// app/AppKernel.php
For the usage guide and reference, see [wiki][wiki]

// ...
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
// ...
new Tienvx\Bundle\MbtBundle\TienvxMbtBundle(),
);
## Contributing

// ...
}
Pull requests are welcome, please [send pull requests][pulls].

// ...
}
```
If you found any bug, please [report issues][issues].

Thanks to
[everyone who has contributed][contributors] already.

## License

This package is available under the [MIT license](LICENSE).

### Note
If you are using symfony version 4 or later, this step is not required. Symfony do it
automatically for you.
[travis_badge]: https://travis-ci.org/tienvx/mbt-bundle.svg?branch=master
[travis_link]: https://travis-ci.org/tienvx/mbt-bundle

## Resources
[coveralls_badge]: https://coveralls.io/repos/tienvx/mbt-bundle/badge.svg?branch=master&service=github
[coveralls_link]: https://coveralls.io/github/tienvx/mbt-bundle?branch=master

* [Report issues](https://github.com/tienvx/mbt-bundle/issues)
* [Send Pull Requests](https://github.com/tienvx/mbt-bundle/pulls)
[wiki]: https://github.com/tienvx/mbt-bundle/wiki
[contributors]: https://github.com/tienvx/mbt-bundle/graphs/contributors
[pulls]: https://github.com/tienvx/mbt-bundle/pulls
[issues]: https://github.com/tienvx/mbt-bundle/issues
27 changes: 14 additions & 13 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,30 @@
"homepage": "https://github.com/tienvx/TienvxMbtBundle/contributors"
}
],
"minimum-stability": "dev",
"minimum-stability": "beta",
"require": {
"graphp/algorithms": "^0.8.1",
"php": "^7.1.0",
"symfony/framework-bundle": "4.1.x-dev",
"symfony/console": "4.1.x-dev",
"symfony/workflow": "4.1.x-dev",
"symfony/expression-language": "4.1.x-dev",
"symfony/validator": "4.1.x-dev",
"symfony/framework-bundle": "4.1.0-beta1",
"symfony/console": "4.1.0-beta1",
"symfony/workflow": "4.1.0-beta1",
"symfony/expression-language": "4.1.0-beta1",
"symfony/validator": "4.1.0-beta1",
"doctrine/doctrine-bundle": "^1.9",
"doctrine/orm": "^2.6",
"symfony/messenger": "4.1.x-dev"
"symfony/messenger": "4.1.0-beta1"
},
"require-dev": {
"api-platform/core": "^2.2",
"phpunit/phpunit": "^6.5",
"symfony/browser-kit": "4.1.x-dev",
"symfony/browser-kit": "4.1.0-beta1",
"doctrine/doctrine-fixtures-bundle": "^3.0",
"symfony/yaml": "4.1.x-dev",
"symfony/templating": "4.1.x-dev",
"symfony/twig-bundle": "4.1.x-dev",
"symfony/asset": "4.1.x-dev",
"symfony/swiftmailer-bundle": "^3.2"
"symfony/yaml": "4.1.0-beta1",
"symfony/templating": "4.1.0-beta1",
"symfony/twig-bundle": "4.1.0-beta1",
"symfony/asset": "4.1.0-beta1",
"symfony/swiftmailer-bundle": "^3.2",
"php-coveralls/php-coveralls": "^2.0"
},
"autoload": {
"psr-4": {
Expand Down
5 changes: 1 addition & 4 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,9 @@

<filter>
<whitelist>
<directory>./</directory>
<directory>./src</directory>
<exclude>
<directory>./src/Resources</directory>
<directory>./tests</directory>
<directory>./vendor</directory>
<directory>./doc</directory>
</exclude>
</whitelist>
</filter>
Expand Down
2 changes: 1 addition & 1 deletion src/Command/BugOutputTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ private function printBug(string $message, Path $path, OutputInterface $output)
['Step', 'Action', 'Data'],
]);
foreach ($path->getEdges() as $index => $edge) {
$table->addRow([$index + 1, $edge->getAttribute('label'), json_encode($path->hasDataAt($index) ? $path->getDataAt($index) : [])]);
$table->addRow([$index + 1, $edge->getAttribute('label'), json_encode($path->getDataAt($index) ?? [])]);
}
$table->render();
}
Expand Down
8 changes: 2 additions & 6 deletions src/Command/DumpModelCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,13 @@ protected function configure()
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$model = $input->getArgument('model');
$workflowMetadata = $this->modelRegistry->getModel($model);
$subject = $workflowMetadata['subject'];
$subject = new $subject();
$workflow = $this->workflows->get($subject, $model);
$model = $this->modelRegistry->getModel($input->getArgument('model'));

if ('puml' === $input->getOption('format')) {
$dumper = new PlantUmlDumper(PlantUmlDumper::STATEMACHINE_TRANSITION);
} else {
$dumper = new StateMachineGraphvizDumper();
}
$output->write($dumper->dump($workflow->getDefinition()));
$output->write($dumper->dump($model->getDefinition()));
}
}
48 changes: 13 additions & 35 deletions src/Command/ExecuteTaskCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,36 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Throwable;
use Tienvx\Bundle\MbtBundle\Entity\Bug;
use Tienvx\Bundle\MbtBundle\Entity\Task;
use Tienvx\Bundle\MbtBundle\Generator\GeneratorArgumentsTrait;
use Tienvx\Bundle\MbtBundle\Model\Constants;
use Tienvx\Bundle\MbtBundle\Service\GeneratorManager;
use Tienvx\Bundle\MbtBundle\Service\ModelRegistry;
use Tienvx\Bundle\MbtBundle\Service\PathReducerManager;
use Tienvx\Bundle\MbtBundle\Service\ReporterManager;
use Tienvx\Bundle\MbtBundle\Service\StopConditionManager;

class ExecuteTaskCommand extends Command
{
use GeneratorArgumentsTrait;

private $modelRegistry;
private $generatorManager;
private $pathReducerManager;
private $entityManager;
private $reporterManager;
private $defaultReporter;
private $stopConditionManager;

public function __construct(
ModelRegistry $modelRegistry,
GeneratorManager $generatorManager,
PathReducerManager $pathReducerManager,
EntityManagerInterface $entityManager,
ReporterManager $reporterManager)
ReporterManager $reporterManager,
StopConditionManager $stopConditionManager)
{
$this->modelRegistry = $modelRegistry;
$this->generatorManager = $generatorManager;
$this->pathReducerManager = $pathReducerManager;
$this->entityManager = $entityManager;
$this->reporterManager = $reporterManager;
$this->stopConditionManager = $stopConditionManager;

parent::__construct();
}
Expand All @@ -53,11 +51,6 @@ protected function configure()
->addArgument('task-id', InputArgument::REQUIRED, 'The task id to execute.');
}

public function setDefaultReporter(string $defaultReporter)
{
$this->defaultReporter = $defaultReporter;
}

/**
* @param InputInterface $input
* @param OutputInterface $output
Expand All @@ -68,45 +61,30 @@ protected function execute(InputInterface $input, OutputInterface $output)
$taskId = $input->getArgument('task-id');
$task = $this->entityManager->getRepository(Task::class)->find($taskId);

if (!$task) {
if (!$task || !$task instanceof Task) {
$output->writeln(sprintf('No task found for id %d', $taskId));
return;
}

/** @var Task $task */
$model = $task->getModel();
$generator = $this->generatorManager->getGenerator($task->getGenerator());
$workflowMetadata = $this->modelRegistry->getModel($model);
$subject = $workflowMetadata['subject'];
$arguments = $this->parseGeneratorArguments($task->getArguments());
$model = $this->modelRegistry->getModel($task->getModel());
$subject = $model->createSubject();
$stopCondition = $this->stopConditionManager->getStopCondition($task->getStopCondition());
$stopCondition->setArguments(json_decode($task->getStopConditionArguments(), true));

$generator->init($model, $subject, $arguments);
$generator->init($model, $subject, $stopCondition);

try {
while (!$generator->meetStopCondition() && $edge = $generator->getNextStep()) {
if ($generator->canGoNextStep($edge)) {
$generator->goToNextStep($edge);
}
$generator->goToNextStep($edge);
}
}
catch (Throwable $throwable) {
$path = $generator->getPath();
$reducer = $task->getReducer();
if ($reducer) {
$pathReducer = $this->pathReducerManager->getPathReducer($reducer);
$path = $pathReducer->reduce($path, $model, $subject, $throwable);
}

if ($this->reporterManager->hasReporter($this->defaultReporter)) {
$bug = new Bug();
$bug->setTitle($throwable->getMessage());
$bug->setMessage($throwable->getMessage());
$bug->setTask($task);
$bug->setSteps($path);
$bug->setStatus('unverified');
$bug->setReporter($this->defaultReporter);
$this->entityManager->persist($bug);
$this->entityManager->flush();
$pathReducer->reduce($path, $model, $throwable->getMessage(), $taskId);
}
}
}
Expand Down
28 changes: 15 additions & 13 deletions src/Command/GenerateStepsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,25 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Tienvx\Bundle\MbtBundle\Generator\GeneratorArgumentsTrait;
use Tienvx\Bundle\MbtBundle\Model\Constants;
use Tienvx\Bundle\MbtBundle\Service\GeneratorManager;
use Tienvx\Bundle\MbtBundle\Service\ModelRegistry;
use Tienvx\Bundle\MbtBundle\Service\StopConditionManager;

class GenerateStepsCommand extends Command
{
use GeneratorArgumentsTrait;

private $modelRegistry;
private $generatorManager;
private $stopConditionManager;

public function __construct(ModelRegistry $modelRegistry, GeneratorManager $generatorManager)
public function __construct(
ModelRegistry $modelRegistry,
GeneratorManager $generatorManager,
StopConditionManager $stopConditionManager)
{
$this->modelRegistry = $modelRegistry;
$this->generatorManager = $generatorManager;
$this->stopConditionManager = $stopConditionManager;

parent::__construct();
}
Expand All @@ -35,7 +38,8 @@ protected function configure()
->setHelp('Generate steps from model. So that it can be run with mbt:run-steps command.')
->addArgument('model', InputArgument::REQUIRED, 'The model to generate.')
->addOption('generator', 'g', InputOption::VALUE_OPTIONAL, 'The generator to generate steps from the model.', Constants::DEFAULT_GENERATOR)
->addOption('arguments', 'a', InputOption::VALUE_OPTIONAL, 'The arguments of the generator.', Constants::DEFAULT_ARGUMENTS);
->addOption('stop-condition', 's', InputOption::VALUE_OPTIONAL, 'When generator stop generate steps.', Constants::DEFAULT_STOP_CONDITION)
->addOption('stop-condition-arguments', 'a', InputOption::VALUE_OPTIONAL, 'The arguments of the stop condition.', Constants::DEFAULT_STOP_CONDITION_ARGUMENTS);
}

/**
Expand All @@ -45,18 +49,16 @@ protected function configure()
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$model = $input->getArgument('model');
$generator = $this->generatorManager->getGenerator($input->getOption('generator'));
$workflowMetadata = $this->modelRegistry->getModel($model);
$subject = $workflowMetadata['subject'];
$arguments = $this->parseGeneratorArguments($input->getOption('arguments'));
$model = $this->modelRegistry->getModel($input->getArgument('model'));
$subject = $model->createSubject(true);
$stopCondition = $this->stopConditionManager->getStopCondition($input->getOption('stop-condition'));
$stopCondition->setArguments(json_decode($input->getOption('stop-condition-arguments'), true));

$generator->init($model, $subject, $arguments, true);
$generator->init($model, $subject, $stopCondition);

while (!$generator->meetStopCondition() && $edge = $generator->getNextStep()) {
if ($generator->canGoNextStep($edge)) {
$generator->goToNextStep($edge);
}
$generator->goToNextStep($edge);
}

$path = $generator->getPath();
Expand Down
Loading

0 comments on commit deb88c7

Please sign in to comment.