Skip to content

Commit f6b5a9b

Browse files
committed
Performance optimization: check test files once per TestClass and not once per test case
1 parent ba91db7 commit f6b5a9b

File tree

1 file changed

+63
-82
lines changed

1 file changed

+63
-82
lines changed

neo/test/iotest/common_io_test.py

Lines changed: 63 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -95,113 +95,93 @@ class BaseTestIO:
9595
local_test_dir = get_local_testing_data_folder()
9696

9797
def setUp(self):
98-
'''
99-
Set up the test fixture. This is run for every test
100-
'''
101-
self.higher = self.ioclass.supported_objects[0]
102-
self.shortname = self.ioclass.__name__.lower().rstrip('io')
98+
"""
99+
This is run once per test case
100+
"""
101+
pass
102+
103+
@classmethod
104+
def setUpClass(cls, *args, **kwargs):
105+
"""
106+
This is run once per test class instance
107+
"""
108+
if cls == BaseTestIO:
109+
cls.run = lambda self, *args, **kwargs: None
110+
return
111+
112+
super(BaseTestIO, cls).setUpClass()
113+
cls.higher = cls.ioclass.supported_objects[0]
114+
cls.shortname = cls.ioclass.__name__.lower().rstrip('io')
103115
# these objects can both be written and read
104-
self.io_readandwrite = list(set(self.ioclass.readable_objects) &
105-
set(self.ioclass.writeable_objects))
116+
cls.io_readandwrite = list(set(cls.ioclass.readable_objects) &
117+
set(cls.ioclass.writeable_objects))
106118
# these objects can be either written or read
107-
self.io_readorwrite = list(set(self.ioclass.readable_objects) |
108-
set(self.ioclass.writeable_objects))
109-
119+
cls.io_readorwrite = list(set(cls.ioclass.readable_objects) |
120+
set(cls.ioclass.writeable_objects))
110121
if HAVE_DATALAD:
111-
for remote_path in self.entities_to_download:
122+
for remote_path in cls.entities_to_download:
112123
download_dataset(repo=repo_for_test, remote_path=remote_path)
113124

114-
self.files_generated = []
115-
self.generate_files_for_io_able_to_write()
125+
cls.files_generated = []
126+
cls.generate_files_for_io_able_to_write()
116127

117-
# be carefull self.entities_to_test is class attributes
118-
self.files_to_test = [self.get_local_path(e) for e in self.entities_to_test]
128+
# be careful cls.entities_to_test is class attributes
129+
cls.files_to_test = [cls.get_local_path(e) for e in cls.entities_to_test]
119130
else:
120-
self.files_to_test = []
131+
cls.files_to_test = []
121132
raise unittest.SkipTest("Requires datalad download of data from the web")
122133

