2 new revisions:
Revision: 6976bda18d1d
Branch: default
Author: Pekka Klärck
Date: Wed Sep 19 14:15:36 2012
Log: Tidy: Support for output file...
http://code.google.com/p/robotframework/source/detail?r=6976bda18d1d
Revision: 0fdc9755e365
Branch: default
Author: Pekka Klärck
Date: Wed Sep 19 14:18:17 2012
Log: Tidy: Made --format case-insensitive. Backend already handled
that but...
http://code.google.com/p/robotframework/source/detail?r=0fdc9755e365
==============================================================================
Revision: 6976bda18d1d
Branch: default
Author: Pekka Klärck
Date: Wed Sep 19 14:15:36 2012
Log: Tidy: Support for output file
Update issue 1239
Status: Started
Implementation and tests done. Need to update User Guide and wait for
test results from Windows CI before this can be considered done.
http://code.google.com/p/robotframework/source/detail?r=6976bda18d1d
Modified:
/atest/robot/tidy/TidyLib.py
/atest/robot/tidy/tidy.txt
/atest/robot/tidy/tidy_resource.txt
/src/robot/tidy.py
/src/robot/utils/application.py
/utest/tidy/test_argument_validation.py
=======================================
--- /atest/robot/tidy/TidyLib.py Wed Aug 29 04:09:07 2012
+++ /atest/robot/tidy/TidyLib.py Wed Sep 19 14:15:36 2012
@@ -6,41 +6,50 @@
import tempfile
from robot.utils.asserts import assert_equals
+from robot.utils import decode_output
ROBOT_SRC = join(dirname(abspath(__file__)), '..', '..', '..', 'src')
DATA_DIR = join(dirname(abspath(__file__)), '..', '..', 'testdata', 'tidy')
+TEMP_FILE = join(os.getenv('TEMPDIR'), 'tidy-test-dir', 'tidy-test-file')
class TidyLib(object):
def __init__(self, interpreter):
- self._cmd = [interpreter, '-m', 'robot.tidy']
+ self._tidy = [interpreter, '-m', 'robot.tidy']
self._interpreter = interpreter
- def run_tidy(self, options, input, command=None):
+ def run_tidy(self, options, input, output=None, tidy=None):
"""Runs tidy in the operating system and returns output."""
- options = options.split(' ') if options else []
- command = command or self._cmd
- with tempfile.TemporaryFile() as output:
- rc = call(command + options + [self._path(input)],
stdout=output,
- stderr=STDOUT, cwd=ROBOT_SRC, shell=os.sep=='\\')
- output.seek(0)
- content = output.read()
+ command = (tidy or self._tidy)[:]
+ if options:
+ command.extend(options.split(' '))
+ command.append(self._path(input))
+ if output:
+ command.append(output)
+ print ' '.join(command)
+ with tempfile.TemporaryFile() as stdout:
+ rc = call(command, stdout=stdout, stderr=STDOUT,
+ cwd=ROBOT_SRC, shell=os.sep=='\\')
+ stdout.seek(0)
+ content = decode_output(stdout.read().strip())
if rc:
raise RuntimeError(content)
- return content.decode('UTF-8')
+ return content
- def run_tidy_and_check_result(self, options, input, expected=None):
+ def run_tidy_and_check_result(self, options, input, output=TEMP_FILE,
+ expected=None):
"""Runs tidy and checks that output matches content of file
`expected`."""
- result = self.run_tidy(options, input)
- self.compare_tidy_results(result, expected or input)
+ result = self.run_tidy(options, input, output)
+ self.compare_tidy_results(output or result, expected or input)
- def run_tidy_as_a_script_and_check_result(self, options, input,
expected=None):
+ def run_tidy_as_a_script_and_check_result(self, options, input,
+ output=TEMP_FILE,
expected=None):
"""Runs tidy and checks that output matches content of file
`expected`."""
- cmd = [self._interpreter, join(ROBOT_SRC, 'robot', 'tidy.py')]
- result = self.run_tidy(options, input, cmd)
- self.compare_tidy_results(result, expected or input)
+ tidy = [self._interpreter, join(ROBOT_SRC, 'robot', 'tidy.py')]
+ result = self.run_tidy(options, input, output, tidy)
+ self.compare_tidy_results(output or result, expected or input)
def compare_tidy_results(self, result, expected):
if os.path.isfile(result):
@@ -54,7 +63,7 @@
assert_equals(repr(unicode(line1)), repr(unicode(line2)), msg)
def _path(self, path):
- return join(DATA_DIR, path.replace('/', os.sep))
+ return abspath(join(DATA_DIR, path.replace('/', os.sep)))
def _read(self, path):
with open(self._path(path)) as f:
=======================================
--- /atest/robot/tidy/tidy.txt Wed Aug 29 04:09:07 2012
+++ /atest/robot/tidy/tidy.txt Wed Sep 19 14:15:36 2012
@@ -2,8 +2,8 @@
Force Tags pybot jybot regression
Library OperatingSystem
Resource tidy_resource.txt
-Suite Setup Create Directory ${TEMP}
-Suite Teardown Remove Directory ${TEMP} recursive=True
+Test Setup Create Directory ${TEMP}
+Test Teardown Remove Directory ${TEMP} recursive=True
*** Test cases ***
Tidying single test case file
@@ -24,13 +24,17 @@
Tidying single init file
Run tidy and check result ${EMPTY} __init__.txt
+ File Should Exist ${TEMP FILE}
+
+Tidying single file without output prints output to console
+ Run tidy and check result ${EMPTY} golden.txt output=
+ File Should Not Exist ${TEMP FILE}
Tidying single file in place
[Setup] Copy File ${DATA}/golden.txt ${TEMP}/golden.txt
Run tidy --inplace --usepipes ${TEMP}/golden.txt
Compare tidy results ${TEMP}/golden.txt ${DATA}/golden_pipes.txt
Check file count ${TEMP} *.txt 1
- [Teardown] Empty Directory ${TEMP}
Tidying single file in place and change format
[Setup] Copy File ${DATA}/golden.txt ${TEMP}/golden.txt
@@ -38,7 +42,6 @@
Compare tidy results ${TEMP}/golden.html ${DATA}/golden.html
Check file count ${TEMP} *.html 1
Check file count ${TEMP} *.txt 0
- [Teardown] Empty Directory ${TEMP}
Tidying many files in place
[Setup] Copy Golden Files
@@ -48,7 +51,6 @@
Check file count ${TEMP} *.html 2
Check file count ${TEMP} *.txt 0
Check file count ${TEMP} *.tsv 0
- [Teardown] Empty Directory ${TEMP}
Tidying directory
[Setup] Copy Directory ${DATA}/tests ${TEMP}/tests
@@ -58,7 +60,6 @@
Check file count ${TEMP}/tests *.txt 0
${output_after}= Run Robot Directly ${TEMP}/tests
Should Be Equal ${output_before} ${output_after}
- [Teardown] Remove Directory ${TEMP}/tests recursive=True
Custom headers are preserved and tables aligned accordingly
Run tidy and check result ${EMPTY} golden_with_headers.txt
=======================================
--- /atest/robot/tidy/tidy_resource.txt Wed Sep 12 15:17:39 2012
+++ /atest/robot/tidy/tidy_resource.txt Wed Sep 19 14:15:36 2012
@@ -4,19 +4,20 @@
*** Variables ***
${DATA} ${CURDIR}/../../testdata/tidy
-${TEMP} %{TEMPDIR}/tidy-test
+${TEMP} %{TEMPDIR}${/}tidy-test-dir
+${TEMPFILE} ${TEMP}${/}tidy-test-file
*** Keywords ***
Run tidy with golden file and check result
- [Arguments] ${options} ${expected_result_file}
- Run tidy and check result ${options} ${DATA}/golden.txt
${expected_result_file}
+ [Arguments] ${options} ${expected}
+ Run tidy and check result ${options} golden.txt
expected=${expected}
Run tidy with golden resource file and check result
- [Arguments] ${options} ${expected_result_file}
- Run tidy and check result ${options}
${DATA}/golden_resource.txt ${expected_result_file}
+ [Arguments] ${options} ${expected}
+ Run tidy and check result ${options} golden_resource.txt
expected=${expected}
Check file count
- [Arguments] ${directory} ${pattern} ${expected count}
+ [Arguments] ${directory} ${pattern} ${expected}
${files}= Count Files In Directory ${directory} ${pattern}
- Should Be Equal As Numbers ${files} ${expected count}
+ Should Be Equal As Numbers ${files} ${expected}
=======================================
--- /src/robot/tidy.py Wed Sep 19 08:20:47 2012
+++ /src/robot/tidy.py Wed Sep 19 14:15:36 2012
@@ -19,14 +19,14 @@
Version: <VERSION>
Usage: python -m robot.tidy [options] inputfile
- or: python -m robot.tidy [options] inputfile > outputfile
+ or: python -m robot.tidy [options] inputfile [outputfile]
or: python -m robot.tidy --inplace [options] inputfile [more input
files]
or: python -m robot.tidy --recursive [options] directory
Tidy tool can be used to clean up and change format of Robot Framework test
-data files. By default, the output is written to the standard output
stream,
-but it can be redirected to a file. Alternatively, files can be modified
-in-place using --inplace or --recursive options.
+data files. The output is written to the standard output stream by default,
+but an optional output file can be given starting from Robot Framework
2.7.5.
+Files can also be modified in-place using --inplace or --recursive options.
Options
=======
@@ -38,7 +38,7 @@
python -m robot.tidy --inplace tests.html
python -m robot.tidy --inplace --format txt *.html
-r --recursive Process given directory recursively. Files in the
directory
- are processed in place similarly as when --inplace option
+ are processed in-place similarly as when --inplace option
is used.
-f --format txt|html|tsv
Output file format. If omitted, the format of the input
@@ -57,7 +57,7 @@
settings, and consistent amount of whitespace between cells and tables.
Examples:
- python -m robot.tidy messed_up_tests.html > cleaned_tests.html
+ python -m robot.tidy messed_up_tests.html cleaned_tests.html
python -m robot.tidy --inplace tests.txt
Changing the test data format
@@ -69,7 +69,7 @@
the --format option.
Examples:
- python -m robot.tidy --format tsv tests_in_html.html > tests_in_tsv.tsv
+ python -m robot.tidy --format tsv tests_in_html.html tests_in_tsv.tsv
python -m robot.tidy --format txt --recursive mytests
Output encoding
@@ -105,11 +105,15 @@
def __init__(self, **options):
self._options = options
- def file(self, path):
- output = StringIO()
+ def file(self, path, output=None):
data = self._create_datafile(path)
- data.save(output=output, **self._options)
- return output.getvalue().decode('UTF-8')
+ outfile = open(output, 'w') if output else StringIO()
+ try:
+ self._save_file(data, outfile)
+ if not output:
+ return outfile.getvalue().decode('UTF-8')
+ finally:
+ outfile.close()
def directory(self, path):
self._save_directory(TestDataDirectory(source=path).populate())
@@ -117,11 +121,11 @@
def inplace(self, path):
self._save_file(self._create_datafile(path))
- def _save_file(self, data):
+ def _save_file(self, data, output=None):
source = data.initfile if self._is_directory(data) else data.source
- if source:
+ if source and not output:
os.remove(source)
- data.save(**self._options)
+ data.save(output=output, **self._options)
def _save_directory(self, data):
if not self._is_directory(data):
@@ -154,46 +158,41 @@
class TidyCommandLine(Application):
def __init__(self):
- Application.__init__(self, USAGE)
+ Application.__init__(self, USAGE, arg_limits=(1,))
- def main(self, inputs, recursive=False, inplace=False, format='txt',
+ def main(self, arguments, recursive=False, inplace=False, format='txt',
usepipes=False, spacecount=4):
tidy = Tidy(format=format, pipe_separated=usepipes,
txt_separating_spaces=spacecount)
if recursive:
- tidy.directory(inputs[0])
+ tidy.directory(arguments[0])
elif inplace:
- for source in inputs:
+ for source in arguments:
tidy.inplace(source)
else:
- self._print(tidy.file(inputs[0]))
+ output = tidy.file(*arguments)
+ self.console(output)
- def _print(self, msg):
- if isatty(sys.stdout):
- msg = encode_output(msg)
+ def validate(self, opts, args):
+ self._validate_mode_and_arguments(opts['inplace'],
opts['recursive'], args)
+ self._validate_format(opts['format'])
+ if not opts['spacecount']:
+ opts.pop('spacecount')
else:
- if os.sep == '\\' and 'b' not in sys.stdout.mode:
- msg = msg.replace('\r\n', '\n')
- msg = msg.encode('UTF-8')
- sys.stdout.write(msg)
+ opts['spacecount'] =
self._validate_spacecount(opts['spacecount'])
+ return opts, args
- def validate(self, options, arguments):
- if options['inplace'] and options['recursive']:
+ def _validate_mode_and_arguments(self, inplace, recursive, args):
+ if inplace and recursive:
raise DataError('--recursive and --inplace can not be used
together.')
- if not options['inplace'] and len(arguments) > 1:
- raise DataError('Expected exactly 1 input file.')
- if not arguments:
- raise DataError('Expected at least 1 input file.')
- if options['recursive'] and not os.path.isdir(arguments[0]):
- raise DataError('--recursive requires input to be a
directory.')
- format = options['format']
+ if recursive and (len(args) > 1 or not os.path.isdir(args[0])):
+ raise DataError('--recursive requires exactly one directory as
argument.')
+ if not (inplace or recursive) and len(args) > 2:
+ raise DataError('Default mode requires 1 or 2 arguments.')
+
+ def _validate_format(self, format):
if format and format not in ['txt', 'tsv', 'html']:
raise DataError("Invalid format: %s." % format)
- if not options['spacecount']:
- options.pop('spacecount')
- else:
- options['spacecount'] =
self._validate_spacecount(options['spacecount'])
- return options, arguments
def _validate_spacecount(self, spacecount):
try:
@@ -201,7 +200,7 @@
if spacecount < 2:
raise ValueError
except ValueError:
- raise DataError('--spacecount must be an integer greater than
1')
+ raise DataError('--spacecount must be an integer greater than
1.')
return spacecount
=======================================
--- /src/robot/utils/application.py Wed Sep 19 08:20:47 2012
+++ /src/robot/utils/application.py Wed Sep 19 14:15:36 2012
@@ -45,7 +45,8 @@
self._exit(rc)
def console(self, msg):
- print encode_output(msg)
+ if msg:
+ print encode_output(msg)
@contextmanager
def _logging(self):
=======================================
--- /utest/tidy/test_argument_validation.py Thu Jun 14 03:37:02 2012
+++ /utest/tidy/test_argument_validation.py Wed Sep 19 14:15:36 2012
@@ -2,32 +2,59 @@
from robot.errors import DataError
from robot.tidy import TidyCommandLine
-from robot.utils.asserts import assert_raises
+from robot.utils.asserts import assert_raises_with_msg
class TestArgumentValidation(unittest.TestCase):
def test_invalid_format(self):
- self._should_raise_error(format='invalid')
+ self._validate(format='invalid', error='Invalid format: invalid.')
def test_invalid_space_count(self):
- self._should_raise_error(spacecount='not a number')
- self._should_raise_error(spacecount='1')
+ error = '--spacecount must be an integer greater than 1.'
+ self._validate(spacecount='not a number', error=error)
+ self._validate(spacecount='1', error=error)
def test_inplace_and_recursive_cannot_be_used_together(self):
- self._should_raise_error(inplace=True, recursive=True)
+ self._validate(inplace=True, recursive=True,
+ error='--recursive and --inplace can not be used
together.')
- def test_arguments_are_required(self):
- self._should_raise_error(args=[])
+ def test_zero_argument_is_never_accepted(self):
+ class Stubbed(TidyCommandLine):
+ def _report_error(self, message, **args):
+ raise DataError(message)
+ for args in [], ['--inplace'], ['--recursive']:
+ assert_raises_with_msg(DataError, 'Expected at least 1
argument, got 0.',
+ Stubbed().execute_cli, args)
- def test_if_not_inplace_only_one_argument_is_accepted(self):
- self._should_raise_error(args=['a', 'b'])
+ def test_default_mode_accepts_one_or_two_arguments(self):
+ self._validate(args=['1'])
+ self._validate(args=['1', '2'])
+ self._validate(args=['1', '2', '3'],
+ error='Default mode requires 1 or 2 arguments.')
+
+ def test_recursive_accepts_only_one_argument(self):
+ self._validate(recursive=True, args=['a', 'b'],
+ error='--recursive requires exactly one directory
as argument.')
+
+ def test_inplace_accepts_one_or_more_arguments(self):
+ for count in range(1, 10):
+ self._validate(inplace=True, args=['a']*count)
def test_recursive_requires_input_to_be_directory(self):
- self._should_raise_error(recursive=True)
+ self._validate(recursive=True,
+ error='--recursive requires exactly one directory
as argument.')
- def _should_raise_error(self, inplace=False, recursive=False,
format=None,
- spacecount=None, args=['a_file.txt']):
- opts = {'inplace': inplace, 'recursive': recursive, 'format':
format,
- 'spacecount': spacecount}
- assert_raises(DataError, TidyCommandLine().validate, opts, args)
+ def _validate(self, inplace=False, recursive=False, format=None,
+ spacecount=None, args=['a_file.txt'], error=None):
+ opts = {'inplace': inplace, 'recursive': recursive,
+ 'format': format, 'spacecount': spacecount}
+ validate = lambda: TidyCommandLine().validate(opts, args)
+ if error:
+ assert_raises_with_msg(DataError, error, validate)
+ else:
+ validate()
+
+
+if __name__ == '__main__':
+ unittest.main()
==============================================================================
Revision: 0fdc9755e365
Branch: default
Author: Pekka Klärck
Date: Wed Sep 19 14:18:17 2012
Log: Tidy: Made --format case-insensitive. Backend already handled
that but we had too strict validation.
http://code.google.com/p/robotframework/source/detail?r=0fdc9755e365
Modified:
/atest/robot/tidy/tidy.txt
/src/robot/tidy.py
=======================================
--- /atest/robot/tidy/tidy.txt Wed Sep 19 14:15:36 2012
+++ /atest/robot/tidy/tidy.txt Wed Sep 19 14:18:17 2012
@@ -46,7 +46,7 @@
Tidying many files in place
[Setup] Copy Golden Files
List Directory ${TEMP}
- Run tidy --InPlace --ForMat html ${TEMP}/golden*
+ Run tidy --InPlace --ForMat HtmL ${TEMP}/golden*
List Directory ${TEMP}
Check file count ${TEMP} *.html 2
Check file count ${TEMP} *.txt 0
@@ -55,7 +55,7 @@
Tidying directory
[Setup] Copy Directory ${DATA}/tests ${TEMP}/tests
${output_before}= Run Robot Directly ${DATA}/tests
- Run Tidy --recursive --format tsv ${TEMP}/tests
+ Run Tidy --recursive --format TSV ${TEMP}/tests
Check file count ${TEMP}/tests *.tsv 2
Check file count ${TEMP}/tests *.txt 0
${output_after}= Run Robot Directly ${TEMP}/tests
=======================================
--- /src/robot/tidy.py Wed Sep 19 14:15:36 2012
+++ /src/robot/tidy.py Wed Sep 19 14:18:17 2012
@@ -191,7 +191,7 @@
raise DataError('Default mode requires 1 or 2 arguments.')
def _validate_format(self, format):
- if format and format not in ['txt', 'tsv', 'html']:
+ if format and format.lower() not in ['txt', 'tsv', 'html']:
raise DataError("Invalid format: %s." % format)
def _validate_spacecount(self, spacecount):