Merge pull request #282 from D4N/testsuite_update

Testsuite update
v0.27.3
D4N 7 years ago committed by GitHub
commit c922aa7b46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/175" url = "https://github.com/Exiv2/exiv2/issues/175"
filename = "{data_path}/cve_2017_1000126_stack-oob-read.webp" filename = "$data_path/cve_2017_1000126_stack-oob-read.webp"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerCorruptedMetadata} $kerCorruptedMetadata
"""] """]
retval = [1] retval = [1]

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestPoC(system_tests.Case): class TestPoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/176" url = "https://github.com/Exiv2/exiv2/issues/176"
filename = "{data_path}/heap-oob-write.tiff" filename = "$data_path/heap-oob-write.tiff"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]
retval = [1] retval = [1]

@ -3,15 +3,15 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/49" url = "https://github.com/Exiv2/exiv2/issues/49"
filename = "{data_path}/POC2" filename = "$data_path/POC2"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
retval = [1] retval = [1]
stdout = [""] stdout = [""]
stderr = [ stderr = [
"""{exiv2_exception_message} """ + filename + """: """$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/50" url = "https://github.com/Exiv2/exiv2/issues/50"
filename = "{data_path}/POC3" filename = "$data_path/POC3"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]
retval = [1] retval = [1]

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/51" url = "https://github.com/Exiv2/exiv2/issues/51"
filename = "{data_path}/POC4" filename = "$data_path/POC4"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]
retval = [1] retval = [1]

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/52" url = "https://github.com/Exiv2/exiv2/issues/52"
filename = "{data_path}/POC5" filename = "$data_path/POC5"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]
retval = [1] retval = [1]

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/53" url = "https://github.com/Exiv2/exiv2/issues/53"
filename = "{data_path}/POC6" filename = "$data_path/POC6"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]
retval = [1] retval = [1]

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/54" url = "https://github.com/Exiv2/exiv2/issues/54"
filename = "{data_path}/POC7" filename = "$data_path/POC7"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]
retval = [1] retval = [1]

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/55" url = "https://github.com/Exiv2/exiv2/issues/55"
filename = "{data_path}/POC8" filename = "$data_path/POC8"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]
retval = [1] retval = [1]

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/56" url = "https://github.com/Exiv2/exiv2/issues/56"
filename = "{data_path}/POC9" filename = "$data_path/POC9"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""""""] stdout = [""""""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]
retval = [1] retval = [1]

@ -3,15 +3,15 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/57" url = "https://github.com/Exiv2/exiv2/issues/57"
filename = "{data_path}/POC" filename = "$data_path/POC"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{kerInvalidTypeValue}: 0 stderr = ["""$kerInvalidTypeValue: 0
{exiv2_exception_message} """ + filename + """: $exiv2_exception_message """ + filename + """:
{kerInvalidTypeValue} $kerInvalidTypeValue
"""] """]
retval = [1] retval = [1]

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/58" url = "https://github.com/Exiv2/exiv2/issues/58"
filename = "{data_path}/POC11" filename = "$data_path/POC11"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]
retval = [1] retval = [1]

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/59" url = "https://github.com/Exiv2/exiv2/issues/59"
filename = "{data_path}/POC12" filename = "$data_path/POC12"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]
retval = [1] retval = [1]

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/60" url = "https://github.com/Exiv2/exiv2/issues/60"
filename = "{data_path}/POC13" filename = "$data_path/POC13"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]
retval = [1] retval = [1]

@ -3,15 +3,15 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/76" url = "https://github.com/Exiv2/exiv2/issues/76"
filename = "{data_path}/010_bad_free" filename = "$data_path/010_bad_free"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
retval = [1] retval = [1]
stdout = [""] stdout = [""]
stderr = [ stderr = [
"""{exiv2_exception_message} """ + filename + """: """$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/138" url = "https://github.com/Exiv2/exiv2/issues/138"
filename = "{data_path}/007-heap-buffer-over" filename = "$data_path/007-heap-buffer-over"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]
retval = [1] retval = [1]

@ -3,15 +3,15 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/74" url = "https://github.com/Exiv2/exiv2/issues/74"
filename = "{data_path}/005-invalid-mem" filename = "$data_path/005-invalid-mem"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerCorruptedMetadata} $kerCorruptedMetadata
"""] """]
retval = [1] retval = [1]

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/73" url = "https://github.com/Exiv2/exiv2/issues/73"
filename = "{data_path}/003-heap-buffer-over" filename = "$data_path/003-heap-buffer-over"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerCorruptedMetadata} $kerCorruptedMetadata
"""] """]
retval = [1] retval = [1]

@ -3,17 +3,17 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = [ url = [
"https://github.com/Exiv2/exiv2/issues/139", "https://github.com/Exiv2/exiv2/issues/139",
"https://bugzilla.redhat.com/show_bug.cgi?id=1494787" "https://bugzilla.redhat.com/show_bug.cgi?id=1494787"
] ]
filename = "{data_path}/009-stack-over" filename = "$data_path/009-stack-over"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]
retval = [1] retval = [1]

@ -3,15 +3,15 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/75" url = "https://github.com/Exiv2/exiv2/issues/75"
filename = "{data_path}/008-invalid-mem" filename = "$data_path/008-invalid-mem"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerCorruptedMetadata} $kerCorruptedMetadata
"""] """]
retval = [1] retval = [1]

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/132" url = "https://github.com/Exiv2/exiv2/issues/132"
filename = "{data_path}/01-Null-exiv2-poc" filename = "$data_path/01-Null-exiv2-poc"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]
retval = [1] retval = [1]

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/73" url = "https://github.com/Exiv2/exiv2/issues/73"
filename = "{data_path}/02-Invalid-mem-def" filename = "$data_path/02-Invalid-mem-def"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerCorruptedMetadata} $kerCorruptedMetadata
"""] """]
retval = [1] retval = [1]

@ -3,15 +3,15 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/134" url = "https://github.com/Exiv2/exiv2/issues/134"
filename = "{data_path}/004-heap-buffer-over" filename = "$data_path/004-heap-buffer-over"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{kerInvalidTypeValue}: 250 stderr = ["""$kerInvalidTypeValue: 250
{exiv2_exception_message} """ + filename + """: $exiv2_exception_message """ + filename + """:
{kerInvalidTypeValue} $kerInvalidTypeValue
"""] """]
retval = [1] retval = [1]

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/140" url = "https://github.com/Exiv2/exiv2/issues/140"
filename = "{data_path}/006-heap-buffer-over" filename = "$data_path/006-heap-buffer-over"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]
retval = [1] retval = [1]

