Skip to content

Commit 9db8c1d

Browse files
authored
Merge pull request #43 from common-workflow-language/directory-checking
Enhance directory checks & refactoring
2 parents 4693cb9 + 520c6bc commit 9db8c1d

File tree

2 files changed

+131
-112
lines changed

2 files changed

+131
-112
lines changed

cwltest/__init__.py

Lines changed: 13 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,15 @@
2424
from concurrent.futures import ThreadPoolExecutor
2525
from typing import Any, Dict, List, Text
2626

27+
from cwltest.utils import compare, CompareFail
28+
2729
_logger = logging.getLogger("cwltest")
2830
_logger.addHandler(logging.StreamHandler())
2931
_logger.setLevel(logging.INFO)
3032

3133
UNSUPPORTED_FEATURE = 33
3234
RUNTIME = sys.version_info.major
3335

34-
class CompareFail(Exception):
35-
36-
@classmethod
37-
def format(cls, expected, actual, cause=None):
38-
# type: (Any, Any, Any) -> CompareFail
39-
message = u"expected: %s\ngot: %s" % (
40-
json.dumps(expected, indent=4, sort_keys=True),
41-
json.dumps(actual, indent=4, sort_keys=True))
42-
if cause:
43-
message += u"\ncaused by: %s" % cause
44-
return cls(message)
45-
4636

4737
class TestResult(object):
4838

@@ -69,100 +59,9 @@ def create_test_case(self, test):
6959
return case
7060

7161

72-
def compare_file(expected, actual):
73-
# type: (Dict[str,Any], Dict[str,Any]) -> None
74-
if "path" in expected:
75-
comp = "path"
76-
if "path" not in actual:
77-
actual["path"] = actual["location"]
78-
else:
79-
comp = "location"
80-
if expected[comp] != "Any" and (not (actual[comp].endswith("/" + expected[comp]) or
81-
("/" not in actual[comp] and expected[comp] == actual[comp]))):
82-
raise CompareFail.format(expected, actual, u"%s does not end with %s" % (actual[comp], expected[comp]))
83-
84-
check_keys = set(expected.keys()) - {'path', 'location'}
85-
86-
for k in check_keys:
87-
try:
88-
compare(expected.get(k), actual.get(k))
89-
except CompareFail as e:
90-
raise CompareFail.format(expected, actual, u"field '%s' failed comparison: %s" %(
91-
k, str(e)
92-
))
93-
94-
95-
def compare_directory(expected, actual):
96-
# type: (Dict[str,Any], Dict[str,Any]) -> None
97-
if actual.get("class") != 'Directory':
98-
raise CompareFail.format(expected, actual, u"expected object with a class 'Directory'")
99-
if "listing" not in actual:
100-
raise CompareFail.format(expected, actual, u"'listing' is mandatory field in Directory object")
101-
for i in expected["listing"]:
102-
found = False
103-
for j in actual["listing"]:
104-
try:
105-
compare(i, j)
106-
found = True
107-
break
108-
except CompareFail:
109-
pass
110-
if not found:
111-
raise CompareFail.format(expected, actual, u"%s not found" % json.dumps(i, indent=4, sort_keys=True))
112-
113-
114-
def compare_dict(expected, actual):
115-
# type: (Dict[str,Any], Dict[str,Any]) -> None
116-
for c in expected:
117-
try:
118-
compare(expected[c], actual.get(c))
119-
except CompareFail as e:
120-
raise CompareFail.format(expected, actual, u"failed comparison for key '%s': %s" % (c, e))
121-
extra_keys = set(actual.keys()).difference(list(expected.keys()))
122-
for k in extra_keys:
123-
if actual[k] is not None:
124-
raise CompareFail.format(expected, actual, u"unexpected key '%s'" % k)
125-
126-
127-
def compare(expected, actual): # type: (Any, Any) -> None
128-
if expected == "Any":
129-
return
130-
if expected is not None and actual is None:
131-
raise CompareFail.format(expected, actual)
132-
133-
try:
134-
if isinstance(expected, dict):
135-
if not isinstance(actual, dict):
136-
raise CompareFail.format(expected, actual)
137-
138-
if expected.get("class") == "File":
139-
compare_file(expected, actual)
140-
elif expected.get("class") == "Directory":
141-
compare_directory(expected, actual)
142-
else:
143-
compare_dict(expected, actual)
144-
145-
elif isinstance(expected, list):
146-
if not isinstance(actual, list):
147-
raise CompareFail.format(expected, actual)
148-
149-
if len(expected) != len(actual):
150-
raise CompareFail.format(expected, actual, u"lengths don't match")
151-
for c in range(0, len(expected)):
152-
try:
153-
compare(expected[c], actual[c])
154-
except CompareFail as e:
155-
raise CompareFail.format(expected, actual, e)
156-
else:
157-
if expected != actual:
158-
raise CompareFail.format(expected, actual)
159-
160-
except Exception as e:
161-
raise CompareFail(str(e))
162-
163-
16462
templock = threading.Lock()
16563

