Repository: exasol/integration-test-docker-environment (language: python)

Issue:
Fix graph plot generator
When running exaslct test, the latest networkx module throws following error:
```
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/exasol_integration_test_docker_environment/cli/common.py", line 108, in run_task
    handle_success(task, task_dependencies_dot_file, start_time)
  File "/usr/local/lib/python3.8/dist-packages/exasol_integration_test_docker_environment/cli/common.py", line 135, in handle_success
    generate_graph_from_task_dependencies(task, task_dependencies_dot_file)
  File "/usr/local/lib/python3.8/dist-packages/exasol_integration_test_docker_environment/cli/common.py", line 152, in generate_graph_from_task_dependencies
    networkx.nx_pydot.write_dot(g, task_dependencies_dot_file)
  File "/usr/local/lib/python3.8/dist-packages/networkx/utils/decorators.py", line 845, in func
    return argmap._lazy_compile(__wrapper)(*args, **kwargs)
  File "<class 'networkx.utils.decorators.argmap'> compilation 5", line 5, in argmap_write_dot_1
    import itertools
  File "/usr/local/lib/python3.8/dist-packages/networkx/drawing/nx_pydot.py", line 51, in write_dot
    P = to_pydot(G)
  File "/usr/local/lib/python3.8/dist-packages/networkx/drawing/nx_pydot.py", line 263, in to_pydot
    raise ValueError(
ValueError: Node names and attributes should not contain ":" unless they are quoted with "".                For example the string 'attribute:data1' should be written as '"attribute:data1"'.                Please refer https://github.com/pydot/pydot/issues/258
```

We need to encapsulate all labels in function `generate_graph_from_task_dependencies()`
Fix graph plot generator
When running exaslct test, the latest networkx module throws following error:
```
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/exasol_integration_test_docker_environment/cli/common.py", line 108, in run_task
    handle_success(task, task_dependencies_dot_file, start_time)
  File "/usr/local/lib/python3.8/dist-packages/exasol_integration_test_docker_environment/cli/common.py", line 135, in handle_success
    generate_graph_from_task_dependencies(task, task_dependencies_dot_file)
  File "/usr/local/lib/python3.8/dist-packages/exasol_integration_test_docker_environment/cli/common.py", line 152, in generate_graph_from_task_dependencies
    networkx.nx_pydot.write_dot(g, task_dependencies_dot_file)
  File "/usr/local/lib/python3.8/dist-packages/networkx/utils/decorators.py", line 845, in func
    return argmap._lazy_compile(__wrapper)(*args, **kwargs)
  File "<class 'networkx.utils.decorators.argmap'> compilation 5", line 5, in argmap_write_dot_1
    import itertools
  File "/usr/local/lib/python3.8/dist-packages/networkx/drawing/nx_pydot.py", line 51, in write_dot
    P = to_pydot(G)
  File "/usr/local/lib/python3.8/dist-packages/networkx/drawing/nx_pydot.py", line 263, in to_pydot
    raise ValueError(
ValueError: Node names and attributes should not contain ":" unless they are quoted with "".                For example the string 'attribute:data1' should be written as '"attribute:data1"'.                Please refer https://github.com/pydot/pydot/issues/258
```

We need to encapsulate all labels in function `generate_graph_from_task_dependencies()`


Discussion / hints:

Fix patch:
diff --git a/doc/changes/changes_0.12.0.md b/doc/changes/changes_0.12.0.md
index c65b7419e..37572f691 100644
--- a/doc/changes/changes_0.12.0.md
+++ b/doc/changes/changes_0.12.0.md
@@ -18,7 +18,7 @@ TBD
 
 
 ## Bug Fixes:
-- TBD
+- #228: Fixed graph plot generator
 
 ## Features / Enhancements:
 
diff --git a/poetry.lock b/poetry.lock
index 72b09e6ab..08258bbe6 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,10 +1,10 @@
 [[package]]
 name = "certifi"
-version = "2021.10.8"
+version = "2022.6.15"
 description = "Python package for providing Mozilla's CA Bundle."
 category = "main"
 optional = false
-python-versions = "*"
+python-versions = ">=3.6"
 
 [[package]]
 name = "charset-normalizer"
@@ -30,7 +30,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""}
 
 [[package]]
 name = "colorama"
-version = "0.4.4"
+version = "0.4.5"
 description = "Cross-platform colored terminal text."
 category = "main"
 optional = false
