mirror of https://github.com/commaai/openpilot.git
ci: faster unit_test (#34019)
* multiple * CACHE * ... * cache * now fast * maybe * bp * vv * slow * fast * fix * faster * ruff * info * timeout * info * more * clean * faster * test * collection time * is this real? * fix * back * clean * just to make sure * faster!
This commit is contained in:
parent
26b928596d
commit
50aac48fba
|
@ -173,12 +173,19 @@ jobs:
|
||||||
- name: Build openpilot
|
- name: Build openpilot
|
||||||
timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 10 || 30) }} # allow more time when we missed the scons cache
|
timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 10 || 30) }} # allow more time when we missed the scons cache
|
||||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||||
|
- name: Setup cache
|
||||||
|
uses: ./.github/workflows/auto-cache
|
||||||
|
with:
|
||||||
|
path: .ci_cache/comma_download_cache
|
||||||
|
key: unit_tests_${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}
|
||||||
- name: Run unit tests
|
- name: Run unit tests
|
||||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && 1 || 20 }}
|
timeout-minutes: ${{ contains(runner.name, 'nsc') && 1 || 20 }}
|
||||||
run: |
|
run: |
|
||||||
${{ env.RUN }} "MAX_EXAMPLES=1 $PYTEST --timeout 60 -m 'not slow' && \
|
${{ env.RUN }} "$PYTEST --collect-only -m 'not slow' &> /dev/null && \
|
||||||
|
MAX_EXAMPLES=1 $PYTEST -m 'not slow' && \
|
||||||
./selfdrive/ui/tests/create_test_translations.sh && \
|
./selfdrive/ui/tests/create_test_translations.sh && \
|
||||||
QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations"
|
QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \
|
||||||
|
chmod -R 777 /tmp/comma_download_cache"
|
||||||
- name: "Upload coverage to Codecov"
|
- name: "Upload coverage to Codecov"
|
||||||
uses: codecov/codecov-action@v4
|
uses: codecov/codecov-action@v4
|
||||||
with:
|
with:
|
||||||
|
|
|
@ -168,18 +168,18 @@ class TestMessaging:
|
||||||
|
|
||||||
# this test doesn't work with ZMQ since multiprocessing interrupts it
|
# this test doesn't work with ZMQ since multiprocessing interrupts it
|
||||||
if "ZMQ" not in os.environ:
|
if "ZMQ" not in os.environ:
|
||||||
# wait 15 socket timeouts and make sure it's still retrying
|
# wait 5 socket timeouts and make sure it's still retrying
|
||||||
p = multiprocessing.Process(target=messaging.recv_one_retry, args=(sub_sock,))
|
p = multiprocessing.Process(target=messaging.recv_one_retry, args=(sub_sock,))
|
||||||
p.start()
|
p.start()
|
||||||
time.sleep(sock_timeout*15)
|
time.sleep(sock_timeout*5)
|
||||||
assert p.is_alive()
|
assert p.is_alive()
|
||||||
p.terminate()
|
p.terminate()
|
||||||
|
|
||||||
# wait 15 socket timeouts before sending
|
# wait 5 socket timeouts before sending
|
||||||
msg = random_carstate()
|
msg = random_carstate()
|
||||||
delayed_send(sock_timeout*15, pub_sock, msg.to_bytes())
|
delayed_send(sock_timeout*5, pub_sock, msg.to_bytes())
|
||||||
start_time = time.monotonic()
|
start_time = time.monotonic()
|
||||||
recvd = messaging.recv_one_retry(sub_sock)
|
recvd = messaging.recv_one_retry(sub_sock)
|
||||||
assert (time.monotonic() - start_time) >= sock_timeout*15
|
assert (time.monotonic() - start_time) >= sock_timeout*5
|
||||||
assert isinstance(recvd, capnp._DynamicStructReader)
|
assert isinstance(recvd, capnp._DynamicStructReader)
|
||||||
assert_carstate(msg.carState, recvd.carState)
|
assert_carstate(msg.carState, recvd.carState)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import copy
|
import copy
|
||||||
|
import os
|
||||||
from hypothesis import given, HealthCheck, Phase, settings
|
from hypothesis import given, HealthCheck, Phase, settings
|
||||||
import hypothesis.strategies as st
|
import hypothesis.strategies as st
|
||||||
from parameterized import parameterized
|
from parameterized import parameterized
|
||||||
|
@ -14,13 +15,15 @@ import openpilot.selfdrive.test.process_replay.process_replay as pr
|
||||||
NOT_TESTED = ['selfdrived', 'controlsd', 'card', 'plannerd', 'calibrationd', 'dmonitoringd', 'paramsd', 'dmonitoringmodeld', 'modeld']
|
NOT_TESTED = ['selfdrived', 'controlsd', 'card', 'plannerd', 'calibrationd', 'dmonitoringd', 'paramsd', 'dmonitoringmodeld', 'modeld']
|
||||||
|
|
||||||
TEST_CASES = [(cfg.proc_name, copy.deepcopy(cfg)) for cfg in pr.CONFIGS if cfg.proc_name not in NOT_TESTED]
|
TEST_CASES = [(cfg.proc_name, copy.deepcopy(cfg)) for cfg in pr.CONFIGS if cfg.proc_name not in NOT_TESTED]
|
||||||
|
MAX_EXAMPLES = int(os.environ.get("MAX_EXAMPLES", "10"))
|
||||||
|
|
||||||
class TestFuzzProcesses:
|
class TestFuzzProcesses:
|
||||||
|
|
||||||
# TODO: make this faster and increase examples
|
# TODO: make this faster and increase examples
|
||||||
@parameterized.expand(TEST_CASES)
|
@parameterized.expand(TEST_CASES)
|
||||||
@given(st.data())
|
@given(st.data())
|
||||||
@settings(phases=[Phase.generate, Phase.target], max_examples=10, deadline=1000, suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large])
|
@settings(phases=[Phase.generate, Phase.target], max_examples=MAX_EXAMPLES, deadline=1000,
|
||||||
|
suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large])
|
||||||
def test_fuzz_process(self, proc_name, cfg, data):
|
def test_fuzz_process(self, proc_name, cfg, data):
|
||||||
msgs = FuzzyGenerator.get_random_event_msg(data.draw, events=cfg.pubs, real_floats=True)
|
msgs = FuzzyGenerator.get_random_event_msg(data.draw, events=cfg.pubs, real_floats=True)
|
||||||
lr = [log.Event.new_message(**m).as_reader() for m in msgs]
|
lr = [log.Event.new_message(**m).as_reader() for m in msgs]
|
||||||
|
|
|
@ -19,7 +19,7 @@ class TestLogmessaged:
|
||||||
self.error_sock = messaging.sub_sock("logMessage", timeout=1000, conflate=False)
|
self.error_sock = messaging.sub_sock("logMessage", timeout=1000, conflate=False)
|
||||||
|
|
||||||
# ensure sockets are connected
|
# ensure sockets are connected
|
||||||
time.sleep(1)
|
time.sleep(0.5)
|
||||||
messaging.drain_sock(self.sock)
|
messaging.drain_sock(self.sock)
|
||||||
messaging.drain_sock(self.error_sock)
|
messaging.drain_sock(self.error_sock)
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class TestLogmessaged:
|
||||||
msgs = [f"abc {i}" for i in range(10)]
|
msgs = [f"abc {i}" for i in range(10)]
|
||||||
for m in msgs:
|
for m in msgs:
|
||||||
cloudlog.error(m)
|
cloudlog.error(m)
|
||||||
time.sleep(1)
|
time.sleep(0.5)
|
||||||
m = messaging.drain_sock(self.sock)
|
m = messaging.drain_sock(self.sock)
|
||||||
assert len(m) == len(msgs)
|
assert len(m) == len(msgs)
|
||||||
assert len(self._get_log_files()) >= 1
|
assert len(self._get_log_files()) >= 1
|
||||||
|
@ -45,7 +45,7 @@ class TestLogmessaged:
|
||||||
msg = "a"*3*1024*1024
|
msg = "a"*3*1024*1024
|
||||||
for _ in range(n):
|
for _ in range(n):
|
||||||
cloudlog.info(msg)
|
cloudlog.info(msg)
|
||||||
time.sleep(1)
|
time.sleep(0.5)
|
||||||
|
|
||||||
msgs = messaging.drain_sock(self.sock)
|
msgs = messaging.drain_sock(self.sock)
|
||||||
assert len(msgs) == 0
|
assert len(msgs) == 0
|
||||||
|
|
|
@ -23,42 +23,6 @@ bool download_to_file(const std::string &url, const std::string &local_file, int
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("httpMultiPartDownload") {
|
|
||||||
char filename[] = "/tmp/XXXXXX";
|
|
||||||
close(mkstemp(filename));
|
|
||||||
|
|
||||||
const size_t chunk_size = 5 * 1024 * 1024;
|
|
||||||
std::string content;
|
|
||||||
SECTION("download to file") {
|
|
||||||
REQUIRE(download_to_file(TEST_RLOG_URL, filename, chunk_size));
|
|
||||||
content = util::read_file(filename);
|
|
||||||
}
|
|
||||||
SECTION("download to buffer") {
|
|
||||||
for (int i = 0; i < 3 && content.empty(); ++i) {
|
|
||||||
content = httpGet(TEST_RLOG_URL, chunk_size);
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
|
||||||
}
|
|
||||||
REQUIRE(!content.empty());
|
|
||||||
}
|
|
||||||
REQUIRE(content.size() == 9112651);
|
|
||||||
REQUIRE(sha256(content) == TEST_RLOG_CHECKSUM);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("FileReader") {
|
|
||||||
auto enable_local_cache = GENERATE(true, false);
|
|
||||||
std::string cache_file = cacheFilePath(TEST_RLOG_URL);
|
|
||||||
system(("rm " + cache_file + " -f").c_str());
|
|
||||||
|
|
||||||
FileReader reader(enable_local_cache);
|
|
||||||
std::string content = reader.read(TEST_RLOG_URL);
|
|
||||||
REQUIRE(sha256(content) == TEST_RLOG_CHECKSUM);
|
|
||||||
if (enable_local_cache) {
|
|
||||||
REQUIRE(sha256(util::read_file(cache_file)) == TEST_RLOG_CHECKSUM);
|
|
||||||
} else {
|
|
||||||
REQUIRE(util::file_exists(cache_file) == false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("LogReader") {
|
TEST_CASE("LogReader") {
|
||||||
SECTION("corrupt log") {
|
SECTION("corrupt log") {
|
||||||
FileReader reader(true);
|
FileReader reader(true);
|
||||||
|
@ -134,34 +98,3 @@ std::string download_demo_route() {
|
||||||
|
|
||||||
return data_dir;
|
return data_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("Getting route") {
|
|
||||||
std::string data_dir = download_demo_route();
|
|
||||||
|
|
||||||
auto flags = GENERATE(0, REPLAY_FLAG_QCAMERA);
|
|
||||||
Route route(DEMO_ROUTE, data_dir);
|
|
||||||
REQUIRE(route.load());
|
|
||||||
REQUIRE(route.segments().size() == 2);
|
|
||||||
for (int i = 0; i < TEST_REPLAY_SEGMENTS; ++i) {
|
|
||||||
read_segment(i, route.at(i), flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("seek_to") {
|
|
||||||
QEventLoop loop;
|
|
||||||
int seek_to = util::random_int(0, 2 * 59);
|
|
||||||
Replay replay(DEMO_ROUTE, {}, {}, nullptr, REPLAY_FLAG_NO_VIPC);
|
|
||||||
|
|
||||||
QObject::connect(&replay, &Replay::seekedTo, [&](double sec) {
|
|
||||||
INFO("seek to " << seek_to << "s sought to" << sec);
|
|
||||||
REQUIRE(sec >= seek_to);
|
|
||||||
loop.quit();
|
|
||||||
});
|
|
||||||
|
|
||||||
REQUIRE(replay.load());
|
|
||||||
replay.start();
|
|
||||||
replay.seekTo(seek_to, false);
|
|
||||||
|
|
||||||
loop.exec();
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue