robotframework-pabot 3.0.1__tar.gz → 4.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/PKG-INFO +4 -1
  2. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/README.md +9 -5
  3. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/__init__.py +1 -1
  4. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/arguments.py +87 -95
  5. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/pabot.py +10 -9
  6. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/robotframework_pabot.egg-info/PKG-INFO +4 -1
  7. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/robotframework_pabot.egg-info/SOURCES.txt +12 -1
  8. robotframework_pabot-4.0.0/tests/test_arguments_output.py +67 -0
  9. robotframework_pabot-4.0.0/tests/test_depends.py +133 -0
  10. robotframework_pabot-4.0.0/tests/test_functional.py +51 -0
  11. robotframework_pabot-4.0.0/tests/test_ordering.py +194 -0
  12. robotframework_pabot-4.0.0/tests/test_pabot.py +1396 -0
  13. robotframework_pabot-4.0.0/tests/test_pabotlib.py +315 -0
  14. robotframework_pabot-4.0.0/tests/test_pabotsuitenames_io.py +65 -0
  15. robotframework_pabot-4.0.0/tests/test_resultmerger.py +73 -0
  16. robotframework_pabot-4.0.0/tests/test_stacktrace.py +45 -0
  17. robotframework_pabot-4.0.0/tests/test_testlevelsplit_include.py +53 -0
  18. robotframework_pabot-4.0.0/tests/test_testlevelsplit_output_task_order.py +90 -0
  19. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/LICENSE.txt +0 -0
  20. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/MANIFEST.in +0 -0
  21. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/pyproject.toml +0 -0
  22. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/setup.cfg +0 -0
  23. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/setup.py +0 -0
  24. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/SharedLibrary.py +0 -0
  25. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/clientwrapper.py +0 -0
  26. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/coordinatorwrapper.py +0 -0
  27. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/execution_items.py +0 -0
  28. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/pabotlib.py +0 -0
  29. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/py3/__init__.py +0 -0
  30. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/py3/client.py +0 -0
  31. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/py3/coordinator.py +0 -0
  32. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/py3/messages.py +0 -0
  33. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/py3/worker.py +0 -0
  34. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/result_merger.py +0 -0
  35. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/robotremoteserver.py +0 -0
  36. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/pabot/workerwrapper.py +0 -0
  37. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/robotframework_pabot.egg-info/dependency_links.txt +0 -0
  38. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/robotframework_pabot.egg-info/entry_points.txt +0 -0
  39. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/robotframework_pabot.egg-info/requires.txt +0 -0
  40. {robotframework-pabot-3.0.1 → robotframework_pabot-4.0.0}/src/robotframework_pabot.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: robotframework-pabot
3
- Version: 3.0.1
3
+ Version: 4.0.0
4
4
  Summary: Parallel test runner for Robot Framework
5
5
  Home-page: https://pabot.org
6
6
  Download-URL: https://pypi.python.org/pypi/robotframework-pabot
@@ -17,5 +17,8 @@ Classifier: Development Status :: 5 - Production/Stable
17
17
  Classifier: Framework :: Robot Framework
18
18
  Requires-Python: >=3.6
19
19
  License-File: LICENSE.txt
20
+ Requires-Dist: robotframework>=3.2
21
+ Requires-Dist: robotframework-stacktrace>=0.4.1
22
+ Requires-Dist: natsort>=8.2.0
20
23
 
21
24
  A parallel executor for Robot Framework tests. With Pabot you can split one execution into multiple and save test execution time.
@@ -55,14 +55,14 @@ There are several ways you can help in improving this tool:
55
55
  ## Command-line options
56
56
 
57
57
  pabot [--verbose|--testlevelsplit|--command .. --end-command|
58
- --processes num|--pabotlib|--pabotlibhost host|--pabotlibport port|
58
+ --processes num|--no-pabotlib|--pabotlibhost host|--pabotlibport port|
59
59
  --processtimeout num|
60
60
  --shard i/n|
61
61
  --artifacts extensions|--artifactsinsubfolders|
62
62
  --resourcefile file|--argumentfile[num] file|--suitesfrom file]
63
63
  [robot options] [path ...]
