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):

Reply via email to