Skip to content

Commit 2a1194c

Browse files
Improve CIFuzz tests (google#4868)
1. Fix problem where permissions were being changed to root by non-root test (test was doing this by invoking test_all.py within docker). 2. Mark tests as integration tests so that cifuzz_test.py can be run in a reasonable amount of time. 3. Prevent some unittests from polluting source repo. 4. Add .venv to .gitignore 5. Rename test_test_all.py to the correctly formatted name "test_all_test.py"
1 parent 878612f commit 2a1194c

File tree

6 files changed

+48
-41
lines changed

6 files changed

+48
-41
lines changed

.github/workflows/infra_tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,6 @@ jobs:
3333
sudo env "PATH=$PATH" gcloud info
3434
3535
- name: Run infra tests
36-
run: sudo env "PATH=$PATH" python infra/presubmit.py infra-tests
36+
run: sudo env "PATH=$PATH" INTEGRATION_TESTS=1 python infra/presubmit.py infra-tests
3737

3838

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
*~
55
.DS_Store
66
*.swp
7+
.venv

infra/base-images/base-runner/test_all.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,16 @@ def recreate_directory(directory):
4747

4848
def move_directory_contents(src_directory, dst_directory):
4949
"""Moves contents of |src_directory| to |dst_directory|."""
50-
src_files = os.listdir(src_directory)
51-
for filename in src_files:
52-
src_path = os.path.join(src_directory, filename)
53-
shutil.move(src_path, dst_directory)
50+
# Use mv because mv preserves file permissions. If we don't preserve file
51+
# permissions that can mess up CheckFuzzerBuildTest in cifuzz_test.py and
52+
# other cases where one is calling test_all on files not in OSS-Fuzz's real
53+
# out directory.
54+
src_contents = [
55+
os.path.join(src_directory, filename)
56+
for filename in os.listdir(src_directory)
57+
]
58+
command = ['mv'] + src_contents + [dst_directory]
59+
subprocess.check_call(command)
5460

5561

5662
def is_elf(filepath):

infra/cifuzz/cifuzz_test.py

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ def test_correct_host_repo_path(self, _, __):
128128
os.path.basename(image_repo_path))
129129

130130

131+
@unittest.skipIf(not os.getenv('INTEGRATION_TESTS'),
132+
'INTEGRATION_TESTS=1 not set')
131133
class BuildFuzzersIntegrationTest(unittest.TestCase):
132134
"""Integration tests for build_fuzzers."""
133135

@@ -229,75 +231,57 @@ def test_invalid_workspace(self):
229231
))
230232

231233

232-
def remove_test_files(out_parent_dir, allowlist):
233-
"""Removes test files from |out_parent_dir| that are not in |allowlist|, a
234-
list of files with paths relative to the out directory."""
235-
out_dir = os.path.join(out_parent_dir, 'out')
236-
allowlist = set(allowlist)
237-
for rel_out_path in os.listdir(out_dir):
238-
if rel_out_path in allowlist:
239-
continue
240-
path_to_remove = os.path.join(out_dir, rel_out_path)
241-
if os.path.isdir(path_to_remove):
242-
shutil.rmtree(path_to_remove)
243-
else:
244-
os.remove(path_to_remove)
245-
246-
247234
class RunFuzzerIntegrationTestMixin: # pylint: disable=too-few-public-methods,invalid-name
248235
"""Mixin for integration test classes that runbuild_fuzzers on builds of a
249236
specific sanitizer."""
250237
# These must be defined by children.
251238
FUZZER_DIR = None
252239
FUZZER = None
253240

254-
def tearDown(self):
255-
"""Removes any existing crashes and test files."""
256-
remove_test_files(self.FUZZER_DIR, self.FUZZER)
257-
258241
def _test_run_with_sanitizer(self, fuzzer_dir, sanitizer):
259242
"""Calls run_fuzzers on fuzzer_dir and |sanitizer| and asserts
260243
the run succeeded and that no bug was found."""
261-
run_success, bug_found = cifuzz.run_fuzzers(10,
262-
fuzzer_dir,
263-
'curl',
264-
sanitizer=sanitizer)
244+
with test_helpers.temp_dir_copy(fuzzer_dir) as fuzzer_dir_copy:
245+
run_success, bug_found = cifuzz.run_fuzzers(10,
246+
fuzzer_dir_copy,
247+
'curl',
248+
sanitizer=sanitizer)
265249
self.assertTrue(run_success)
266250
self.assertFalse(bug_found)
267251

268252

