mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-28 00:53:45 +08:00
22a8575199
Keep official openpilot ancestry and dependency licenses. Store resources directly in Git, remove LFS setup requirements, and leave the downloadable big model outside the source tree.
108 lines
3.5 KiB
Python
Executable File
108 lines
3.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import os
|
|
import glob
|
|
import unittest
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
import random
|
|
from concurrent.futures import ProcessPoolExecutor
|
|
|
|
HERE = os.path.abspath(os.path.dirname(__file__))
|
|
ROOT = os.path.join(HERE, "../../")
|
|
|
|
# skip mutating these paths
|
|
IGNORED_PATHS = (
|
|
'board/obj',
|
|
'board/body',
|
|
'board/stm32h7/inc',
|
|
'board/fake_stm.h',
|
|
|
|
# bootstub only files
|
|
'board/flasher.h',
|
|
'board/bootstub.c',
|
|
'board/bootstub_declarations.h',
|
|
'board/stm32h7/llflash.h',
|
|
)
|
|
|
|
mutations = [
|
|
(None, None, False), # no mods, should pass
|
|
("board/stm32h7/llfdcan.h", "s/return ret;/if (true) { return ret; } else { return false; }/g", True),
|
|
]
|
|
|
|
patterns = [
|
|
# misra-c2012-13.3
|
|
"$a void test(int tmp) { int tmp2 = tmp++ + 2; if (tmp2) {;}}",
|
|
# misra-c2012-13.4
|
|
"$a int test(int x, int y) { return (x=2) && (y=2); }",
|
|
# misra-c2012-13.5
|
|
"$a void test(int tmp) { if (true && tmp++) {;} }",
|
|
# misra-c2012-13.6
|
|
"$a void test(int tmp) { if (sizeof(tmp++)) {;} }",
|
|
# misra-c2012-14.1
|
|
"$a void test(float len) { for (float j = 0; j < len; j++) {;} }",
|
|
# misra-c2012-14.4
|
|
"$a void test(int len) { if (len - 8) {;} }",
|
|
# misra-c2012-16.4
|
|
r"$a void test(int temp) {switch (temp) { case 1: ; }}\n",
|
|
# misra-c2012-17.8
|
|
"$a void test(int cnt) { for (cnt=0;;cnt++) {;} }",
|
|
# misra-c2012-20.4
|
|
r"$a #define auto 1\n",
|
|
# misra-c2012-20.5
|
|
r"$a #define TEST 1\n#undef TEST\n",
|
|
]
|
|
|
|
all_files = glob.glob('board/**', root_dir=ROOT, recursive=True)
|
|
files = sorted(f for f in all_files if f.endswith(('.c', '.h')) and not f.startswith(IGNORED_PATHS))
|
|
assert len(files) > 50, all(d in files for d in ('board/main.c', 'board/stm32h7/llfdcan.h'))
|
|
|
|
# fixed seed for reproducible mutation selection
|
|
rng = random.Random(len(files))
|
|
for p in patterns:
|
|
mutations.append((rng.choice(files), p, True))
|
|
|
|
# sample to keep CI fast, but always include the no-mutation case
|
|
mutations = [mutations[0]] + rng.sample(mutations[1:], min(2, len(mutations) - 1))
|
|
|
|
def run_mutation(fn, patch, should_fail):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
shutil.copytree(ROOT, tmp + "/panda", ignore=shutil.ignore_patterns(".git", ".venv", "__pycache__"))
|
|
|
|
# apply patch
|
|
if fn is not None:
|
|
fpath = os.path.join(tmp, "panda", fn)
|
|
with open(fpath) as f:
|
|
content = f.read()
|
|
if patch.startswith("s/"):
|
|
old, new = patch[2:].rsplit("/g", 1)[0].split("/", 1)
|
|
content = content.replace(old, new)
|
|
elif patch.startswith("$a "):
|
|
content += patch[3:].replace(r"\n", "\n")
|
|
with open(fpath, "w") as f:
|
|
f.write(content)
|
|
|
|
return subprocess.run("SKIP_TABLES_DIFF=1 panda/tests/misra/test_misra.sh", cwd=tmp, shell=True,
|
|
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
|
|
|
|
|
|
class TestMisraMutation(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
# Each mutation has its own checkout and subprocess; keep the expensive checks parallel.
|
|
executor = ProcessPoolExecutor(max_workers=len(mutations))
|
|
cls.addClassCleanup(executor.shutdown)
|
|
cls.results = [executor.submit(run_mutation, *mutation) for mutation in mutations]
|
|
|
|
|
|
def mutation_test(index):
|
|
def test(self):
|
|
result = self.results[index].result()
|
|
self.assertEqual(result.returncode != 0, mutations[index][2], result.stdout)
|
|
test.__doc__ = f"MISRA mutation: {mutations[index]}"
|
|
return test
|
|
|
|
|
|
for index in range(len(mutations)):
|
|
setattr(TestMisraMutation, f"test_misra_mutation_{index}", mutation_test(index))
|