Skip to content

Commit bdc74eb

Browse files
committed
Add new test to validate CLI help
Signed-off-by: Philippe Ombredanne <[email protected]>
1 parent 173aeb1 commit bdc74eb

File tree

8 files changed

+291
-85
lines changed

8 files changed

+291
-85
lines changed

tests/test_cmd.py

Lines changed: 128 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
from __future__ import print_function
1919
from __future__ import unicode_literals
2020

21+
import unittest
22+
2123
from attributecode import CRITICAL
2224
from attributecode import DEBUG
2325
from attributecode import ERROR
@@ -27,8 +29,11 @@
2729
from attributecode import cmd
2830
from attributecode import Error
2931

32+
from testing_utils import run_about_command_test
33+
from testing_utils import get_test_loc
34+
3035

31-
# NB: these tests depends on py.test stdout/err capture capabilities
36+
# NB: the test_report_errors* tests depend on py.test stdout/err capture capabilities
3237

3338
def test_report_errors(capsys):
3439
errors = [
@@ -146,86 +151,138 @@ def test_report_errors_with_verbose_flag(capsys):
146151
assert expected_out == out
147152
assert '' == err
148153

149-
def test_filter_errors_default():
150-
errors = [
151-
Error(CRITICAL, 'msg1'),
152-
Error(ERROR, 'msg2'),
153-
Error(INFO, 'msg3'),
154-
Error(WARNING, 'msg4'),
155-
Error(DEBUG, 'msg4'),
156-
Error(NOTSET, 'msg4'),
157-
]
158-
expected = [
159-
Error(CRITICAL, 'msg1'),
160-
Error(ERROR, 'msg2'),
161-
Error(WARNING, 'msg4'),
162-
]
163-
assert expected == cmd.filter_errors(errors)
164154

155+
class TestFilterError(unittest.TestCase):
156+
def test_filter_errors_default(self):
157+
errors = [
158+
Error(CRITICAL, 'msg1'),
159+
Error(ERROR, 'msg2'),
160+
Error(INFO, 'msg3'),
161+
Error(WARNING, 'msg4'),
162+
Error(DEBUG, 'msg4'),
163+
Error(NOTSET, 'msg4'),
164+
]
165+
expected = [
166+
Error(CRITICAL, 'msg1'),
167+
Error(ERROR, 'msg2'),
168+
Error(WARNING, 'msg4'),
169+
]
170+
assert expected == cmd.filter_errors(errors)
165171

166-
def test_filter_errors_with_min():
167-
errors = [
168-
Error(CRITICAL, 'msg1'),
169-
Error(ERROR, 'msg2'),
170-
Error(INFO, 'msg3'),
171-
Error(WARNING, 'msg4'),
172-
Error(DEBUG, 'msg4'),
173-
Error(NOTSET, 'msg4'),
174-
]
175-
expected = [
176-
Error(CRITICAL, 'msg1'),
177-
]
178-
assert expected == cmd.filter_errors(errors, CRITICAL)
179172

173+
def test_filter_errors_with_min(self):
174+
errors = [
175+
Error(CRITICAL, 'msg1'),
176+
Error(ERROR, 'msg2'),
177+
Error(INFO, 'msg3'),
178+
Error(WARNING, 'msg4'),
179+
Error(DEBUG, 'msg4'),
180+
Error(NOTSET, 'msg4'),
181+
]
182+
expected = [
183+
Error(CRITICAL, 'msg1'),
184+
]
185+
assert expected == cmd.filter_errors(errors, CRITICAL)
180186

181-
def test_filter_errors_no_errors():
182-
errors = [
183-
Error(INFO, 'msg3'),
184-
Error(DEBUG, 'msg4'),
185-
Error(NOTSET, 'msg4'),
186-
]
187-
assert [] == cmd.filter_errors(errors)
188187

188+
def test_filter_errors_no_errors(self):
189+
errors = [
190+
Error(INFO, 'msg3'),
191+
Error(DEBUG, 'msg4'),
192+
Error(NOTSET, 'msg4'),
193+
]
194+
assert [] == cmd.filter_errors(errors)
189195

190-
def test_filter_errors_none():
191-
assert [] == cmd.filter_errors([])
192196

197+
def test_filter_errors_none(self):
198+
assert [] == cmd.filter_errors([])
193199

194-
def test_parse_key_values_empty():
195-
assert ({}, []) == cmd.parse_key_values([])
196-
assert ({}, []) == cmd.parse_key_values(None)
197200

201+
class TestParseKeyValues(unittest.TestCase):
198202

199-
def test_parse_key_values_simple():
200-
test = [
201-
'key=value',
202-
'This=THat',
203-
'keY=bar',
204-
]
205-
expected = {
206-
'key': ['value', 'bar'],
207-
'this': ['THat']
203+
def test_parse_key_values_empty(self):
204+
assert ({}, []) == cmd.parse_key_values([])
205+
assert ({}, []) == cmd.parse_key_values(None)
206+
207+
208+
def test_parse_key_values_simple(self):
209+
test = [
210+
'key=value',
211+
'This=THat',
212+
'keY=bar',
213+
]
214+
expected = {
215+
'key': ['value', 'bar'],
216+
'this': ['THat']
217+
}
218+
keyvals, errors = cmd.parse_key_values(test)
219+
assert expected == keyvals
220+
assert not errors
221+
222+
223+
def test_parse_key_values_with_errors(self):
224+
test = [
225+
'key',
226+
'=THat',
227+
'keY=',
228+
'FOO=bar'
229+
]
230+
expected = {
231+
'foo': ['bar'],
208232
}
209-
keyvals, errors = cmd.parse_key_values(test)
210-
assert expected == keyvals
211-
assert not errors
233+
keyvals, errors = cmd.parse_key_values(test)
234+
assert expected == keyvals
235+
expected = [
236+
'missing <key> in "=THat".',
237+
'missing <value> in "keY=".',
238+
'missing <value> in "key".'
239+
]
240+
assert expected == errors
212241

213242

214-
def test_parse_key_values_with_errors():
215-
test = [
216-
'key',
217-
'=THat',
218-
'keY=',
219-
'FOO=bar'
220-
]
221-
expected = {
222-
'foo': ['bar'],
223-
}
224-
keyvals, errors = cmd.parse_key_values(test)
225-
assert expected == keyvals
226-
expected = [
227-
'missing <key> in "=THat".',
228-
'missing <value> in "keY=".',
229-
'missing <value> in "key".'
230-
]
231-
assert expected == errors
243+
###############################################################################
244+
# Run full cli command
245+
###############################################################################
246+
247+
def check_about_stdout(options, expected_loc, regen=False):
248+
"""
249+
Run the about command with the `options` list of options. Assert that
250+
command success and that the stdout is equal to the `expected_loc` test file
251+
content.
252+
"""
253+
stdout, _stderr = run_about_command_test(options)
254+
if regen:
255+
expected_file = get_test_loc(expected_loc, must_exists=False)
256+
with open(expected_file, 'wb') as ef:
257+
ef.write(stdout)
258+
259+
expected_file = get_test_loc(expected_loc, must_exists=True)
260+
assert open(expected_file).read() == stdout
261+
262+
263+
def test_about_help_text(regen=False):
264+
check_about_stdout(['--help'], 'test_cmd/help/about_help.txt')
265+
266+
267+
def test_about_inventory_help_text(regen=False):
268+
check_about_stdout(
269+
['inventory', '--help'],
270+
'test_cmd/help/about_inventory_help.txt')
271+
272+
273+
def test_about_gen_help_text(regen=False):
274+
check_about_stdout(
275+
['gen', '--help'],
276+
'test_cmd/help/about_gen_help.txt')
277+
278+
279+
def test_about_check_help_text(regen=False):
280+
check_about_stdout(
281+
['check', '--help'],
282+
'test_cmd/help/about_check_help.txt')
283+
284+
285+
def test_about_attrib_help_text(regen=False):
286+
check_about_stdout(
287+
['attrib', '--help'],
288+
'test_cmd/help/about_attrib_help.txt')

tests/test_validate.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,48 +20,47 @@
2020
from __future__ import unicode_literals
2121

2222
import os
23-
import subprocess
23+
24+
from testing_utils import run_about_command_test
2425

2526
"""
26-
Common and global checks such as codestyle and related.
27+
Common and global checks such as codestyle and check own ABOUT files.
2728
"""
2829

2930

3031
root_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
3132

3233

3334
def disabled_test_codestyle():
35+
3436
# TODO: enable me
37+
import subprocess
3538
args = [
3639
os.path.join(root_dir, 'bin', 'pycodestyle'),
3740
'--ignore',
3841
'E501,W503,W504,W605',
3942
'--exclude=lib,lib64,tests,thirdparty,docs,bin,man,settings,local,tmp',
4043
'.',
4144
]
45+
4246
subprocess.check_output(args=args, cwd=root_dir)
4347

4448

4549
def check_about(path):
46-
args = [os.path.join(root_dir, 'bin', 'about'), 'check', path]
47-
try:
48-
subprocess.check_output(args=args, cwd=root_dir)
49-
except subprocess.CalledProcessError as cpe:
50-
print('Failed to validate ABOUT files:\n' + cpe.output)
51-
raise Exception(cpe.output)
50+
run_about_command_test(['check', path])
5251

5352

5453
def test_about_thirdparty():
55-
check_about('thirdparty')
54+
run_about_command_test(['check', 'thirdparty'])
5655

5756

5857
def test_about_src():
59-
check_about('src')
58+
run_about_command_test(['check', 'src'])
6059

6160

6261
def test_about_etc():
63-
check_about('etc')
62+
run_about_command_test(['check', 'etc'])
6463

6564

6665
def test_about_myself():
67-
check_about('about.ABOUT')
66+
run_about_command_test(['check', 'about.ABOUT'])
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Usage: about attrib [OPTIONS] LOCATION OUTPUT
2+
3+
Generate an attribution document at OUTPUT using .ABOUT files at LOCATION.
4+
5+
LOCATION: Path to a file, directory or .zip archive containing .ABOUT
6+
files.
7+
8+
OUTPUT: Path where to write the attribution document.
9+
10+
Options:
11+
--template TEMPLATE_FILE_PATH Path to an optional custom attribution
12+
template to generate the attribution
13+
document.
14+
--variable <key>=<value> Add variable(s) as key=value for use in a
15+
custom attribution template.
16+
--inventory PATH Path to an optional JSON or CSV inventory
17+
file listing the subset of .ABOUT files paths
18+
to consider when generating the attribution
19+
document.
20+
--mapping Use the default file mapping.config
21+
(./attributecode/mapping.config) with mapping
22+
between input keys and ABOUT field names.
23+
--mapping-file FILE Use a custom mapping file with mapping
24+
between input keys and ABOUT field names.
25+
-q, --quiet Do not print error or warning messages.
26+
--verbose Show all error and warning messages.
27+
-h, --help Show this message and exit.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Usage: about check [OPTIONS] LOCATION
2+
3+
Check .ABOUT file(s) at LOCATION for validity and print error messages.
4+
5+
LOCATION: Path to a file or directory containing .ABOUT files.
6+
7+
Options:
8+
--verbose Show all error and warning messages.
9+
-h, --help Show this message and exit.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
Usage: about gen [OPTIONS] LOCATION OUTPUT
2+
3+
Generate .ABOUT files in OUTPUT directory from a JSON or CSV inventory of
4+
.ABOUT files at LOCATION.
5+
6+
LOCATION: Path to a JSON or CSV inventory file.
7+
8+
OUTPUT: Path to a directory where ABOUT files are generated.
9+
10+
Options:
11+
--fetch-license KEY Fetch license data and texts from a a
12+
DejaCode License Library API. Create
13+
<license>.LICENSE files from the text of
14+
each license key side-by-side with the
15+
generated .ABOUT file. Also enhance the
16+
.ABOUT file with other data such name and
17+
category.
18+
19+
The following additional options
20+
are required:
21+
22+
api_url - URL to the DejaCode
23+
License Library API endpoint
24+
25+
api_key -
26+
DejaCode API key
27+
Example syntax:
28+
29+
about gen
30+
--fetch-license 'api_url' 'api_key'
31+
--license-notice-text-location PATH
32+
Copy the 'license_file' from the directory
33+
to the generated location.
34+
--mapping Use the default file mapping.config
35+
(./attributecode/mapping.config) with
36+
mapping between input keys and ABOUT field
37+
names.
38+
--mapping-file FILE Use a custom mapping file with mapping
39+
between input keys and ABOUT field names.
40+
-q, --quiet Do not print error or warning messages.
41+
--verbose Show all error and warning messages.
42+
-h, --help Show this message and exit.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Usage: about [OPTIONS] COMMAND [ARGS]...
2+
3+
Generate licensing attribution and credit notices from .ABOUT files and
4+
inventories.
5+
6+
Read, write and collect provenance and license inventories from .ABOUT
7+
files to and from JSON or CSV files.
8+
9+
Use about <command> --help for help on a command.
10+
11+
Options:
12+
--version Show the version and exit.
13+
-h, --help Show this message and exit.
14+
15+
Commands:
16+
attrib Generate an attribution document from .ABOUT files.
17+
check Validate that the format of .ABOUT files is correct.
18+
gen Generate .ABOUT files from an inventory as CSV or JSON.
19+
inventory Collect .ABOUT files and write an inventory as CSV or JSON.

0 commit comments

Comments
 (0)