#!/usr/bin/env python3
import os
import glob
import pytest
import shutil
import subprocess
import tempfile
import random
HERE = os . path . abspath ( os . path . dirname ( __file__ ) )
ROOT = os . path . join ( HERE , " ../../ " )
IGNORED_PATHS = (
' board/obj ' ,
' board/jungle ' ,
' board/stm32h7/inc ' ,
' board/stm32f4/inc ' ,
' board/fake_stm.h ' ,
# bootstub only files
' board/flasher.h ' ,
' board/bootstub.c ' ,
' board/bootstub_declarations.h ' ,
' board/stm32h7/llflash.h ' ,
' board/stm32f4/llflash.h ' ,
)
mutations = [
# default
( None , None , False ) ,
# F4 only
( " board/stm32f4/llbxcan.h " , " s/1U/1/g " , True ) ,
# H7 only
( " 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 = [ f for f in all_files if f . endswith ( ( ' .c ' , ' .h ' ) ) and not f . startswith ( IGNORED_PATHS ) ]
assert len ( files ) > 70 , all ( d in files for d in ( ' board/main.c ' , ' board/stm32f4/llbxcan.h ' , ' board/stm32h7/llfdcan.h ' ) )
for p in patterns :
mutations . append ( ( random . choice ( files ) , p , True ) )
@pytest . mark . parametrize ( " fn, patch, should_fail " , mutations )
def test_misra_mutation ( fn , patch , should_fail ) :
with tempfile . TemporaryDirectory ( ) as tmp :
shutil . copytree ( ROOT , tmp + " /panda " , dirs_exist_ok = True )
shutil . copytree ( ROOT + " ../opendbc " , tmp + " /opendbc " , dirs_exist_ok = True )
# apply patch
if fn is not None :
r = os . system ( f " cd { tmp } /panda && sed -i ' { patch } ' { fn } " )
assert r == 0
# run test
r = subprocess . run ( " SKIP_TABLES_DIFF=1 panda/tests/misra/test_misra.sh " , cwd = tmp , shell = True )
failed = r . returncode != 0
assert failed == should_fail