Changeset: 6b863eccdaae for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=6b863eccdaae
Modified Files:
        common/stream/Tests/read_iconv.py
        common/stream/Tests/read_tests.py
        common/stream/Tests/testdata.py
        common/stream/Tests/write_iconv.py
        common/stream/Tests/write_tests.py
        common/stream/text_stream.c
Branch: makelibstreamgreatagain
Log Message:

Properly convert line endings


diffs (truncated from 1153 to 300 lines):

diff --git a/common/stream/Tests/read_iconv.py 
b/common/stream/Tests/read_iconv.py
--- a/common/stream/Tests/read_iconv.py
+++ b/common/stream/Tests/read_iconv.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-from testdata import Doc
+from testdata import Doc, TestFile
 
 import os
 import subprocess
@@ -9,8 +9,11 @@ import sys
 
 def run_streamcat(text, enc):
     content = bytes(text, enc)
-    d = Doc(f'read_iconv_{enc}.txt', content)
-    filename = d.write_tmp()
+    name = f'read_iconv_{enc}.txt'
+
+    tf = TestFile(name, None)
+    filename = tf.write(content)
+
     cmd = ['streamcat', 'read', filename, 'rstream', f'iconv:{enc}']
     print(f"Input with encoding '{enc}' is {repr(content)}")
     # print(cmd)
diff --git a/common/stream/Tests/read_tests.py 
b/common/stream/Tests/read_tests.py
--- a/common/stream/Tests/read_tests.py
+++ b/common/stream/Tests/read_tests.py
@@ -1,6 +1,7 @@
 #!/usr/bin/env python3
 
 import testdata
+from testdata import Doc, TestFile
 
 import hashlib
 import json
@@ -11,135 +12,119 @@ import sys
 
 BOM = b'\xEF\xBB\xBF'
 
-COMPRESSIONS = [None, "gz", "bz2", "xz", "lz4"]
-def compr_name(name, compression):
-    if compression:
-        return name + '.' + compression
-    else:
-        return name
+
+class TestCase:
+    def __init__(self, name, doc, compression, openers, expected):
+        self.tf = TestFile(name, compression)
+        self.name = self.tf.name
+        self.doc = doc
+        self.compression = compression
+        self.openers = openers
+        self.expected = expected
+
+    def run(self):
+        doc = self.doc
+        openers = self.openers
+        filename = self.tf.write(doc.content)
+
+        test = f"read {openers} {self.name}"
+
+        if not isinstance(openers, list):
+            openers = [openers]
 