@@ -49,8 +49,8 @@ requests = ">=2.14.2,<2.18.0 || >2.18.0"
 websocket-client = ">=0.32.0"
 
 [package.extras]
-ssh = ["paramiko (>=2.4.2)"]
 tls = ["pyOpenSSL (>=17.5.0)", "cryptography (>=3.4.7)", "idna (>=2.0.0)"]
+ssh = ["paramiko (>=2.4.2)"]
 
 [[package]]
 name = "docutils"
@@ -117,7 +117,7 @@ i18n = ["Babel (>=2.7)"]
 
 [[package]]
 name = "jsonpickle"
-version = "2.1.0"
+version = "2.2.0"
 description = "Python library for serializing any arbitrary object graph into JSON"
 category = "main"
 optional = false
@@ -125,8 +125,8 @@ python-versions = ">=2.7"
 
 [package.extras]
 docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
-testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-black-multipy", "pytest-cov", "ecdsa", "feedparser", "numpy", "pandas", "pymongo", "scikit-learn", "sqlalchemy", "enum34", "jsonlib"]
-"testing.libs" = ["demjson", "simplejson", "ujson", "yajl"]
+"testing.libs" = ["simplejson", "ujson", "yajl"]
+testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-black-multipy", "pytest-cov", "ecdsa", "feedparser", "numpy", "pandas", "pymongo", "scikit-learn", "sqlalchemy", "pytest-flake8 (<1.1.0)", "enum34", "jsonlib", "pytest-flake8 (>=1.1.1)"]
 
 [[package]]
 name = "lockfile"
@@ -138,7 +138,7 @@ python-versions = "*"
 
 [[package]]
 name = "luigi"
-version = "3.0.3"
+version = "3.1.0"
 description = "Workflow mgmgt + task scheduling + dependency resolution."
 category = "main"
 optional = false
@@ -147,11 +147,11 @@ python-versions = "*"
 [package.dependencies]
 python-daemon = "*"
 python-dateutil = ">=2.7.5,<3"
-tenacity = ">=6.3.0,<7"
+tenacity = ">=8,<9"
 tornado = ">=5.0,<7"
 
 [package.extras]
-prometheus = ["prometheus-client (==0.5.0)"]
+prometheus = ["prometheus-client (>=0.5,<0.15)"]
 toml = ["toml (<2.0.0)"]
 
 [[package]]
@@ -172,7 +172,7 @@ python-versions = "*"
 
 [[package]]
 name = "networkx"
-version = "2.8"
+version = "2.8.4"
 description = "Python package for creating and manipulating graphs and networks"
 category = "main"
 optional = false
@@ -180,10 +180,10 @@ python-versions = ">=3.8"
 
 [package.extras]
 default = ["numpy (>=1.19)", "scipy (>=1.8)", "matplotlib (>=3.4)", "pandas (>=1.3)"]
-developer = ["pre-commit (>=2.18)", "mypy (>=0.942)"]
-doc = ["sphinx (>=4.5)", "pydata-sphinx-theme (>=0.8.1)", "sphinx-gallery (>=0.10)", "numpydoc (>=1.2)", "pillow (>=9.1)", "nb2plots (>=0.6)", "texext (>=0.6.6)"]
-extra = ["lxml (>=4.6)", "pygraphviz (>=1.9)", "pydot (>=1.4.2)", "sympy (>=1.10)"]
+doc = ["sphinx (>=5)", "pydata-sphinx-theme (>=0.9)", "sphinx-gallery (>=0.10)", "numpydoc (>=1.4)", "pillow (>=9.1)", "nb2plots (>=0.6)", "texext (>=0.6.6)"]
 test = ["pytest (>=7.1)", "pytest-cov (>=3.0)", "codecov (>=2.1)"]
+developer = ["pre-commit (>=2.19)", "mypy (>=0.960)"]
+extra = ["lxml (>=4.6)", "pygraphviz (>=1.9)", "pydot (>=1.4.2)", "sympy (>=1.10)"]
 
 [[package]]
 name = "pydot"
@@ -243,21 +243,21 @@ six = ">=1.5"
 
 [[package]]
 name = "requests"
-version = "2.27.1"
+version = "2.28.0"
 description = "Python HTTP for Humans."
 category = "main"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+python-versions = ">=3.7, <4"
 
 [package.dependencies]
 certifi = ">=2017.4.17"
-charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
-idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
+charset-normalizer = ">=2.0.0,<2.1.0"
+idna = ">=2.5,<4"
 urllib3 = ">=1.21.1,<1.27"
 
 [package.extras]
-socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
 use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
+socks = ["PySocks (>=1.5.6,!=1.5.7)"]
 
 [[package]]
 name = "simplejson"
@@ -285,22 +285,19 @@ python-versions = ">=3.6"
 
 [[package]]
 name = "stopwatch.py"
-version = "1.0.1"
+version = "2.0.0"
 description = "A simple stopwatch for python"
 category = "main"
 optional = false
-python-versions = "*"
+python-versions = ">=3.5"
 
 [[package]]
 name = "tenacity"
-version = "6.3.1"
+version = "8.0.1"
 description = "Retry code until it succeeds"
 category = "main"
 optional = false
-python-versions = "*"
-
-[package.dependencies]
-six = ">=1.9.0"
+python-versions = ">=3.6"
 
 [package.extras]
 doc = ["reno", "sphinx", "tornado (>=4.5)"]
@@ -336,16 +333,16 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
 
 [[package]]
 name = "websocket-client"
-version = "1.3.2"
+version = "1.3.3"
 description = "WebSocket client for Python with low level API options"
 category = "main"
 optional = false
 python-versions = ">=3.7"
 
 [package.extras]
+test = ["websockets"]
 docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"]
 optional = ["python-socks", "wsaccel"]
-test = ["websockets"]
 
 [metadata]
 lock-version = "1.1"
@@ -354,8 +351,8 @@ content-hash = "5bf35a9683d3809e74fe10df4fed37ef58c8bfe2507441870f3c901bd99836ad
 
 [metadata.files]
 certifi = [
-    {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
-    {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
+    {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"},
+    {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"},
 ]
 charset-normalizer = [
     {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"},
@@ -366,8 +363,8 @@ click = [
     {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
 ]
 colorama = [
-    {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
-    {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
+    {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
+    {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
 ]
 docker = [
     {file = "docker-5.0.3-py2.py3-none-any.whl", hash = "sha256:7a79bb439e3df59d0a72621775d600bc8bc8b422d285824cb37103eab91d1ce0"},
@@ -398,15 +395,15 @@ jinja2 = [
     {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
 ]
 jsonpickle = [
-    {file = "jsonpickle-2.1.0-py2.py3-none-any.whl", hash = "sha256:1dee77ddc5d652dfdabc33d33cff9d7e131d428007007da4fd6f7071ae774b0f"},
-    {file = "jsonpickle-2.1.0.tar.gz", hash = "sha256:84684cfc5338a534173c8dd69809e40f2865d0be1f8a2b7af8465e5b968dcfa9"},
+    {file = "jsonpickle-2.2.0-py2.py3-none-any.whl", hash = "sha256:de7f2613818aa4f234138ca11243d6359ff83ae528b2185efdd474f62bcf9ae1"},
+    {file = "jsonpickle-2.2.0.tar.gz", hash = "sha256:7b272918b0554182e53dc340ddd62d9b7f902fec7e7b05620c04f3ccef479a0e"},
 ]
 lockfile = [
     {file = "lockfile-0.12.2-py2.py3-none-any.whl", hash = "sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa"},
     {file = "lockfile-0.12.2.tar.gz", hash = "sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799"},
 ]
 luigi = [
-    {file = "luigi-3.0.3.tar.gz", hash = "sha256:7edc05a32bcff5aad28d7c7e3b15b761ef13fe2a495692602ebf0800eba66849"},
+    {file = "luigi-3.1.0.tar.gz", hash = "sha256:1ae7d76e6f8889e9ed40c699891f990eb6697c974eeaf8ab010f0dfc3766adf1"},
 ]
 markupsafe = [
     {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"},
@@ -455,8 +452,8 @@ netaddr = [
     {file = "netaddr-0.8.0.tar.gz", hash = "sha256:d6cc57c7a07b1d9d2e917aa8b36ae8ce61c35ba3fcd1b83ca31c5a0ee2b5a243"},
 ]
 networkx = [
-    {file = "networkx-2.8-py3-none-any.whl", hash = "sha256:1a1e8fe052cc1b4e0339b998f6795099562a264a13a5af7a32cad45ab9d4e126"},
-    {file = "networkx-2.8.tar.gz", hash = "sha256:4a52cf66aed221955420e11b3e2e05ca44196b4829aab9576d4d439212b0a14f"},
+    {file = "networkx-2.8.4-py3-none-any.whl", hash = "sha256:6933b9b3174a0bdf03c911bb4a1ee43a86ce3edeb813e37e1d4c553b3f4a2c4f"},
+    {file = "networkx-2.8.4.tar.gz", hash = "sha256:5e53f027c0d567cf1f884dbb283224df525644e43afd1145d64c9d88a3584762"},
 ]
 pydot = [
     {file = "pydot-1.4.2-py2.py3-none-any.whl", hash = "sha256:66c98190c65b8d2e2382a441b4c0edfdb4f4c025ef9cb9874de478fb0793a451"},
@@ -479,8 +476,8 @@ python-dateutil = [
     {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
 ]
 requests = [
-    {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
-    {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
+    {file = "requests-2.28.0-py3-none-any.whl", hash = "sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f"},
+    {file = "requests-2.28.0.tar.gz", hash = "sha256:d568723a7ebd25875d8d1eaf5dfa068cd2fc8194b2e483d7b1f7c81918dbec6b"},
 ]
 simplejson = [
     {file = "simplejson-3.17.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a89acae02b2975b1f8e4974cb8cdf9bf9f6c91162fb8dec50c259ce700f2770a"},
@@ -554,11 +551,12 @@ smmap = [
     {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"},
 ]
 "stopwatch.py" = [
-    {file = "stopwatch.py-1.0.1.tar.gz", hash = "sha256:861fee329c470017ea7d617326645ae2e6896cf9b7016a6b74a15b4287e4fd90"},
+    {file = "stopwatch.py-2.0.0-py3-none-any.whl", hash = "sha256:03ab3ee2876900cca1b830a2bcc4b1d29c536255724c6b0f5cb308992558d910"},
+    {file = "stopwatch.py-2.0.0.tar.gz", hash = "sha256:feb213b636135bcd377e889b27108038d0eaf3e781764c39c59e982863a5f676"},
 ]
 tenacity = [
-    {file = "tenacity-6.3.1-py2.py3-none-any.whl", hash = "sha256:baed357d9f35ec64264d8a4bbf004c35058fad8795c5b0d8a7dc77ecdcbb8f39"},
-    {file = "tenacity-6.3.1.tar.gz", hash = "sha256:e14d191fb0a309b563904bbc336582efe2037de437e543b38da749769b544d7f"},
+    {file = "tenacity-8.0.1-py3-none-any.whl", hash = "sha256:f78f4ea81b0fabc06728c11dc2a8c01277bfc5181b321a4770471902e3eb844a"},
+    {file = "tenacity-8.0.1.tar.gz", hash = "sha256:43242a20e3e73291a28bcbcacfd6e000b02d3857a9a9fff56b297a27afdc932f"},
 ]
 toml = [
     {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
@@ -612,6 +610,6 @@ urllib3 = [
     {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"},
 ]
 websocket-client = [
-    {file = "websocket-client-1.3.2.tar.gz", hash = "sha256:50b21db0058f7a953d67cc0445be4b948d7fc196ecbeb8083d68d94628e4abf6"},
-    {file = "websocket_client-1.3.2-py3-none-any.whl", hash = "sha256:722b171be00f2b90e1d4fb2f2b53146a536ca38db1da8ff49c972a4e1365d0ef"},
+    {file = "websocket-client-1.3.3.tar.gz", hash = "sha256:d58c5f284d6a9bf8379dab423259fe8f85b70d5fa5d2916d5791a84594b122b1"},
+    {file = "websocket_client-1.3.3-py3-none-any.whl", hash = "sha256:5d55652dc1d0b3c734f044337d929aaf83f4f9138816ec680c1aefefb4dc4877"},
 ]


Test patch:
diff --git a/exasol_integration_test_docker_environment/cli/common.py b/exasol_integration_test_docker_environment/cli/common.py
index 4ac01b18d..74f6894dc 100644
--- a/exasol_integration_test_docker_environment/cli/common.py
+++ b/exasol_integration_test_docker_environment/cli/common.py
@@ -144,10 +144,10 @@ def generate_graph_from_task_dependencies(task: DependencyLoggerBaseTask, task_d
         dependencies = collect_dependencies(task)
         g = DiGraph()
         for dependency in dependencies:
-            g.add_node(dependency.source, label=dependency.source.representation)
-            g.add_node(dependency.target, label=dependency.target.representation)
-            g.add_edge(dependency.source, dependency.target,
-                       dependency=dependency,
+            g.add_node(dependency.source.formatted_id, label=dependency.source.formatted_representation)
+            g.add_node(dependency.target.formatted_id, label=dependency.target.formatted_representation)
+            g.add_edge(dependency.source.formatted_id, dependency.target.formatted_id,
+                       dependency=dependency.formatted,
                        label=f"\"type={dependency.type}, index={dependency.index}\"")
         networkx.nx_pydot.write_dot(g, task_dependencies_dot_file)
 
diff --git a/exasol_integration_test_docker_environment/lib/base/task_dependency.py b/exasol_integration_test_docker_environment/lib/base/task_dependency.py
index 7dba1e848..97282695b 100644
--- a/exasol_integration_test_docker_environment/lib/base/task_dependency.py
+++ b/exasol_integration_test_docker_environment/lib/base/task_dependency.py
@@ -9,23 +9,15 @@ def __init__(self, id: str, representation: str):
         self.representation = representation
         self.id = id
 
-    def to_json(self):
-        jsonpickle.set_preferred_backend('simplejson')
-        jsonpickle.set_encoder_options('simplejson', sort_keys=True)
-        return jsonpickle.encode(self)
+    @property
+    def formatted_representation(self):
+        assert '"' not in self.representation
+        return f'"{self.representation}"'
 
-    def __hash__(self):
-        return hash(self.to_json())
-
-    def __eq__(self, other):
-        return (
-                self.__class__ == other.__class__ and
-                self.id == other.id and
-                self.representation == other.representation
-        )
-
-    def __repr__(self):
-        return self.id
+    @property
+    def formatted_id(self):
+        assert '"' not in self.id
+        return f'"{self.id}"'
 
 
 class DependencyType(Enum):
@@ -60,18 +52,15 @@ def from_json(cls, json_string: object) -> object:
             raise TypeError("Type %s of loaded object does not match %s" % (type(loaded_object), cls))
         return loaded_object
 
-    def __hash__(self):
-        return hash(self.to_json())
+    @property
+    def formatted(self):
+        assert '"' not in str(self)
+        return f'"{str(self)}"'
 
-    def __eq__(self, other):
+    def __str__(self):
         return (
-                self.__class__ == other.__class__ and
-                self.index == other.index and
-                self.state == other.state and
-                self.type == other.type and
-                self.target == other.target and
-                self.source == other.source
-        )
-
-    def __repr__(self):
-        return f"TaskDependency(source={self.source}, target={self.target}, type={self.type}, index={self.index}, state={self.state})"
+                    f"TaskDependency(source={self.source}, "
+                    f"target={self.target}, type={self.type}, " 
+                    f"index={self.index}, state={self.state})"
+               )
+
diff --git a/exasol_integration_test_docker_environment/test/test_generate_graph_plot.py b/exasol_integration_test_docker_environment/test/test_generate_graph_plot.py
new file mode 100644
index 000000000..c9b6a3817
--- /dev/null
+++ b/exasol_integration_test_docker_environment/test/test_generate_graph_plot.py
@@ -0,0 +1,48 @@
+import os
+import sys
+import tempfile
+import unittest
+from pathlib import Path
+
+import luigi
+
+from exasol_integration_test_docker_environment.cli.common import generate_root_task, run_task
+from exasol_integration_test_docker_environment.lib.base.dependency_logger_base_task import DependencyLoggerBaseTask
+
+from exasol_integration_test_docker_environment.lib.base.luigi_log_config import LOG_ENV_VARIABLE_NAME
+
+
+class TestTask(DependencyLoggerBaseTask):
+    x = luigi.Parameter()
+
+    def register_required(self):
+        self.register_dependency(self.create_child_task(task_class=TestChildTask))
+
+    def run(self):
+        pass
+
+
+class TestChildTask(DependencyLoggerBaseTask):
+    def run_task(self):
+        pass
+
+
+class BaseTaskTest(unittest.TestCase):
+
+    def test_generate_dependency_dot_file(self):
+        NUMBER_TASK = 5
+        task_id_generator = (x for x in range(NUMBER_TASK))
+
+        def create_task():
+            return generate_root_task(task_class=TestTask, x=f"{next(task_id_generator)}")
+
+        with tempfile.TemporaryDirectory() as d:
+            for i in range(NUMBER_TASK):
+                dot_file = Path(d) / f"dot_file_{i}.dot"
+                success, task = run_task(create_task, workers=5, task_dependencies_dot_file=str(dot_file))
+                assert success
+                assert dot_file.exists()
+
+
+if __name__ == '__main__':
+    unittest.main()