Merge pull request #329 from Exiv2/test_suite_improvements
Test suite improvements: - the variables defined in the config file are now accessible inside the system_tests namespace - a new function path was added, that converts unix paths to Windows paths on Windows - hooks were added to the test suite and are used to remove the usage of $cat
This commit is contained in:
commit
2ab4f72c89
@ -43,7 +43,6 @@ build_script:
|
||||
- cmd: unit_tests.exe
|
||||
- cmd: cd ../../tests/
|
||||
- cmd: set EXIV2_EXT=.exe
|
||||
- cmd: set EXIV2_CAT=type
|
||||
- cmd: c:\Python36\python.exe runner.py -v
|
||||
|
||||
cache:
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import system_tests
|
||||
import os.path
|
||||
from system_tests import CaseMeta, path, check_no_ASAN_UBSAN_errors
|
||||
|
||||
class TestFirstPoC(metaclass=system_tests.CaseMeta):
|
||||
|
||||
class TestFirstPoC(metaclass=CaseMeta):
|
||||
"""
|
||||
Regression test for the bug described in:
|
||||
https://github.com/Exiv2/exiv2/issues/283
|
||||
@ -16,16 +16,15 @@ class TestFirstPoC(metaclass=system_tests.CaseMeta):
|
||||
Here we want to also check that the two last lines of got_stderr have the expected_stderr
|
||||
"""
|
||||
|
||||
system_tests.check_no_ASAN_UBSAN_errors(self, i, command, got_stderr, expected_stderr)
|
||||
check_no_ASAN_UBSAN_errors(self, i, command, got_stderr, expected_stderr)
|
||||
self.assertListEqual(expected_stderr.splitlines(), got_stderr.splitlines()[-2:])
|
||||
|
||||
filename = os.path.join("$data_path", "pocIssue283.jpg")
|
||||
filename = path("$data_path/pocIssue283.jpg")
|
||||
commands = ["$exiv2 $filename"]
|
||||
stdout = [""]
|
||||
stderr = [
|
||||
"""$exiv2_exception_message """ + filename + """:
|
||||
"""$exiv2_exception_message $filename:
|
||||
$kerCorruptedMetadata
|
||||
"""]
|
||||
compare_stderr = check_no_ASAN_UBSAN_errors
|
||||
retval = [1]
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import system_tests
|
||||
from system_tests import CaseMeta, path
|
||||
|
||||
|
||||
class CanonEOSM100(metaclass=system_tests.CaseMeta):
|
||||
class CanonEOSM100(metaclass=CaseMeta):
|
||||
|
||||
filename = "$data_path/exiv2-pr317.exv"
|
||||
filename = path("$data_path/exiv2-pr317.exv")
|
||||
commands = ["$exiv2 -pa --grep model/i $filename"]
|
||||
|
||||
stdout = ["""Exif.Image.Model Ascii 15 Canon EOS M100
|
||||
Exif.Canon.ModelID Long 1 EOS M100
|
||||
Exif.Photo.LensModel Ascii 29 EF-M15-45mm f/3.5-6.3 IS STM
|
||||
"""
|
||||
]
|
||||
]
|
||||
stderr = [""]
|
||||
retval = [0]
|
||||
retval = [0]
|
||||
|
||||
@ -1,33 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import system_tests
|
||||
import os.path
|
||||
from system_tests import DeleteFiles, CopyFiles, CaseMeta, path
|
||||
|
||||
|
||||
@system_tests.DeleteFiles("$xmpname")
|
||||
@system_tests.CopyFiles("$data_path/exiv2-empty.jpg")
|
||||
class AdobeXmpNamespace(metaclass=system_tests.CaseMeta):
|
||||
@DeleteFiles("$xmpname")
|
||||
@CopyFiles("$data_path/exiv2-empty.jpg")
|
||||
class AdobeXmpNamespace(metaclass=CaseMeta):
|
||||
|
||||
url = "http://dev.exiv2.org/issues/751"
|
||||
|
||||
filename = os.path.join("$data_path", "exiv2-empty_copy.jpg")
|
||||
xmpname = os.path.join("$data_path", "exiv2-empty_copy.xmp")
|
||||
filename = path("$data_path/exiv2-empty_copy.jpg")
|
||||
xmpname = path("$data_path/exiv2-empty_copy.xmp")
|
||||
|
||||
commands = [
|
||||
"""$exiv2 -v -M"reg imageapp orig/" -M "set Xmp.imageapp.uuid abcd" $filename""",
|
||||
"$exiv2 -f -eX $filename",
|
||||
"$cat $xmpname",
|
||||
"""$exiv2 -v -M"reg imageapp dest/" -M "set Xmp.imageapp.uuid abcd" $filename""",
|
||||
"$exiv2 -f -eX $filename",
|
||||
"$cat $xmpname",
|
||||
]
|
||||
|
||||
stdout = [
|
||||
"""File 1/1: $filename
|
||||
Reg imageapp="orig/"
|
||||
Set Xmp.imageapp.uuid "abcd" (XmpText)
|
||||
""",
|
||||
"",
|
||||
def post_command_hook(self, i, command):
|
||||
def read_xmpfile():
|
||||
with open(self.xmpname, "r", encoding='utf-8') as xmp:
|
||||
return xmp.read(-1)
|
||||
|
||||
if i == 2 or i == 4:
|
||||
self.assertMultiLineEqual(self.xmp_packets[i//2 - 1], read_xmpfile())
|
||||
|
||||
|
||||
xmp_packets = [
|
||||
"""<?xpacket begin="\ufeff" id="W5M0MpCehiHzreSzNTczkc9d"?>
|
||||
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 4.4.0-Exiv2">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
@ -37,11 +38,6 @@ Set Xmp.imageapp.uuid "abcd" (XmpText)
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
<?xpacket end="w"?>""",
|
||||
"""File 1/1: $filename
|
||||
Reg imageapp="dest/"
|
||||
Set Xmp.imageapp.uuid "abcd" (XmpText)
|
||||
""",
|
||||
"",
|
||||
"""<?xpacket begin="\ufeff" id="W5M0MpCehiHzreSzNTczkc9d"?>
|
||||
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 4.4.0-Exiv2">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
@ -51,16 +47,26 @@ Set Xmp.imageapp.uuid "abcd" (XmpText)
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
<?xpacket end="w"?>"""
|
||||
]
|
||||
|
||||
stdout = [
|
||||
"""File 1/1: $filename
|
||||
Reg imageapp="orig/"
|
||||
Set Xmp.imageapp.uuid "abcd" (XmpText)
|
||||
""",
|
||||
"",
|
||||
"""File 1/1: $filename
|
||||
Reg imageapp="dest/"
|
||||
Set Xmp.imageapp.uuid "abcd" (XmpText)
|
||||
""",
|
||||
"",
|
||||
]
|
||||
stderr = [
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"""Warning: Updating namespace URI for imageapp from orig/ to dest/
|
||||
""",
|
||||
"""Warning: Updating namespace URI for imageapp from dest/ to orig/
|
||||
""",
|
||||
""
|
||||
]
|
||||
retval = [0] * 6
|
||||
retval = [0] * 4
|
||||
|
||||
@ -1,19 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import system_tests
|
||||
import os
|
||||
from system_tests import DeleteFiles, CopyFiles, CaseMeta, path
|
||||
|
||||
|
||||
@system_tests.DeleteFiles("$xmpfile")
|
||||
@system_tests.CopyFiles("$data_path/exiv2-empty.jpg")
|
||||
class WrongXmpTypeForNestedXmpKeys(metaclass=system_tests.CaseMeta):
|
||||
@DeleteFiles("$xmpfile")
|
||||
@CopyFiles("$data_path/exiv2-empty.jpg")
|
||||
class WrongXmpTypeForNestedXmpKeys(metaclass=CaseMeta):
|
||||
|
||||
url = "http://dev.exiv2.org/issues/$num"
|
||||
|
||||
num = 799
|
||||
cmdfile = os.path.join("$data_path", "bug$num.cmd")
|
||||
cmdfile = path("$data_path/bug$num.cmd")
|
||||
|
||||
filename_common = os.path.join("$data_path", "exiv2-empty_copy")
|
||||
filename_common = path("$data_path/exiv2-empty_copy")
|
||||
filename = "$filename_common.jpg"
|
||||
xmpfile = "$filename_common.xmp"
|
||||
|
||||
@ -21,50 +20,13 @@ class WrongXmpTypeForNestedXmpKeys(metaclass=system_tests.CaseMeta):
|
||||
"$exiv2 -v -m $cmdfile $filename",
|
||||
"$exiv2 -v -pa $filename",
|
||||
"$exiv2 -f -eX $filename",
|
||||
"$cat $xmpfile",
|
||||
]
|
||||
|
||||
stdout = [
|
||||
"""File 1/1: $filename
|
||||
Set Xmp.MP.RegionInfo/MPRI:Regions "" (XmpBag)
|
||||
Set Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:Rectangle "0.11, 0.22, 0.33, 0.44" (XmpText)
|
||||
Set Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:PersonDisplayName "Baby Gnu" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:w "1600" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:h "800" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:unit "pixel" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:RegionList "" (XmpBag)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Name "Baby Gnu" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Type "Face" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:x "0.275312" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:y "0.3775" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:w "0.164375" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:h "0.28125" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:unit "normalized" (XmpText)
|
||||
""",
|
||||
"""File 1/1: $filename
|
||||
Xmp.MP.RegionInfo XmpText 0 type="Struct"
|
||||
Xmp.MP.RegionInfo/MPRI:Regions XmpText 0 type="Bag"
|
||||
Xmp.MP.RegionInfo/MPRI:Regions[1] XmpText 0 type="Struct"
|
||||
Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:Rectangle XmpText 22 0.11, 0.22, 0.33, 0.44
|
||||
Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:PersonDisplayName XmpText 8 Baby Gnu
|
||||
Xmp.mwg-rs.Regions XmpText 0 type="Struct"
|
||||
Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions XmpText 0 type="Struct"
|
||||
Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:w XmpText 4 1600
|
||||
Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:h XmpText 3 800
|
||||
Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:unit XmpText 5 pixel
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList XmpText 0 type="Bag"
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1] XmpText 0 type="Struct"
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Name XmpText 8 Baby Gnu
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Type XmpText 4 Face
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area XmpText 0 type="Struct"
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:x XmpText 8 0.275312
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:y XmpText 6 0.3775
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:w XmpText 8 0.164375
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:h XmpText 7 0.28125
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:unit XmpText 10 normalized
|
||||
""",
|
||||
"",
|
||||
"""<?xpacket begin="\ufeff" id="W5M0MpCehiHzreSzNTczkc9d"?>
|
||||
def post_tests_hook(self):
|
||||
with open(self.xmpfile, "r", encoding='utf-8') as xmp_file:
|
||||
self.assertMultiLineEqual(self.xmp_packet, xmp_file.read(-1))
|
||||
|
||||
xmp_packet = """<?xpacket begin="\ufeff" id="W5M0MpCehiHzreSzNTczkc9d"?>
|
||||
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 4.4.0-Exiv2">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about=""
|
||||
@ -109,6 +71,47 @@ Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:unit XmpText 10 n
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
<?xpacket end="w"?>"""
|
||||
|
||||
stdout = [
|
||||
"""File 1/1: $filename
|
||||
Set Xmp.MP.RegionInfo/MPRI:Regions "" (XmpBag)
|
||||
Set Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:Rectangle "0.11, 0.22, 0.33, 0.44" (XmpText)
|
||||
Set Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:PersonDisplayName "Baby Gnu" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:w "1600" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:h "800" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:unit "pixel" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:RegionList "" (XmpBag)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Name "Baby Gnu" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Type "Face" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:x "0.275312" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:y "0.3775" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:w "0.164375" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:h "0.28125" (XmpText)
|
||||
Set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:unit "normalized" (XmpText)
|
||||
""",
|
||||
"""File 1/1: $filename
|
||||
Xmp.MP.RegionInfo XmpText 0 type="Struct"
|
||||
Xmp.MP.RegionInfo/MPRI:Regions XmpText 0 type="Bag"
|
||||
Xmp.MP.RegionInfo/MPRI:Regions[1] XmpText 0 type="Struct"
|
||||
Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:Rectangle XmpText 22 0.11, 0.22, 0.33, 0.44
|
||||
Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:PersonDisplayName XmpText 8 Baby Gnu
|
||||
Xmp.mwg-rs.Regions XmpText 0 type="Struct"
|
||||
Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions XmpText 0 type="Struct"
|
||||
Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:w XmpText 4 1600
|
||||
Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:h XmpText 3 800
|
||||
Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:unit XmpText 5 pixel
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList XmpText 0 type="Bag"
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1] XmpText 0 type="Struct"
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Name XmpText 8 Baby Gnu
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Type XmpText 4 Face
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area XmpText 0 type="Struct"
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:x XmpText 8 0.275312
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:y XmpText 6 0.3775
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:w XmpText 8 0.164375
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:h XmpText 7 0.28125
|
||||
Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:unit XmpText 10 normalized
|
||||
""",
|
||||
"",
|
||||
]
|
||||
|
||||
stderr = [
|
||||
@ -117,6 +120,5 @@ Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:unit XmpText 10 n
|
||||
$filename: No IPTC data found in the file
|
||||
""",
|
||||
"",
|
||||
""
|
||||
]
|
||||
retval = [0] * 4
|
||||
retval = [0] * 3
|
||||
|
||||
101
tests/doc.md
101
tests/doc.md
@ -220,10 +220,14 @@ following a `$` with variables either defined in this class alongside (like
|
||||
configuration file. Please note that defining a variable with the same name as a
|
||||
variable in the suite's configuration file will result in an error (otherwise
|
||||
one of the variables would take precedence leading to unexpected results). The
|
||||
substitution of values is performed using the template module from Python's
|
||||
string library via `safe_substitute`.
|
||||
variables defined in the test suites configuration file are also available in
|
||||
the `system_tests` namespace. In the above example it would be therefore
|
||||
possible to access `abort_exit_value` via `system_tests.abort_exit_value`
|
||||
(please be aware that all values will be strings though).
|
||||
|
||||
In the above example the command would thus expand to:
|
||||
The substitution of values is performed using the template module from Python's
|
||||
string library via `safe_substitute`. In the above example the command would
|
||||
thus expand to:
|
||||
``` shell
|
||||
/path/to/the/dir/build/bin/binary -c /path/to/the/dir/conf/main.cfg -i invalid_input_file
|
||||
```
|
||||
@ -232,14 +236,23 @@ and similarly for `stdout` and `stderr`.
|
||||
Once the substitution is performed, each command is run using Python's
|
||||
`subprocess` module, its output is compared to the values in `stdout` and
|
||||
`stderr` and its return value to `retval`. Please note that for portability
|
||||
reasons the subprocess module is run with `shell=False`, thus shell expansions
|
||||
or pipes will not work.
|
||||
reasons the subprocess module is run with `shell=False`, thus shell expansions,
|
||||
pipes and redirections into files will not work.
|
||||
|
||||
As the test cases are implemented in Python, one can take full advantage of
|
||||
Python for the construction of the necessary lists. For example when 10 commands
|
||||
should be run and all return 0, one can write `retval = 10 * [0]` instead of
|
||||
writing 0 ten times. The same is of course possible for strings.
|
||||
|
||||
|
||||
### Multiline strings
|
||||
|
||||
It is generally recommended to use Python's multiline strings (strings starting
|
||||
and ending with three `"` instead of one `"`) for the elements of the `commands`
|
||||
list, especially when the commands include `"` or escape sequences. Proper
|
||||
escaping is tricky to get right in a platform independent way, as it depends on
|
||||
the terminal that is used. Using multiline strings circumvents this issue.
|
||||
|
||||
There are however some peculiarities with multiline strings in Python. Normal
|
||||
strings start and end with a single `"` but multiline strings start with three
|
||||
`"`. Also, while the variable names must be indented, new lines in multiline
|
||||
@ -267,6 +280,31 @@ as the indentation might have suggested.
|
||||
Also note that in this example the string will not be terminated with a newline
|
||||
character. To achieve that put the `"""` on the following line.
|
||||
|
||||
### Paths
|
||||
|
||||
Some test cases require the specification of paths (e.g. to the location of test
|
||||
cases). This can be problematic when working with the Windows operating system,
|
||||
as it sometimes exhibits problems with `/` as path separators instead of `\`,
|
||||
which cannot be used on every other platform.
|
||||
|
||||
This can be circumvented by creating the paths via `os.path.join`, but that is
|
||||
quite verbose. A slightly simpler alternative is the function `path` from
|
||||
`system_tests` which converts all `/` inside your string into the platform's
|
||||
default path separator:
|
||||
|
||||
``` python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from system_tests import CaseMeta, path
|
||||
|
||||
|
||||
class AnInformativeName(metaclass=CaseMeta):
|
||||
|
||||
filename = path("$path_to_test_files/invalid_input_file")
|
||||
|
||||
# the rest of your test case
|
||||
```
|
||||
|
||||
|
||||
## Advanced test cases
|
||||
|
||||
@ -321,6 +359,7 @@ the test suite features a decorator which creates a copy of the supplied files
|
||||
and deletes the copies after the test ran.
|
||||
|
||||
Example:
|
||||
|
||||
``` python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
@ -358,10 +397,12 @@ cases, one can customize how stdout and stderr checked for errors.
|
||||
|
||||
The `system_tests.Case` class has two public functions for the check of stdout &
|
||||
stderr: `compare_stdout` & `compare_stderr`. They have the following interface:
|
||||
|
||||
``` python
|
||||
compare_stdout(self, i, command, got_stdout, expected_stdout)
|
||||
compare_stderr(self, i, command, got_stderr, expected_stderr)
|
||||
```
|
||||
|
||||
with the parameters:
|
||||
- i: index of the command in the `commands` list
|
||||
- command: a string of the actually invoked command
|
||||
@ -382,6 +423,7 @@ errors from AddressSanitizer and undefined behavior sanitizer are not present in
|
||||
the obtained output to standard error **and nothing else**. This is useful for
|
||||
test cases where stderr is filled with warnings that are not worth being tracked
|
||||
by the test suite. It can be used in the following way:
|
||||
|
||||
``` python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
@ -411,6 +453,7 @@ variable substitution using the test suite's configuration file.
|
||||
|
||||
Unfortunately, it has to run in a class member function. The `setUp()` function
|
||||
can be used for this, as it is run before each test. For example like this:
|
||||
|
||||
``` python
|
||||
class SomeName(metaclass=system_tests.CaseMeta):
|
||||
|
||||
@ -425,6 +468,7 @@ This example will work, as the test runner reads the data for `commands`,
|
||||
`stderr`, `stdout` and `retval` from the class instance. What however will not
|
||||
work is creating a new member in `setUp()` and trying to use it as a variable
|
||||
for expansion, like this:
|
||||
|
||||
``` python
|
||||
class SomeName(metaclass=system_tests.CaseMeta):
|
||||
|
||||
@ -451,6 +495,53 @@ class SomeName(metaclass=system_tests.CaseMeta):
|
||||
will result in `another_string` being "foo" and not "bar".
|
||||
|
||||
|
||||
### Hooks
|
||||
|
||||
The `Case` class provides two hooks that are run after each command and after
|
||||
all commands, respectively. The hook which is run after each successful command
|
||||
has the following signature:
|
||||
|
||||
``` Python
|
||||
post_command_hook(self, i, command)
|
||||
```
|
||||
with the following parameters:
|
||||
- `i`: index of the command in the `commands` list
|
||||
- `command`: a string of the actually invoked command
|
||||
|
||||
The hook which is run after all test takes no parameters except `self`:
|
||||
|
||||
``` Python
|
||||
post_tests_hook(self)
|
||||
```
|
||||
|
||||
By default, these hooks do nothing. They can be used to implement custom checks
|
||||
after certain commands, e.g. to check if a file was created. Such a test can be
|
||||
implemented as follows:
|
||||
|
||||
``` Python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import system_tests
|
||||
|
||||
|
||||
class AnInformativeName(metaclass=system_tests.CaseMeta):
|
||||
|
||||
filename = "input_file"
|
||||
output = "out"
|
||||
commands = ["$binary -o output -i $filename"]
|
||||
retval = [0]
|
||||
stdout = [""]
|
||||
stderr = [""]
|
||||
|
||||
output_contents = """Hello World!
|
||||
"""
|
||||
|
||||
def post_tests_hook(self):
|
||||
with open(self.output, "r") as out:
|
||||
self.assertMultiLineEqual(self.output_contents, out.read(-1))
|
||||
```
|
||||
|
||||
|
||||
### Possible pitfalls
|
||||
|
||||
- Do not provide a custom `setUpClass()` function for the test
|
||||
|
||||
@ -4,11 +4,9 @@ timeout: 1
|
||||
[ENV]
|
||||
exiv2_path: EXIV2_PATH
|
||||
binary_extension: EXIV2_EXT
|
||||
cat: EXIV2_CAT
|
||||
|
||||
[ENV fallback]
|
||||
exiv2_path: ../build/bin
|
||||
cat: cat
|
||||
|
||||
[paths]
|
||||
exiv2: ${ENV:exiv2_path}/exiv2${ENV:binary_extension}
|
||||
@ -29,4 +27,3 @@ addition_overflow_message: Overflow in addition
|
||||
exiv2_exception_message: Exiv2 exception in print action for file
|
||||
exiv2_overflow_exception_message: std::overflow_error exception in print action for file
|
||||
exception_in_extract: Exiv2 exception in extract action for file
|
||||
cat: ${ENV:cat}
|
||||
|
||||
@ -177,6 +177,12 @@ def configure_suite(config_file):
|
||||
)
|
||||
_parameters[key] = abs_path
|
||||
|
||||
for key in _parameters:
|
||||
if key in globals():
|
||||
raise ValueError("Variable name {!s} already used.")
|
||||
|
||||
globals()[key] = _parameters[key]
|
||||
|
||||
|
||||
class FileDecoratorBase(object):
|
||||
"""
|
||||
@ -468,6 +474,24 @@ class DeleteFiles(FileDecoratorBase):
|
||||
return expanded_file_name
|
||||
|
||||
|
||||
def path(path_string):
|
||||
r"""
|
||||
Converts a path which uses ``/`` as a separator into a path which uses the
|
||||
path separator of the current operating system.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
>>> import platform
|
||||
>>> sep = "\\" if platform.system() == "Windows" else "/"
|
||||
>>> path("a/b") == "a" + sep + "b"
|
||||
True
|
||||
>>> path("a/more/complex/path") == sep.join(['a', 'more', 'complex', 'path'])
|
||||
True
|
||||
"""
|
||||
return os.path.join(*path_string.split('/'))
|
||||
|
||||
|
||||
def test_run(self):
|
||||
"""
|
||||
This function reads in the members commands, retval, stdout, stderr and runs
|
||||
@ -560,6 +584,10 @@ def test_run(self):
|
||||
retval, proc.returncode, msg="Return value does not match"
|
||||
)
|
||||
|
||||
self.post_command_hook(i, command)
|
||||
|
||||
self.post_tests_hook()
|
||||
|
||||
|
||||
class Case(unittest.TestCase):
|
||||
"""
|
||||
@ -622,6 +650,34 @@ class Case(unittest.TestCase):
|
||||
return string.Template(str(unexpanded_string))\
|
||||
.safe_substitute(**self.variable_dict)
|
||||
|
||||
def post_command_hook(self, i, command):
|
||||
""" Function that is run after the successful execution of one command.
|
||||
|
||||
It is invoked with the following parameters:
|
||||
i - the index of the current command that is run in self.commands
|
||||
command - the command that was run
|
||||
|
||||
It should return nothing.
|
||||
|
||||
This function can be overridden to perform additional checks after the
|
||||
command ran, for instance it can check whether files were created.
|
||||
|
||||
The default implementation does nothing.
|
||||
"""
|
||||
pass
|
||||
|
||||
def post_tests_hook(self):
|
||||
"""
|
||||
Function that is run after the successful execution all commands. It
|
||||
should return nothing.
|
||||
|
||||
This function can be overridden to run additional checks that only make
|
||||
sense after all commands ran.
|
||||
|
||||
The default implementation does nothing.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class CaseMeta(type):
|
||||
""" System tests generation metaclass.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user