64
64
 
65
- Supports all [Robot Framework command line options](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#all-command-line-options) and also following options (these must be before RF options):
65
+ Supports all [Robot Framework command line options](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#all-command-line-options) and also following pabot options:
66
66
 
67
67
  --verbose
68
68
  more output from the parallel execution
@@ -83,8 +83,11 @@ Supports all [Robot Framework command line options](https://robotframework.org/r
83
83
  Special option "all" will use as many processes as there are
84
84
  executable suites or tests.
85
85
 
86
- --pabotlib
87
- Start PabotLib remote server. This enables locking and resource distribution between parallel test executions.
86
+ PabotLib remote server is started by default to enable locking and resource distribution
87
+ between parallel test executions.
88
+
89
+ --no-pabotlib
90
+ Disable the PabotLib remote server if you don't need locking or resource distribution features.
88
91
 
89
92
  --pabotlibhost [HOSTNAME]
90
93
  Host name of the PabotLib remote server (default is 127.0.0.1)
@@ -142,8 +145,9 @@ Example usages:
142
145
  pabot --command java -jar robotframework.jar --end-command --include SMOKE tests
143
146
  pabot --processes 10 tests
144
147
  pabot --pabotlibhost 192.168.1.123 --pabotlibport 8271 --processes 10 tests
145
- pabot --pabotlib --pabotlibhost 192.168.1.111 --pabotlibport 8272 --processes 10 tests
146
148
  pabot --artifacts png,mp4,txt --artifactsinsubfolders directory_to_tests
149
+ # To disable PabotLib:
150
+ pabot --no-pabotlib tests
147
151
 
148
152
  ### PabotLib
149
153
 
@@ -1,4 +1,4 @@
1
1
  from __future__ import absolute_import
2
2
 
3
3
  from .pabotlib import PabotLib
4
- __version__ = "3.0.1"
4
+ __version__ = "4.0.0"
@@ -80,7 +80,7 @@ def _parse_pabot_args(args): # type: (List[str]) -> Tuple[List[str], Dict[str,
80
80
  "verbose": False,
81
81
  "help": False,
82
82
  "testlevelsplit": False,
83
- "pabotlib": False,
83
+ "pabotlib": True,
84
84
  "pabotlibhost": "127.0.0.1",
85
85
  "pabotlibport": 8270,
86
86
  "processes": _processes_count(),
@@ -91,108 +91,100 @@ def _parse_pabot_args(args): # type: (List[str]) -> Tuple[List[str], Dict[str,
91
91
  "shardcount": 1,
92
92
  "chunk": False,
93
93
  }
94
+ # Explicitly define argument types for validation
95
+ flag_args = {
96
+ "verbose", "help", "testlevelsplit", "pabotlib",
97
+ "artifactsinsubfolders", "chunk"
98
+ }
99
+ value_args = {
100
+ "hive": str,
101
+ "processes": lambda x: int(x) if x != 'all' else None,
102
+ "resourcefile": str,
103
+ "pabotlibhost": str,
104
+ "pabotlibport": int,
105
+ "processtimeout": int,
106
+ "ordering": _parse_ordering,
107
+ "suitesfrom": str,
108
+ "artifacts": lambda x: x.split(","),
109
+ "shard": _parse_shard
110
+ }
111
+
94
112
  argumentfiles = []
95
- while args and (
96
- args[0]
97
- in [
98
- "--" + param
99
- for param in [
100
- "hive",
101
- "command",
102
- "processes",
103
- "verbose",
104
- "resourcefile",
105
- "testlevelsplit",
106
- "pabotlib",
107
- "pabotlibhost",
108
- "pabotlibport",
109
- "processtimeout",
110
- "ordering",
111
- "suitesfrom",
112
- "artifacts",
113
- "artifactsinsubfolders",
114
- "help",
115
- "shard",
116
- "chunk",
117
- ]
118
- ]
119
- or ARGSMATCHER.match(args[0])
120
- ):
121
- if args[0] == "--hive":
122
- pabot_args["hive"] = args[1]
123
- args = args[2:]
124
- continue
125
- if args[0] == "--command":
126
- end_index = args.index("--end-command")
127
- pabot_args["command"] = args[1:end_index]
128
- args = args[end_index + 1 :]
129
- continue
130
- if args[0] == "--processes":
131
- pabot_args["processes"] = int(args[1]) if args[1] != 'all' else None
132
- args = args[2:]
133
- continue
134
- if args[0] == "--verbose":
135
- pabot_args["verbose"] = True
136
- args = args[1:]
137
- continue
138
- if args[0] == "--chunk":
139
- pabot_args["chunk"] = True
140
- args = args[1:]
141
- continue
142
- if args[0] == "--resourcefile":
143
- pabot_args["resourcefile"] = args[1]
144
- args = args[2:]
145
- continue
146
- if args[0] == "--pabotlib":
147
- pabot_args["pabotlib"] = True
148
- args = args[1:]
149
- continue
150
- if args[0] == "--ordering":
151
- pabot_args["ordering"] = _parse_ordering(args[1])
152
- args = args[2:]
113
+ remaining_args = []
114
+ i = 0
115
+
116
+ # Track conflicting options during parsing
117
+ saw_pabotlib_flag = False
118
+ saw_no_pabotlib = False
119
+
120
+ while i < len(args):
121
+ arg = args[i]
122
+ if not arg.startswith('--'):
123
+ remaining_args.append(arg)
124
+ i += 1
153
125
  continue
154
- if args[0] == "--testlevelsplit":
155
- pabot_args["testlevelsplit"] = True
126
+
127
+ arg_name = arg[2:] # Strip '--'
128
+
129
+ if arg_name == "no-pabotlib":
130
+ saw_no_pabotlib = True
131
+ pabot_args["pabotlib"] = False # Just set the main flag
156
132
  args = args[1:]
157
133
  continue
158
- if args[0] == "--pabotlibhost":
159
- pabot_args["pabotlibhost"] = args[1]
160
- args = args[2:]
161
- continue
162
- if args[0] == "--pabotlibport":
163
- pabot_args["pabotlibport"] = int(args[1])
164
- args = args[2:]
165
- continue
166
- if args[0] == "--processtimeout":
167
- pabot_args["processtimeout"] = int(args[1])
168
- args = args[2:]
169
- continue
170
- if args[0] == "--suitesfrom":
171
- pabot_args["suitesfrom"] = args[1]
172
- args = args[2:]
173
- continue
174
- if args[0] == "--artifacts":
175
- pabot_args["artifacts"] = args[1].split(",")
176
- args = args[2:]
177
- continue
178
- if args[0] == "--artifactsinsubfolders":
179
- pabot_args["artifactsinsubfolders"] = True
134
+ if arg_name == "pabotlib":
135
+ saw_pabotlib_flag = True
180
136
  args = args[1:]
181
137
  continue
182
- if args[0] == "--shard":
183
- pabot_args["shardindex"], pabot_args["shardcount"] = _parse_shard(args[1])
184
- args = args[2:]
185
- continue
186
- match = ARGSMATCHER.match(args[0])
138
+
139
+ # Special case for command
140
+ if arg_name == "command":
141
+ try:
142
+ end_index = args.index("--end-command", i)
143
+ pabot_args["command"] = args[i+1:end_index]
144
+ i = end_index + 1
145
+ continue
146
+ except ValueError:
147
+ raise DataError("--command requires matching --end-command")
148
+
149
+ # Handle flag arguments
150
+ if arg_name in flag_args:
151
+ pabot_args[arg_name] = True
152
+ i += 1
153
+ continue
154
+
155
+ # Handle value arguments
156
+ if arg_name in value_args:
157
+ if i + 1 >= len(args):
158
+ raise DataError(f"--{arg_name} requires a value")
159
+ try:
160
+ value = value_args[arg_name](args[i + 1])
161
+ if arg_name == "shard":
162
+ pabot_args["shardindex"], pabot_args["shardcount"] = value
163
+ else:
164
+ pabot_args[arg_name] = value
165
+ i += 2
166
+ continue
167
+ except (ValueError, TypeError) as e:
168
+ raise DataError(f"Invalid value for --{arg_name}: {args[i + 1]}")
169
+
170
+ # Handle argument files
171
+ match = ARGSMATCHER.match(arg)
187
172
  if match:
188
- argumentfiles += [(match.group(1), args[1])]
189
- args = args[2:]
190
- continue
191
- if args and args[0] == "--help":
192
- pabot_args["help"] = True
193
- args = args[1:]
173
+ if i + 1 >= len(args):
174
+ raise DataError(f"{arg} requires a value")
175
+ argumentfiles.append((match.group(1), args[i + 1]))
176
+ i += 2
177
+ continue
178
+
179
+ # If we get here, it's a non-pabot argument
180
+ remaining_args.append(arg)
181
+ i += 1
182
+
183
+ if saw_pabotlib_flag and saw_no_pabotlib:
184
+ raise DataError("Cannot use both --pabotlib and --no-pabotlib options together")
185
+
194
186
  pabot_args["argumentfiles"] = argumentfiles
195
- return args, pabot_args
187
+ return remaining_args, pabot_args
196
188
 
197
189
 
198
190
  def _parse_ordering(filename): # type: (str) -> List[ExecutionItem]
@@ -1152,14 +1152,15 @@ def generate_suite_names_with_builder(outs_dir, datasources, options):
1152
1152
  settings = RobotSettings(opts)
1153
1153
 
1154
1154
  # Note: first argument (included_suites) is deprecated from RobotFramework 6.1
1155
- if ROBOT_VERSION < "6.1":
1155
+ if ROBOT_VERSION >= "6.1":
1156
1156
  builder = TestSuiteBuilder(
1157
- settings["SuiteNames"], settings.extension, rpa=settings.rpa
1157
+ included_extensions=settings.extension, rpa=settings.rpa, lang=opts.get("language")
1158
1158
  )
1159
1159
  else:
1160
1160
  builder = TestSuiteBuilder(
1161
- included_extensions=settings.extension, rpa=settings.rpa
1162
- )
1161
+ settings["SuiteNames"], settings.extension, rpa=settings.rpa
1162
+ )
1163
+
1163
1164
  suite = builder.build(*datasources)
1164
1165
  settings.rpa = builder.rpa
1165
1166
  suite.configure(**settings.suite_config)
@@ -1775,7 +1776,7 @@ def _create_execution_items_for_dry_run(
1775
1776
  chunk_size = (
1776
1777
  round(len(items) / processes_count)
1777
1778
  if len(items) > processes_count
1778
- else len(items)
1779
+ else 1
1779
1780
  )
1780
1781
  chunked_items = list(_chunk_items(items, chunk_size))
1781
1782
  _NUMBER_OF_ITEMS_TO_BE_EXECUTED += len(chunked_items)
@@ -2018,17 +2019,17 @@ def _verify_depends(suite_names):
2018
2019
  )
2019
2020
  )
