From 523b9861b968fe6ca8679e56ffef38e53605216f Mon Sep 17 00:00:00 2001 From: "tien.xuan.vo" Date: Thu, 13 Dec 2018 17:29:36 +0700 Subject: [PATCH 1/3] Fix escaping characters --- src/Graph/Dumper/PlantUmlDumper.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Graph/Dumper/PlantUmlDumper.php b/src/Graph/Dumper/PlantUmlDumper.php index 4254794c..e054f373 100644 --- a/src/Graph/Dumper/PlantUmlDumper.php +++ b/src/Graph/Dumper/PlantUmlDumper.php @@ -32,13 +32,13 @@ public function dump(string $initialPlaces, Graph $graph, array $options = array $code = $this->initialize($options); /** @var Vertex $vertex */ foreach ($graph->getVertices() as $vertex) { - $placeEscaped = $this->escape($vertex->getId()); + $placeEscaped = $this->escape(implode(',', json_decode($vertex->getId(), true))); $code[] = "state $placeEscaped" . ($initialPlaces === $vertex->getId() ? ' '.self::INITIAL : ''); } /** @var Directed $edge */ foreach ($graph->getEdges() as $edge) { - $fromEscaped = $this->escape($edge->getVertexStart()->getId()); - $toEscaped = $this->escape($edge->getVertexEnd()->getId()); + $fromEscaped = $this->escape(implode(',', json_decode($edge->getVertexStart()->getId()))); + $toEscaped = $this->escape(implode(',', json_decode($edge->getVertexEnd()->getId()))); $transitionEscaped = $this->escape($edge->getAttribute('name')); $code[] = "$fromEscaped --> $toEscaped: $transitionEscaped"; } From 2b4eb0e7c9203c94fa3c397727928360f1d1f5f2 Mon Sep 17 00:00:00 2001 From: "tien.xuan.vo" Date: Thu, 13 Dec 2018 17:34:22 +0700 Subject: [PATCH 2/3] Fix generating wrong graph for workflow --- src/Service/GraphBuilder.php | 5 +- tests/Command/GraphDumpCommandTest.php | 68 +++++++++++++++++++++++--- 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/src/Service/GraphBuilder.php b/src/Service/GraphBuilder.php index 535a7717..13a90276 100644 --- a/src/Service/GraphBuilder.php +++ b/src/Service/GraphBuilder.php @@ -96,11 +96,12 @@ private function buildForWorkflow(Workflow $workflow) { $vertices = $graph->getVertices()->getVerticesMatch(function (Vertex $vertex) use ($froms) { $places = $vertex->getAttribute('places'); - return array_diff($places, $froms) && array_intersect($places, $froms) && !array_diff(array_intersect($places, $froms), $froms); + $intersect = array_intersect($places, $froms); + return array_diff($places, $froms) && count($intersect) === count($froms) && !array_diff($intersect, $froms); }); foreach ($vertices as $vertex) { $places = $vertex->getAttribute('places'); - $newPlaces = array_replace($places, $froms, $tos); + $newPlaces = array_unique(array_merge(array_diff($places, $froms), $tos)); sort($places); sort($newPlaces); $from = json_encode($places); diff --git a/tests/Command/GraphDumpCommandTest.php b/tests/Command/GraphDumpCommandTest.php index c57ef1b3..1582a177 100644 --- a/tests/Command/GraphDumpCommandTest.php +++ b/tests/Command/GraphDumpCommandTest.php @@ -8,9 +8,66 @@ class GraphDumpCommandTest extends CommandTestCase { public function modelData() { + $articleDot = +'digraph workflow { + ratio="compress" rankdir="LR" label="Article" + node [fontsize="9" fontname="Arial" color="#333333" fillcolor="lightblue" fixedsize="false" width="1"]; + edge [fontsize="9" fontname="Arial" color="#333333" arrowhead="normal" arrowsize="0.5"]; + + place_5916096c35437dab3f6a120790f2a431656affb5 [label="[\"draft\"]", shape=circle, style="filled"]; + place_9e77ae793650b6bd67da69003adb32a9b17eb712 [label="[\"wait_for_journalist\",\"wait_for_spellchecker\"]", shape=circle]; + place_03ef92587dde689543f7a2bfaa85e3744e9627f0 [label="[\"wait_for_journalist\"]", shape=circle]; + place_ef8332535747bd04b47faa11e2d6d8a767585208 [label="[\"approved_by_journalist\"]", shape=circle]; + place_429a3f35b9bcae743353aa5f800f0bda20c6c5e7 [label="[\"approved_by_journalist\",\"wait_for_spellchecker\"]", shape=circle]; + place_40a974ae46330f7c0eaf9998260b86bddde9f616 [label="[\"wait_for_spellchecker\"]", shape=circle]; + place_9d8d5371bae52cae02416988609683522d86ab14 [label="[\"approved_by_spellchecker\"]", shape=circle]; + place_37e58158cff8e991ff736f76ef5256dc8b4ae850 [label="[\"approved_by_spellchecker\",\"wait_for_journalist\"]", shape=circle]; + place_309776058fe7a6debc12bc716d2cac6a3167eb2a [label="[\"approved_by_journalist\",\"approved_by_spellchecker\"]", shape=circle]; + place_0176512e721e0f9b27d66831090c2408c3c4a040 [label="[\"published\"]", shape=circle]; + place_5916096c35437dab3f6a120790f2a431656affb5 -> place_9e77ae793650b6bd67da69003adb32a9b17eb712 [label="request_review" style="solid"]; + place_03ef92587dde689543f7a2bfaa85e3744e9627f0 -> place_ef8332535747bd04b47faa11e2d6d8a767585208 [label="journalist_approval" style="solid"]; + place_9e77ae793650b6bd67da69003adb32a9b17eb712 -> place_429a3f35b9bcae743353aa5f800f0bda20c6c5e7 [label="journalist_approval" style="solid"]; + place_9e77ae793650b6bd67da69003adb32a9b17eb712 -> place_37e58158cff8e991ff736f76ef5256dc8b4ae850 [label="spellchecker_approval" style="solid"]; + place_40a974ae46330f7c0eaf9998260b86bddde9f616 -> place_9d8d5371bae52cae02416988609683522d86ab14 [label="spellchecker_approval" style="solid"]; + place_429a3f35b9bcae743353aa5f800f0bda20c6c5e7 -> place_309776058fe7a6debc12bc716d2cac6a3167eb2a [label="spellchecker_approval" style="solid"]; + place_309776058fe7a6debc12bc716d2cac6a3167eb2a -> place_0176512e721e0f9b27d66831090c2408c3c4a040 [label="publish" style="solid"]; + place_37e58158cff8e991ff736f76ef5256dc8b4ae850 -> place_309776058fe7a6debc12bc716d2cac6a3167eb2a [label="journalist_approval" style="solid"]; +} + +'; + $pullRequestPuml = +'@startuml +allow_mixing +title pull_request +skinparam titleBorderRoundCorner 15 +skinparam titleBorderThickness 2 +skinparam state { + BackgroundColor<> #87b741 + BackgroundColor<> #3887C6 + BorderColor #3887C6 + BorderColor<> Black + FontColor<> White +} +state "start" <> +state "coding" +state "travis" +state "review" +state "merged" +state "closed" +"start" --> "travis": "submit" +"coding" --> "travis": "update" +"travis" --> "travis": "update" +"review" --> "travis": "update" +"travis" --> "review": "wait_for_review" +"review" --> "coding": "request_change" +"review" --> "merged": "accept" +"review" --> "closed": "reject" +"closed" --> "review": "reopen" +@enduml +'; return [ - ['article', 'Article', 'dot', 'digraph workflow'], - ['pull_request', 'Pull Request', 'puml', '@startuml'], + ['article', 'Article', 'dot', $articleDot], + ['pull_request', 'Pull Request', 'puml', $pullRequestPuml], ]; } @@ -19,9 +76,9 @@ public function modelData() * @param string $model * @param string $label * @param string $format - * @param string $contains + * @param string $output */ - public function testExecute(string $model, string $label, string $format, string $contains) + public function testExecute(string $model, string $label, string $format, string $output) { $command = $this->application->find('mbt:graph:dump'); @@ -33,7 +90,6 @@ public function testExecute(string $model, string $label, string $format, string '--dump-format' => $format, ]); - $output = $commandTester->getDisplay(); - $this->assertContains($contains, $output); + $this->assertEquals($output, $commandTester->getDisplay()); } } From 6cc8bd47e85d0ae3d59a085ef38ab4b7de280384 Mon Sep 17 00:00:00 2001 From: "tien.xuan.vo" Date: Thu, 13 Dec 2018 17:39:42 +0700 Subject: [PATCH 3/3] Create graph from the component that connect to the initial vertex --- src/Service/GraphBuilder.php | 4 ++++ tests/Command/GraphDumpCommandTest.php | 6 ------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Service/GraphBuilder.php b/src/Service/GraphBuilder.php index 13a90276..97d69ef1 100644 --- a/src/Service/GraphBuilder.php +++ b/src/Service/GraphBuilder.php @@ -5,6 +5,7 @@ use Exception; use Fhaculty\Graph\Graph; use Fhaculty\Graph\Vertex; +use Graphp\Algorithms\ConnectedComponents; use Psr\SimpleCache\CacheInterface; use Psr\SimpleCache\CacheException; use Symfony\Component\Workflow\StateMachine; @@ -38,6 +39,9 @@ public function build(Workflow $workflow): Graph $graph = $this->buildForStateMachine($workflow); } else { $graph = $this->buildForWorkflow($workflow); + $initVertex = json_encode([$workflow->getDefinition()->getInitialPlace()]); + $components = new ConnectedComponents($graph); + return $components->createGraphComponentVertex($graph->getVertex($initVertex)); } $this->cache->set('mbt.graph.' . $workflow->getName(), $graph); diff --git a/tests/Command/GraphDumpCommandTest.php b/tests/Command/GraphDumpCommandTest.php index 1582a177..78587403 100644 --- a/tests/Command/GraphDumpCommandTest.php +++ b/tests/Command/GraphDumpCommandTest.php @@ -16,19 +16,13 @@ public function modelData() place_5916096c35437dab3f6a120790f2a431656affb5 [label="[\"draft\"]", shape=circle, style="filled"]; place_9e77ae793650b6bd67da69003adb32a9b17eb712 [label="[\"wait_for_journalist\",\"wait_for_spellchecker\"]", shape=circle]; - place_03ef92587dde689543f7a2bfaa85e3744e9627f0 [label="[\"wait_for_journalist\"]", shape=circle]; - place_ef8332535747bd04b47faa11e2d6d8a767585208 [label="[\"approved_by_journalist\"]", shape=circle]; place_429a3f35b9bcae743353aa5f800f0bda20c6c5e7 [label="[\"approved_by_journalist\",\"wait_for_spellchecker\"]", shape=circle]; - place_40a974ae46330f7c0eaf9998260b86bddde9f616 [label="[\"wait_for_spellchecker\"]", shape=circle]; - place_9d8d5371bae52cae02416988609683522d86ab14 [label="[\"approved_by_spellchecker\"]", shape=circle]; place_37e58158cff8e991ff736f76ef5256dc8b4ae850 [label="[\"approved_by_spellchecker\",\"wait_for_journalist\"]", shape=circle]; place_309776058fe7a6debc12bc716d2cac6a3167eb2a [label="[\"approved_by_journalist\",\"approved_by_spellchecker\"]", shape=circle]; place_0176512e721e0f9b27d66831090c2408c3c4a040 [label="[\"published\"]", shape=circle]; place_5916096c35437dab3f6a120790f2a431656affb5 -> place_9e77ae793650b6bd67da69003adb32a9b17eb712 [label="request_review" style="solid"]; - place_03ef92587dde689543f7a2bfaa85e3744e9627f0 -> place_ef8332535747bd04b47faa11e2d6d8a767585208 [label="journalist_approval" style="solid"]; place_9e77ae793650b6bd67da69003adb32a9b17eb712 -> place_429a3f35b9bcae743353aa5f800f0bda20c6c5e7 [label="journalist_approval" style="solid"]; place_9e77ae793650b6bd67da69003adb32a9b17eb712 -> place_37e58158cff8e991ff736f76ef5256dc8b4ae850 [label="spellchecker_approval" style="solid"]; - place_40a974ae46330f7c0eaf9998260b86bddde9f616 -> place_9d8d5371bae52cae02416988609683522d86ab14 [label="spellchecker_approval" style="solid"]; place_429a3f35b9bcae743353aa5f800f0bda20c6c5e7 -> place_309776058fe7a6debc12bc716d2cac6a3167eb2a [label="spellchecker_approval" style="solid"]; place_309776058fe7a6debc12bc716d2cac6a3167eb2a -> place_0176512e721e0f9b27d66831090c2408c3c4a040 [label="publish" style="solid"]; place_37e58158cff8e991ff736f76ef5256dc8b4ae850 -> place_309776058fe7a6debc12bc716d2cac6a3167eb2a [label="journalist_approval" style="solid"];