64+
16665
def run_test(args, i, tests): # type: (argparse.Namespace, int, List[Dict[str, str]]) -> TestResult
16766
global templock
16867

@@ -240,17 +139,17 @@ def run_test(args, i, tests): # type: (argparse.Namespace, int, List[Dict[str,
240139
fail_message = ''
241140

242141
if t.get("should_fail", False):
243-
_logger.warn(u"""Test failed: %s""", " ".join([pipes.quote(tc) for tc in test_command]))
244-
_logger.warn(t.get("doc"))
245-
_logger.warn(u"Returned zero but it should be non-zero")
142+
_logger.warning(u"""Test failed: %s""", " ".join([pipes.quote(tc) for tc in test_command]))
143+
_logger.warning(t.get("doc"))
144+
_logger.warning(u"Returned zero but it should be non-zero")
246145
return TestResult(1, outstr, outerr, duration, args.classname)
247146

248147
try:
249148
compare(t.get("output"), out)
250149
except CompareFail as ex:
251-
_logger.warn(u"""Test failed: %s""", " ".join([pipes.quote(tc) for tc in test_command]))
252-
_logger.warn(t.get("doc"))
253-
_logger.warn(u"Compare failure %s", ex)
150+
_logger.warning(u"""Test failed: %s""", " ".join([pipes.quote(tc) for tc in test_command]))
151+
_logger.warning(t.get("doc"))
152+
_logger.warning(u"Compare failure %s", ex)
254153
fail_message = str(ex)
255154

256155
if outdir:
@@ -260,6 +159,8 @@ def run_test(args, i, tests): # type: (argparse.Namespace, int, List[Dict[str,
260159

261160

262161
def main(): # type: () -> int
162+
_logger.handlers.pop() # prints log messages twice without this line
163+
263164
parser = argparse.ArgumentParser(description='Compliance tests for cwltool')
264165
parser.add_argument("--test", type=str, help="YAML file describing test cases", required=True)
265166
parser.add_argument("--basedir", type=str, help="Basedir to use for tests", default=".")
@@ -358,10 +259,10 @@ def main(): # type: () -> int
358259
_logger.info("All tests passed")
359260
return 0
360261
elif failures == 0 and unsupported > 0:
361-
_logger.warn("%i tests passed, %i unsupported features", total - unsupported, unsupported)
262+
_logger.warning("%i tests passed, %i unsupported features", total - unsupported, unsupported)
362263
return 0
363264
else:
364-
_logger.warn("%i tests passed, %i failures, %i unsupported features", total - (failures + unsupported), failures, unsupported)
265+
_logger.warning("%i tests passed, %i failures, %i unsupported features", total - (failures + unsupported), failures, unsupported)
365266
return 1
366267

367268