2020
2021
  if suites_with_depends != suites_with_found_dependencies:
2021
- raise Exception("There are unmet dependencies using #DEPENDS")
2022
+ raise DataError("Invalid test configuration: Some test suites have dependencies (#DEPENDS) that cannot be found.")
2022
2023
  suites_with_circular_dependencies = list(
2023
2024
  filter(lambda suite: suite.depends == suite.name, suites_with_depends)
2024
2025
  )
2025
2026
  if suites_with_circular_dependencies:
2026
- raise Exception("There are suites with circular dependencies using #DEPENDS")
2027
+ raise DataError("Invalid test configuration: Test suites cannot depend on themselves.")
2027
2028
  grouped_suites = list(
2028
2029
  filter(lambda suite: isinstance(suite, GroupItem), suite_names)
2029
2030
  )
2030
2031
  if grouped_suites and suites_with_depends:
2031
- raise Exception("#DEPENDS and grouped suites are incompatible")
2032
+ raise DataError("Invalid test configuration: Cannot use both #DEPENDS and grouped suites.")
2032
2033
 
2033
2034
 
2034
2035
  def _group_by_depend(suite_names):
@@ -2056,7 +2057,7 @@ def _group_by_depend(suite_names):
2056
2057
  dependency_tree += [dependent_on_last_stage]
