Skip to content

Commit 7191a14

Browse files
gaogaotiantianzhengruifeng
authored andcommitted
[SPARK-54450][INFRA] Allow run-tests to accept unittest style string for test names
### What changes were proposed in this pull request? Allow `run-tests` to take unittest style string like `test_module.TestClass.test_case`, in addition to `test_module TestClass.test_case`. Also some extra check is added for the existence of the test module - we are not sending it to `bin/pyspark` if we can't find the module. ### Why are the changes needed? It's extra work to split the string when copy/paste directly from unittest output (CI failures for example). ### Does this PR introduce _any_ user-facing change? No ### How was this patch tested? Manually confirmed the `.` connection works and the original way is not impacted (except that we have some new checks for the module). ### Was this patch authored or co-authored using generative AI tooling? No. Closes #53157 from gaogaotiantian/improve-run-test. Authored-by: Tian Gao <[email protected]> Signed-off-by: Ruifeng Zheng <[email protected]>
1 parent c6e8dbe commit 7191a14

File tree

2 files changed

+46
-1
lines changed

2 files changed

+46
-1
lines changed

python/docs/source/development/testing.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ After that, the PySpark test cases can be run via using ``python/run-tests``. Fo
3636
3737
python/run-tests --python-executable=python3
3838
39+
You can run individual tests by using ``--testnames`` option. For example,
40+
41+
.. code-block:: bash
42+
43+
python/run-tests --testnames pyspark.sql.tests.test_dataframe
44+
python/run-tests --testnames pyspark.sql.tests.test_dataframe.DataFrameTests.test_range
45+
3946
Note that you may set ``OBJC_DISABLE_INITIALIZE_FORK_SAFETY`` environment variable to ``YES`` if you are running tests on Mac OS.
4047

4148
Please see the guidance on how to |building_spark|_,

python/run-tests.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import logging
2121
from argparse import ArgumentParser
2222
import os
23+
import importlib
2324
import platform
2425
import re
2526
import shutil
@@ -224,6 +225,42 @@ def get_default_python_executables():
224225
return python_execs
225226

226227

228+
def split_and_validate_testnames(testnames):
229+
testnames_to_test = []
230+
231+
def module_exists(module):
232+
try:
233+
return importlib.util.find_spec(module) is not None
234+
except ModuleNotFoundError:
235+
return False
236+
237+
for testname in testnames.split(','):
238+
if " " in testname:
239+
# "{module} {class.testcase_name}"
240+
module, testcase = testname.split(" ")
241+
if not module_exists(module):
242+
print(f"Error: Can't find module '{module}'.")
243+
sys.exit(-1)
244+
testnames_to_test.append(f"{module} {testcase}")
245+
else:
246+
if module_exists(testname):
247+
# "{module}"
248+
testnames_to_test.append(testname)
249+
else:
250+
# "{module.class.testcase_name}"
251+
index = len(testname)
252+
while (index := testname.rfind(".", 0, index)) != -1:
253+
module, testcase = testname[:index], testname[index + 1:]
254+
if module_exists(module):
255+
testnames_to_test.append(f"{module} {testcase}")
256+
break
257+
else:
258+
print(f"Error: Invalid testname '{testname}'.")
259+
sys.exit(-1)
260+
261+
return testnames_to_test
262+
263+
227264
def parse_opts():
228265
parser = ArgumentParser(
229266
prog="run-tests"
@@ -256,6 +293,7 @@ def parse_opts():
256293
"For example, 'pyspark.sql.foo' to run the module as unittests or doctests, "
257294
"'pyspark.sql.tests FooTests' to run the specific class of unittests, "
258295
"'pyspark.sql.tests FooTests.test_foo' to run the specific unittest in the class. "
296+
"'pyspark.sql.tests.FooTests.test_foo' will work too. "
259297
"'--modules' option is ignored if they are given.")
260298
)
261299
group.add_argument(
@@ -312,7 +350,7 @@ def main():
312350
sys.exit(-1)
313351
LOGGER.info("Will test the following Python modules: %s", [x.name for x in modules_to_test])
314352
else:
315-
testnames_to_test = opts.testnames.split(',')
353+
testnames_to_test = split_and_validate_testnames(opts.testnames)
316354
LOGGER.info("Will test the following Python tests: %s", testnames_to_test)
317355

318356
task_queue = Queue.PriorityQueue()

0 commit comments

Comments
 (0)