-def gen_compr_variants(name, content, limit):
-    for compr in COMPRESSIONS:
-        yield testdata.Doc(compr_name(name, compr), content, limit, compr)
+        print()
+        print(f"Test: {test}")
+
+        cmd = ['streamcat', 'read', filename, *openers]
+        results = subprocess.run(
+            cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        if results.returncode != 0 or results.stderr:
+            print(
+                f"\tFAIL: streamcat returned with exit code 
{results.returncode}:\n{results.stderr or ''}")
+            return False
 
-def gen_bom_compr_variants(name, content, limit):
-    yield from gen_compr_variants(name + ".txt", content, limit)
-    yield from gen_compr_variants(name + "_bom.txt", BOM + content, limit)
+        output = results.stdout or b""
+        complaint = self.expected.verify(output)
 
-def broken_boms():
-    limit = 2000
-    for compr in COMPRESSIONS:
-        yield testdata.Doc(compr_name('brokenbom1.txt', compr), BOM[:1] + 
testdata.SHERLOCK, limit, compr)
-        yield testdata.Doc(compr_name('brokenbom2.txt', compr), BOM[:2] + 
testdata.SHERLOCK, limit, compr)
+        if complaint:
+            print(f"\tFAIL: {complaint}")
+            return False
+        else:
+            print(f"\tOK")
+            os.remove(filename)
+            return True
+
 
 def gen_docs():
-    input = testdata.SHERLOCK
+    # We use a document with DOS line endings.
+    # This way we can verify that rastream replaces them with \n
+    # and rstream leaves them alone.
+    text = Doc(testdata.SHERLOCK).to_dos().content
 
     # Whole file
-    yield from gen_bom_compr_variants('sherlock', input, None)
+    yield 'sherlock.txt', Doc(text)
+    yield 'sherlock_bom.txt', Doc(text, prepend_bom=True)
 
     # Empty file
-    yield from gen_bom_compr_variants('empty', b'', None)
+    yield 'empty.txt', Doc(b'')
+    yield 'empty_bom.txt', Doc(b'', prepend_bom=True)
 
-    # First 16 lines
-    head = b'\n'.join(input.split(b'\n')[:16]) + b'\n'
-    yield from gen_bom_compr_variants('small', head, None)
+    # First few lines
+    small = b'\n'.join(text.split(b'\n')[:16]) + b'\n'
+    yield 'small.txt', Doc(small)
+    yield 'small_bom.txt', Doc(small, prepend_bom=True)
 
     # Buffer size boundary cases
     for base_size in [1024, 2048, 4096, 8192, 16384]:
         for delta in [-1, 0, 1]:
             size = base_size + delta
-            yield from gen_bom_compr_variants(f'block{size}', input, size)
+            yield f'block{size}.txt', Doc(text, truncate=size)
+            yield f'block{size}_bom.txt', Doc(text, prepend_bom=True, 
truncate=size)
 
     # \r at end of first block, \n at start of next
-    head = (1023 * b'a') + b'\r\n' + (20 * b'b') + b'\r\n' + (20 * b'c')
+    abc = (1023 * b'a') + b'\r\n' + (20 * b'b') + b'\r\n' + (20 * b'c')
     # word of wisdom: you have to test your tests
-    assert head[:1024].endswith(b'\r')
-    assert head[1024:].startswith(b'\n')
-    yield from gen_compr_variants('crlf1024.txt', head, None)
-
-    yield from broken_boms()
-
-def test_read(opener, text_mode, doc):
-    filename = doc.write_tmp()
-
-    test = f"read {opener} {doc.name}"
-
-    if not isinstance(opener, list):
-        opener = [opener]
-
-    print()
-    print(f"Test: {test}")
+    assert abc[:1024].endswith(b'\r')
+    assert abc[1024:].startswith(b'\n')
+    yield 'crlf1024.txt', Doc(abc)
 
-    cmd = ['streamcat', 'read', filename, *opener]
-    results = subprocess.run(
-        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-    if results.returncode != 0 or results.stderr:
-        print(
-            f"\tFAIL: streamcat returned with exit code 
{results.returncode}:\n{results.stderr or ''}")
-        return False
-
-    output = results.stdout or b""
-    complaint = doc.verify(output, text_mode)
-
-    if complaint:
-        print(f"\tFAIL: {complaint}")
-        return False
-    else:
-        print(f"\tOK")
-        os.remove(filename)
-        return True
+    yield 'brokenbom1.txt', Doc(BOM[:1] + small)
+    yield 'brokenbom2.txt', Doc(BOM[:2] + small)
 
 
-def test_reads(doc):
-    failures = 0
-
-    # rstream does not strip BOM
-    failures += not test_read('rstream', False, doc)
-
-    # rastream does strip the BOM
-    failures += not test_read('rastream', True, doc)
-
-    return failures
-
-
-def test_nonstd_reads(doc):
-    failures = 0
-
-    failures += not test_read(['rstream', 'blocksize:2'], False, doc)
-    failures += not test_read(['rastream', 'blocksize:2'], True, doc)
-
-    failures += not test_read(['rstream', 'blocksize:1000000'], False, doc)
-    failures += not test_read(['rastream', 'blocksize:1000000'], True, doc)
-
-    return failures
+def gen_tests():
+    for compr in testdata.COMPRESSIONS:
+        for name, doc in gen_docs():
+            yield TestCase(name, doc, compr, "rstream", doc)
+            yield TestCase(name, doc, compr, "rastream", 
doc.without_bom().to_unix())
+        for name, doc in gen_docs():
+            if not name.startswith('sherlock') or name.startswith('empty'):
+                continue
+            yield TestCase(name, doc, compr, ["rstream", "blocksize:2"], doc)
+            yield TestCase(name, doc, compr, ["rastream", "blocksize:2"], 
doc.without_bom().to_unix())
+            yield TestCase(name, doc, compr, ["rstream", "blocksize:1000000"], 
doc)
+            yield TestCase(name, doc, compr, ["rastream", 
"blocksize:1000000"], doc.without_bom().to_unix())
 
 
 def all_tests(filename_filter):
     failures = 0
-    for d in gen_docs():
-        if not filename_filter(d.name):
+    for t in gen_tests():
+        if not filename_filter(t.name):
             continue
-        failures += test_reads(d)
+        failures += t.run()
 
-    for d in gen_docs():
-        if not d.name.startswith('sherlock') or d.name.startswith('empty'):
-            continue
-        if not filename_filter(d.name):
-            continue
-        failures += test_nonstd_reads(d)
     return failures
 
 
 if __name__ == "__main__":
     # generate test data for manual testing
     if len(sys.argv) == 1:
-        for d in gen_docs():
-            print(d.name)
+        for name, d in gen_docs():
+            print(name)
     elif len(sys.argv) == 2:
-        for d in gen_docs():
-            if d.name == sys.argv[1]:
-                d.write(sys.stdout.buffer)
+        for name, d in gen_docs():
+            if name == sys.argv[1]:
+                sys.stdout.buffer.write(d.content)
     else:
         print("Usage: python3 read_tests.py [TESTDATANAME]", file=sys.stderr)
         sys.exit(1)
diff --git a/common/stream/Tests/testdata.py b/common/stream/Tests/testdata.py
--- a/common/stream/Tests/testdata.py
+++ b/common/stream/Tests/testdata.py
@@ -12,52 +12,197 @@ import sys
 import tempfile
 
 
+LF = b'\n'
+CRLF = b'\r\n'
 BOM = b'\xEF\xBB\xBF'
 
 SRCDIR = os.environ.get(
     'TSTSRCDIR',
     os.path.dirname(os.path.abspath(sys.argv[0]))
 )
+# The functions we pass this to will pick their own default if None:
 TMPDIR = os.environ.get('TSTTRGDIR')
 
-# Often used for testfile contents. Uses DOS line endings, which is
-# important because we can check whether they are transformed or left alone.
-TESTDATA = os.path.join(SRCDIR, '1661-0.txt.gz')
-SHERLOCK = gzip.open(TESTDATA, 'rb').read()
-assert SHERLOCK.find(b'\x0d\x0a') >= 0
+SHERLOCK = gzip.open(os.path.join(SRCDIR, '1661-0.txt.gz'), 
'rb').read().replace(CRLF, LF)
 
+COMPRESSIONS = [None, "gz", "bz2", "xz", "lz4"]
 
 class Doc:
-    def __init__(self, name, content, length_limit=None, compression=None):
-        if length_limit:
-            content = content[:length_limit]
+    """Contents to be read or written. The constructor has several options
+    to make it easy to construct certain variants. The verify method tries
+    to give a human friendly description of the differences found.
+    """
+
+    def __init__(self, content, prepend_bom=False, dos_line_endings=False, 
truncate=None):
+        assert isinstance(content, bytes)
+        if prepend_bom:
+            content = BOM + content
+        if dos_line_endings:
+            content = content.replace(LF, CRLF)
+        if truncate != None:
+            assert truncate >= 0
+            content = content[:truncate]
+        self.content = content
+
+    def with_bom(self):
+        assert not self.content.startswith(BOM)
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to