2057
2058
  flattened_dependency_tree = sum(dependency_tree, [])
2058
2059
  if len(flattened_dependency_tree) != len(runnable_suites):
2059
- raise Exception("There are circular or unmet dependencies using #DEPENDS")
2060
+ raise DataError("Invalid test configuration: Circular or unmet dependencies detected between test suites. Please check your #DEPENDS definitions.")
2060
2061
  return dependency_tree
2061
2062
 
2062
2063
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: robotframework-pabot
3
- Version: 3.0.1
3
+ Version: 4.0.0
4
4
  Summary: Parallel test runner for Robot Framework
5
5
  Home-page: https://pabot.org
6
6
  Download-URL: https://pypi.python.org/pypi/robotframework-pabot
@@ -17,5 +17,8 @@ Classifier: Development Status :: 5 - Production/Stable
17
17
  Classifier: Framework :: Robot Framework
18
18
  Requires-Python: >=3.6
19
19
  License-File: LICENSE.txt
20
+ Requires-Dist: robotframework>=3.2
21
+ Requires-Dist: robotframework-stacktrace>=0.4.1
22
+ Requires-Dist: natsort>=8.2.0
20
23
 
21
24
  A parallel executor for Robot Framework tests. With Pabot you can split one execution into multiple and save test execution time.