@ -3,16 +3,16 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/187" url = "https://github.com/Exiv2/exiv2/issues/187"
filename = "{data_path}/issue_187" filename = "$data_path/issue_187"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
retval = [1] retval = [1]
stdout = [""] stdout = [""]
stderr = [ stderr = [
"""{exiv2_exception_message} """ + filename + """: """$exiv2_exception_message """ + filename + """:
{kerFailedToReadImageData} $kerFailedToReadImageData
""" """
] ]

@ -3,15 +3,15 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/208" url = "https://github.com/Exiv2/exiv2/issues/208"
filename = "{data_path}/2018-01-09-exiv2-crash-001.tiff" filename = "$data_path/2018-01-09-exiv2-crash-001.tiff"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
retval = [1] retval = [1]
stdout = [""] stdout = [""]
stderr = [ stderr = [
"""{exiv2_exception_message} """ + filename + """: """$exiv2_exception_message """ + filename + """:
{kerCorruptedMetadata} $kerCorruptedMetadata
"""] """]

@ -3,15 +3,15 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/188" url = "https://github.com/Exiv2/exiv2/issues/188"
found_by = ["Wei You", "@youwei1988"] found_by = ["Wei You", "@youwei1988"]
filename = "{data_path}/poc_2017-12-12_issue188" filename = "$data_path/poc_2017-12-12_issue188"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_overflow_exception_message} """ + filename + """: stderr = ["""$exiv2_overflow_exception_message """ + filename + """:
{addition_overflow_message} $addition_overflow_message
"""] """]
retval = [1] retval = [1]

@ -3,7 +3,7 @@
import system_tests import system_tests
class TestPoC(system_tests.Case): class TestPoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/168" url = "https://github.com/Exiv2/exiv2/issues/168"
@ -12,20 +12,20 @@ Error: Offset of directory Image, entry 0x0117 is out of bounds: Offset = 0x3030
""" + 12 * """Error: Offset of directory Image, entry 0x3030 is out of bounds: Offset = 0x30303030; truncating the entry """ + 12 * """Error: Offset of directory Image, entry 0x3030 is out of bounds: Offset = 0x30303030; truncating the entry
""" """
filename = "{data_path}/cve_2017_18005_reproducer.tiff" filename = "$data_path/cve_2017_18005_reproducer.tiff"
commands = [ commands = [
"{exiv2} -v pr -P EIXxgklnycsvth " + filename, "$exiv2 -v pr -P EIXxgklnycsvth " + filename,
"{exiv2json} " + filename "$exiv2json " + filename
] ]
stdout = ["""File 1/1: """ + filename + """ stdout = ["""File 1/1: """ + filename + """
0x0117 Image Exif.Image.StripByteCounts StripByteCounts Strip Byte Count SByte 0 0 0x0117 Image Exif.Image.StripByteCounts StripByteCounts Strip Byte Count SByte 0 0
""", """,
"""{{ """{
"Exif": {{ "Exif": {
"Image": {{ "Image": {
"StripByteCounts": 0, "StripByteCounts": 0,
"0x3030": 0, "0x3030": 0,
"0x3030": "", "0x3030": "",
@ -40,9 +40,9 @@ Error: Offset of directory Image, entry 0x0117 is out of bounds: Offset = 0x3030
"0x3030": 0, "0x3030": 0,
"0x3030": 0, "0x3030": 0,
"0x3030": 0 "0x3030": 0
}} }
}} }
}} }
""" """
] ]
stderr = [ stderr = [

@ -3,14 +3,14 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/144" url = "https://github.com/Exiv2/exiv2/issues/144"
filename = "{data_path}/POC1" filename = "$data_path/POC1"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerInvalidMalloc} $kerInvalidMalloc
"""] """]
retval = [1] retval = [1]

@ -3,16 +3,16 @@
import system_tests import system_tests
class TestCvePoC(system_tests.Case): class TestCvePoC(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/202" url = "https://github.com/Exiv2/exiv2/issues/202"
cve_url = "http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-4868" cve_url = "http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-4868"
found_by = ["afl", "topsecLab", "xcainiao"] found_by = ["afl", "topsecLab", "xcainiao"]
filename = "{data_path}/exiv2-memorymmap-error" filename = "$data_path/exiv2-memorymmap-error"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerCorruptedMetadata} $kerCorruptedMetadata
"""] """]
retval = [1] retval = [1]

@ -3,18 +3,18 @@
import system_tests import system_tests
class TestFirstPoC(system_tests.Case): class TestFirstPoC(metaclass=system_tests.CaseMeta):
""" """
Regression test for the first bug described in: Regression test for the first bug described in:
https://github.com/Exiv2/exiv2/issues/159 https://github.com/Exiv2/exiv2/issues/159
""" """
url = "https://github.com/Exiv2/exiv2/issues/159" url = "https://github.com/Exiv2/exiv2/issues/159"
filename = "{data_path}/printStructure" filename = "$data_path/printStructure"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerCorruptedMetadata} $kerCorruptedMetadata
"""] """]
retval = [1] retval = [1]

@ -3,15 +3,15 @@
import system_tests import system_tests
class DecodeIHDRChunkOutOfBoundsRead(system_tests.Case): class DecodeIHDRChunkOutOfBoundsRead(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/170" url = "https://github.com/Exiv2/exiv2/issues/170"
filename = "{data_path}/issue_170_poc" filename = "$data_path/issue_170_poc"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerFailedToReadImageData} $kerFailedToReadImageData
"""] """]
retval = [1] retval = [1]

@ -3,7 +3,7 @@
import system_tests import system_tests
class TamronSupport(system_tests.Case): class TamronSupport(metaclass=system_tests.CaseMeta):
description = "Added support for 'Tamron SP 15-30mm f/2.8 Di VC USD A012' and 'Tamron SP 90mm f/2.8 Di VC USD MACRO1:1'" description = "Added support for 'Tamron SP 15-30mm f/2.8 Di VC USD A012' and 'Tamron SP 90mm f/2.8 Di VC USD MACRO1:1'"
@ -14,9 +14,9 @@ class TamronSupport(system_tests.Case):
"TamronSP90mmF2.8DiVCUSDMacroF004.exv", "TamronSP90mmF2.8DiVCUSDMacroF004.exv",
"TamronSP90mmF2.8DiVCUSDMacroF017.exv" "TamronSP90mmF2.8DiVCUSDMacroF017.exv"
] ]
commands = ["{exiv2} -pa --grep lens/i ../../../test/data/" + files[0]] \ commands = ["$exiv2 -pa --grep lens/i ../../../test/data/" + files[0]] \
+ list(map( + list(map(
lambda fname: "{exiv2} -pa --grep lenstype/i ../../../test/data/" + fname, lambda fname: "$exiv2 -pa --grep lenstype/i ../../../test/data/" + fname,
files[1:] files[1:]
)) ))
retval = [0] * len(files) retval = [0] * len(files)

@ -3,7 +3,7 @@
import system_tests import system_tests
class SigmaLenses(system_tests.Case): class SigmaLenses(metaclass=system_tests.CaseMeta):
files = [ files = [
"Sigma_120-300_DG_OS_HSM_Sport_lens.exv", "Sigma_120-300_DG_OS_HSM_Sport_lens.exv",
@ -12,7 +12,7 @@ class SigmaLenses(system_tests.Case):
] ]
commands = list( commands = list(
map(lambda fname: "{exiv2} -pa --grep lens/i {data_path}/" + fname, files) map(lambda fname: "$exiv2 -pa --grep lens/i $data_path/" + fname, files)
) )
retval = 3 * [0] retval = 3 * [0]

@ -2,15 +2,16 @@
import system_tests import system_tests
class TestFirstPoC(system_tests.Case):
class TestFirstPoC(metaclass=system_tests.CaseMeta):
""" """
Regression test for the first bug described in: Regression test for the first bug described in:
https://github.com/Exiv2/exiv2/issues/246 https://github.com/Exiv2/exiv2/issues/246
""" """
url = "https://github.com/Exiv2/exiv2/issues/246" url = "https://github.com/Exiv2/exiv2/issues/246"
filename = "{data_path}/1-string-format.jpg" filename = "$data_path/1-string-format.jpg"
commands = ["{exiv2} -pS " + filename] commands = ["$exiv2 -pS " + filename]
stdout = [ stdout = [
"""STRUCTURE OF JPEG FILE: """ + filename + """ """STRUCTURE OF JPEG FILE: """ + filename + """
address | marker | length | data address | marker | length | data
@ -19,7 +20,7 @@ class TestFirstPoC(system_tests.Case):
"""] """]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerNoImageInInputData} $kerNoImageInInputData
"""] """]
retval = [1] retval = [1]

@ -2,15 +2,15 @@
import system_tests import system_tests
class TestFirstPoC(system_tests.Case): class TestFirstPoC(metaclass=system_tests.CaseMeta):
""" """
Regression test for the first bug described in: Regression test for the first bug described in:
https://github.com/Exiv2/exiv2/issues/247 https://github.com/Exiv2/exiv2/issues/247
""" """
url = "https://github.com/Exiv2/exiv2/issues/247" url = "https://github.com/Exiv2/exiv2/issues/247"
filename = "{data_path}/2-invalid-memory-access" filename = "$data_path/2-invalid-memory-access"
commands = ["{exiv2} -pt " + filename] commands = ["$exiv2 -pt " + filename]
stdout = [ stdout = [
"""Exif.Image.Make Ascii 6 Canon """Exif.Image.Make Ascii 6 Canon
Exif.Image.Orientation Short 1 top, left Exif.Image.Orientation Short 1 top, left

@ -2,17 +2,18 @@
import system_tests import system_tests
class TestFirstPoC(system_tests.Case):
class TestFirstPoC(metaclass=system_tests.CaseMeta):
""" """
Regression test for the first bug described in: Regression test for the first bug described in:
https://github.com/Exiv2/exiv2/issues/253 https://github.com/Exiv2/exiv2/issues/253
""" """
url = "https://github.com/Exiv2/exiv2/issues/253" url = "https://github.com/Exiv2/exiv2/issues/253"
filename = "{data_path}/3-stringformat-outofbound-read" filename = "$data_path/3-stringformat-outofbound-read"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = [""] stdout = [""]
stderr = ["""{exiv2_exception_message} """ + filename + """: stderr = ["""$exiv2_exception_message """ + filename + """:
{kerNotAJpeg} $kerNotAJpeg
"""] """]
retval = [1] retval = [1]

@ -3,12 +3,12 @@
import system_tests import system_tests
class Sigma24_105mmRecognization(system_tests.Case): class Sigma24_105mmRecognization(metaclass=system_tests.CaseMeta):
url = "https://github.com/Exiv2/exiv2/issues/45" url = "https://github.com/Exiv2/exiv2/issues/45"
filename = "{data_path}/exiv2-g45.exv" filename = "$data_path/exiv2-g45.exv"
commands = ["{exiv2} -pa --grep lens/i " + filename] commands = ["$exiv2 -pa --grep lens/i " + filename]
stdout = ["""Exif.CanonCs.LensType Short 1 Sigma 24-105mm F4 DG OS HSM [Art 013] stdout = ["""Exif.CanonCs.LensType Short 1 Sigma 24-105mm F4 DG OS HSM [Art 013]
Exif.CanonCs.Lens Short 3 24.0 - 105.0 mm Exif.CanonCs.Lens Short 3 24.0 - 105.0 mm
Exif.CanonCf.LensAFStopButton Short 1 0 Exif.CanonCf.LensAFStopButton Short 1 0

@ -3,9 +3,9 @@
import system_tests import system_tests
class ShadowingError(system_tests.Case): class ShadowingError(metaclass=system_tests.CaseMeta):
commands = ["{exiv2} -PE {data_path}/IMGP0020.exv"] commands = ["$exiv2 -PE $data_path/IMGP0020.exv"]
stdout = [""] stdout = [""]
stderr = [""] stderr = [""]
retval = [0] retval = [0]

@ -3,7 +3,7 @@
import system_tests import system_tests
class Issue1305Test(system_tests.Case): class Issue1305Test(metaclass=system_tests.CaseMeta):
err_msg_dir_img = """Warning: Directory Image, entry 0x3030 has unknown Exif (TIFF) type 12336; setting type size 1. err_msg_dir_img = """Warning: Directory Image, entry 0x3030 has unknown Exif (TIFF) type 12336; setting type size 1.
Error: Directory Image, entry 0x3030 has invalid size 808464432*1; skipping entry. Error: Directory Image, entry 0x3030 has invalid size 808464432*1; skipping entry.
""" """
@ -19,8 +19,8 @@ Error: Directory Pentax, entry 0x3030 has invalid size 808464432*1; skipping ent
name = "regression test for issue 1305" name = "regression test for issue 1305"
url = "http://dev.exiv2.org/issues/1305" url = "http://dev.exiv2.org/issues/1305"
filename = "{data_path}/IMGP0006-min.jpg" filename = "$data_path/IMGP0006-min.jpg"
commands = ["{exiv2} " + filename] commands = ["$exiv2 " + filename]
stdout = ["""File name : """ + filename + """ stdout = ["""File name : """ + filename + """
File size : 12341 Bytes File size : 12341 Bytes
MIME type : image/jpeg MIME type : image/jpeg

@ -184,51 +184,44 @@ An example test case file would look like this:
import system_tests import system_tests
class AnInformativeName(system_tests.Case): class AnInformativeName(metaclass=system_tests.CaseMeta):
filename = "invalid_input_file" filename = "invalid_input_file"
commands = [ commands = [
"{binary} -c {import_file} -i {filename}" "$binary -c $import_file -i $filename"
] ]
retval = ["{abort_exit_value}"] retval = ["$abort_exit_value"]
stdout = ["Reading {filename}"] stdout = ["Reading $filename"]
stderr = [ stderr = [
"""{abort_error} """$abort_error
error in {filename} error in $filename
""" """
] ]
``` ```
The first 6 lines are necessary boilerplate to pull in the necessary routines to The first 6 lines are necessary boilerplate to pull in the necessary routines to
run the actual tests (these are implemented in the module `system_tests` with run the actual tests (these are implemented in the module `system_tests` with
the class `system_tests.Case` extending `unittest.TestCase`). When adding new the meta-class `system_tests.CaseMeta` which performs the necessary preparations
tests one should choose a new class name that briefly summarizes the test. Note for the tests to run). When adding new tests one should choose a new class name
that the file name (without the extension) with the directory structure is that briefly summarizes the test. Note that the file name (without the
interpreted as the module by Python and pre-pended to the class name when extension) with the directory structure is interpreted as the module by Python
reporting about the tests. E.g. the file `regression/crashes/test_bug_15.py` and pre-pended to the class name when reporting about the tests. E.g. the file
with the class `OutOfBoundsRead` gets reported as `regression/crashes/test_bug_15.py` with the class `OutOfBoundsRead` gets
`regression.crashes.test_bug_15.OutOfBoundsRead** already including a brief reported as `regression.crashes.test_bug_15.OutOfBoundsRead` already including
summary of this test. a brief summary of this test.
**Caution:** Always import `system_tests` in the aforementioned syntax and don't
use `from system_tests import Case`. This will not work, as the `system_tests`
module stores the suite's config internally which will not be available if you
perform a `from system_tests import Case` (this causes Python to create a copy
of the class `system_tests.Case` for your module, without reading the
configuration file).
In the following lines the lists `commands`, `retval`, `stdout` and `stderr` In the following lines the lists `commands`, `retval`, `stdout` and `stderr`
should be defined. These are lists of strings and must all have the same amount should be defined. These are lists of strings and must all have the same number
of elements. of elements.
The test suite at first takes all these strings and substitutes all values in The test suite at first takes all these strings and substitutes all values
curly braces with variables either defined in this class alongside (like following a `$` with variables either defined in this class alongside (like
`filename` in the above example) or with the values defined in the test suite's `filename` in the above example) or with the values defined in the test suite's
configuration file. Please note that defining a variable with the same name as a 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 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 one of the variables would take precedence leading to unexpected results). The
substitution of values in performed using Python's string `format()` method and substitution of values is performed using the template module from Python's
more elaborate format strings can be used when necessary. string library via `safe_substitute`.
In the above example the command would thus expand to: In the above example the command would thus expand to:
``` shell ``` shell
@ -281,6 +274,45 @@ This section describes more advanced features that are probably not necessary
the "standard" usage of the test suite. the "standard" usage of the test suite.
### Using a different output encoding
The test suite will try to interpret the program's output as utf-8 encoded
strings and if that fails it will try the `iso-8859-1` encoding (also know as
`latin-1`).
If the tested program outputs characters in another encoding then it can be
supplied as the `encodings` parameter in each test case:
``` python
# -*- coding: utf-8 -*-
import system_tests
class AnInformativeName(metaclass=system_tests.CaseMeta):
encodings = ['ascii']
filename = "invalid_input_file"
commands = [
"$binary -c $import_file -i $filename"
]
retval = ["$abort_exit_value"]
stdout = ["Reading $filename"]
stderr = [
"""$abort_error
error in $filename
"""
]
```
The test suite will try to decode the program's output with the provided
encodings in the order that they appear in the list. It will select the first
encoding that can decode the output successfully. If no encoding is able to
decode the program's output, then an error is raised. The list of all supported
encodings can be found
[here](https://docs.python.org/3/library/codecs.html#standard-encodings).
### Creating file copies ### Creating file copies
For tests that modify their input file it is useful to run these with a For tests that modify their input file it is useful to run these with a
@ -295,26 +327,26 @@ Example:
import system_tests import system_tests
@system_tests.CopyFiles("{filename}", "{some_path}/another_file.txt") @system_tests.CopyFiles("$filename", "$some_path/another_file.txt")
class AnInformativeName(system_tests.Case): class AnInformativeName(metaclass=system_tests.CaseMeta):
filename = "invalid_input_file" filename = "invalid_input_file"
commands = [ commands = [
"{binary} -c {import_file} -i {filename}" "$binary -c $import_file -i $filename"
] ]
retval = ["{abort_exit_value}"] retval = ["$abort_exit_value"]
stdout = ["Reading {filename}"] stdout = ["Reading $filename"]
stderr = [ stderr = [
"""{abort_error} """$abort_error
error in {filename} error in $filename
""" """
] ]
``` ```
In this example, the test suite would automatically create a copy of the files In this example, the test suite would automatically create a copy of the files
`invalid_input_file` and `{some_path}/another_file.txt` (`some_path` would be of `invalid_input_file` and `$some_path/another_file.txt` (`some_path` would be of
course expanded too) named `invalid_input_file_copy` and course expanded too) named `invalid_input_file_copy` and
`{some_path}/another_file_copy.txt`. After the test ran, the copies are `$some_path/another_file_copy.txt`. After the test ran, the copies are
deleted. Please note that variable expansion in the filenames is possible. deleted. Please note that variable expansion in the filenames is possible.
@ -356,12 +388,12 @@ by the test suite. It can be used in the following way:
import system_tests import system_tests
class AnInformativeName(system_tests.Case): class AnInformativeName(metaclass=system_tests.CaseMeta):
filename = "invalid_input_file" filename = "invalid_input_file"
commands = ["{binary} -c {import_file} -i {filename}"] commands = ["$binary -c $import_file -i $filename"]
retval = ["{abort_exit_value}"] retval = ["$abort_exit_value"]
stdout = ["Reading {filename}"] stdout = ["Reading $filename"]
stderr = ["""A huge amount of error messages would be here that we absolutely do not care about. Actually everything in this string gets ignored, so we can just leave it empty. stderr = ["""A huge amount of error messages would be here that we absolutely do not care about. Actually everything in this string gets ignored, so we can just leave it empty.
""" """
] ]
@ -380,12 +412,12 @@ variable substitution using the test suite's configuration file.
Unfortunately, it has to run in a class member function. The `setUp()` function 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: can be used for this, as it is run before each test. For example like this:
``` python ``` python
class SomeName(system_tests.Case): class SomeName(metaclass=system_tests.CaseMeta):
def setUp(self): def setUp(self):
self.commands = [self.expand_variables("{some_var}/foo.txt")] self.commands = [self.expand_variables("$some_var/foo.txt")]
self.stderr = [""] self.stderr = [""]
self.stdout = [self.expand_variables("{success_message}")] self.stdout = [self.expand_variables("$success_message")]
self.retval = [0] self.retval = [0]
``` ```
@ -394,11 +426,11 @@ This example will work, as the test runner reads the data for `commands`,
work is creating a new member in `setUp()` and trying to use it as a variable work is creating a new member in `setUp()` and trying to use it as a variable
for expansion, like this: for expansion, like this:
``` python ``` python
class SomeName(system_tests.Case): class SomeName(metaclass=system_tests.CaseMeta):
def setUp(self): def setUp(self):
self.new_var = "foo" self.new_var = "foo"
self.another_string = self.expand_variables("{new_var}") self.another_string = self.expand_variables("$new_var")
``` ```
This example fails in `self.expand_variables` because the expansion uses only This example fails in `self.expand_variables` because the expansion uses only
@ -407,13 +439,13 @@ class member in `setUp()` the changed version will **not** be used for variable
expansion, as the variables are saved in a new dictionary **before** `setUp()` expansion, as the variables are saved in a new dictionary **before** `setUp()`
runs. Thus this: runs. Thus this:
``` python ``` python
class SomeName(system_tests.Case): class SomeName(metaclass=system_tests.CaseMeta):
new_var = "foo" new_var = "foo"
def setUp(self): def setUp(self):
self.new_var = "bar" self.new_var = "bar"
self.another_string = self.expand_variables("{new_var}") self.another_string = self.expand_variables("$new_var")
``` ```
will result in `another_string` being "foo" and not "bar". will result in `another_string` being "foo" and not "bar".
@ -425,27 +457,27 @@ will result in `another_string` being "foo" and not "bar".
cases. `setUpClass()` is used by `system_tests.Case` to store the variables cases. `setUpClass()` is used by `system_tests.Case` to store the variables
for expansion. for expansion.
- Keep in mind that the variable expansion uses Python's `format()`
function. This can make it more cumbersome to include formatted strings into
variables like `commands` which will likely contain other variables from the
test suite. E.g.: `commands = ["{binary} {:s}".format(f) for f in files]` will
not work as `format()` will expect a value for binary. This can be worked
around using either the old Python formatting via `%` or by formatting first
and then concatenating the problematic parts.
## Running the test suite ## Running the test suite
The test suite is written for Python 3 but is in principle also compatible with The test suite is written for Python 3 and is not compatible with Python 2, thus
Python 2, albeit it is not regularly tested, so its functionality is not it must be run with `python3` and not with `python` (which is usually an alias
guaranteed with Python 2. for Python 2).
Then navigate to the `tests/` subdirectory and run: Then navigate to the `tests/` subdirectory and run:
``` shell ``` shell
python3 runner.py python3 runner.py
``` ```
One can supply the script with a directory where the suite should look for the
tests (it will search the directory recursively). If omitted, the runner will
look in the directory where the configuration file is located.
The runner script also supports the optional arguments `--config_file` which The runner script also supports the optional arguments `--config_file` which
allows to provide a different test suite configuration file than the default allows to provide a different test suite configuration file than the default
`suite.conf`. It also forwards the verbosity setting via the `-v`/`--verbose` `suite.conf`. It also forwards the verbosity setting via the `-v`/`--verbose`
flags to Python's unittest module. flags to Python's unittest module.
Optionally one can provide the `--debug` flag which will instruct test suite to
print all command invocations and all expected and obtained outputs to the
standard output.

@ -15,16 +15,34 @@ if __name__ == '__main__':
"--config_file", "--config_file",
type=str, type=str,
nargs=1, nargs=1,
help="Path to the suite's configuration file",
default=['suite.conf'] default=['suite.conf']
) )
parser.add_argument( parser.add_argument(
"--verbose", "-v", "--verbose", "-v",
action='count', action='count',
help="verbosity level",
default=1 default=1
) )
parser.add_argument(
"--debug",
help="enable debugging output",
action='store_true'
)
parser.add_argument(
"dir",
help="directory where the test are searched for (defaults to the config"
"file's location)",
default=None,
type=str,
nargs='?'
)
args = parser.parse_args() args = parser.parse_args()
conf_file = args.config_file[0] conf_file = args.config_file[0]
discovery_root = os.path.dirname(conf_file) discovery_root = os.path.dirname(conf_file if args.dir is None else args.dir)
system_tests.set_debug_mode(args.debug)
system_tests.configure_suite(conf_file) system_tests.configure_suite(conf_file)

@ -12,7 +12,8 @@ exiv2_path: ../build/bin
exiv2: ${ENV:exiv2_path}/exiv2${ENV:binary_extension} exiv2: ${ENV:exiv2_path}/exiv2${ENV:binary_extension}
exiv2json: ${ENV:exiv2_path}/exiv2json${ENV:binary_extension} exiv2json: ${ENV:exiv2_path}/exiv2json${ENV:binary_extension}
data_path: ../test/data data_path: ../test/data
tiff-test: ${ENV:exiv2_path}/tiff-test${ENV:binary_extension} tiff_test: ${ENV:exiv2_path}/tiff-test${ENV:binary_extension}
[variables] [variables]
kerFailedToReadImageData: Failed to read image data kerFailedToReadImageData: Failed to read image data

@ -8,6 +8,7 @@ import threading
import shlex import shlex
import sys import sys
import shutil import shutil
import string
import unittest import unittest
@ -75,9 +76,24 @@ class CasePreservingConfigParser(configparser.ConfigParser):
return option return option
#: global parameters extracted from the test suite's configuration file
_parameters = {} _parameters = {}
#: setting whether debug mode is enabled or not
_debug_mode = False
def set_debug_mode(debug):
""" Enable or disable debug mode
In debug mode the test suite will print out all commands that it runs, the
expected output and the actually obtained output
"""
global _debug_mode
_debug_mode = debug
def configure_suite(config_file): def configure_suite(config_file):
""" """
Populates a global datastructure with the parameters from the suite's Populates a global datastructure with the parameters from the suite's
@ -406,42 +422,120 @@ class CopyFiles(FileDecoratorBase):
return shutil.copyfile(expanded_file_name, new_name) return shutil.copyfile(expanded_file_name, new_name)
class Case(unittest.TestCase): def test_run(self):
""" """
System test case base class, provides the functionality to interpret static This function reads in the members commands, retval, stdout, stderr and runs
class members as system tests and runs them. the `expand_variables` function on each. The resulting commands are then run
using the subprocess module and compared against the expected values that
were provided in the static members via `compare_stdout` and
`compare_stderr`. Furthermore a threading.Timer is used to abort the
execution if a configured timeout is reached.
This class reads in the members commands, retval, stdout, stderr and runs It is automatically added as a member function to each system test by the
the format function on each, where format is called with the kwargs being a CaseMeta metaclass. This ensures that it is run by each system test
merged dictionary of all variables that were extracted from the suite's **after** setUp() and setUpClass() were run.
configuration file and all static members of the current class. """
if not (len(self.commands) == len(self.retval)
== len(self.stdout) == len(self.stderr)):
raise ValueError(
"commands, retval, stdout and stderr don't have the same length"
)
for i, command, retval, stdout, stderr in zip(range(len(self.commands)),
self.commands,
self.retval,
self.stdout,
self.stderr):
command, retval, stdout, stderr = map(
self.expand_variables, [command, retval, stdout, stderr]
)
retval = int(retval)
timeout = {"flag": False}
if _debug_mode:
print(
'', "="*80, "will run: " + command, "expected stdout:", stdout,
"expected stderr:", stderr,
"expected return value: {:d}".format(retval),
sep='\n'
)
The resulting commands are then run using the subprocess module and compared proc = subprocess.Popen(
against the expected values that were provided in the static _cmd_splitter(command),
members. Furthermore a threading.Timer is used to abort the execution if a stdout=subprocess.PIPE,
configured timeout is reached. stderr=subprocess.PIPE,
cwd=self.work_dir
)
def timeout_reached(timeout):
timeout["flag"] = True
proc.kill()
t = threading.Timer(
_parameters["timeout"], timeout_reached, args=[timeout]
)
t.start()
got_stdout, got_stderr = proc.communicate()
t.cancel()
processed_stdout = None
processed_stderr = None
for encoding in self.encodings:
try:
processed_stdout = _process_output_post(
got_stdout.decode(encoding)
)
processed_stderr = _process_output_post(
got_stderr.decode(encoding)
)
except UnicodeError:
pass
else:
break
if processed_stdout is None or processed_stderr is None:
raise ValueError(
"Could not decode the output of the command '{!s}' with the "
"following encodings: {!s}"
.format(command, ','.join(self.encodings))
)
if _debug_mode:
print(
"got stdout:", processed_stdout, "got stderr:",
processed_stderr, "got return value: {:d}"
.format(proc.returncode),
sep='\n'
)
The class itself must be inherited from, otherwise it is not useful at all, self.assertFalse(timeout["flag"], msg="Timeout reached")
as it does not provide any static members that could be used to run system self.compare_stdout(i, command, processed_stdout, stdout)
tests. However, a class that inherits from this class needn't provide any self.compare_stderr(i, command, processed_stderr, stderr)
member functions at all, the inherited test_run() function performs all self.assertEqual(
required functionality in child classes. retval, proc.returncode, msg="Return value does not match"
)
class Case(unittest.TestCase):
"""
System test case base class, provides the functionality to interpret static
class members as system tests.
The class itself only provides utility functions and system tests need not
inherit from it, as it is automatically added via the CaseMeta metaclass.
""" """
""" maxDiff set so that arbitrarily large diffs will be shown """ #: maxDiff set so that arbitrarily large diffs will be shown
maxDiff = None maxDiff = None
#: list of encodings that are used to decode the test program's output
#: the first encoding that does not raise a UnicodeError is used
encodings = ['utf-8', 'iso-8859-1']
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
""" """
This function adds the variables variable_dict & work_dir to the class. This function adds the variable work_dir to the class, which is the path
to the directory where the python source file is located.
work_dir - set to the file where the current class is defined
variable_dict - a merged dictionary of all static members of the current
class and all variables extracted from the suite's
configuration file
""" """
cls.variable_dict = _disjoint_dict_merge(cls.__dict__, _parameters)
cls.work_dir = os.path.dirname(inspect.getfile(cls)) cls.work_dir = os.path.dirname(inspect.getfile(cls))
def compare_stdout(self, i, command, got_stdout, expected_stdout): def compare_stdout(self, i, command, got_stdout, expected_stdout):
@ -460,71 +554,89 @@ class Case(unittest.TestCase):
unittest.TestCase. This function can be overridden in a child class to unittest.TestCase. This function can be overridden in a child class to
implement a custom check. implement a custom check.
""" """
self.assertMultiLineEqual(expected_stdout, got_stdout) self.assertMultiLineEqual(
expected_stdout, got_stdout, msg="Standard output does not match"
)
def compare_stderr(self, i, command, got_stderr, expected_stderr): def compare_stderr(self, i, command, got_stderr, expected_stderr):
""" """ Same as compare_stdout only for standard-error. """
Same as compare_stdout only for standard-error. self.assertMultiLineEqual(
""" expected_stderr, got_stderr, msg="Standard error does not match"
self.assertMultiLineEqual(expected_stderr, got_stderr) )
def expand_variables(self, string): def expand_variables(self, unexpanded_string):
""" """
Expands all variables in curly braces in the given string using the Expands all variables of the form ``$var`` in the given string using the
dictionary variable_dict. dictionary `variable_dict`.
The expansion itself is performed by the builtin string method format(). The expansion itself is performed by the string's template module using
A KeyError indicates that the supplied string contains a variable via `safe_substitute`.
in curly braces that is missing from self.variable_dict
""" """
return str(string).format(**self.variable_dict) return string.Template(str(unexpanded_string))\
.safe_substitute(**self.variable_dict)
def test_run(self):
""" class CaseMeta(type):
Actual system test function which runs the provided commands, """ System tests generation metaclass.
pre-processes all variables and post processes the output before passing
it on to compare_stderr() & compare_stdout(). This metaclass is performs the following tasks:
1. Add the `test_run` function as a member of the test class
2. Add the `Case` class as the parent class
3. Expand all variables already defined in the class, so that any additional
code does not have to perform this task
Using a metaclass instead of inheriting from case has the advantage, that we
can expand all variables in the strings before any test case or even the
class constructor is run! Thus users will immediately see the expanded
result. Also adding the `test_run` function as a direct member and not via
inheritance enforces that it is being run **after** the test cases setUp &
setUpClass (which oddly enough seems not to be the case in the unittest
module where test functions of the parent class run before setUpClass of the
child class).
""" """
for i, command, retval, stdout, stderr in zip(range(len(self.commands)), def __new__(mcs, clsname, bases, dct):
self.commands,
self.retval,
self.stdout,
self.stderr):
command, retval, stdout, stderr = map(
self.expand_variables, [command, retval, stdout, stderr]
)
retval = int(retval)
timeout = {"flag": False}
proc = subprocess.Popen( changed = False
_cmd_splitter(command),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=self.work_dir
)
def timeout_reached(timeout): # expand all non-private variables by brute force
timeout["flag"] = True # => try all expanding all elements defined in the current class until
proc.kill() # there is no change in them any more
keys = [key for key in list(dct.keys()) if not key.startswith('_')]
while not changed:
for key in keys:
t = threading.Timer( old_value = dct[key]
_parameters["timeout"], timeout_reached, args=[timeout]
)
t.start()
got_stdout, got_stderr = proc.communicate()
t.cancel()
self.assertFalse(timeout["flag"] and "Timeout reached") # only try expanding strings and lists
self.compare_stdout( if isinstance(old_value, str):
i, command, new_value = string.Template(old_value).safe_substitute(
_process_output_post(got_stdout.decode('utf-8')), stdout **_disjoint_dict_merge(dct, _parameters)
) )
self.compare_stderr( elif isinstance(old_value, list):
i, command, # do not try to expand anything but strings in the list
_process_output_post(got_stderr.decode('utf-8')), stderr new_value = [
string.Template(elem).safe_substitute(
**_disjoint_dict_merge(dct, _parameters)
) )
self.assertEqual(retval, proc.returncode) if isinstance(elem, str) else elem
for elem in old_value
]
else:
continue
if old_value != new_value:
changed = True
dct[key] = new_value
dct['variable_dict'] = _disjoint_dict_merge(dct, _parameters)
dct['test_run'] = test_run
if Case not in bases:
bases += (Case,)
return super(CaseMeta, mcs).__new__(mcs, clsname, bases, dct)
def check_no_ASAN_UBSAN_errors(self, i, command, got_stderr, expected_stderr): def check_no_ASAN_UBSAN_errors(self, i, command, got_stderr, expected_stderr):

@ -3,7 +3,7 @@
import system_tests import system_tests
class OutputTagExtract(system_tests.Case): class OutputTagExtract(metaclass=system_tests.CaseMeta):
""" """
Test whether exiv2 -pa $file and exiv2 -pS $file produces the same output. Test whether exiv2 -pa $file and exiv2 -pS $file produces the same output.
""" """
@ -107,7 +107,7 @@ class OutputTagExtract(system_tests.Case):
self.compare_pS_pa() self.compare_pS_pa()
commands = [ commands = [
"{exiv2} %s {data_path}/mini9.tif" % (opt) for opt in ["-pa", "-pS"] "$exiv2 %s $data_path/mini9.tif" % (opt) for opt in ["-pa", "-pS"]
] ]
stderr = [""] * 2 stderr = [""] * 2
@ -131,7 +131,7 @@ Exif.Image.YResolution Rational 1 72
Exif.Image.PlanarConfiguration Short 1 1 Exif.Image.PlanarConfiguration Short 1 1
Exif.Image.ResolutionUnit Short 1 inch Exif.Image.ResolutionUnit Short 1 inch
""", """,
"""STRUCTURE OF TIFF FILE (II): {data_path}/mini9.tif """STRUCTURE OF TIFF FILE (II): $data_path/mini9.tif
address | tag | type | count | offset | value address | tag | type | count | offset | value
254 | 0x00fe NewSubfileType | LONG | 1 | | 0 254 | 0x00fe NewSubfileType | LONG | 1 | | 0
266 | 0x0100 ImageWidth | SHORT | 1 | | 9 266 | 0x0100 ImageWidth | SHORT | 1 | | 9
@ -150,5 +150,5 @@ Exif.Image.ResolutionUnit Short 1 inch
422 | 0x011b YResolution | RATIONAL | 1 | 518 | 1207959552/16777216 422 | 0x011b YResolution | RATIONAL | 1 | 518 | 1207959552/16777216
434 | 0x011c PlanarConfiguration | SHORT | 1 | | 1 434 | 0x011c PlanarConfiguration | SHORT | 1 | | 1
446 | 0x0128 ResolutionUnit | SHORT | 1 | | 2 446 | 0x0128 ResolutionUnit | SHORT | 1 | | 2
END {data_path}/mini9.tif END $data_path/mini9.tif
"""] """]

@ -3,10 +3,10 @@
import system_tests import system_tests
@system_tests.CopyFiles("{data_path}/mini9.tif") @system_tests.CopyFiles("$data_path/mini9.tif")
class TestTiffTestProg(system_tests.Case): class TestTiffTestProg(metaclass=system_tests.CaseMeta):
commands = ["{tiff-test} {data_path}/mini9_copy.tif"] commands = ["$tiff_test $data_path/mini9_copy.tif"]
stdout = [ stdout = [
"""Test 1: Writing empty Exif data without original binary data: ok. """Test 1: Writing empty Exif data without original binary data: ok.

@ -11,13 +11,13 @@ The simplest test has the following structure:
import system_tests import system_tests
class GoodTestName(system_tests.Case): class GoodTestName(metaclass=system_tests.CaseMeta):
filename = "{data_path}/test_file" filename = "$data_path/test_file"
commands = ["{exiv2} " + filename, "{exiv2} " + filename + '_2'] commands = ["$exiv2 $filename", "$exiv2 $filename" + '_2']
stdout = [""] * 2 stdout = [""] * 2
stderr = ["""{exiv2_exception_msg} """ + filename + """: stderr = ["""$exiv2_exception_msg $filename:
{error_58_message} $kerFailedToReadImageData
"""] * 2 """] * 2
retval = [1] * 2 retval = [1] * 2
``` ```
@ -25,8 +25,8 @@ class GoodTestName(system_tests.Case):
The test suite will run the provided commands in `commands` and compare them to The test suite will run the provided commands in `commands` and compare them to
the output in `stdout` and `stderr` and it will compare the return values. the output in `stdout` and `stderr` and it will compare the return values.
The strings in curly braces are variables either defined in this test's class or The strings after a `$` are variables either defined in this test's class or are
are taken from the suite's configuration file (see `doc.md` for a complete taken from the suite's configuration file (see `doc.md` for a complete
explanation). explanation).
When creating new tests, follow roughly these steps: When creating new tests, follow roughly these steps:

Loading…
Cancel
Save