]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
Added tests to detect leaks on copy from
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sat, 16 Jan 2021 02:51:19 +0000 (03:51 +0100)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sat, 16 Jan 2021 10:26:19 +0000 (11:26 +0100)
tests/fix_faker.py
tests/test_adapt.py
tests/test_copy.py
tests/test_copy_async.py
tests/test_cursor.py
tests/test_cursor_async.py

index d6928ae8615c7a252ae35407aa7afe596d61e99f..fbd806bec9c0d6d8233d13e873b018ad6342939f 100644 (file)
@@ -29,7 +29,7 @@ class Faker:
 
     def __init__(self, connection):
         self.conn = connection
-        self.format = Format.AUTO
+        self._format = Format.BINARY
         self.records = []
 
         self._schema = None
@@ -37,6 +37,16 @@ class Faker:
         self._makers = {}
         self.table_name = sql.Identifier("fake_table")
 
+    @property
+    def format(self):
+        return self._format
+
+    @format.setter
+    def format(self, format):
+        if format != Format.BINARY:
+            pytest.xfail("faker to extend to all text dumpers")
+        self._format = format
+
     @property
     def schema(self):
         if not self._schema:
index 222538cda3fb9f7c65f683bcd9fa2cf763db3355..5c602367506958d1dd45a7ded9548bbce3621f26 100644 (file)
@@ -294,9 +294,6 @@ def test_optimised_adapters():
 @pytest.mark.slow
 @pytest.mark.parametrize("fmt", [Format.AUTO, Format.TEXT, Format.BINARY])
 def test_random(conn, faker, fmt):
-    if fmt != Format.BINARY:
-        pytest.xfail("faker to extend to all text dumpers")
-
     faker.format = fmt
     faker.choose_schema(ncols=20)
     faker.make_records(50)
index bcf2ec2fcf6458eaff5f6cb22d1af21a834c2161..0ff86ea83a5808a2a4a0536da37a293579199516 100644 (file)
@@ -500,9 +500,6 @@ def test_worker_life(conn, format, buffer):
 @pytest.mark.parametrize("fmt", [Format.TEXT, Format.BINARY])
 @pytest.mark.parametrize("method", ["read", "iter", "row", "rows"])
 def test_copy_to_leaks(dsn, faker, fmt, method):
-    if fmt != Format.BINARY:
-        pytest.xfail("faker to extend to all text dumpers")
-
     faker.format = PgFormat.from_pq(fmt)
     faker.choose_schema(ncols=20)
     faker.make_records(20)
@@ -557,6 +554,47 @@ def test_copy_to_leaks(dsn, faker, fmt, method):
     ), f"objects leaked: {n[1] - n[0]}, {n[2] - n[1]}"
 
 
+@pytest.mark.slow
+@pytest.mark.parametrize("fmt", [Format.TEXT, Format.BINARY])
+def test_copy_from_leaks(dsn, faker, fmt):
+    faker.format = PgFormat.from_pq(fmt)
+    faker.choose_schema(ncols=20)
+    faker.make_records(20)
+
+    n = []
+    for i in range(3):
+        with psycopg3.connect(dsn) as conn:
+            with conn.cursor(binary=fmt) as cur:
+                cur.execute(faker.drop_stmt)
+                cur.execute(faker.create_stmt)
+
+                stmt = sql.SQL("copy {} ({}) from stdin (format {})").format(
+                    faker.table_name,
+                    sql.SQL(", ").join(faker.fields_names),
+                    sql.SQL(fmt.name),
+                )
+                with cur.copy(stmt) as copy:
+                    for row in faker.records:
+                        copy.write_row(row)
+
+                cur.execute(faker.select_stmt)
+                recs = cur.fetchall()
+
+                for got, want in zip(recs, faker.records):
+                    faker.assert_record(got, want)
+
+                del recs
+
+        del cur, conn
+        gc.collect()
+        gc.collect()
+        n.append(len(gc.get_objects()))
+
+    assert (
+        n[0] == n[1] == n[2]
+    ), f"objects leaked: {n[1] - n[0]}, {n[2] - n[1]}"
+
+
 def py_to_raw(item, fmt):
     """Convert from Python type to the expected result from the db"""
     if fmt == Format.TEXT:
index 69d61772be2be6ad3b7cb814c6ce0f99f57e81c4..236b86d6d1fc0e116b475a0ad9add81af773bff5 100644 (file)
@@ -479,9 +479,6 @@ async def test_worker_life(aconn, format, buffer):
 @pytest.mark.parametrize("fmt", [Format.TEXT, Format.BINARY])
 @pytest.mark.parametrize("method", ["read", "iter", "row", "rows"])
 async def test_copy_to_leaks(dsn, faker, fmt, method):
-    if fmt != Format.BINARY:
-        pytest.xfail("faker to extend to all text dumpers")
-
     faker.format = PgFormat.from_pq(fmt)
     faker.choose_schema(ncols=20)
     faker.make_records(20)
@@ -538,6 +535,47 @@ async def test_copy_to_leaks(dsn, faker, fmt, method):
     ), f"objects leaked: {n[1] - n[0]}, {n[2] - n[1]}"
 
 
+@pytest.mark.slow
+@pytest.mark.parametrize("fmt", [Format.TEXT, Format.BINARY])
+async def test_copy_from_leaks(dsn, faker, fmt):
+    faker.format = PgFormat.from_pq(fmt)
+    faker.choose_schema(ncols=20)
+    faker.make_records(20)
+
+    n = []
+    for i in range(3):
+        async with await psycopg3.AsyncConnection.connect(dsn) as conn:
+            async with await conn.cursor(binary=fmt) as cur:
+                await cur.execute(faker.drop_stmt)
+                await cur.execute(faker.create_stmt)
+
+                stmt = sql.SQL("copy {} ({}) from stdin (format {})").format(
+                    faker.table_name,
+                    sql.SQL(", ").join(faker.fields_names),
+                    sql.SQL(fmt.name),
+                )
+                async with cur.copy(stmt) as copy:
+                    for row in faker.records:
+                        await copy.write_row(row)
+
+                await cur.execute(faker.select_stmt)
+                recs = await cur.fetchall()
+
+                for got, want in zip(recs, faker.records):
+                    faker.assert_record(got, want)
+
+                del recs
+
+        del cur, conn
+        gc.collect()
+        gc.collect()
+        n.append(len(gc.get_objects()))
+
+    assert (
+        n[0] == n[1] == n[2]
+    ), f"objects leaked: {n[1] - n[0]}, {n[2] - n[1]}"
+
+
 async def ensure_table(cur, tabledef, name="copy_in"):
     await cur.execute(f"drop table if exists {name}")
     await cur.execute(f"create table {name} ({tabledef})")
index 11629afc4966f2c909fc0ea5ced6106f0db1f036..5b9a3e57bbe09522d2922fad97b1606571f9ac53 100644 (file)
@@ -407,9 +407,6 @@ def test_str(conn):
 @pytest.mark.parametrize("fmt", [Format.AUTO, Format.TEXT, Format.BINARY])
 @pytest.mark.parametrize("fetch", ["one", "many", "all", "iter"])
 def test_leak(dsn, faker, fmt, fetch):
-    if fmt != Format.BINARY:
-        pytest.xfail("faker to extend to all text dumpers")
-
     faker.format = fmt
     faker.choose_schema(ncols=5)
     faker.make_records(10)
index c9b7f1fee40e30c202d12353a9f7b76aa451b716..e73b9ea08683bf5557c251a0dfd9f5e913a28854 100644 (file)
@@ -320,9 +320,6 @@ async def test_str(aconn):
 @pytest.mark.parametrize("fmt", [Format.AUTO, Format.TEXT, Format.BINARY])
 @pytest.mark.parametrize("fetch", ["one", "many", "all", "iter"])
 async def test_leak(dsn, faker, fmt, fetch):
-    if fmt != Format.BINARY:
-        pytest.xfail("faker to extend to all text dumpers")
-
     faker.format = fmt
     faker.choose_schema(ncols=5)
     faker.make_records(10)