@@ -25,4 +25,15 @@ src/robotframework_pabot.egg-info/SOURCES.txt
25
25
  src/robotframework_pabot.egg-info/dependency_links.txt
26
26
  src/robotframework_pabot.egg-info/entry_points.txt
27
27
  src/robotframework_pabot.egg-info/requires.txt
28
- src/robotframework_pabot.egg-info/top_level.txt
28
+ src/robotframework_pabot.egg-info/top_level.txt
29
+ tests/test_arguments_output.py
30
+ tests/test_depends.py
31
+ tests/test_functional.py
32
+ tests/test_ordering.py
33
+ tests/test_pabot.py
34
+ tests/test_pabotlib.py
35
+ tests/test_pabotsuitenames_io.py
36
+ tests/test_resultmerger.py
37
+ tests/test_stacktrace.py
38
+ tests/test_testlevelsplit_include.py
39
+ tests/test_testlevelsplit_output_task_order.py
@@ -0,0 +1,67 @@
1
+ import sys
2
+ import tempfile
3
+ import textwrap
4
+ import unittest
5
+ import shutil
6
+ import subprocess
7
+
8
+
9
+ class PabotArgumentsOutputsTest(unittest.TestCase):
10
+ def setUp(self):
11
+ self.tmpdir = tempfile.mkdtemp()
12
+
13
+ def tearDown(self):
14
+ shutil.rmtree(self.tmpdir)
15
+
16
+ def _run_tests_with(self, testfile, arg1file, arg2file):
17
+ robot_file = open("{}/test.robot".format(self.tmpdir), "w")
18
+ robot_file.write(textwrap.dedent(testfile))
19
+ robot_file.close()
20
+ with open("{}/arg1.txt".format(self.tmpdir), "w") as f:
21
+ f.write(textwrap.dedent(arg1file))
22
+ with open("{}/arg2.txt".format(self.tmpdir), "w") as f:
23
+ f.write(textwrap.dedent(arg2file))
24
+ process = subprocess.Popen(
25
+ [
26
+ sys.executable,
27
+ "-m" "pabot.pabot",
28
+ "--processes",
29
+ "2",
30
+ "--argumentfile1",
31
+ "{}/arg1.txt".format(self.tmpdir),
32
+ "--argumentfile2",
33
+ "{}/arg2.txt".format(self.tmpdir),
34
+ "--outputdir",
35
+ self.tmpdir,
36
+ "--output",
37
+ "test.xml",
38
+ "{}/test.robot".format(self.tmpdir),
39
+ ],
40
+ cwd=self.tmpdir,
41
+ stdout=subprocess.PIPE,
42
+ stderr=subprocess.PIPE,
43
+ )
44
+ return process.communicate(), process.returncode
45
+
46
+ def test_argumentfile_outputs(self):
47
+ (stdout, stderr), rc = self._run_tests_with(
48
+ """
49
+ *** Test Cases ***
50
+ Test 1
51
+ Log ${VALUE}
52
+ Should Be True ${VALUE} == 2
53
+ """,
54
+ """
55
+ --variable VALUE:1
56
+ """,
57
+ """
58
+ --variable VALUE:2
59
+ """,
60
+ )
61
+ self.assertEqual(rc, 1)
62
+ if sys.version_info < (3, 0):
63
+ self.assertIn("PASSED", stdout, stderr)
64
+ self.assertIn("failed", stdout, stderr)
65
+ else:
66
+ self.assertIn(b"PASSED", stdout, stderr)
67
+ self.assertIn(b"failed", stdout, stderr)
@@ -0,0 +1,133 @@
1
+ import subprocess
2
+ import sys
3
+ import textwrap
4
+ import shutil
5
+ import tempfile
6
+ import unittest
7
+
8
+
9
+ def _string_convert(byte_string):
10
+ legacy_python = sys.version_info < (3, 0)
11
+ return byte_string.decode() if legacy_python else byte_string
12
+
13
+
14
+ class DependsTest(unittest.TestCase):
15
+ test_file = """
16
+ *** Settings ***
17
+ Test Template Test1
18
+ *** Test Cases ***
19
+ The Test S1Test 01 1
20
+ The Test S1Test 02 1
21
+ The Test S1Test 03 1
22
+ The Test S1Test 04 1
23
+ The Test S1Test 05 1
24
+ The Test S1Test 06 1
25
+ The Test S1Test 07 1
26
+ The Test S1Test 08 1
27
+ The Test S1Test 09 1
28
+ The Test S1Test 10 1
29
+ The Test S1Test 11 1
30
+ The Test S1Test 12 1
31
+ *** Keywords ***
32
+ Test1
33
+ [Arguments] ${arg}
34
+ Log Test
35
+ """
36
+ passed = _string_convert(b"PASSED")
37
+ failed = _string_convert(b"FAILED")
38
+ test_01 = _string_convert(b"S1Test 01")
39
+ test_02 = _string_convert(b"S1Test 02")
40
+ test_08 = _string_convert(b"S1Test 08")
41
+
42
+ def setUp(self):
43
+ self.tmpdir = tempfile.mkdtemp()
44
+
45
+ def tearDown(self):
46
+ shutil.rmtree(self.tmpdir)
47
+
48
+ def _run_tests_with(self, testfile, orderfile):
49
+ robot_file = open("{}/test.robot".format(self.tmpdir), "w")
50
+ robot_file.write(textwrap.dedent(testfile))
51
+ robot_file.close()
52
+ with open("{}/order.dat".format(self.tmpdir), "w") as f:
53
+ f.write(textwrap.dedent(orderfile))
54
+ process = subprocess.Popen(
55
+ [
56
+ sys.executable,
57
+ "-m" "pabot.pabot",
58
+ "--testlevelsplit",
59
+ "--ordering",
60
+ "{}/order.dat".format(self.tmpdir),
61
+ "{}/test.robot".format(self.tmpdir),
62
+ ],
63
+ cwd=self.tmpdir,
64
+ stdout=subprocess.PIPE,
65
+ stderr=subprocess.PIPE,
66
+ )
67
+ return process.communicate()
68
+
69
+ def test_dependency_ok(self):
70
+ stdout, stderr = self._run_tests_with(
71
+ self.test_file,
72
+ """
73
+ --test Test.The Test S1Test 01 #DEPENDS Test.The Test S1Test 02
74
+ --test Test.The Test S1Test 02 #DEPENDS Test.The Test S1Test 08
75
+ --test Test.The Test S1Test 08
76
+ """,
77
+ )
78
+ self.assertIn(self.passed, stdout, stderr)
79
+ self.assertNotIn(self.failed, stdout, stderr)
80
+ self.assertEqual(stdout.count(self.passed), 12)
81
+ test_01_index = stdout.find(self.test_01)
82
+ test_02_index = stdout.find(self.test_02)
83
+ test_08_index = stdout.find(self.test_08)
84
+ self.assertNotEqual(test_01_index, -1)
85
+ self.assertNotEqual(test_02_index, -1)
86
+ self.assertNotEqual(test_08_index, -1)
87
+ self.assertTrue(test_08_index < test_02_index)
88
+ self.assertTrue(test_02_index < test_01_index)
89
+
90
+ def test_circular_dependency(self):
91
+ stdout, stderr = self._run_tests_with(
92
+ self.test_file,
93
+ """
94
+ --test Test.The Test S1Test 01 #DEPENDS Test.The Test S1Test 02
95
+ --test Test.The Test S1Test 02 #DEPENDS Test.The Test S1Test 01
96
+ --test Test.The Test S1Test 08
97
+ """,
98
+ )
99
+ self.assertIn(b"Invalid test configuration: Circular or unmet dependencies detected between test suites", stdout)
100
+
101
+ def test_unmet_dependency(self):
102
+ stdout, stderr = self._run_tests_with(
103
+ self.test_file,
104
+ """
105
+ --test Test.The Test S1Test 01
106
+ --test Test.The Test S1Test 02 #DEPENDS Test.The Test S1Test 23
107
+ --test Test.The Test S1Test 08
108
+ """,
109
+ )
110
+ self.assertIn(b"Invalid test configuration: Circular or unmet dependencies detected between test suites. Please check your #DEPENDS definitions.", stdout)
111
+
112
+ def test_same_reference(self):
113
+ stdout, stderr = self._run_tests_with(
114
+ self.test_file,
115
+ """
116
+ --test Test.The Test S1Test 01
117
+ --test Test.The Test S1Test 02 #DEPENDS Test.The Test S1Test 02
118
+ --test Test.The Test S1Test 08
119
+ """,
120
+ )
121
+ self.assertIn(b"Invalid test configuration: Circular or unmet dependencies detected between test suites. Please check your #DEPENDS definitions.", stdout)
122
+
123
+ def test_wait(self):
124
+ stdout, stderr = self._run_tests_with(
125
+ self.test_file,
126
+ """
127
+ --test Test.The Test S1Test 01
128
+ --test Test.The Test S1Test 02 #DEPENDS Test.The Test S1Test 08
129
+ #WAIT
130
+ --test Test.The Test S1Test 08
131
+ """,
132
+ )
133
+ self.assertIn(b"Invalid test configuration: Circular or unmet dependencies detected between test suites", stdout)
@@ -0,0 +1,51 @@
1
+ import os
2
+ import sys
3
+ import tempfile
4
+ import textwrap
5
+ import unittest
6
+ import shutil
7
+ import subprocess
8
+
9
+
10
+ if os.name != "posix":
11
+ raise unittest.SkipTest("Only posix test")
12
+
13
+
14
+ class PabotPassJsonUsingVariableOptionTests(unittest.TestCase):
15
+ def setUp(self):
16
+ self.tmpdir = tempfile.mkdtemp()
17
+ robot_file = open("{}/test.robot".format(self.tmpdir), "w")
18
+ robot_file.write(
19
+ textwrap.dedent(
20
+ """
21
+ *** Test Cases ***
22
+ Test Passing Json With -v option
23
+ Should Be Equal ${custom_var} {"key": "value"}
24
+ """
25
+ )
26
+ )
27
+ robot_file.close()
28
+
29
+ process = subprocess.Popen(
30
+ [
31
+ sys.executable,
32
+ "-m" "pabot.pabot",
33
+ "-v",
34
+ 'custom_var:{"key": "value"}',
35
+ "{}/test.robot".format(self.tmpdir),
36
+ ],
37
+ cwd=self.tmpdir,
38
+ stdout=subprocess.PIPE,
39
+ stderr=subprocess.PIPE,
40
+ )
41
+
42
+ self.stdout, self.stderr = process.communicate()
43
+
44
+ def test_stdout_should_display_passed_test(self):
45
+ if sys.version_info < (3, 0):
46
+ self.assertIn("PASSED Test", self.stdout, self.stderr)
47
+ else:
48
+ self.assertIn(b"PASSED Test", self.stdout, self.stderr)
49
+
50
+ def tearDown(self):
51
+ shutil.rmtree(self.tmpdir)