269-
class RunMemoryFuzzerIntegrationTest(unittest.TestCase,
270-
RunFuzzerIntegrationTestMixin):
253+
class RunMemoryFuzzerIntegrationTest(RunFuzzerIntegrationTestMixin,
254+
unittest.TestCase):
271255
"""Integration test for build_fuzzers with an MSAN build."""
272256
FUZZER_DIR = MEMORY_FUZZER_DIR
273257
FUZZER = MEMORY_FUZZER
274258

259+
@unittest.skipIf(not os.getenv('INTEGRATION_TESTS'),
260+
'INTEGRATION_TESTS=1 not set')
275261
def test_run_with_memory_sanitizer(self):
276262
"""Tests run_fuzzers with a valid MSAN build."""
277263
self._test_run_with_sanitizer(self.FUZZER_DIR, 'memory')
278264

279265

280-
class RunUndefinedFuzzerIntegrationTest(unittest.TestCase,
281-
RunFuzzerIntegrationTestMixin):
266+
class RunUndefinedFuzzerIntegrationTest(RunFuzzerIntegrationTestMixin,
267+
unittest.TestCase):
282268
"""Integration test for build_fuzzers with an UBSAN build."""
283269
FUZZER_DIR = UNDEFINED_FUZZER_DIR
284270
FUZZER = UNDEFINED_FUZZER
285271

272+
@unittest.skipIf(not os.getenv('INTEGRATION_TESTS'),
273+
'INTEGRATION_TESTS=1 not set')
286274
def test_run_with_undefined_sanitizer(self):
287-
"""Tests run_fuzzers with a valid MSAN build."""
275+
"""Tests run_fuzzers with a valid UBSAN build."""
288276
self._test_run_with_sanitizer(self.FUZZER_DIR, 'undefined')
289277

290278

291-
class RunAddressFuzzersIntegrationTest(unittest.TestCase):
279+
class RunAddressFuzzersIntegrationTest(RunFuzzerIntegrationTestMixin,
280+
unittest.TestCase):
292281
"""Integration tests for build_fuzzers with an ASAN build."""
293282

294-
def tearDown(self):
295-
"""Removes any existing crashes and test files."""
296-
files_to_keep = [
297-
'undefined', 'memory', EXAMPLE_CRASH_FUZZER, EXAMPLE_NOCRASH_FUZZER
298-
]
299-
remove_test_files(TEST_FILES_PATH, files_to_keep)
300-
283+
@unittest.skipIf(not os.getenv('INTEGRATION_TESTS'),
284+
'INTEGRATION_TESTS=1 not set')
301285
def test_new_bug_found(self):
302286
"""Tests run_fuzzers with a valid ASAN build."""
303287
# Set the first return value to True, then the second to False to
@@ -314,6 +298,8 @@ def test_new_bug_found(self):
314298
self.assertTrue(run_success)
315299
self.assertTrue(bug_found)
316300

301+
@unittest.skipIf(not os.getenv('INTEGRATION_TESTS'),
302+
'INTEGRATION_TESTS=1 not set')
317303
def test_old_bug_found(self):
318304
"""Tests run_fuzzers with a bug found in OSS-Fuzz before."""
319305
with mock.patch.object(fuzz_target.FuzzTarget,
@@ -409,6 +395,8 @@ def test_allow_broken_fuzz_targets_percentage(self, mocked_docker_run):
409395
' '.join(mocked_docker_run.call_args[0][0]))
410396

411397

398+
@unittest.skipIf(not os.getenv('INTEGRATION_TESTS'),
399+
'INTEGRATION_TESTS=1 not set')
412400
class GetFilesCoveredByTargetTest(unittest.TestCase):
413401
"""Tests get_files_covered_by_target."""
414402

infra/test_helpers.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
# limitations under the License.
1414
"""Contains convenient helpers for writing tests."""
1515

16+
import contextlib
1617
import os
18+
import shutil
19+
import tempfile
1720
from unittest import mock
1821

1922

@@ -25,3 +28,12 @@ def patch_environ(testcase_obj, env=None):
2528
patcher = mock.patch.dict(os.environ, env)
2629
testcase_obj.addCleanup(patcher.stop)
2730
patcher.start()
31+
32+
33+
@contextlib.contextmanager
34+
def temp_dir_copy(directory):
35+
"""Context manager that yields a temporary copy of |directory|."""
36+
with tempfile.TemporaryDirectory() as temp_dir:
37+
temp_copy_path = os.path.join(temp_dir, os.path.basename(directory))
38+
shutil.copytree(directory, temp_copy_path)
39+
yield temp_copy_path

0 commit comments

Comments
 (0)