cwltest/utils.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import json
2+
from typing import Any, Dict, Set
3+
4+
from six.moves import range
5+
6+
7+
class CompareFail(Exception):
8+
9+
@classmethod
10+
def format(cls, expected, actual, cause=None):
11+
# type: (Any, Any, Any) -> CompareFail
12+
message = u"expected: %s\ngot: %s" % (
13+
json.dumps(expected, indent=4, sort_keys=True),
14+
json.dumps(actual, indent=4, sort_keys=True))
15+
if cause:
16+
message += u"\ncaused by: %s" % cause
17+
return cls(message)
18+
19+
20+
def compare_location(expected, actual):
21+
# type: (Dict[str,Any], Dict[str,Any]) -> None
22+
if "path" in expected:
23+
comp = "path"
24+
if "path" not in actual:
25+
actual["path"] = actual["location"]
26+
else:
27+
comp = "location"
28+
if expected[comp] != "Any" and (not (actual[comp].endswith("/" + expected[comp]) or
29+
("/" not in actual[comp] and expected[comp] == actual[comp]))):
30+
raise CompareFail.format(expected, actual, u"%s does not end with %s" % (actual[comp], expected[comp]))
31+
32+
33+
def check_keys(keys, expected, actual):
34+
# type: (Set[str], Dict[str,Any], Dict[str,Any]) -> None
35+
for k in keys:
36+
try:
37+
compare(expected.get(k), actual.get(k))
38+
except CompareFail as e:
39+
raise CompareFail.format(expected, actual, u"field '%s' failed comparison: %s" %(
40+
k, str(e)
41+
))
42+
43+
44+
def compare_file(expected, actual):
45+
# type: (Dict[str,Any], Dict[str,Any]) -> None
46+
compare_location(expected, actual)
47+
other_keys = set(expected.keys()) - {'path', 'location'}
48+
check_keys(other_keys, expected, actual)
49+
50+
51+
def compare_directory(expected, actual):
52+
# type: (Dict[str,Any], Dict[str,Any]) -> None
53+
if actual.get("class") != 'Directory':
54+
raise CompareFail.format(expected, actual, u"expected object with a class 'Directory'")
55+
if "listing" not in actual:
56+
raise CompareFail.format(expected, actual, u"'listing' is mandatory field in Directory object")
57+
for i in expected["listing"]:
58+
found = False
59+
for j in actual["listing"]:
60+
try:
61+
compare(i, j)
62+
found = True
63+
break
64+
except CompareFail:
65+
pass
66+
if not found:
67+
raise CompareFail.format(expected, actual, u"%s not found" % json.dumps(i, indent=4, sort_keys=True))
68+
compare_file(expected, actual)
69+
70+
71+
def compare_dict(expected, actual):
72+
# type: (Dict[str,Any], Dict[str,Any]) -> None
73+
for c in expected:
74+
try:
75+
compare(expected[c], actual.get(c))
76+
except CompareFail as e:
77+
raise CompareFail.format(expected, actual, u"failed comparison for key '%s': %s" % (c, e))
78+
extra_keys = set(actual.keys()).difference(list(expected.keys()))
79+
for k in extra_keys:
80+
if actual[k] is not None:
81+
raise CompareFail.format(expected, actual, u"unexpected key '%s'" % k)
82+
83+
84+
def compare(expected, actual): # type: (Any, Any) -> None
85+
if expected == "Any":
86+
return
87+
if expected is not None and actual is None:
88+
raise CompareFail.format(expected, actual)
89+
90+
try:
91+
if isinstance(expected, dict):
92+
if not isinstance(actual, dict):
93+
raise CompareFail.format(expected, actual)
94+
95+
if expected.get("class") == "File":
96+
compare_file(expected, actual)
97+
elif expected.get("class") == "Directory":
98+
compare_directory(expected, actual)
99+
else:
100+
compare_dict(expected, actual)
101+
102+
elif isinstance(expected, list):
103+
if not isinstance(actual, list):
104+
raise CompareFail.format(expected, actual)
105+
106+
if len(expected) != len(actual):
107+
raise CompareFail.format(expected, actual, u"lengths don't match")
108+
for c in range(0, len(expected)):
109+
try:
110+
compare(expected[c], actual[c])
111+
except CompareFail as e:
112+
raise CompareFail.format(expected, actual, e)
113+
else:
114+
if expected != actual:
115+
raise CompareFail.format(expected, actual)
116+
117+
except Exception as e:
118+
raise CompareFail(str(e))

0 commit comments

Comments
 (0)