123-
def create_local_dir_if_not_exists(self):
124-
'''
125-
Create a local directory to store testing files and return it.
126-
127-
The directory path is also written to self.local_test_dir
128-
'''
129-
self.local_test_dir = create_local_temp_dir(
130-
self.shortname, directory=os.environ.get("NEO_TEST_FILE_DIR", None))
131-
return self.local_test_dir
132-
133-
def download_test_files_if_not_present(self):
134-
'''
135-
Download %s file at G-node for testing
136-
url_for_tests is global at beginning of this file.
137-
138-
''' % self.ioclass.__name__
139-
if not self.use_network:
140-
raise unittest.SkipTest("Requires download of data from the web")
141-
142-
url = url_for_tests + self.shortname
143-
try:
144-
make_all_directories(self.files_to_download, self.local_test_dir)
145-
download_test_file(self.files_to_download,
146-
self.local_test_dir, url)
147-
except OSError as exc:
148-
raise unittest.TestCase.failureException(exc)
149-
150-
download_test_files_if_not_present.__test__ = False
151-
152-
def cleanup_file(self, path):
153-
'''
154-
Remove test files or directories safely.
155-
'''
156-
cleanup_test_file(self.ioclass, path, directory=self.local_test_dir)
157-
158-
def able_to_write_or_read(self, writeread=False, readwrite=False):
159-
'''
134+
@classmethod
135+
def able_to_write_or_read(cls, writeread=False, readwrite=False):
136+
"""
160137
Return True if generalized writing or reading is possible.
161138
162139
If writeread=True, return True if writing then reading is
163140
possible and produces identical neo objects.
164141
165142
If readwrite=True, return True if reading then writing is possible
166143
and produces files with identical hashes.
167-
'''
144+
"""
168145
# Find the highest object that is supported by the IO
169146
# Test only if it is a Block or Segment, and if it can both read
170147
# and write this object.
171-
if self.higher not in self.io_readandwrite:
148+
if cls.higher not in cls.io_readandwrite:
172149
return False
173-
if self.higher not in [Block, Segment]:
150+
if cls.higher not in [Block, Segment]:
174151
return False
175152

176-
# when io need external knowldge for writting or read such as
177-
# sampling_rate (RawBinaryIO...) the test is too much complex to design
178-
# genericaly.
179-
if (self.higher in self.ioclass.read_params and
180-
len(self.ioclass.read_params[self.higher]) != 0):
153+
# when io need external knowledge for writing or reading such as
154+
# sampling_rate (RawBinaryIO...) the test is too complex to design
155+
# generically.
156+
if (cls.higher in cls.ioclass.read_params and
157+
len(cls.ioclass.read_params[cls.higher]) != 0):
181158
return False
182159

183160
# handle cases where the test should write then read
184-
if writeread and not self.read_and_write_is_bijective:
161+
if writeread and not cls.read_and_write_is_bijective:
185162
return False
186163

187164
# handle cases where the test should read then write
188-
if readwrite and not self.hash_conserved_when_write_read:
165+
if readwrite and not cls.hash_conserved_when_write_read:
189166
return False
190167

191168
return True
192169

193-
def get_local_base_folder(self):
170+
@staticmethod
171+
def get_local_base_folder():
194172
return get_local_testing_data_folder()
195173

196-
def get_local_path(self, sub_path):
197-
root_local_path = self.get_local_base_folder()
174+
@classmethod
175+
def get_local_path(cls, sub_path):
176+
root_local_path = cls.get_local_base_folder()
198177
local_path = root_local_path / sub_path
199178
# TODO later : remove the str when all IOs handle the pathlib.Path objects
200179
local_path = str(local_path)
201180
return local_path
202181

203-
def generic_io_object(self, filename=None, return_path=False, clean=False):
204-
'''
182+
@classmethod
183+
def generic_io_object(cls, filename=None, return_path=False, clean=False):
184+
"""
205185
Create an io object in a generic way that can work with both
206186
file-based and directory-based io objects.
207187
@@ -212,10 +192,10 @@ def generic_io_object(self, filename=None, return_path=False, clean=False):
212192
213193
If clean is True, try to delete existing versions of the file
214194
before creating the io object. Default is False.
215-
'''
216-
return create_generic_io_object(ioclass=self.ioclass,
195+
"""
196+
return create_generic_io_object(ioclass=cls.ioclass,
217197
filename=filename,
218-
directory=self.local_test_dir,
198+
directory=cls.local_test_dir,
219199
return_path=return_path,
220200
clean=clean)
221201

@@ -369,28 +349,29 @@ def iter_objects(self, target=None, return_path=False, return_ioobj=False,
369349
clean=clean, readall=readall,
370350
lazy=lazy)
371351

372-
def generate_files_for_io_able_to_write(self):
373-
'''
352+
@classmethod
353+
def generate_files_for_io_able_to_write(cls):
354+
"""
374355
Write files for use in testing.
375-
'''
376-
self.files_generated = []
377-
if not self.able_to_write_or_read():
356+
"""
357+
cls.files_generated = []
358+
if not cls.able_to_write_or_read():
378359
return
379360

380-
generate_from_supported_objects(self.ioclass.supported_objects)
361+
generate_from_supported_objects(cls.ioclass.supported_objects)
381362

382-
ioobj, path = self.generic_io_object(return_path=True, clean=True)
363+
ioobj, path = cls.generic_io_object(return_path=True, clean=True)
383364
if ioobj is None:
384365
return
385366

386-
self.files_generated.append(path)
367+
cls.files_generated.append(path)
387368

388-
write_generic(ioobj, target=self.higher)
369+
write_generic(ioobj, target=cls.higher)
389370

390371
close_object_safe(ioobj)
391372

392373
def test_write_then_read(self):
393-
'''
374+
"""
394375
Test for IO that are able to write and read - here %s:
395376
1 - Generate a full schema with supported objects.
396377
2 - Write to a file
@@ -400,7 +381,7 @@ def test_write_then_read(self):
400381
401382
Work only for IO for Block and Segment for the highest object
402383
(main cases).
403-
''' % self.ioclass.__name__
384+
""" % self.ioclass.__name__
404385

405386
if not self.able_to_write_or_read(writeread=True):
406387
return

0 commit comments

Comments
 (0)