Skip to content
This repository was archived by the owner on Aug 25, 2024. It is now read-only.

Commit fffca85

Browse files
John Andersenpdxjohnny
John Andersen
authored andcommitted
util: testing: Add FileSourceTest
Signed-off-by: John Andersen <[email protected]>
1 parent 1c231b2 commit fffca85

File tree

5 files changed

+98
-86
lines changed

5 files changed

+98
-86
lines changed

CHANGELOG.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2222
- Docs get version from dffml.version.VERSION.
2323
- FileSource zipfiles are wrapped with TextIOWrapper because CSVSource expects
2424
the underlying file object to return str instances rather than bytes.
25+
- FileSource zipfiles are wrapped with TextIOWrapper because CSVSource expects
26+
the underlying file object to return str instances rather than bytes.
27+
- FileSourceTest inherits from SourceTest and is used to test json and csv
28+
sources.
29+
- A temporary directory is used to replicate `mktemp -u` functionality so as to
30+
provide tests using a FileSource with a valid tempfile name.
2531

2632
## [0.2.1] - 2019-06-07
2733
### Added
@@ -36,8 +42,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3642
writing a Python meta static analysis tool,
3743
[shouldi](https://pypi.org/project/shouldi/)
3844
### Changed
39-
- OperationImplementation add_label and add_orig_label methods now use op.name
40-
instead of ENTRY_POINT_ORIG_LABEL and ENTRY_POINT_NAME.
45+
- OperationImplementation `add_label` and `add_orig_label` methods now use
46+
op.name instead of `ENTRY_POINT_ORIG_LABEL` and `ENTRY_POINT_NAME`.
4147
- Make output specs and remap arguments optional for Operations CLI commands.
4248
- Feature skeleton project is now operations skeleton project
4349
### Fixed

dffml/util/testing/source.py

Lines changed: 63 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# SPDX-License-Identifier: MIT
22
# Copyright (c) 2019 Intel Corporation
3+
import os
34
import abc
5+
import random
46
import tempfile
57

68
from ...repo import Repo
@@ -17,13 +19,10 @@ class SourceTest(abc.ABC):
1719
>>> from dffml.util.testing.source import SourceTest
1820
>>> from dffml.util.asynctestcase import AsyncTestCase
1921
>>> class TestJSONSource(SourceTest, AsyncTestCase):
20-
>>> async def setUpSource(self, fileobj):
21-
>>> return JSONSource(FileSourceConfig(filename=fileobj.name))
22+
>>> async def setUpSource(self):
23+
>>> return MemorySource(MemorySourceConfig(repos=[Repo('a')]))
2224
"""
2325

24-
async def setUpFile(self, fileobj):
25-
pass
26-
2726
@abc.abstractmethod
2827
async def setUpSource(self, fileobj):
2928
pass # pragma: no cover
@@ -60,40 +59,62 @@ async def test_update(self):
6059
},
6160
)
6261

63-
with tempfile.NamedTemporaryFile() as testfile:
64-
self.maxDiff = 3000
65-
await self.setUpFile(testfile)
66-
source = await self.setUpSource(testfile)
67-
async with source as testSource:
68-
# Open, update, and close
69-
async with testSource() as sourceContext:
70-
await sourceContext.update(full_repo)
71-
await sourceContext.update(empty_repo)
72-
async with source as testSource:
73-
# Open and confirm we saved and loaded correctly
74-
async with testSource() as sourceContext:
75-
with self.subTest(src_url=full_src_url):
76-
repo = await sourceContext.repo(full_src_url)
77-
self.assertEqual(
78-
repo.data.prediction.classification, "feedface"
79-
)
80-
self.assertEqual(repo.data.prediction.confidence, 0.42)
81-
with self.subTest(src_url=empty_src_url):
82-
repo = await sourceContext.repo(empty_src_url)
83-
self.assertFalse(repo.data.prediction.classification)
84-
self.assertFalse(repo.data.prediction.confidence)
85-
with self.subTest(both=[full_src_url, empty_src_url]):
86-
repos = {
87-
repo.src_url: repo
88-
async for repo in sourceContext.repos()
89-
}
90-
self.assertIn(full_src_url, repos)
91-
self.assertIn(empty_src_url, repos)
92-
self.assertEqual(
93-
repos[full_src_url].features(),
94-
full_repo.features(),
95-
)
96-
self.assertEqual(
97-
repos[empty_src_url].features(),
98-
empty_repo.features(),
99-
)
62+
source = await self.setUpSource()
63+
async with source as testSource:
64+
# Open, update, and close
65+
async with testSource() as sourceContext:
66+
await sourceContext.update(full_repo)
67+
await sourceContext.update(empty_repo)
68+
async with source as testSource:
69+
# Open and confirm we saved and loaded correctly
70+
async with testSource() as sourceContext:
71+
with self.subTest(src_url=full_src_url):
72+
repo = await sourceContext.repo(full_src_url)
73+
self.assertEqual(
74+
repo.data.prediction.classification, "feedface"
75+
)
76+
self.assertEqual(repo.data.prediction.confidence, 0.42)
77+
with self.subTest(src_url=empty_src_url):
78+
repo = await sourceContext.repo(empty_src_url)
79+
self.assertFalse(repo.data.prediction.classification)
80+
self.assertFalse(repo.data.prediction.confidence)
81+
with self.subTest(both=[full_src_url, empty_src_url]):
82+
repos = {
83+
repo.src_url: repo
84+
async for repo in sourceContext.repos()
85+
}
86+
self.assertIn(full_src_url, repos)
87+
self.assertIn(empty_src_url, repos)
88+
self.assertEqual(
89+
repos[full_src_url].features(), full_repo.features()
90+
)
91+
self.assertEqual(
92+
repos[empty_src_url].features(), empty_repo.features()
93+
)
94+
95+
96+
class FileSourceTest(SourceTest):
97+
"""
98+
Test case class used to test a Source implementation. Subclass from and set
99+
the SOURCE property to run tests on that source.
100+
101+
>>> from dffml.source.file import FileSourceConfig
102+
>>> from dffml.source.json import JSONSource
103+
>>> from dffml.util.testing.source import FileSourceTest
104+
>>> from dffml.util.asynctestcase import AsyncTestCase
105+
>>> class TestJSONSource(FileSourceTest, AsyncTestCase):
106+
>>> async def setUpSource(self):
107+
>>> return JSONSource(FileSourceConfig(filename=self.testfile))
108+
"""
109+
110+
async def test_update(self):
111+
with tempfile.TemporaryDirectory() as testdir:
112+
with self.subTest(extension=None):
113+
self.testfile = os.path.join(testdir, str(random.random))
114+
await super().test_update()
115+
for extension in ["xz", "gz", "bz2", "lzma", "zip"]:
116+
with self.subTest(extension=extension):
117+
self.testfile = os.path.join(
118+
testdir, str(random.random) + "." + extension
119+
)
120+
await super().test_update()

tests/source/test_csv.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
# Copyright (c) 2019 Intel Corporation
33
from dffml.source.file import FileSourceConfig
44
from dffml.source.csv import CSVSource
5-
from dffml.util.testing.source import SourceTest
5+
from dffml.util.testing.source import FileSourceTest
66
from dffml.util.asynctestcase import AsyncTestCase
77

88

9-
class TestCSVSource(SourceTest, AsyncTestCase):
10-
async def setUpSource(self, fileobj):
11-
return CSVSource(FileSourceConfig(filename=fileobj.name))
9+
class TestCSVSource(FileSourceTest, AsyncTestCase):
10+
async def setUpSource(self):
11+
return CSVSource(FileSourceConfig(filename=self.testfile))

tests/source/test_json.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,11 @@
22
# Copyright (c) 2019 Intel Corporation
33
from dffml.source.file import FileSourceConfig
44
from dffml.source.json import JSONSource
5-
from dffml.util.testing.source import SourceTest
5+
from dffml.util.testing.source import FileSourceTest
66
from dffml.util.asynctestcase import AsyncTestCase
77

88

9-
class TestJSONSource(SourceTest, AsyncTestCase):
9+
class TestJSONSource(FileSourceTest, AsyncTestCase):
1010

11-
SOURCE = JSONSource
12-
13-
async def setUpFile(self, fileobj):
14-
fileobj.write(b"{}")
15-
fileobj.seek(0)
16-
17-
async def setUpSource(self, fileobj):
18-
return JSONSource(FileSourceConfig(filename=fileobj.name))
11+
async def setUpSource(self):
12+
return JSONSource(FileSourceConfig(filename=self.testfile))

tests/test_cli.py

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -46,32 +46,29 @@
4646

4747

4848
@contextmanager
49-
def empty_json_file():
49+
def non_existant_tempfile():
5050
"""
51-
JSONSource will try to parse a file if it exists and so it needs to be
52-
given a file with an empty JSON object in it, {}.
51+
Yield the filename of a non-existant file within a temporary directory
5352
"""
54-
with tempfile.NamedTemporaryFile() as fileobj:
55-
fileobj.write(b"{}")
56-
fileobj.seek(0)
57-
yield fileobj
53+
with tempfile.TemporaryDirectory() as testdir:
54+
yield os.path.join(testdir, str(random.random))
5855

5956

6057
class ReposTestCase(AsyncTestCase):
6158
async def setUp(self):
6259
super().setUp()
6360
self.repos = [Repo(str(random.random())) for _ in range(0, 10)]
64-
self.__temp_json_fileobj = empty_json_file()
65-
self.temp_json_fileobj = self.__temp_json_fileobj.__enter__()
66-
self.sconfig = FileSourceConfig(filename=self.temp_json_fileobj.name)
61+
self.__temp_filename = non_existant_tempfile()
62+
self.temp_filename = self.__temp_filename.__enter__()
63+
self.sconfig = FileSourceConfig(filename=self.temp_filename)
6764
async with JSONSource(self.sconfig) as source:
6865
async with source() as sctx:
6966
for repo in self.repos:
7067
await sctx.update(repo)
7168

7269
def tearDown(self):
7370
super().tearDown()
74-
self.__temp_json_fileobj.__exit__(None, None, None)
71+
self.__temp_filename.__exit__(None, None, None)
7572

7673

7774
class FakeFeature(Feature):
@@ -149,7 +146,7 @@ async def test_run(self):
149146
"-sources",
150147
"primary=json",
151148
"-source-primary-filename",
152-
self.temp_json_fileobj.name,
149+
self.temp_filename,
153150
"-source-primary-readonly",
154151
"false",
155152
)
@@ -161,10 +158,7 @@ class TestOperationsAll(ReposTestCase):
161158
async def test_run(self):
162159
self.repo_keys = {"add 40 and 2": 42, "multiply 42 and 10": 420}
163160
self.repos = list(map(Repo, self.repo_keys.keys()))
164-
self.temp_json_fileobj.seek(0)
165-
self.temp_json_fileobj.truncate(0)
166-
self.temp_json_fileobj.write(b"{}")
167-
self.temp_json_fileobj.flush()
161+
os.unlink(self.temp_filename)
168162
async with JSONSource(self.sconfig) as source:
169163
async with source() as sctx:
170164
for repo in self.repos:
@@ -176,7 +170,7 @@ async def test_run(self):
176170
"-sources",
177171
"primary=json",
178172
"-source-filename",
179-
self.temp_json_fileobj.name,
173+
self.temp_filename,
180174
"-repo-def",
181175
"calc_string",
182176
"-remap",
@@ -206,10 +200,7 @@ async def test_run(self):
206200
test_key = "multiply 42 and 10"
207201
self.repo_keys = {"add 40 and 2": 42, "multiply 42 and 10": 420}
208202
self.repos = list(map(Repo, self.repo_keys.keys()))
209-
self.temp_json_fileobj.seek(0)
210-
self.temp_json_fileobj.truncate(0)
211-
self.temp_json_fileobj.write(b"{}")
212-
self.temp_json_fileobj.flush()
203+
os.unlink(self.temp_filename)
213204
async with JSONSource(self.sconfig) as source:
214205
async with source() as sctx:
215206
for repo in self.repos:
@@ -221,7 +212,7 @@ async def test_run(self):
221212
"-sources",
222213
"primary=json",
223214
"-source-filename",
224-
self.temp_json_fileobj.name,
215+
self.temp_filename,
225216
"-keys",
226217
test_key,
227218
"-repo-def",
@@ -255,7 +246,7 @@ async def test_run(self):
255246
"-sources",
256247
"primary=json",
257248
"-source-filename",
258-
self.temp_json_fileobj.name,
249+
self.temp_filename,
259250
"-features",
260251
"fake",
261252
)
@@ -281,7 +272,7 @@ async def test_run(self):
281272
"-sources",
282273
"primary=json",
283274
"-source-filename",
284-
self.temp_json_fileobj.name,
275+
self.temp_filename,
285276
"-features",
286277
"fake",
287278
"-keys",
@@ -308,7 +299,7 @@ async def test_run(self):
308299
"-sources",
309300
"primary=json",
310301
"-source-filename",
311-
self.temp_json_fileobj.name,
302+
self.temp_filename,
312303
"-model",
313304
"fake",
314305
"-classifications",
@@ -331,7 +322,7 @@ async def test_run(self):
331322
"-sources",
332323
"primary=json",
333324
"-source-filename",
334-
self.temp_json_fileobj.name,
325+
self.temp_filename,
335326
"-model",
336327
"fake",
337328
"-classifications",
@@ -357,7 +348,7 @@ async def test_run(self):
357348
"-sources",
358349
"primary=json",
359350
"-source-filename",
360-
self.temp_json_fileobj.name,
351+
self.temp_filename,
361352
"-model",
362353
"fake",
363354
"-classifications",
@@ -389,7 +380,7 @@ async def test_run(self):
389380
"-sources",
390381
"primary=json",
391382
"-source-filename",
392-
self.temp_json_fileobj.name,
383+
self.temp_filename,
393384
"-model",
394385
"fake",
395386
"-classifications",

0 commit comments

Comments
 (0)