From 5ea01c6339902a5167369d10c0317ebfae5ea701 Mon Sep 17 00:00:00 2001 From: blocksmith314 Date: Sat, 7 Nov 2026 09:45:44 +0800 Subject: [PATCH 1/4] ci: build and test on Linux, macOS and Windows Adds a push/PR workflow that configures, builds and runs ctest on three platforms. Parser tests are already registered through gtest_discover_tests. --- .github/workflows/ci.yml | 98 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1d7b11b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,98 @@ +name: ci + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + linux: + runs-on: ubuntu-24.04 + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + cmake ninja-build pkg-config \ + qt6-base-dev qt6-multimedia-dev \ + libavformat-dev libavcodec-dev libavutil-dev \ + libswscale-dev libswresample-dev + + - name: Configure + run: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug + + - name: Build + run: cmake --build build + + - name: Test + env: + QT_QPA_PLATFORM: offscreen + run: ctest --test-dir build --output-on-failure + + macos: + runs-on: macos-14 + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: brew install cmake ninja qt ffmpeg pkg-config + + - name: Configure + run: >- + cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug + -DCMAKE_PREFIX_PATH="$(brew --prefix)" + + - name: Build + run: cmake --build build + + - name: Test + env: + QT_QPA_PLATFORM: offscreen + run: ctest --test-dir build --output-on-failure + + windows: + runs-on: windows-latest + timeout-minutes: 30 + defaults: + run: + shell: msys2 {0} + steps: + - uses: actions/checkout@v4 + + - uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: true + install: >- + mingw-w64-x86_64-cmake + mingw-w64-x86_64-gcc + mingw-w64-x86_64-ninja + mingw-w64-x86_64-pkgconf + mingw-w64-x86_64-qt6-base + mingw-w64-x86_64-qt6-multimedia + mingw-w64-x86_64-ffmpeg + git + + - name: Configure + run: >- + cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug + -DCMAKE_PREFIX_PATH=/mingw64 + + - name: Build + run: cmake --build build + + - name: Test + env: + QT_QPA_PLATFORM: offscreen + run: ctest --test-dir build --output-on-failure From 73499391b0658dbf14f227e103102a80777ef61b Mon Sep 17 00:00:00 2001 From: blocksmith314 Date: Sat, 7 Nov 2026 09:49:26 +0800 Subject: [PATCH 2/4] fix(tools): include for std::strlen in gen Linux GCC does not get strlen through other headers, so the CI build failed on gen.cpp. --- test/tools/gen.cpp | 97 ++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 50 deletions(-) diff --git a/test/tools/gen.cpp b/test/tools/gen.cpp index ab45706..11f75b9 100644 --- a/test/tools/gen.cpp +++ b/test/tools/gen.cpp @@ -12,6 +12,8 @@ extern "C" { #include #include +#include + static const uint8_t FONT5x7[][5] = { {0x00, 0x00, 0x00, 0x00, 0x00}, // ' ' {0x00, 0x00, 0x5F, 0x00, 0x00}, // '!' @@ -194,7 +196,7 @@ static void draw_filled_rect_yuv(AVFrame* frame, int x, int y, int w, int h, static void format_number(char* buf, int n, int width) { char tmp[32]; snprintf(tmp, sizeof(tmp), "%d", n); - int len = (int)strlen(tmp); + int len = (int)std::strlen(tmp); int pad = width - len; int pos = 0; for (int i = 0; i < pad; i++) { @@ -204,28 +206,23 @@ static void format_number(char* buf, int n, int width) { buf[pos + len] = '\0'; } - -bool parseInt(const char* s, int& out, int min_val, int max_val) -{ - try - { - size_t pos{}; - long v = std::stol(s, &pos); - if (pos != std::strlen(s)) - {return false;} - if (v < min_val || v > max_val) - {return false;} - out = static_cast(v); - return true; +bool parseInt(const char* s, int& out, int min_val, int max_val) { + try { + size_t pos{}; + long v = std::stol(s, &pos); + if (pos != std::strlen(s)) { + return false; } - catch (...) - { - return false; + if (v < min_val || v > max_val) { + return false; } + out = static_cast(v); + return true; + } catch (...) { + return false; + } } - - int main(int argc, char** argv) { int width = 352; int height = 288; @@ -235,42 +232,42 @@ int main(int argc, char** argv) { const char* codec_name = "libx264"; const char* output_path = "output.h264"; -if (argc >= 2) { - if (!parseInt(argv[1], width, 16, 8192)) { - fprintf(stderr, "invalid width: %s\n", argv[1]); - return 1; - } - } - if (argc >= 3) { - if (!parseInt(argv[2], height, 16, 8192)) { - fprintf(stderr, "invalid height: %s\n", argv[2]); - return 1; - } - } - if (argc >= 4) { - if (!parseInt(argv[3], fps, 1, 240)) { - fprintf(stderr, "invalid fps: %s\n", argv[3]); - return 1; - } + if (argc >= 2) { + if (!parseInt(argv[1], width, 16, 8192)) { + fprintf(stderr, "invalid width: %s\n", argv[1]); + return 1; } - if (argc >= 5) { - if (!parseInt(argv[4], total_frames, 1, 100000)) { // 至少1帧 - fprintf(stderr, "invalid total_frames: %s\n", argv[4]); - return 1; - } + } + if (argc >= 3) { + if (!parseInt(argv[2], height, 16, 8192)) { + fprintf(stderr, "invalid height: %s\n", argv[2]); + return 1; } - if (argc >= 6) { - if (!parseInt(argv[5], keyint, 1, 1000)) { - fprintf(stderr, "invalid keyint: %s\n", argv[5]); - return 1; - } + } + if (argc >= 4) { + if (!parseInt(argv[3], fps, 1, 240)) { + fprintf(stderr, "invalid fps: %s\n", argv[3]); + return 1; } - if (argc >= 7) { - codec_name = argv[6]; + } + if (argc >= 5) { + if (!parseInt(argv[4], total_frames, 1, 100000)) { // 至少1帧 + fprintf(stderr, "invalid total_frames: %s\n", argv[4]); + return 1; } - if (argc >= 8) { - output_path = argv[7]; + } + if (argc >= 6) { + if (!parseInt(argv[5], keyint, 1, 1000)) { + fprintf(stderr, "invalid keyint: %s\n", argv[5]); + return 1; } + } + if (argc >= 7) { + codec_name = argv[6]; + } + if (argc >= 8) { + output_path = argv[7]; + } int is_h265 = (strstr(codec_name, "265") != nullptr); // int codec_id = is_h265 ? AV_CODEC_ID_HEVC : AV_CODEC_ID_H264; From 694e507331dc141c555d47aad648fd3604456a8a Mon Sep 17 00:00:00 2001 From: blocksmith314 Date: Sat, 7 Nov 2026 09:52:07 +0800 Subject: [PATCH 3/4] ci: use macos-15 runner macos-14 jobs were cancelled before a runner was assigned (runner_id 0), which matches the image being retired. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d7b11b..d025cc0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: run: ctest --test-dir build --output-on-failure macos: - runs-on: macos-14 + runs-on: macos-15 timeout-minutes: 30 steps: - uses: actions/checkout@v4 From dbbb363df03ae9278d42cdfb09963a392fe56ffa Mon Sep 17 00:00:00 2001 From: blocksmith314 Date: Sat, 7 Nov 2026 10:04:03 +0800 Subject: [PATCH 4/4] test(platform): close mapped file before removing it on Windows Windows keeps the file locked while a mapping is open, so removing it first fails. --- test/test_platform_file.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_platform_file.cpp b/test/test_platform_file.cpp index 9d9d49d..ff0d564 100644 --- a/test/test_platform_file.cpp +++ b/test/test_platform_file.cpp @@ -36,6 +36,7 @@ TEST(MappedFileTest, MapsExistingFile) { file.ReleaseResidentPages(); // must not crash and must keep the data valid EXPECT_EQ(std::memcmp(file.data(), payload, sizeof(payload)), 0); + file.close(); std::filesystem::remove(path); }