diff --git a/.covcollect b/.covcollect new file mode 100644 index 00000000..f8b6acfe --- /dev/null +++ b/.covcollect @@ -0,0 +1,4 @@ +[collect] + compiler = /home/marcin/code/coverage/git2-cxx/apps/tests/copy/mocks/g++ + bin-dir = build/ + src-dir = . diff --git a/.covcollect.in b/.covcollect.in new file mode 100644 index 00000000..344a90d6 --- /dev/null +++ b/.covcollect.in @@ -0,0 +1,15 @@ +[collect] + compiler = @CMAKE_CXX_COMPILER@ + output = collected.json + bin-dir = . + src-dir = ../.. + include = @SRC_INCLUDE@ + exclude = @SRC_EXCLUDE@ + +[clang] + exec = bin/cov + exec = bin/cov-* + exec = libexec/cov/cov-* + exec = share/cov-*/**/filters/* + raw = .profraw + profdata = llvm-profiler diff --git a/.covmodules b/.covmodules index d1fb65c1..c766c029 100644 --- a/.covmodules +++ b/.covmodules @@ -11,6 +11,12 @@ [module "app/main"] path = apps/cov/ path = apps/builtins/ +[module "ext/filters"] + path = extensions/filters +[module "ext/libs"] + path = extensions/libs +[module "ext"] + path = extensions [module "hilite/lighter"] path = libs/hilite/hilite path = libs/hilite/lighter diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 275cb542..1b36bdd8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,7 @@ jobs: BUILD_TYPE: ${{ matrix.build_type }} CONAN_REVISIONS_ENABLED: 1 FLOW_COMMAND: python ./flow.py run --github --cutdown-os -c os=${{ matrix.os }} build_type=${{ matrix.build_type }} compiler=${{ matrix.compiler }} sanitizer=${{ matrix.sanitizer }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} runs-on: ${{ matrix.github_os }} name: ${{ matrix.build_type }} with ${{ matrix.compiler }} on ${{ matrix.os }}${{ matrix.sanitizer && ' (and sanitizer)' || '' }} @@ -89,6 +90,7 @@ jobs: sudo rm -rf `which gcc-11` sudo rm -rf `which clang++-14` sudo rm -rf `which clang-14` + sudo locale-gen pl_PL.UTF-8 en_US.UTF-8 en_GB.UTF-8 - name: Install Conan id: conan @@ -116,6 +118,9 @@ jobs: uses: KungFuDonkey/Install-OpenCppCoverage@v1 if: ${{ matrix.windows && matrix.build_type == 'Debug' }} + - name: Get latest archive from GitHub + run: python ./tools/download-latest.py + # ######## ######## ####### ## ######## ###### ######## # ## ## ## ## ## ## ## ## ## ## ## # ## ## ## ## ## ## ## ## ## ## @@ -136,7 +141,9 @@ jobs: - name: Pack id: artifacts if: ${{ fromJson(needs.M.outputs.RELEASE) }} - run: ${{ env.FLOW_COMMAND }} -s Pack,StorePackages + run: ${{ env.FLOW_COMMAND }} -s Sign,Pack,SignPackages,StorePackages + env: + SIGN_TOKEN: ${{ secrets.SIGN_TOKEN }} # ## ## ######## ## ####### ### ######## # ## ## ## ## ## ## ## ## ## ## ## diff --git a/.github/workflows/flow.json b/.github/workflows/flow.json index c9188936..8899c4a0 100644 --- a/.github/workflows/flow.json +++ b/.github/workflows/flow.json @@ -21,7 +21,7 @@ "cpack_generator": ["TGZ", "DEB"], "ubuntu": true, "home": "/home/runner", - "latest_conan_hash": "e903f3ceb636ec2e496810737eb6033a1a8c39c227b437a0693d31086d8aa9df" + "latest_conan_hash": "0dbd3bbacea7ac58e0064195451c86646e20419f302f8539956ab60fa4b0bcfd" }, { "os": "windows", @@ -31,7 +31,7 @@ "cpack_generator": ["ZIP", "WIX"], "windows": true, "home": "C:/Users/runneradmin", - "latest_conan_hash": "1f8eec6aa067e5a60fc4f0ab2668dae66a983ae5902b1eda006f134b009f84eb" + "latest_conan_hash": "586b1c2983ee7a95473708c44799b6027035f6df0503ed7dbcbcf3d3d847dd0e" }, {"os": "ubuntu", "report_os": "Linux"}, {"compiler": "gcc", "report_compiler": "GNU", "gcc": true}, diff --git a/.gitignore b/.gitignore index 172b62e2..fe7d6fae 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,5 @@ build/ web.log super-linter.log *.profraw +signature.key +temp.pfx diff --git a/.vscode/launch.json b/.vscode/launch.json index 2ca1e8fc..3be4f482 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,6 +10,22 @@ "args": [ "--gtest_filter=*" ] + }, + { + "type": "cppdbg", + "request": "launch", + "name": "Launch cov collect (gdb)", + "cwd": "${workspaceFolder}", + "program": "${workspaceFolder}/build/debug/libexec/cov/cov-collect", + "args": [] + }, + { + "type": "cppvsdbg", + "request": "launch", + "name": "Launch cov collect (vs)", + "cwd": "${workspaceFolder}", + "program": "${workspaceFolder}/build/debug/libexec/cov/cov-collect", + "args": [] } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index d7e68769..d33fb5d3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,24 +5,32 @@ "arglist", "cfglng", "cobertura", + "covcollect", "covdata", "covlng", "covmodules", + "cpack", + "ctest", + "DCOV", "demangled", + "devel", "errlng", "fmtlng", "hilite", "initlng", + "libexec", "lngs", "loglng", "modcnt", "modlng", + "oneline", "pofile", "polib", "propset", "refslng", "replng", "resetlng", + "stdclang", "workdir" ], "files.associations": { @@ -127,6 +135,7 @@ "cfloat": "cpp", "climits": "cpp", "queue": "cpp", - "source_location": "cpp" + "source_location": "cpp", + "condition_variable": "cpp" } } diff --git a/CMakeGraphVizOptions.cmake b/CMakeGraphVizOptions.cmake index b5cf614f..cd6fcefc 100644 --- a/CMakeGraphVizOptions.cmake +++ b/CMakeGraphVizOptions.cmake @@ -6,4 +6,6 @@ set(GRAPHVIZ_IGNORE_TARGETS "CONAN_LIB::.*" ".*_DEPS_TARGET" ".*-test" cov-echo + cov-pwd + "mock-.*" ) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1297ac52..02040e59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,35 +44,55 @@ list(APPEND CMAKE_PREFIX_PATH "${PROJECT_BINARY_DIR}/conan") set(CONAN_CMAKE_SILENT_OUTPUT ON) find_package(Python3 COMPONENTS Interpreter REQUIRED) +find_package(ctre REQUIRED) find_package(libgit2 REQUIRED) find_package(fmt REQUIRED) find_package(mbits-args REQUIRED) find_package(mbits-lngs REQUIRED) +find_package(expat REQUIRED) if (COV_TESTING) enable_testing() find_package(GTest REQUIRED) + find_program(JsonRunner_EXECUTABLE json-runner REQUIRED HINTS + "${PROJECT_SOURCE_DIR}/build/downloads" + "${PROJECT_SOURCE_DIR}/../json-runner/build/release/bin" + "${PROJECT_SOURCE_DIR}/../runner/build/release/bin" + ) + message(STATUS "JsonRunner_EXECUTABLE is: ${JsonRunner_EXECUTABLE}") set(COVERALLS_PREFIX cov_) - set(cov_COVERALLS_DIRS - apps/builtins - apps/cov - ) + set(INCLUDED_IN_COVERAGE apps extensions) foreach(module libs/cov-api - libs/cell libs/hilite/hilite libs/hilite/lighter libs/app libs/cov-rt ) - list(APPEND cov_COVERALLS_DIRS ${module}/include ${module}/src) + list(APPEND INCLUDED_IN_COVERAGE ${module}/include ${module}/src) endforeach() + foreach(extra cxx py3 ts) - list(APPEND cov_COVERALLS_DIRS libs/hilite/hilite-${extra}/src) + list(APPEND INCLUDED_IN_COVERAGE libs/hilite/hilite-${extra}/src) endforeach() + + set(EXCLUDED_FROM_COVERAGE + apps/tests + extensions/tests + ) + foreach(extension + collect-api + excludes + native + ) + list(APPEND EXCLUDED_FROM_COVERAGE + extensions/${extension}/test) + endforeach() + + set(cov_COVERALLS_DIRS ${INCLUDED_IN_COVERAGE}) include(${PROJECT_SOURCE_DIR}/tools/coveralls/Coveralls.cmake) set(TEST_REPORT_DIR "${PROJECT_BINARY_DIR}/test-results") @@ -176,53 +196,13 @@ set(CORE_ROOT_DIR "../..") message(STATUS "Sanitizer: ${COV_SANITIZE}") +include(cov) + add_subdirectory(external) add_subdirectory(libs) +add_subdirectory(extensions) add_subdirectory(apps) - -if (CMAKE_GENERATOR MATCHES "Visual Studio" AND TARGET cov_coveralls_test) - add_dependencies(cov_coveralls_test - git2-test git2-stress-test - cov-test cov-stress-test - lighter-test lighter-stress-test - cov - ) -endif() - -if (MSVC_VERSION GREATER_EQUAL 1936 AND MSVC_IDE) - foreach (TGT - cov - cov-rt - json - app_main - app - cov-api - date-tz - lighter - hilite-cxx - hilite-py - hilite-ts - hilite - testing-lib - cov-echo - git2-test - git2-stress-test - cov-test - cov-stress-test - cov-rt-test - cov-rt-stress-test - lighter-test - lighter-stress-test - date-tz - json - ) - if(TARGET ${TGT}) - set_target_properties(${TGT} PROPERTIES VS_USER_PROPS "${PROJECT_SOURCE_DIR}/cmake/VS17.NoModules.props") - endif() - endforeach() -endif() - execute_process(COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/packages/system.py props OUTPUT_VARIABLE COV_PROPERTIES OUTPUT_STRIP_TRAILING_WHITESPACE @@ -252,3 +232,6 @@ else() endif() file(GENERATE OUTPUT "${PROJECT_BINARY_DIR}/report_answers.txt" CONTENT "${COV_PROPERTIES}\n") +string(REPLACE ";" "\n include = " SRC_INCLUDE "${INCLUDED_IN_COVERAGE}") +string(REPLACE ";" "\n exclude = " SRC_EXCLUDE "${EXCLUDED_FROM_COVERAGE}") +configure_file("${PROJECT_SOURCE_DIR}/.covcollect.in" "${PROJECT_BINARY_DIR}/.covcollect" @ONLY) diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index a90ba898..85b5cbda 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -34,15 +34,9 @@ string(REPLACE ";" ", " BUILTIN_LIST "${BUILTINS}") message(STATUS "Builtins: ${BUILTIN_LIST}") include(builtins) -string(REPLACE ";" ", " REPORT_FILTER_LIST "${REPORT_FILTERS}") +string(REPLACE ";" ", " REPORT_FILTER_LIST "${REPORT_FILTERS};${NATIVE_FILTERS}") message(STATUS "Filters: ${REPORT_FILTER_LIST}") -if (WIN32) - list(APPEND SOURCES - win32/cov.rc - ) -endif() - source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES}) source_group(TREE ${CMAKE_CURRENT_BINARY_DIR} FILES ${CMAKE_CURRENT_BINARY_DIR}/gen/cov/builtins.hh ${CMAKE_CURRENT_BINARY_DIR}/gen/cov/versioninfo.rc) @@ -69,18 +63,16 @@ if (WIN32) list(APPEND SOURCES "${CMAKE_CURRENT_BINARY_DIR}/gen/cov/versioninfo.rc" - "${CMAKE_SOURCE_DIR}/data/icons/win32.ico" ) endif() add_cov_tool(cov ${SOURCES}) -target_compile_options(cov PRIVATE ${ADDITIONAL_WALL_FLAGS}) -target_link_options(cov PRIVATE ${ADDITIONAL_LINK_FLAGS}) +add_win32_icon(cov "win32.ico") target_include_directories(cov PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/gen ) -target_link_libraries(cov PRIVATE cov-rt) +target_link_libraries(cov PRIVATE cov-rt app_main) set_target_properties(cov PROPERTIES FOLDER apps) ######################################################## @@ -109,15 +101,11 @@ if (COV_TESTING) enable_testing() add_cov_tool(cov-echo tests/cov-echo.cc) - target_compile_options(cov-echo PRIVATE ${ADDITIONAL_WALL_FLAGS}) - target_link_options(cov-echo PRIVATE ${ADDITIONAL_LINK_FLAGS}) - target_link_libraries(cov-echo PRIVATE fmt::fmt mbits::args) + target_link_libraries(cov-echo PRIVATE app_main fmt::fmt mbits::args) set_target_properties(cov-echo PROPERTIES FOLDER tests/spawn) add_cov_tool(cov-pwd tests/cov-pwd.cc) - target_compile_options(cov-pwd PRIVATE ${ADDITIONAL_WALL_FLAGS}) - target_link_options(cov-pwd PRIVATE ${ADDITIONAL_LINK_FLAGS}) - target_link_libraries(cov-pwd PRIVATE fmt::fmt mbits::args) + target_link_libraries(cov-pwd PRIVATE app_main fmt::fmt mbits::args) set_target_properties(cov-pwd PROPERTIES FOLDER tests/spawn) set_target_properties(cov-echo PROPERTIES @@ -136,6 +124,8 @@ if (COV_TESTING) ) endforeach() + string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_PRESET) + foreach(TEST_SET 001-cov 002-init @@ -147,31 +137,31 @@ if (COV_TESTING) 012-checkout 013-reset 014-show + 015-filters-A + 016-collect ) add_test( - NAME cov-exec--${TEST_SET} - COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_runner.py" - --bin "${PROJECT_BINARY_DIR}" + NAME cov-exec--${TEST_SET}-alt + COMMAND "${JsonRunner_EXECUTABLE}" + --preset "${CMAKE_PRESET}" --tests "main-set/${TEST_SET}" - --version "${PROJECT_VERSION}${PROJECT_VERSION_STABILITY}${PROJECT_VERSION_BUILD_META}" - ) + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + ) endforeach() if (NOT cov_COVERALLS) add_test( - NAME cov-exec-pl - COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_runner.py" - --bin "${PROJECT_BINARY_DIR}" + NAME cov-exec-pl-alt + COMMAND "${JsonRunner_EXECUTABLE}" + --preset "${CMAKE_PRESET}" --tests "messages-pl" - --version "${PROJECT_VERSION}${PROJECT_VERSION_STABILITY}${PROJECT_VERSION_BUILD_META}" - ) + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + ) endif() if (CMAKE_GENERATOR MATCHES "Visual Studio" AND TARGET cov_coveralls_test) add_dependencies(cov_coveralls_test - cov-echo + cov cov-echo cov-pwd ) endif() endif() @@ -202,3 +192,5 @@ cpack_add_component_group(apps DISPLAY_NAME "Executables" EXPANDED ) + +set_parent_scope() diff --git a/apps/builtins/builtin_show.cc b/apps/builtins/builtin_show.cc index bc82c75e..d6642794 100644 --- a/apps/builtins/builtin_show.cc +++ b/apps/builtins/builtin_show.cc @@ -103,7 +103,6 @@ namespace cov::app::builtin::show { ref_ptr get_files(git::oid_view id, cov::repository const& repo) { - git::oid ref{}; std::error_code ec{}; auto generic = repo.lookup(id, ec); if (!generic || ec) return {}; @@ -167,7 +166,7 @@ namespace cov::app::builtin::show { view.fname.prefix.empty(); auto const print_build_props = [&repo = info.repo, &range = info.range, - &ec = ec, &p = p]() { + &p = p]() { using namespace std::chrono; auto const now = floor(system_clock::now()); diff --git a/apps/filters/coveralls.py b/apps/filters/coveralls.py index 2d8adb21..1efb2f46 100755 --- a/apps/filters/coveralls.py +++ b/apps/filters/coveralls.py @@ -5,13 +5,13 @@ from typing import Dict, List, Optional -def lines_from(lines: List[Optional[int]]) -> Dict[int, int]: +def lines_from(lines: List[Optional[int]]) -> Dict[str, int]: result: Dict[int, int] = {} for index in range(len(lines)): count = lines[index] if count is not None: result[index + 1] = count - return result + return {str(line): count for line, count in result.items()} def cov_from(coveralls: dict) -> dict: @@ -36,4 +36,5 @@ def cov_from(coveralls: dict) -> dict: "files": [cov_from(source_file) for source_file in data["source_files"]], }, sys.stdout, + sort_keys=True, ) diff --git a/apps/report-schema.json b/apps/report-schema.json index 8e53d24c..c636e9a8 100644 --- a/apps/report-schema.json +++ b/apps/report-schema.json @@ -94,11 +94,11 @@ "patternProperties": { "^[1-9][0-9]*$": { "type": "integer", - "minimum": 1 + "minimum": 0 }, "^0[0-9]+$": { "type": "integer", - "minimum": 1 + "minimum": 0 }, "^0$": { "title": "line numbers should start from 1", diff --git a/apps/tests/data/covcollect/OpenCppCoverage.xml b/apps/tests/data/covcollect/OpenCppCoverage.xml new file mode 100644 index 00000000..418efa95 --- /dev/null +++ b/apps/tests/data/covcollect/OpenCppCoverage.xml @@ -0,0 +1,35 @@ + + + + $SOURCES + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/tests/data/covcollect/cobertura.xml b/apps/tests/data/covcollect/cobertura.xml new file mode 100644 index 00000000..418efa95 --- /dev/null +++ b/apps/tests/data/covcollect/cobertura.xml @@ -0,0 +1,35 @@ + + + + $SOURCES + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/tests/data/covcollect/ini b/apps/tests/data/covcollect/ini new file mode 100644 index 00000000..b8923d28 --- /dev/null +++ b/apps/tests/data/covcollect/ini @@ -0,0 +1,7 @@ +[collect] + compiler = $COMPILER + bin-dir = build/ + src-dir = . + include = src + include = include + exclude = src/test diff --git a/apps/tests/data/extensions.tar b/apps/tests/data/extensions.tar new file mode 100644 index 00000000..a5b73acb Binary files /dev/null and b/apps/tests/data/extensions.tar differ diff --git a/apps/tests/data/extensions/report-broken-A.json b/apps/tests/data/extensions/report-broken-A.json new file mode 100644 index 00000000..dd084761 --- /dev/null +++ b/apps/tests/data/extensions/report-broken-A.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://raw.githubusercontent.com/mzdun/cov/v0.21.1/apps/report-schema.json", + "git": { + "branch": "main", + "head": "98d4978ecbddf677c1e6bf5a40da84bd0b337340" + }, + "files": [ + { + "digest": "sha1:8d9b6856ab8d5620a0f95b350e0e0a99c2f38216", + "line_coverage": { + "4": 1, + "5": 1, + "6": 1, + "7": 1, + "8": 1, + "9": 1, + "39": 1 + }, + "functions": [ + { + "name": "partial_function_exclusion1", + "count": 0, + "start_line": 4, + "end_line": 10 + } + ] + } + ] +} diff --git a/apps/tests/data/extensions/report-broken-B.json b/apps/tests/data/extensions/report-broken-B.json new file mode 100644 index 00000000..4d9ae8f9 --- /dev/null +++ b/apps/tests/data/extensions/report-broken-B.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://raw.githubusercontent.com/mzdun/cov/v0.21.1/apps/report-schema.json", + "git": { + "branch": "main", + "head": "98d4978ecbddf677c1e6bf5a40da84bd0b337340" + }, + "files": [ + { + "name": "main.cc", + "line_coverage": { + "4": 1, + "5": 1, + "6": 1, + "7": 1, + "8": 1, + "9": 1, + "39": 1 + }, + "functions": [ + { + "name": "partial_function_exclusion1", + "count": 0, + "start_line": 4, + "end_line": 10 + } + ] + } + ] +} diff --git a/apps/tests/data/extensions/report-broken-C.json b/apps/tests/data/extensions/report-broken-C.json new file mode 100644 index 00000000..e4a3144c --- /dev/null +++ b/apps/tests/data/extensions/report-broken-C.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://raw.githubusercontent.com/mzdun/cov/v0.21.1/apps/report-schema.json", + "git": { + "branch": "main", + "head": "98d4978ecbddf677c1e6bf5a40da84bd0b337340" + }, + "files": [ + { + "name": "main.cc", + "digest": "sha1:8d9b6856ab8d5620a0f95b350e0e0a99c2f38216", + "functions": [ + { + "name": "partial_function_exclusion1", + "count": 0, + "start_line": 4, + "end_line": 10 + } + ] + } + ] +} diff --git a/apps/tests/data/extensions/report-llvm.json b/apps/tests/data/extensions/report-llvm.json new file mode 100644 index 00000000..45601e53 --- /dev/null +++ b/apps/tests/data/extensions/report-llvm.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://raw.githubusercontent.com/mzdun/cov/v0.21.1/apps/report-schema.json", + "git": { + "branch": "main", + "head": "98d4978ecbddf677c1e6bf5a40da84bd0b337340" + }, + "files": [ + { + "name": "llvm.cc", + "digest": "sha1:0aa758b8ddf6e4f546a353f20a4781fab8ff33fa", + "line_coverage": { + "3": 0, + "4": 0, + "5": 0, + "6": 0 + }, + "functions": [ + { + "name": "foo", + "count": 0, + "start_line": 1, + "end_line": 8 + } + ] + }, + { + "name": "clang.cc", + "digest": "sha1:a7d8793014e5e0dd0186427d7f49607fef1b7f36", + "line_coverage": { + "3": 0, + "4": 0, + "5": 0, + "6": 0 + }, + "functions": [ + { + "name": "foo", + "count": 0, + "start_line": 1, + "end_line": 8 + } + ] + } + ] +} diff --git a/apps/tests/data/extensions/report-multi.json b/apps/tests/data/extensions/report-multi.json new file mode 100644 index 00000000..e9fe4935 --- /dev/null +++ b/apps/tests/data/extensions/report-multi.json @@ -0,0 +1,89 @@ +{ + "$schema": "https://raw.githubusercontent.com/mzdun/cov/v0.21.1/apps/report-schema.json", + "git": { + "branch": "main", + "head": "98d4978ecbddf677c1e6bf5a40da84bd0b337340" + }, + "files": [ + { + "name": "no-exclusions.cc", + "digest": "sha1:7a8387f153415d9943ed4194242acbdd19be01a0", + "line_coverage": { + "1": 1, + "2": 1, + "3": 1, + "4": 1, + "5": 1, + "6": 1 + }, + "functions": [ + { + "name": "foo", + "count": 1, + "start_line": 1, + "end_line": 6 + } + ] + }, + { + "name": "start-start.cc", + "digest": "sha1:ca07ba87cceec9631bc941369dba40d424804d9b", + "line_coverage": { + "1": 1, + "3": 1, + "4": 1, + "5": 1, + "6": 1, + "8": 1 + }, + "functions": [ + { + "name": "foo", + "count": 1, + "start_line": 1, + "end_line": 8 + } + ] + }, + { + "name": "start-end.cc", + "digest": "sha1:c37d4dd51a1a6d24bbe16ce734bd6939d0126750", + "line_coverage": { + "1": 1, + "3": 1, + "4": 1, + "5": 1, + "6": 1, + "8": 1 + }, + "functions": [ + { + "name": "foo", + "count": 1, + "start_line": 1, + "end_line": 8 + } + ] + }, + { + "name": "start.cc", + "digest": "sha1:070cb0e88d0b5ea1dfb8c003ff3a74d895ca0a4e", + "line_coverage": { + "1": 1, + "3": 1, + "4": 1, + "5": 1, + "6": 1, + "7": 1 + }, + "functions": [ + { + "name": "foo", + "count": 1, + "start_line": 1, + "end_line": 7 + } + ] + } + ] +} diff --git a/apps/tests/data/extensions/report-no-such.json b/apps/tests/data/extensions/report-no-such.json new file mode 100644 index 00000000..f0a7ce87 --- /dev/null +++ b/apps/tests/data/extensions/report-no-such.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://raw.githubusercontent.com/mzdun/cov/v0.21.1/apps/report-schema.json", + "git": { + "branch": "main", + "head": "98d4978ecbddf677c1e6bf5a40da84bd0b337340" + }, + "files": [ + { + "name": "no-such-file.cc", + "digest": "sha1:0aa758b8ddf6e4f546a353f20a4781fab8ff33fa", + "line_coverage": { + "3": 0, + "4": 0, + "5": 0, + "6": 0 + }, + "functions": [ + { + "name": "foo", + "count": 0, + "start_line": 1, + "end_line": 8 + } + ] + } + ] +} diff --git a/apps/tests/data/extensions/report-strip.json b/apps/tests/data/extensions/report-strip.json new file mode 100644 index 00000000..01b06bc2 --- /dev/null +++ b/apps/tests/data/extensions/report-strip.json @@ -0,0 +1,76 @@ +{ + "$schema": "https://raw.githubusercontent.com/mzdun/cov/v0.21.1/apps/report-schema.json", + "git": { + "branch": "main", + "head": "98d4978ecbddf677c1e6bf5a40da84bd0b337340" + }, + "files": [ + { + "name": "main.cc", + "digest": "sha1:8d9b6856ab8d5620a0f95b350e0e0a99c2f38216", + "line_coverage": { + "4": 0, + "5": 0, + "6": 0, + "7": 0, + "8": 0, + "9": 0, + "10": 0, + "14": 4, + "17": 0, + "18": 0, + "19": 0, + "20": 0, + "21": 0, + "22": 0, + "23": 0, + "25": 5, + "28": 0, + "29": 0, + "30": 0, + "31": 0, + "32": 0, + "33": 0, + "37": 1, + "38": 1, + "39": 1 + }, + "functions": [ + { + "name": "partial_function_exclusion1", + "count": 0, + "start_line": 4, + "end_line": 10 + }, + { + "name": "sep1", + "count": 0, + "start_line": 14 + }, + { + "name": "partial_function_exclusion2", + "count": 0, + "start_line": 17, + "end_line": 23 + }, + { + "name": "sep2", + "count": 1, + "start_line": 25 + }, + { + "name": "full_function_exclusion", + "count": 0, + "start_line": 28, + "end_line": 33 + }, + { + "name": "main", + "count": 1, + "start_line": 37, + "end_line": 39 + } + ] + } + ] +} diff --git a/apps/tests/data/invalid-coverage.json b/apps/tests/data/invalid-coverage.json new file mode 100644 index 00000000..11ee7a87 --- /dev/null +++ b/apps/tests/data/invalid-coverage.json @@ -0,0 +1,3 @@ +{ + "$schema": "https://raw.githubusercontent.com/mzdun/cov/v0.20.0/apps/report-schema.json" +} diff --git a/apps/tests/main-set/001-cov/001-version.json b/apps/tests/main-set/001-cov/001-version.json index 9064245f..4d76fd89 100644 --- a/apps/tests/main-set/001-cov/001-version.json +++ b/apps/tests/main-set/001-cov/001-version.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "--version", "expected": [ 0, diff --git a/apps/tests/main-set/001-cov/002-help.json b/apps/tests/main-set/001-cov/002-help.json index f9378478..5dca87d4 100644 --- a/apps/tests/main-set/001-cov/002-help.json +++ b/apps/tests/main-set/001-cov/002-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "--help", "expected": [ 0, diff --git a/apps/tests/main-set/001-cov/003-unknown-option.json b/apps/tests/main-set/001-cov/003-unknown-option.json index 67bf2596..1129451f 100644 --- a/apps/tests/main-set/001-cov/003-unknown-option.json +++ b/apps/tests/main-set/001-cov/003-unknown-option.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "--versions", "expected": [ 2, diff --git a/apps/tests/main-set/001-cov/004-alias.json b/apps/tests/main-set/001-cov/004-alias.json index d9a85cc3..34794a4d 100644 --- a/apps/tests/main-set/001-cov/004-alias.json +++ b/apps/tests/main-set/001-cov/004-alias.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "-C recurse first -D", "expected": [ 0, diff --git a/apps/tests/main-set/001-cov/005-alias-short-help.json b/apps/tests/main-set/001-cov/005-alias-short-help.json index 4ae44444..3e8ae39c 100644 --- a/apps/tests/main-set/001-cov/005-alias-short-help.json +++ b/apps/tests/main-set/001-cov/005-alias-short-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "-C recurse second -h", "expected": [ 0, diff --git a/apps/tests/main-set/001-cov/006-alias-long-help.json b/apps/tests/main-set/001-cov/006-alias-long-help.json index 24e2530e..db02145a 100644 --- a/apps/tests/main-set/001-cov/006-alias-long-help.json +++ b/apps/tests/main-set/001-cov/006-alias-long-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "-C recurse second --not-help --help", "expected": [ 0, diff --git a/apps/tests/main-set/001-cov/007-alias-to-nothing.json b/apps/tests/main-set/001-cov/007-alias-to-nothing.json index 117e9833..aa03e781 100644 --- a/apps/tests/main-set/001-cov/007-alias-to-nothing.json +++ b/apps/tests/main-set/001-cov/007-alias-to-nothing.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "-C bad-recurse start-bad", "expected": [ 22, diff --git a/apps/tests/main-set/001-cov/008-bad-alias.json b/apps/tests/main-set/001-cov/008-bad-alias.json index 26902b17..8983fa26 100644 --- a/apps/tests/main-set/001-cov/008-bad-alias.json +++ b/apps/tests/main-set/001-cov/008-bad-alias.json @@ -1,8 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "-C bad-recurse first -D", - "check": { - "stderr": "begin" - }, + "check": {"stderr": "begin"}, "expected": [ 1, "", diff --git a/apps/tests/main-set/001-cov/009-external-tool.json b/apps/tests/main-set/001-cov/009-external-tool.json index a2e266c0..946667df 100644 --- a/apps/tests/main-set/001-cov/009-external-tool.json +++ b/apps/tests/main-set/001-cov/009-external-tool.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "echo arg1 arg2 ...", "expected": [ 0, diff --git a/apps/tests/main-set/001-cov/010-escaped-arguments.json b/apps/tests/main-set/001-cov/010-escaped-arguments.json index 07bbd438..a9ed897b 100644 --- a/apps/tests/main-set/001-cov/010-escaped-arguments.json +++ b/apps/tests/main-set/001-cov/010-escaped-arguments.json @@ -1,5 +1,5 @@ { - "os": "nt", + "$schema": "../../../../build/downloads/runner-schema.json", "args": "echo 'argument with space' 'argument with \\ backslash' 'argument with \"quotes\"' 'argument with '\"'\"'apos'\"'\"'' 'this thing: \\\"' 'that thing: \\\\'", "expected": [ 0, diff --git a/apps/tests/main-set/001-cov/011-multiple-aliases.json b/apps/tests/main-set/001-cov/011-multiple-aliases.json index e21b1837..10bd7988 100644 --- a/apps/tests/main-set/001-cov/011-multiple-aliases.json +++ b/apps/tests/main-set/001-cov/011-multiple-aliases.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "-C recurse name", "expected": [ 0, diff --git a/apps/tests/main-set/001-cov/012-list-cmds.json b/apps/tests/main-set/001-cov/012-list-cmds.json index fefc8f7b..e001880a 100644 --- a/apps/tests/main-set/001-cov/012-list-cmds.json +++ b/apps/tests/main-set/001-cov/012-list-cmds.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "env": { "HOME": null, "XDG_CONFIG_HOME": null, diff --git a/apps/tests/main-set/001-cov/013-bad-alias-full-output.json b/apps/tests/main-set/001-cov/013-bad-alias-full-output.json index 9cbc4496..1924339d 100644 --- a/apps/tests/main-set/001-cov/013-bad-alias-full-output.json +++ b/apps/tests/main-set/001-cov/013-bad-alias-full-output.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "definitely-not-a-tool", "expected": [ 1, diff --git a/apps/tests/main-set/001-cov/014-echo-with-nonascii-arguments.json b/apps/tests/main-set/001-cov/014-echo-with-nonascii-arguments.json index 3a6c59fb..14503997 100644 --- a/apps/tests/main-set/001-cov/014-echo-with-nonascii-arguments.json +++ b/apps/tests/main-set/001-cov/014-echo-with-nonascii-arguments.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "-C 'utf8-αβγδε' first '--hjälp'", "expected": [ 0, diff --git a/apps/tests/main-set/001-cov/015-bad-alias-with-env.json b/apps/tests/main-set/001-cov/015-bad-alias-with-env.json index 5655ac8f..f896fcc5 100644 --- a/apps/tests/main-set/001-cov/015-bad-alias-with-env.json +++ b/apps/tests/main-set/001-cov/015-bad-alias-with-env.json @@ -1,16 +1,13 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "-C bad-recurse first -D", - "env": { - "COV_PATH": [ - "$TMP", - "", - "$TMP/tools", - "" - ] - }, - "check": { - "stderr": "begin" - }, + "env": {"COV_PATH": [ + "$TMP", + "", + "$TMP/tools", + "" + ]}, + "check": {"stderr": "begin"}, "expected": [ 1, "", diff --git a/apps/tests/main-set/001-cov/016-external-tool-outside-libexec.json b/apps/tests/main-set/001-cov/016-external-tool-outside-libexec.json index 33a16972..531aed56 100644 --- a/apps/tests/main-set/001-cov/016-external-tool-outside-libexec.json +++ b/apps/tests/main-set/001-cov/016-external-tool-outside-libexec.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "pwd", "expected": [ 0, diff --git a/apps/tests/main-set/002-init/015-init-no-args.json b/apps/tests/main-set/002-init/015-init-no-args.json index 491cf484..3d07de45 100644 --- a/apps/tests/main-set/002-init/015-init-no-args.json +++ b/apps/tests/main-set/002-init/015-init-no-args.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "init", "expected": [ 0, diff --git a/apps/tests/main-set/002-init/016-init-git-dir.json b/apps/tests/main-set/002-init/016-init-git-dir.json index 0769dbf6..37fd171b 100644 --- a/apps/tests/main-set/002-init/016-init-git-dir.json +++ b/apps/tests/main-set/002-init/016-init-git-dir.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "init --git-dir '$TMP'", "expected": [ 0, diff --git a/apps/tests/main-set/002-init/017-init-directory.json b/apps/tests/main-set/002-init/017-init-directory.json index be99b50a..ef44f771 100644 --- a/apps/tests/main-set/002-init/017-init-directory.json +++ b/apps/tests/main-set/002-init/017-init-directory.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "init '$TMP/subdir'", "expected": [ 0, diff --git a/apps/tests/main-set/002-init/018-init-dependent.json b/apps/tests/main-set/002-init/018-init-dependent.json index b934f15a..62a0d879 100644 --- a/apps/tests/main-set/002-init/018-init-dependent.json +++ b/apps/tests/main-set/002-init/018-init-dependent.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "init --git-dir '$TMP' '$TMP/subdir'", "expected": [ 0, diff --git a/apps/tests/main-set/002-init/019-init-independent.json b/apps/tests/main-set/002-init/019-init-independent.json index e7d97c7e..1fe971b6 100644 --- a/apps/tests/main-set/002-init/019-init-independent.json +++ b/apps/tests/main-set/002-init/019-init-independent.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "init --git-dir '$TMP/repo.git' '$TMP/repo.covdata'", "expected": [ 0, diff --git a/apps/tests/main-set/002-init/020-init-no-gitrepo.json b/apps/tests/main-set/002-init/020-init-no-gitrepo.json index b0021343..23cffec3 100644 --- a/apps/tests/main-set/002-init/020-init-no-gitrepo.json +++ b/apps/tests/main-set/002-init/020-init-no-gitrepo.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "init --git-dir '$TMP/repo.git' '$TMP/repo.covdata'", "expected": [ 2, diff --git a/apps/tests/main-set/002-init/021-init-no-args-no-cwd.json b/apps/tests/main-set/002-init/021-init-no-args-no-cwd.json index 2de4034f..836bd7e9 100644 --- a/apps/tests/main-set/002-init/021-init-no-args-no-cwd.json +++ b/apps/tests/main-set/002-init/021-init-no-args-no-cwd.json @@ -1,8 +1,7 @@ { + "$schema": "../../runner-schema.json", "args": "init", - "patches": { - "cov init: error: argument -C: .*": "cov init: error: argument -C: ERROR MESSAGE" - }, + "patches": {"cov init: error: argument -C: .*": "cov init: error: argument -C: ERROR MESSAGE"}, "expected": [ 2, "", diff --git a/apps/tests/main-set/002-init/022-init-git-dir-relative.json b/apps/tests/main-set/002-init/022-init-git-dir-relative.json index 33476d33..1483d254 100644 --- a/apps/tests/main-set/002-init/022-init-git-dir-relative.json +++ b/apps/tests/main-set/002-init/022-init-git-dir-relative.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "init --git-dir ../.git", "expected": [ 0, diff --git a/apps/tests/main-set/002-init/023-init-no-force.json b/apps/tests/main-set/002-init/023-init-no-force.json index 98f6d691..74243c45 100644 --- a/apps/tests/main-set/002-init/023-init-no-force.json +++ b/apps/tests/main-set/002-init/023-init-no-force.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "init", "expected": [ 1, diff --git a/apps/tests/main-set/002-init/024-init-force.json b/apps/tests/main-set/002-init/024-init-force.json index 666c28b1..265a7d8b 100644 --- a/apps/tests/main-set/002-init/024-init-force.json +++ b/apps/tests/main-set/002-init/024-init-force.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "init --force", "expected": [ 0, diff --git a/apps/tests/main-set/003-config-A/025-config-no-args.json b/apps/tests/main-set/003-config-A/025-config-no-args.json index 3d5506d6..7dc1f144 100644 --- a/apps/tests/main-set/003-config-A/025-config-no-args.json +++ b/apps/tests/main-set/003-config-A/025-config-no-args.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "config", "expected": [ 0, diff --git a/apps/tests/main-set/003-config-A/026-config-list-local.json b/apps/tests/main-set/003-config-A/026-config-list-local.json index 6587c137..e25281e7 100644 --- a/apps/tests/main-set/003-config-A/026-config-list-local.json +++ b/apps/tests/main-set/003-config-A/026-config-list-local.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "config --local --list", "expected": [ 0, diff --git a/apps/tests/main-set/003-config-A/027-config-list-global.json b/apps/tests/main-set/003-config-A/027-config-list-global.json index f8125c56..413f7ff6 100644 --- a/apps/tests/main-set/003-config-A/027-config-list-global.json +++ b/apps/tests/main-set/003-config-A/027-config-list-global.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "env": { "HOME": "$TMP/home", "XDG_CONFIG_HOME": null, @@ -11,7 +12,7 @@ "" ], "prepare": [ - "touch '$DATA/../copy/etc/covconfig' '[group]\nkey = system\n'", + "touch '$INST/../etc/covconfig' '[group]\nkey = system\n'", "touch '$TMP/home/.config/cov/config' '[group]\nkey = global\n'", "cd $TMP" ] diff --git a/apps/tests/main-set/003-config-A/028-config-list-system.json b/apps/tests/main-set/003-config-A/028-config-list-system.json index 051f3b67..69817bde 100644 --- a/apps/tests/main-set/003-config-A/028-config-list-system.json +++ b/apps/tests/main-set/003-config-A/028-config-list-system.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "env": { "HOME": "$TMP/home", "XDG_CONFIG_HOME": null, @@ -11,7 +12,7 @@ "" ], "prepare": [ - "touch '$DATA/../copy/etc/covconfig' '[group]\nkey = system\n'", + "touch '$INST/../etc/covconfig' '[group]\nkey = system\n'", "touch '$TMP/home/.config/cov/config' '[group]\nkey = global\n'", "cd $TMP" ] diff --git a/apps/tests/main-set/003-config-A/029-config-list-file.json b/apps/tests/main-set/003-config-A/029-config-list-file.json index 0ffa8c2d..cae4ca31 100644 --- a/apps/tests/main-set/003-config-A/029-config-list-file.json +++ b/apps/tests/main-set/003-config-A/029-config-list-file.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "config -f '$TMP/some.file' --list", "expected": [ 0, diff --git a/apps/tests/main-set/004-config-B/030-config-list-any.json b/apps/tests/main-set/004-config-B/030-config-list-any.json index 986f9253..76d3fb6a 100644 --- a/apps/tests/main-set/004-config-B/030-config-list-any.json +++ b/apps/tests/main-set/004-config-B/030-config-list-any.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "env": { "HOME": "$TMP/home", "XDG_CONFIG_HOME": null, @@ -21,7 +22,7 @@ "cd $TMP/repo.covdata", "git init --bare $TMP/repo.git", "cov init --git-dir $TMP/repo.git .", - "touch '$DATA/../copy/etc/covconfig' '[group]\nkey = system\n'", + "touch '$INST/../etc/covconfig' '[group]\nkey = system\n'", "touch '$TMP/home/.config/cov/config' '[group]\nkey = global\n'", "cd $TMP/repo.covdata", "cov config --local group.key local" diff --git a/apps/tests/main-set/004-config-B/031-config-list-after-get.json b/apps/tests/main-set/004-config-B/031-config-list-after-get.json index 8a1eed9c..a1b6f9ad 100644 --- a/apps/tests/main-set/004-config-B/031-config-list-after-get.json +++ b/apps/tests/main-set/004-config-B/031-config-list-after-get.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "env": { "HOME": "$TMP/home", "XDG_CONFIG_HOME": null, diff --git a/apps/tests/main-set/004-config-B/032-config-unset-after-list.json b/apps/tests/main-set/004-config-B/032-config-unset-after-list.json index b99f5183..2eb47d1e 100644 --- a/apps/tests/main-set/004-config-B/032-config-unset-after-list.json +++ b/apps/tests/main-set/004-config-B/032-config-unset-after-list.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "env": { "HOME": "$TMP/home", "XDG_CONFIG_HOME": null, diff --git a/apps/tests/main-set/004-config-B/033-config-add-after-unset-all.json b/apps/tests/main-set/004-config-B/033-config-add-after-unset-all.json index 0bd70d7d..ab32f794 100644 --- a/apps/tests/main-set/004-config-B/033-config-add-after-unset-all.json +++ b/apps/tests/main-set/004-config-B/033-config-add-after-unset-all.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "env": { "HOME": "$TMP/home", "XDG_CONFIG_HOME": null, diff --git a/apps/tests/main-set/004-config-B/034-config-get-all-after-get.json b/apps/tests/main-set/004-config-B/034-config-get-all-after-get.json index 1d7afb2e..6844e17b 100644 --- a/apps/tests/main-set/004-config-B/034-config-get-all-after-get.json +++ b/apps/tests/main-set/004-config-B/034-config-get-all-after-get.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "env": { "HOME": "$TMP/home", "XDG_CONFIG_HOME": null, diff --git a/apps/tests/main-set/004-config-B/035-config-list-local-no-cov.json b/apps/tests/main-set/004-config-B/035-config-list-local-no-cov.json index dd87d7d4..19a056c8 100644 --- a/apps/tests/main-set/004-config-B/035-config-list-local-no-cov.json +++ b/apps/tests/main-set/004-config-B/035-config-list-local-no-cov.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "config --local --list", "expected": [ 2, diff --git a/apps/tests/main-set/004-config-B/036-config-add-one-arg.json b/apps/tests/main-set/004-config-B/036-config-add-one-arg.json index 7e55b355..4b76ef69 100644 --- a/apps/tests/main-set/004-config-B/036-config-add-one-arg.json +++ b/apps/tests/main-set/004-config-B/036-config-add-one-arg.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "config --file $TMP/config --add group.key", "expected": [ 2, diff --git a/apps/tests/main-set/004-config-B/037-config-unset-too-many.json b/apps/tests/main-set/004-config-B/037-config-unset-too-many.json index 665bd853..29f5a47d 100644 --- a/apps/tests/main-set/004-config-B/037-config-unset-too-many.json +++ b/apps/tests/main-set/004-config-B/037-config-unset-too-many.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "config --file $TMP/config --unset group.key value", "expected": [ 2, diff --git a/apps/tests/main-set/005-config-C/038-config-set-but-multivar.json b/apps/tests/main-set/005-config-C/038-config-set-but-multivar.json index 16c8c485..d9ab571b 100644 --- a/apps/tests/main-set/005-config-C/038-config-set-but-multivar.json +++ b/apps/tests/main-set/005-config-C/038-config-set-but-multivar.json @@ -1,14 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "config --file $TMP/config group.key 'value 3'", "expected": [ 2, "", - [ - "usage: cov config [-h] [] [ [] | --unset | -l | --list]", - "cov config: error: entry is not unique due to being a multivar\n" - ] + "cov config: error: entry is not unique due to being a multivar\n" ], - "prepare": [ - "touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'" - ] + "prepare": ["touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'"] } diff --git a/apps/tests/main-set/005-config-C/039-config-set-but-include.json b/apps/tests/main-set/005-config-C/039-config-set-but-include.json index bf1f8fda..0abb9a72 100644 --- a/apps/tests/main-set/005-config-C/039-config-set-but-include.json +++ b/apps/tests/main-set/005-config-C/039-config-set-but-include.json @@ -1,12 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "config --file $TMP/config group.key 'value 3'", "expected": [ 2, "", - [ - "usage: cov config [-h] [] [ [] | --unset | -l | --list]", - "cov config: error: entry is not unique due to being included\n" - ] + "cov config: error: entry is not unique due to being included\n" ], "prepare": [ "touch '$TMP/config.inc' '[group]\nkey = value 1'", diff --git a/apps/tests/main-set/005-config-C/040-config-unset-but-multivar.json b/apps/tests/main-set/005-config-C/040-config-unset-but-multivar.json index eeef8daf..84860c80 100644 --- a/apps/tests/main-set/005-config-C/040-config-unset-but-multivar.json +++ b/apps/tests/main-set/005-config-C/040-config-unset-but-multivar.json @@ -1,14 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "config --file $TMP/config --unset group.key", "expected": [ 2, "", - [ - "usage: cov config [-h] [] [ [] | --unset | -l | --list]", - "cov config: error: entry is not unique due to being a multivar\n" - ] + "cov config: error: entry is not unique due to being a multivar\n" ], - "prepare": [ - "touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'" - ] + "prepare": ["touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'"] } diff --git a/apps/tests/main-set/005-config-C/041-config-unset-but-not-there.json b/apps/tests/main-set/005-config-C/041-config-unset-but-not-there.json index 31c19c52..944eb612 100644 --- a/apps/tests/main-set/005-config-C/041-config-unset-but-not-there.json +++ b/apps/tests/main-set/005-config-C/041-config-unset-but-not-there.json @@ -1,14 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "config --file $TMP/config --unset group.other", "expected": [ 2, "", - [ - "usage: cov config [-h] [] [ [] | --unset | -l | --list]", - "cov config: error: could not find key 'group.other' to delete\n" - ] + "cov config: error: could not find key 'group.other' to delete\n" ], - "prepare": [ - "touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'" - ] + "prepare": ["touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'"] } diff --git a/apps/tests/main-set/005-config-C/042-config-unset-but-no-a-key.json b/apps/tests/main-set/005-config-C/042-config-unset-but-no-a-key.json index e6fbbf81..641ea9b1 100644 --- a/apps/tests/main-set/005-config-C/042-config-unset-but-no-a-key.json +++ b/apps/tests/main-set/005-config-C/042-config-unset-but-no-a-key.json @@ -1,14 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "config --file $TMP/config --unset group-other", "expected": [ 2, "", - [ - "usage: cov config [-h] [] [ [] | --unset | -l | --list]", - "cov config: error: invalid config item name 'group-other'\n" - ] + "cov config: error: invalid config item name 'group-other'\n" ], - "prepare": [ - "touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'" - ] + "prepare": ["touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'"] } diff --git a/apps/tests/main-set/005-config-C/043-config-get.json b/apps/tests/main-set/005-config-C/043-config-get.json index 034c7ec4..c234a22e 100644 --- a/apps/tests/main-set/005-config-C/043-config-get.json +++ b/apps/tests/main-set/005-config-C/043-config-get.json @@ -1,11 +1,6 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "config --file $TMP/config group.key", - "expected": [ - 0, - "value 2\n", - "" - ], - "prepare": [ - "touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'" - ] + "expected": [0, "value 2\n", ""], + "prepare": ["touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'"] } diff --git a/apps/tests/main-set/005-config-C/044-config-get-all.json b/apps/tests/main-set/005-config-C/044-config-get-all.json index ce2f77e8..bb8655f2 100644 --- a/apps/tests/main-set/005-config-C/044-config-get-all.json +++ b/apps/tests/main-set/005-config-C/044-config-get-all.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "config --file $TMP/config --get-all group.key", "expected": [ 0, @@ -8,7 +9,5 @@ ], "" ], - "prepare": [ - "touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'" - ] + "prepare": ["touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'"] } diff --git a/apps/tests/main-set/005-config-C/045-config-add.json b/apps/tests/main-set/005-config-C/045-config-add.json index 315af9a1..1c084c3a 100644 --- a/apps/tests/main-set/005-config-C/045-config-add.json +++ b/apps/tests/main-set/005-config-C/045-config-add.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "config --file $TMP/config --get-all group.key", "expected": [ 0, diff --git a/apps/tests/main-set/005-config-C/046-config-unset-all.json b/apps/tests/main-set/005-config-C/046-config-unset-all.json index 6b716c83..fd5108c7 100644 --- a/apps/tests/main-set/005-config-C/046-config-unset-all.json +++ b/apps/tests/main-set/005-config-C/046-config-unset-all.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "config --file $TMP/config --list", "expected": [ 0, diff --git a/apps/tests/main-set/006-module-A/047-module-help.json b/apps/tests/main-set/006-module-A/047-module-help.json index 7ca73419..b3989694 100644 --- a/apps/tests/main-set/006-module-A/047-module-help.json +++ b/apps/tests/main-set/006-module-A/047-module-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module --help", "expected": [ 0, diff --git a/apps/tests/main-set/006-module-A/048-module-build-and-print.json b/apps/tests/main-set/006-module-A/048-module-build-and-print.json index 9c8c19b3..4f961e84 100644 --- a/apps/tests/main-set/006-module-A/048-module-build-and-print.json +++ b/apps/tests/main-set/006-module-A/048-module-build-and-print.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module", "expected": [ 0, diff --git a/apps/tests/main-set/006-module-A/049-module-build-and-print-sep.json b/apps/tests/main-set/006-module-A/049-module-build-and-print-sep.json index 618bce18..a1b1b3d2 100644 --- a/apps/tests/main-set/006-module-A/049-module-build-and-print-sep.json +++ b/apps/tests/main-set/006-module-A/049-module-build-and-print-sep.json @@ -1,10 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module --show-sep", - "expected": [ - 0, - " :: \n", - "" - ], + "expected": [0, " :: \n", ""], "prepare": [ "mkdirs $TMP/repo.git", "mkdirs $TMP/repo.covdata", diff --git a/apps/tests/main-set/006-module-A/050-module-deconstruct-and-print.json b/apps/tests/main-set/006-module-A/050-module-deconstruct-and-print.json index cdc1cbf9..49d2b953 100644 --- a/apps/tests/main-set/006-module-A/050-module-deconstruct-and-print.json +++ b/apps/tests/main-set/006-module-A/050-module-deconstruct-and-print.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module", "expected": [ 0, diff --git a/apps/tests/main-set/006-module-A/051-module-modify-in-bare-git.json b/apps/tests/main-set/006-module-A/051-module-modify-in-bare-git.json index 432f025a..5534de1e 100644 --- a/apps/tests/main-set/006-module-A/051-module-modify-in-bare-git.json +++ b/apps/tests/main-set/006-module-A/051-module-modify-in-bare-git.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module --remove-all \"doesn't matter\"", "expected": [ 2, diff --git a/apps/tests/main-set/006-module-A/052-module-add-duplicate.json b/apps/tests/main-set/006-module-A/052-module-add-duplicate.json index bd5e6010..61af1a77 100644 --- a/apps/tests/main-set/006-module-A/052-module-add-duplicate.json +++ b/apps/tests/main-set/006-module-A/052-module-add-duplicate.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module --add module 'dir 1'", "expected": [ 2, diff --git a/apps/tests/main-set/007-module-B/053-module-remove-from-nothing.json b/apps/tests/main-set/007-module-B/053-module-remove-from-nothing.json index 3eaac4ce..aea193fd 100644 --- a/apps/tests/main-set/007-module-B/053-module-remove-from-nothing.json +++ b/apps/tests/main-set/007-module-B/053-module-remove-from-nothing.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module --remove some-name 'dir 1'", "expected": [ 2, diff --git a/apps/tests/main-set/007-module-B/054-module-print-from-HEAD.json b/apps/tests/main-set/007-module-B/054-module-print-from-HEAD.json index 5f0112fd..1bddc48a 100644 --- a/apps/tests/main-set/007-module-B/054-module-print-from-HEAD.json +++ b/apps/tests/main-set/007-module-B/054-module-print-from-HEAD.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module HEAD", "expected": [ 0, diff --git a/apps/tests/main-set/007-module-B/055-module-print-sep-from-HEAD.json b/apps/tests/main-set/007-module-B/055-module-print-sep-from-HEAD.json index 97a074f4..82520a87 100644 --- a/apps/tests/main-set/007-module-B/055-module-print-sep-from-HEAD.json +++ b/apps/tests/main-set/007-module-B/055-module-print-sep-from-HEAD.json @@ -1,10 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module --show-sep HEAD", - "expected": [ - 0, - "::\n", - "" - ], + "expected": [0, "::\n", ""], "prepare": [ "mkdirs $TMP/repo.covdata", "unpack $DATA/repo.covmodules.tar $TMP", diff --git a/apps/tests/main-set/007-module-B/056-module-open_from_ref-range.json b/apps/tests/main-set/007-module-B/056-module-open_from_ref-range.json index 26b13cdb..a47912c3 100644 --- a/apps/tests/main-set/007-module-B/056-module-open_from_ref-range.json +++ b/apps/tests/main-set/007-module-B/056-module-open_from_ref-range.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module v1.0.0..HEAD", "expected": [ 2, diff --git a/apps/tests/main-set/007-module-B/057-module-open_from_ref-tree.json b/apps/tests/main-set/007-module-B/057-module-open_from_ref-tree.json index 78f7bf53..a831880b 100644 --- a/apps/tests/main-set/007-module-B/057-module-open_from_ref-tree.json +++ b/apps/tests/main-set/007-module-B/057-module-open_from_ref-tree.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module 2559ccfa6", "expected": [ 2, diff --git a/apps/tests/main-set/007-module-B/058-module-no-git.json b/apps/tests/main-set/007-module-B/058-module-no-git.json index 7709fbe3..ad90fcaf 100644 --- a/apps/tests/main-set/007-module-B/058-module-no-git.json +++ b/apps/tests/main-set/007-module-B/058-module-no-git.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module", "expected": [ 2, diff --git a/apps/tests/main-set/007-module-B/059-module-excl-show.json b/apps/tests/main-set/007-module-B/059-module-excl-show.json index ae909b47..70e9ae1a 100644 --- a/apps/tests/main-set/007-module-B/059-module-excl-show.json +++ b/apps/tests/main-set/007-module-B/059-module-excl-show.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module HEAD --add module dir", "expected": [ 2, diff --git a/apps/tests/main-set/007-module-B/060-module-excl-show-sep.json b/apps/tests/main-set/007-module-B/060-module-excl-show-sep.json index 42e6abca..f64934ba 100644 --- a/apps/tests/main-set/007-module-B/060-module-excl-show-sep.json +++ b/apps/tests/main-set/007-module-B/060-module-excl-show-sep.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module --show-sep --add module dir", "expected": [ 2, diff --git a/apps/tests/main-set/007-module-B/061-module-too-many-refs.json b/apps/tests/main-set/007-module-B/061-module-too-many-refs.json index f38a11d7..de8785cb 100644 --- a/apps/tests/main-set/007-module-B/061-module-too-many-refs.json +++ b/apps/tests/main-set/007-module-B/061-module-too-many-refs.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module HEAD ANOTHER_HEAD main v1.0.0", "expected": [ 2, diff --git a/apps/tests/main-set/007-module-B/062-module-too-little-args.json b/apps/tests/main-set/007-module-B/062-module-too-little-args.json index bbd86d9c..67b80eb7 100644 --- a/apps/tests/main-set/007-module-B/062-module-too-little-args.json +++ b/apps/tests/main-set/007-module-B/062-module-too-little-args.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module --add module", "expected": [ 2, diff --git a/apps/tests/main-set/007-module-B/062-module-too-many-args.json b/apps/tests/main-set/007-module-B/062-module-too-many-args.json index 7c04b542..def4a894 100644 --- a/apps/tests/main-set/007-module-B/062-module-too-many-args.json +++ b/apps/tests/main-set/007-module-B/062-module-too-many-args.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module --remove-all module dir", "expected": [ 2, diff --git a/apps/tests/main-set/007-module-B/063-module-no-covmodule.json b/apps/tests/main-set/007-module-B/063-module-no-covmodule.json index 119ab8db..75e741be 100644 --- a/apps/tests/main-set/007-module-B/063-module-no-covmodule.json +++ b/apps/tests/main-set/007-module-B/063-module-no-covmodule.json @@ -1,10 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module", - "expected": [ - 0, - "", - "" - ], + "expected": [0, "", ""], "prepare": [ "git init $TMP/repo", "mkdirs $TMP/repo/.covmodules/nope", diff --git a/apps/tests/main-set/007-module-B/064-module-no-covmodule-add.json b/apps/tests/main-set/007-module-B/064-module-no-covmodule-add.json index 32095757..1ad2ddaa 100644 --- a/apps/tests/main-set/007-module-B/064-module-no-covmodule-add.json +++ b/apps/tests/main-set/007-module-B/064-module-no-covmodule-add.json @@ -1,12 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module --add module dir", "expected": [ 2, "", - [ - "usage: cov module [-h] [--show-sep []] | [--set-sep | --add | --remove | --remove-all ] | []", - "cov module: error: requested file is a directory\n" - ] + "cov module: error: requested file is a directory\n" ], "prepare": [ "git init $TMP/repo", diff --git a/apps/tests/main-set/007-module-B/065-module-open_from_ref-no-such-object.json b/apps/tests/main-set/007-module-B/065-module-open_from_ref-no-such-object.json index ba3a69f5..d0bb69c5 100644 --- a/apps/tests/main-set/007-module-B/065-module-open_from_ref-no-such-object.json +++ b/apps/tests/main-set/007-module-B/065-module-open_from_ref-no-such-object.json @@ -1,10 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "module nonexistent-ref", - "expected": [ - 0, - "", - "" - ], + "expected": [0, "", ""], "prepare": [ "unpack $DATA/repo.git.tar $TMP", "cd $TMP/repo.git" diff --git a/apps/tests/main-set/008-report-A/066-report-no-args.json b/apps/tests/main-set/008-report-A/066-report-no-args.json index b081f041..c68ebf9f 100644 --- a/apps/tests/main-set/008-report-A/066-report-no-args.json +++ b/apps/tests/main-set/008-report-A/066-report-no-args.json @@ -1,10 +1,11 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report", "expected": [ 2, "", [ - "usage: cov report [-h] [-f ] [-p = ...] [--amend]", + "usage: cov report [-h] [-f ] [-p = ...] [--amend] [-o ]", "cov report: error: argument is required\n" ] ] diff --git a/apps/tests/main-set/008-report-A/067-report-help.json b/apps/tests/main-set/008-report-A/067-report-help.json index 0e92e139..46617db8 100644 --- a/apps/tests/main-set/008-report-A/067-report-help.json +++ b/apps/tests/main-set/008-report-A/067-report-help.json @@ -1,18 +1,20 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report -h", "expected": [ 0, [ - "usage: cov report [-h] [-f ] [-p = ...] [--amend]", + "usage: cov report [-h] [-f ] [-p = ...] [--amend] [-o ]", "", "positional arguments:", " selects report to import", "", "optional arguments:", " -h, --help shows this help message and exits", - " -f, --filter filters other report formats to internal cov format; known filters are: ‘cobertura’ and ‘coveralls’", + " -f, --filter filters other report formats to internal cov format; known filters are: ‘cobertura’, ‘coveralls’ and ‘strip-excludes’", " -p, --prop = adds a property to this build report; if the is one of 'true', 'false', 'on' or 'off', it will treated as a boolean, if it looks like a whole number, it will be treated as a number, otherwise it will be treated as string; good names for properties could be 'os', 'arch', 'build_type' or 'compiler'", - " --amend replaces the tip of the current branch by creating a new commit\n" + " --amend replaces the tip of the current branch by creating a new commit", + " -o, --out \n" ], "" ] diff --git a/apps/tests/main-set/008-report-A/068-report-no-cov.json b/apps/tests/main-set/008-report-A/068-report-no-cov.json index 5ffffa42..d061f124 100644 --- a/apps/tests/main-set/008-report-A/068-report-no-cov.json +++ b/apps/tests/main-set/008-report-A/068-report-no-cov.json @@ -1,10 +1,11 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report $DATA/basic-coverage.json", "expected": [ 2, "", [ - "usage: cov report [-h] [-f ] [-p = ...] [--amend]", + "usage: cov report [-h] [-f ] [-p = ...] [--amend] [-o ]", "cov report: error: Cannot find a Cov repository in $TMP\n" ] ], diff --git a/apps/tests/main-set/008-report-A/069-report-no-commit.json b/apps/tests/main-set/008-report-A/069-report-no-commit.json index 2a5769ae..4a8c2487 100644 --- a/apps/tests/main-set/008-report-A/069-report-no-commit.json +++ b/apps/tests/main-set/008-report-A/069-report-no-commit.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report $DATA/basic-coverage.json", "expected": [ 1, diff --git a/apps/tests/main-set/008-report-A/070-report-failing-filter.json b/apps/tests/main-set/008-report-A/070-report-failing-filter.json index 6f57db1d..5901629c 100644 --- a/apps/tests/main-set/008-report-A/070-report-failing-filter.json +++ b/apps/tests/main-set/008-report-A/070-report-failing-filter.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report $DATA/basic-coverage.json -f echo-to-stderr", "expected": [ 1, diff --git a/apps/tests/main-set/008-report-A/071-report-no-filter.json b/apps/tests/main-set/008-report-A/071-report-no-filter.json index 221fadd9..ad821610 100644 --- a/apps/tests/main-set/008-report-A/071-report-no-filter.json +++ b/apps/tests/main-set/008-report-A/071-report-no-filter.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report $DATA/basic-coverage.json -f no-filter", "expected": [ 1, diff --git a/apps/tests/main-set/008-report-A/072-report-not-the-json.json b/apps/tests/main-set/008-report-A/072-report-not-the-json.json index 2dcbc923..4fa28d1a 100644 --- a/apps/tests/main-set/008-report-A/072-report-not-the-json.json +++ b/apps/tests/main-set/008-report-A/072-report-not-the-json.json @@ -1,10 +1,11 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report $DATA/no-git-coverage.json", "expected": [ 2, "", [ - "usage: cov report [-h] [-f ] [-p = ...] [--amend]", + "cov report: /git: undefined", "cov report: error: there were issues with $DATA/no-git-coverage.json\n" ] ], diff --git a/apps/tests/main-set/008-report-A/073-report-not-the-json-filtered.json b/apps/tests/main-set/008-report-A/073-report-not-the-json-filtered.json index 6924bc87..5a5fa7e8 100644 --- a/apps/tests/main-set/008-report-A/073-report-not-the-json-filtered.json +++ b/apps/tests/main-set/008-report-A/073-report-not-the-json-filtered.json @@ -1,11 +1,13 @@ { - "args": "report $DATA/no-git-coverage.json -f echo-to-stdout", + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "report $DATA/invalid-coverage.json -f echo-to-stdout", "expected": [ 2, "", [ - "usage: cov report [-h] [-f ] [-p = ...] [--amend]", - "cov report: error: there were issues with $DATA/no-git-coverage.json processed by echo-to-stdout filter" + "cov report: /git: undefined", + "cov report: /files: undefined", + "cov report: error: there were issues with $DATA/invalid-coverage.json processed by echo-to-stdout filter\n" ] ], "prepare": [ diff --git a/apps/tests/main-set/009-report-B/074-report-no-file.json b/apps/tests/main-set/009-report-B/074-report-no-file.json index 37de75b3..165d71cd 100644 --- a/apps/tests/main-set/009-report-B/074-report-no-file.json +++ b/apps/tests/main-set/009-report-B/074-report-no-file.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report $DATA/basic-coverage.json -f replace-head", "expected": [ 1, diff --git a/apps/tests/main-set/009-report-B/075-report-empty.json b/apps/tests/main-set/009-report-B/075-report-empty.json index 259511a7..454c2c0c 100644 --- a/apps/tests/main-set/009-report-B/075-report-empty.json +++ b/apps/tests/main-set/009-report-B/075-report-empty.json @@ -1,15 +1,12 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report $DATA/empty-coverage.json -f replace-head", - "patches": { - "\u001b\\[31m\\[main [0-9a-fA-F]+\\]\u001b\\[m second": "\u001b[31m[main $REPORT]\u001b[m second", - " \u001b\\[2;37mcontains [0-9a-fA-F]+:\u001b\\[m (.+)": " \u001b[2;37mcontains $BUILD:\u001b[m \\1" - }, "expected": [ 0, [ "\u001b[31m[main $REPORT]\u001b[m second", " 0 files, \u001b[31m 0%\u001b[m (0/0)", - " \u001b[2;37mbased on\u001b[m \u001b[2;33m384512442@main\u001b[m", + " \u001b[2;37mbased on\u001b[m \u001b[2;33m$HEAD@main\u001b[m", " \u001b[2;37mcontains $BUILD:\u001b[m \u001b[31m 0%\u001b[m\n" ], "" diff --git a/apps/tests/main-set/009-report-B/076-report-one-file.json b/apps/tests/main-set/009-report-B/076-report-one-file.json index 20baee88..eacce6e2 100644 --- a/apps/tests/main-set/009-report-B/076-report-one-file.json +++ b/apps/tests/main-set/009-report-B/076-report-one-file.json @@ -1,15 +1,12 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report $DATA/build-coverage.json -f create-report", - "patches": { - "\u001b\\[31m\\[main [0-9a-fA-F]+\\]\u001b\\[m second": "\u001b[31m[main $REPORT]\u001b[m second", - " \u001b\\[2;37mcontains [0-9a-fA-F]+:\u001b\\[m (.+)": " \u001b[2;37mcontains $BUILD:\u001b[m \\1" - }, "expected": [ 0, [ "\u001b[31m[main $REPORT]\u001b[m second", " one file, \u001b[31m 67%\u001b[m (2/3, -1)", - " \u001b[2;37mbased on\u001b[m \u001b[2;33m384512442@main\u001b[m", + " \u001b[2;37mbased on\u001b[m \u001b[2;33m$HEAD@main\u001b[m", " \u001b[2;37mcontains $BUILD:\u001b[m \u001b[31m 67%\u001b[m\n" ], "[ADD] src/main.cc\n" diff --git a/apps/tests/main-set/009-report-B/077-report-amend-on-empty.json b/apps/tests/main-set/009-report-B/077-report-amend-on-empty.json index d4386457..87b638a0 100644 --- a/apps/tests/main-set/009-report-B/077-report-amend-on-empty.json +++ b/apps/tests/main-set/009-report-B/077-report-amend-on-empty.json @@ -1,14 +1,12 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report $DATA/build-coverage.json -f create-report --amend", - "patches": { - "\u001b\\[31m\\[main [0-9a-fA-F]+\\]\u001b\\[m second": "\u001b[31m[main $REPORT]\u001b[m second" - }, "expected": [ 2, "", [ "[ADD] src/main.cc", - "usage: cov report [-h] [-f ] [-p = ...] [--amend]", + "usage: cov report [-h] [-f ] [-p = ...] [--amend] [-o ]", "cov report: error: you have nothing to amend\n" ] ], diff --git a/apps/tests/main-set/009-report-B/078-D-report-with-props.json b/apps/tests/main-set/009-report-B/078-D-report-with-props.json index 5f967441..280c6435 100644 --- a/apps/tests/main-set/009-report-B/078-D-report-with-props.json +++ b/apps/tests/main-set/009-report-B/078-D-report-with-props.json @@ -1,15 +1,12 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report $DATA/build-coverage.json -f create-report -pos=qnx -pdebug=false -pcompiler=javac", - "patches": { - "\u001b\\[31m\\[main [0-9a-fA-F]+\\]\u001b\\[m second": "\u001b[31m[main $REPORT]\u001b[m second", - " \u001b\\[2;37mcontains [0-9a-fA-F]+:\u001b\\[m (.+)": " \u001b[2;37mcontains $BUILD:\u001b[m \\1" - }, "expected": [ 0, [ "\u001b[31m[main $REPORT]\u001b[m second", " one file, \u001b[31m 67%\u001b[m (2/3, -1)", - " \u001b[2;37mbased on\u001b[m \u001b[2;33m384512442@main\u001b[m", + " \u001b[2;37mbased on\u001b[m \u001b[2;33m$HEAD@main\u001b[m", " \u001b[2;37mcontains $BUILD:\u001b[m \u001b[31m 67%\u001b[m\u001b[2;33m (\u001b[m\u001b[33mjavac\u001b[m\u001b[2;33m, debug: \u001b[m\u001b[34moff\u001b[m\u001b[2;33m, \u001b[m\u001b[33mqnx\u001b[m\u001b[2;33m)\u001b[m\n" ], "[ADD] src/main.cc\n" diff --git a/apps/tests/main-set/009-report-B/078-report-amend-on-parentless.json b/apps/tests/main-set/009-report-B/078-report-amend-on-parentless.json index 87efe6ab..12b32df7 100644 --- a/apps/tests/main-set/009-report-B/078-report-amend-on-parentless.json +++ b/apps/tests/main-set/009-report-B/078-report-amend-on-parentless.json @@ -1,15 +1,12 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report $DATA/build-coverage.json -f create-report --amend", - "patches": { - "\u001b\\[31m\\[main [0-9a-fA-F]+\\]\u001b\\[m second": "\u001b[31m[main $REPORT]\u001b[m second", - " \u001b\\[2;37mcontains [0-9a-fA-F]+:\u001b\\[m (.+)": " \u001b[2;37mcontains $BUILD:\u001b[m \\1" - }, "expected": [ 0, [ "\u001b[31m[main $REPORT]\u001b[m second", " one file, \u001b[31m 67%\u001b[m (2/3, -1)", - " \u001b[2;37mbased on\u001b[m \u001b[2;33m384512442@main\u001b[m", + " \u001b[2;37mbased on\u001b[m \u001b[2;33m$HEAD@main\u001b[m", " \u001b[2;37mcontains $BUILD:\u001b[m \u001b[31m 67%\u001b[m\n" ], "[ADD] src/main.cc\n" diff --git a/apps/tests/main-set/009-report-B/079-B-report-the-same-twice.json b/apps/tests/main-set/009-report-B/079-B-report-the-same-twice.json index 1b5dbfae..a4dfb349 100644 --- a/apps/tests/main-set/009-report-B/079-B-report-the-same-twice.json +++ b/apps/tests/main-set/009-report-B/079-B-report-the-same-twice.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report $DATA/build-coverage.json -f create-report", "expected": [ 0, diff --git a/apps/tests/main-set/009-report-B/079-C-report-almost-the-same-twice.json b/apps/tests/main-set/009-report-B/079-C-report-almost-the-same-twice.json index 64243de9..c7cd0e56 100644 --- a/apps/tests/main-set/009-report-B/079-C-report-almost-the-same-twice.json +++ b/apps/tests/main-set/009-report-B/079-C-report-almost-the-same-twice.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report $DATA/build-coverage-same-stats-diff-visits.json -f create-report", "expected": [ 0, diff --git a/apps/tests/main-set/009-report-B/079-report-with-parent.json b/apps/tests/main-set/009-report-B/079-report-with-parent.json index 60d1a8b2..b8998ee4 100644 --- a/apps/tests/main-set/009-report-B/079-report-with-parent.json +++ b/apps/tests/main-set/009-report-B/079-report-with-parent.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report $DATA/build-coverage-2-files.json -f create-report", "patches": { "\u001b\\[0;49;90m\\[master [0-9a-fA-F]+\\] reported files\u001b\\[m": "\u001b[0;49;90m[master $COMMIT] reported files\u001b[m", diff --git a/apps/tests/main-set/009-report-B/080-report-on-detached.json b/apps/tests/main-set/009-report-B/080-report-on-detached.json index 61624cfb..f1c08679 100644 --- a/apps/tests/main-set/009-report-B/080-report-on-detached.json +++ b/apps/tests/main-set/009-report-B/080-report-on-detached.json @@ -1,12 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report $DATA/build-coverage-2-files.json -f create-report", - "patches": { - "\u001b\\[0;49;90m\\[master [0-9a-fA-F]+\\] reported files\u001b\\[m": "\u001b[0;49;90m[master $COMMIT] reported files\u001b[m", - "\u001b\\[31m\\[detached HEAD [0-9a-fA-F]+\\]\u001b\\[m reported files": "\u001b[31m[detached HEAD $REPORT]\u001b[m reported files", - " \u001b\\[2;37mbased on\u001b\\[m \u001b\\[2;33m[0-9a-fA-F]+@main\u001b\\[m": " \u001b[2;37mbased on\u001b[m \u001b[2;33m$HEAD@main\u001b[m", - " parent [0-9a-fA-F]+": " parent $PARENT", - " \u001b\\[2;37mcontains [0-9a-fA-F]+:\u001b\\[m (.+)": " \u001b[2;37mcontains $BUILD:\u001b[m \\1" - }, + "patches": {"\u001b\\[0;49;90m\\[master [0-9a-fA-F]+\\] (.+)": "\u001b[0;49;90m[master $COMMIT] \\1"}, "expected": [ 0, [ diff --git a/apps/tests/main-set/009-report-B/081-report-hash-bang-less.json b/apps/tests/main-set/009-report-B/081-report-hash-bang-less.json index 4e515983..3f76db65 100644 --- a/apps/tests/main-set/009-report-B/081-report-hash-bang-less.json +++ b/apps/tests/main-set/009-report-B/081-report-hash-bang-less.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "report $DATA/build-coverage-2-files.json -f hash-bang-less", "expected": [ 1, diff --git a/apps/tests/main-set/010-log/082-log-no-args.json b/apps/tests/main-set/010-log/082-log-no-args.json index a67211ed..db429304 100644 --- a/apps/tests/main-set/010-log/082-log-no-args.json +++ b/apps/tests/main-set/010-log/082-log-no-args.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "log", "expected": [ 2, @@ -8,7 +9,5 @@ "cov log: error: Cannot find a Cov repository in $TMP\n" ] ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/main-set/010-log/083-log-no-args-inited.json b/apps/tests/main-set/010-log/083-log-no-args-inited.json index 5f01797d..093771af 100644 --- a/apps/tests/main-set/010-log/083-log-no-args-inited.json +++ b/apps/tests/main-set/010-log/083-log-no-args-inited.json @@ -1,12 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "log", "expected": [ 2, "", - [ - "usage: cov log [-h] [] [-n ] [--oneline] [--format ] [--abbrev-hash] [--no-abbrev-hash] [--prop-names] [--no-prop-names] [--color ] [--decorate ]", - "cov log: error: HEAD refers to branch with no reports\n" - ] + "cov log: error: HEAD refers to branch with no reports\n" ], "prepare": [ "git init '$TMP/repo'", diff --git a/apps/tests/main-set/010-log/084-log-no-args-Dmain.json b/apps/tests/main-set/010-log/084-log-no-args-Dmain.json index 4bb87ac0..39658837 100644 --- a/apps/tests/main-set/010-log/084-log-no-args-Dmain.json +++ b/apps/tests/main-set/010-log/084-log-no-args-Dmain.json @@ -1,11 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "log", - "check": { - "stderr": "begin" - }, - "patches": { - "Added: .*": "Added: $DATE" - }, + "check": {"stderr": "begin"}, "expected": [ 0, [ diff --git a/apps/tests/main-set/010-log/085-log-G..K-oneline.json b/apps/tests/main-set/010-log/085-log-G..K-oneline.json index 51a8fd61..ccded4b1 100644 --- a/apps/tests/main-set/010-log/085-log-G..K-oneline.json +++ b/apps/tests/main-set/010-log/085-log-G..K-oneline.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "log --oneline G..K", "expected": [ 0, diff --git a/apps/tests/main-set/010-log/086-log-separate-raw-decorate.json b/apps/tests/main-set/010-log/086-log-separate-raw-decorate.json index eea02e7a..fb5425c2 100644 --- a/apps/tests/main-set/010-log/086-log-separate-raw-decorate.json +++ b/apps/tests/main-set/010-log/086-log-separate-raw-decorate.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "log --format=raw --decorate=short cov-separate", "expected": [ 0, diff --git a/apps/tests/main-set/010-log/087-log-feat3-fuller-decorate.json b/apps/tests/main-set/010-log/087-log-feat3-fuller-decorate.json index c3595a0c..c98d58e4 100644 --- a/apps/tests/main-set/010-log/087-log-feat3-fuller-decorate.json +++ b/apps/tests/main-set/010-log/087-log-feat3-fuller-decorate.json @@ -1,12 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "log --format=fuller --decorate=short feat/cov-3", - "check": { - "stderr": "begin" - }, - "patches": { - "CommitDate: .*": "CommitDate: $DATE", - "Added: .*": "Added: $DATE" - }, + "check": {"stderr": "begin"}, "expected": [ 0, [ diff --git a/apps/tests/main-set/010-log/088-log-G..K-oneline-color.json b/apps/tests/main-set/010-log/088-log-G..K-oneline-color.json index a823657d..f808d38c 100644 --- a/apps/tests/main-set/010-log/088-log-G..K-oneline-color.json +++ b/apps/tests/main-set/010-log/088-log-G..K-oneline-color.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "log --oneline --color always --decorate short G..K", "expected": [ 0, diff --git a/apps/tests/main-set/010-log/089-log-B..K-format-pretty.json b/apps/tests/main-set/010-log/089-log-B..K-format-pretty.json index 182483ea..327ac7b7 100644 --- a/apps/tests/main-set/010-log/089-log-B..K-format-pretty.json +++ b/apps/tests/main-set/010-log/089-log-B..K-format-pretty.json @@ -1,8 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "log '--format=pretty:%C(yellow)%hR%Creset (%s, %rs) %C(red)%hG@%rD%Creset' --color always --decorate short Q..M", - "check": { - "stderr": "begin" - }, + "check": {"stderr": "begin"}, "expected": [ 0, [ diff --git a/apps/tests/main-set/010-log/090-log-B..K-no-format-but-pretty.json b/apps/tests/main-set/010-log/090-log-B..K-no-format-but-pretty.json index e888754b..3a3b9f00 100644 --- a/apps/tests/main-set/010-log/090-log-B..K-no-format-but-pretty.json +++ b/apps/tests/main-set/010-log/090-log-B..K-no-format-but-pretty.json @@ -1,8 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "log '--format=%C(yellow)%hR%Creset (%s, %rs) %C(red)%hG@%rD%Creset' --color always --decorate short Q..M", - "check": { - "stderr": "begin" - }, + "check": {"stderr": "begin"}, "expected": [ 0, [ diff --git a/apps/tests/main-set/010-log/091-2-log-HEAD-format-custom.json b/apps/tests/main-set/010-log/091-2-log-HEAD-format-custom.json index fd759569..6536cb7b 100644 --- a/apps/tests/main-set/010-log/091-2-log-HEAD-format-custom.json +++ b/apps/tests/main-set/010-log/091-2-log-HEAD-format-custom.json @@ -1,8 +1,6 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "log '--format= - %mD(4)' --color always --decorate short", - "patches": { - "Added: .*": "Added: $DATE" - }, "expected": [ 0, [ diff --git a/apps/tests/main-set/010-log/091-log-B..K-format-unknown.json b/apps/tests/main-set/010-log/091-log-B..K-format-unknown.json index 3e48dc4e..9563293a 100644 --- a/apps/tests/main-set/010-log/091-log-B..K-format-unknown.json +++ b/apps/tests/main-set/010-log/091-log-B..K-format-unknown.json @@ -1,8 +1,6 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "log --format=unknown --color always --decorate short Q..M", - "patches": { - "Added: .*": "Added: $DATE" - }, "expected": [ 0, [ diff --git a/apps/tests/main-set/010-log/092-log-help.json b/apps/tests/main-set/010-log/092-log-help.json index e995f69a..7ecaf437 100644 --- a/apps/tests/main-set/010-log/092-log-help.json +++ b/apps/tests/main-set/010-log/092-log-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "log --help", "expected": [ 0, diff --git a/apps/tests/main-set/010-log/093-log-two-HEADs.json b/apps/tests/main-set/010-log/093-log-two-HEADs.json index 17c68e08..38dc84ed 100644 --- a/apps/tests/main-set/010-log/093-log-two-HEADs.json +++ b/apps/tests/main-set/010-log/093-log-two-HEADs.json @@ -1,12 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "log ..", "expected": [ 2, "", - [ - "usage: cov log [-h] [] [-n ] [--oneline] [--format ] [--abbrev-hash] [--no-abbrev-hash] [--prop-names] [--no-prop-names] [--color ] [--decorate ]", - "cov log: error: invalid pattern '..'\n" - ] + "cov log: error: invalid pattern '..'\n" ], "prepare": [ "unpack $DATA/revparse.tar $TMP", diff --git a/apps/tests/main-set/010-log/094-log-123456789.json b/apps/tests/main-set/010-log/094-log-123456789.json index 5eb210cc..6eb5f166 100644 --- a/apps/tests/main-set/010-log/094-log-123456789.json +++ b/apps/tests/main-set/010-log/094-log-123456789.json @@ -1,12 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "log 123456789", "expected": [ 2, "", - [ - "usage: cov log [-h] [] [-n ] [--oneline] [--format ] [--abbrev-hash] [--no-abbrev-hash] [--prop-names] [--no-prop-names] [--color ] [--decorate ]", - "cov log: error: requested object could not be found\n" - ] + "cov log: error: requested object could not be found\n" ], "prepare": [ "unpack $DATA/revparse.tar $TMP", diff --git a/apps/tests/main-set/010-log/095-log-try-non-report.json b/apps/tests/main-set/010-log/095-log-try-non-report.json index ade8455f..2f539230 100644 --- a/apps/tests/main-set/010-log/095-log-try-non-report.json +++ b/apps/tests/main-set/010-log/095-log-try-non-report.json @@ -1,12 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "log $BUILD --prop-names --decorate short", "expected": [ 2, "", - [ - "usage: cov log [-h] [] [-n ] [--oneline] [--format ] [--abbrev-hash] [--no-abbrev-hash] [--prop-names] [--no-prop-names] [--color ] [--decorate ]", - "cov log: cov::category error: Object cannot be cast to required type\n" - ] + "cov log: cov::category error: Object cannot be cast to required type\n" ], "prepare": [ "unpack $DATA/repo.git.tar $TMP", diff --git a/apps/tests/main-set/011-refs/095-branch-no-args.json b/apps/tests/main-set/011-refs/095-branch-no-args.json index 372174ac..661e4464 100644 --- a/apps/tests/main-set/011-refs/095-branch-no-args.json +++ b/apps/tests/main-set/011-refs/095-branch-no-args.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch", "expected": [ 2, @@ -8,7 +9,5 @@ "cov branch: error: Cannot find a Cov repository in $TMP\n" ] ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/main-set/011-refs/096-tag-no-args.json b/apps/tests/main-set/011-refs/096-tag-no-args.json index 7f5d0795..27c269e1 100644 --- a/apps/tests/main-set/011-refs/096-tag-no-args.json +++ b/apps/tests/main-set/011-refs/096-tag-no-args.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "tag", "expected": [ 2, @@ -8,7 +9,5 @@ "cov tag: error: Cannot find a Cov repository in $TMP\n" ] ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/main-set/011-refs/097-branch-help.json b/apps/tests/main-set/011-refs/097-branch-help.json index 587994e2..37d19fa0 100644 --- a/apps/tests/main-set/011-refs/097-branch-help.json +++ b/apps/tests/main-set/011-refs/097-branch-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch --help", "expected": [ 0, @@ -20,7 +21,5 @@ ], "" ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/main-set/011-refs/098-tag-help.json b/apps/tests/main-set/011-refs/098-tag-help.json index 10da3ac6..f093a8aa 100644 --- a/apps/tests/main-set/011-refs/098-tag-help.json +++ b/apps/tests/main-set/011-refs/098-tag-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "tag --help", "expected": [ 0, @@ -17,7 +18,5 @@ ], "" ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/main-set/011-refs/099-branch-list.json b/apps/tests/main-set/011-refs/099-branch-list.json index c9ffe385..f35601cf 100644 --- a/apps/tests/main-set/011-refs/099-branch-list.json +++ b/apps/tests/main-set/011-refs/099-branch-list.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch", "expected": [ 0, diff --git a/apps/tests/main-set/011-refs/100-branch-list-color.json b/apps/tests/main-set/011-refs/100-branch-list-color.json index 3ec24a20..dcc9eb63 100644 --- a/apps/tests/main-set/011-refs/100-branch-list-color.json +++ b/apps/tests/main-set/011-refs/100-branch-list-color.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch --color=always", "expected": [ 0, diff --git a/apps/tests/main-set/011-refs/101-tag-list.json b/apps/tests/main-set/011-refs/101-tag-list.json index a569357d..07bc3fd9 100644 --- a/apps/tests/main-set/011-refs/101-tag-list.json +++ b/apps/tests/main-set/011-refs/101-tag-list.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "tag", "expected": [ 0, diff --git a/apps/tests/main-set/011-refs/102-branch-current.json b/apps/tests/main-set/011-refs/102-branch-current.json index d2bb46b9..5846917c 100644 --- a/apps/tests/main-set/011-refs/102-branch-current.json +++ b/apps/tests/main-set/011-refs/102-branch-current.json @@ -1,10 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch --show-current", - "expected": [ - 0, - "main\n", - "" - ], + "expected": [0, "main\n", ""], "prepare": [ "unpack $DATA/revparse.tar $TMP", "cd $TMP/revparse" diff --git a/apps/tests/main-set/011-refs/103-branch-only-feat.json b/apps/tests/main-set/011-refs/103-branch-only-feat.json index 0873cb1b..31e131b4 100644 --- a/apps/tests/main-set/011-refs/103-branch-only-feat.json +++ b/apps/tests/main-set/011-refs/103-branch-only-feat.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch -l feat/cov-*", "expected": [ 0, diff --git a/apps/tests/main-set/011-refs/104-branch-force-delete.json b/apps/tests/main-set/011-refs/104-branch-force-delete.json index f0fab12c..c3bdce3a 100644 --- a/apps/tests/main-set/011-refs/104-branch-force-delete.json +++ b/apps/tests/main-set/011-refs/104-branch-force-delete.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch --delete main --force", "expected": [ 2, diff --git a/apps/tests/main-set/011-refs/105-branch-two-commands.json b/apps/tests/main-set/011-refs/105-branch-two-commands.json index 1b56a8ce..40e562df 100644 --- a/apps/tests/main-set/011-refs/105-branch-two-commands.json +++ b/apps/tests/main-set/011-refs/105-branch-two-commands.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch --show-current -l feat/cov-*", "expected": [ 2, diff --git a/apps/tests/main-set/011-refs/106-A-branch-create-start-point.json b/apps/tests/main-set/011-refs/106-A-branch-create-start-point.json index 4e6bfc43..1a06b092 100644 --- a/apps/tests/main-set/011-refs/106-A-branch-create-start-point.json +++ b/apps/tests/main-set/011-refs/106-A-branch-create-start-point.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch feat/cov-4 G~2", "post": "log --oneline -n1", "expected": [ diff --git a/apps/tests/main-set/011-refs/106-B-branch-create-invalid-start-point.json b/apps/tests/main-set/011-refs/106-B-branch-create-invalid-start-point.json index bd9d1a85..3e8d7c41 100644 --- a/apps/tests/main-set/011-refs/106-B-branch-create-invalid-start-point.json +++ b/apps/tests/main-set/011-refs/106-B-branch-create-invalid-start-point.json @@ -1,12 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch feat/cov-4 non-existing", "expected": [ 2, "", - [ - "usage: cov branch [-h] [ [] | -d ... | -l [...] | --show-current]", - "cov branch: error: requested object could not be found\n" - ] + "cov branch: error: requested object could not be found\n" ], "prepare": [ "unpack $DATA/revparse.tar $TMP", diff --git a/apps/tests/main-set/011-refs/106-branch-create.json b/apps/tests/main-set/011-refs/106-branch-create.json index 2c4f59f1..86cfc17b 100644 --- a/apps/tests/main-set/011-refs/106-branch-create.json +++ b/apps/tests/main-set/011-refs/106-branch-create.json @@ -1,10 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch feat/cov-4", - "expected": [ - 0, - "", - "" - ], + "expected": [0, "", ""], "prepare": [ "unpack $DATA/revparse.tar $TMP", "cd $TMP/revparse" diff --git a/apps/tests/main-set/011-refs/107-branch-create-forced.json b/apps/tests/main-set/011-refs/107-branch-create-forced.json index fd1b6747..ba1a85c2 100644 --- a/apps/tests/main-set/011-refs/107-branch-create-forced.json +++ b/apps/tests/main-set/011-refs/107-branch-create-forced.json @@ -1,10 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch cov-separate --force", - "expected": [ - 0, - "", - "" - ], + "expected": [0, "", ""], "prepare": [ "unpack $DATA/revparse.tar $TMP", "cd $TMP/revparse" diff --git a/apps/tests/main-set/011-refs/108-branch-create-existing.json b/apps/tests/main-set/011-refs/108-branch-create-existing.json index 2f23258e..7e6af3a5 100644 --- a/apps/tests/main-set/011-refs/108-branch-create-existing.json +++ b/apps/tests/main-set/011-refs/108-branch-create-existing.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch cov-separate", "expected": [ 2, diff --git a/apps/tests/main-set/011-refs/109-branch-create-three-at-a-time.json b/apps/tests/main-set/011-refs/109-branch-create-three-at-a-time.json index 4f4189f9..9ec65951 100644 --- a/apps/tests/main-set/011-refs/109-branch-create-three-at-a-time.json +++ b/apps/tests/main-set/011-refs/109-branch-create-three-at-a-time.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch branch-1 branch-2 branch-3", "expected": [ 2, diff --git a/apps/tests/main-set/011-refs/110-branch-create-unborn.json b/apps/tests/main-set/011-refs/110-branch-create-unborn.json index 36eaac56..02b3daef 100644 --- a/apps/tests/main-set/011-refs/110-branch-create-unborn.json +++ b/apps/tests/main-set/011-refs/110-branch-create-unborn.json @@ -1,12 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch name", "expected": [ 2, "", - [ - "usage: cov branch [-h] [ [] | -d ... | -l [...] | --show-current]", - "cov branch: error: HEAD refers to branch with no reports\n" - ] + "cov branch: error: HEAD refers to branch with no reports\n" ], "prepare": [ "cd $TMP", diff --git a/apps/tests/main-set/011-refs/111-branch-delete-nothing.json b/apps/tests/main-set/011-refs/111-branch-delete-nothing.json index 00fb3e0c..213447da 100644 --- a/apps/tests/main-set/011-refs/111-branch-delete-nothing.json +++ b/apps/tests/main-set/011-refs/111-branch-delete-nothing.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch --delete", "expected": [ 2, diff --git a/apps/tests/main-set/011-refs/112-branch-delete-main.json b/apps/tests/main-set/011-refs/112-branch-delete-main.json index df54e941..c65f4ae0 100644 --- a/apps/tests/main-set/011-refs/112-branch-delete-main.json +++ b/apps/tests/main-set/011-refs/112-branch-delete-main.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch -d main", "expected": [ 2, diff --git a/apps/tests/main-set/011-refs/113-branch-delete-other.json b/apps/tests/main-set/011-refs/113-branch-delete-other.json index 5d51ed42..98e2fff2 100644 --- a/apps/tests/main-set/011-refs/113-branch-delete-other.json +++ b/apps/tests/main-set/011-refs/113-branch-delete-other.json @@ -1,10 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch -d feat/cov-2", - "expected": [ - 0, - "", - "" - ], + "expected": [0, "", ""], "prepare": [ "unpack $DATA/revparse.tar $TMP", "cd $TMP/revparse" diff --git a/apps/tests/main-set/011-refs/114-branch-delete-nonexisting.json b/apps/tests/main-set/011-refs/114-branch-delete-nonexisting.json index d511698e..d613f77c 100644 --- a/apps/tests/main-set/011-refs/114-branch-delete-nonexisting.json +++ b/apps/tests/main-set/011-refs/114-branch-delete-nonexisting.json @@ -1,12 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch -d feat/cov-22", "expected": [ 2, "", - [ - "usage: cov branch [-h] [ [] | -d ... | -l [...] | --show-current]", - "cov branch: error: requested object could not be found\n" - ] + "cov branch: error: requested object could not be found\n" ], "prepare": [ "unpack $DATA/revparse.tar $TMP", diff --git a/apps/tests/main-set/011-refs/115-tag-create.json b/apps/tests/main-set/011-refs/115-tag-create.json index b6f27fb0..4c3d1fdc 100644 --- a/apps/tests/main-set/011-refs/115-tag-create.json +++ b/apps/tests/main-set/011-refs/115-tag-create.json @@ -1,10 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "tag feat/cov-4", - "expected": [ - 0, - "", - "" - ], + "expected": [0, "", ""], "prepare": [ "unpack $DATA/revparse.tar $TMP", "cd $TMP/revparse" diff --git a/apps/tests/main-set/011-refs/116-tag-create-forced.json b/apps/tests/main-set/011-refs/116-tag-create-forced.json index f6207d82..4a993659 100644 --- a/apps/tests/main-set/011-refs/116-tag-create-forced.json +++ b/apps/tests/main-set/011-refs/116-tag-create-forced.json @@ -1,10 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "tag H --force", - "expected": [ - 0, - "", - "" - ], + "expected": [0, "", ""], "prepare": [ "unpack $DATA/revparse.tar $TMP", "cd $TMP/revparse" diff --git a/apps/tests/main-set/011-refs/117-tag-create-existing.json b/apps/tests/main-set/011-refs/117-tag-create-existing.json index b507edad..0eba1b98 100644 --- a/apps/tests/main-set/011-refs/117-tag-create-existing.json +++ b/apps/tests/main-set/011-refs/117-tag-create-existing.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "tag H", "expected": [ 2, diff --git a/apps/tests/main-set/011-refs/118-tag-create-three-at-a-time.json b/apps/tests/main-set/011-refs/118-tag-create-three-at-a-time.json index 8422f095..82dfc821 100644 --- a/apps/tests/main-set/011-refs/118-tag-create-three-at-a-time.json +++ b/apps/tests/main-set/011-refs/118-tag-create-three-at-a-time.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "tag tag-1 tag-2 tag-3", "expected": [ 2, diff --git a/apps/tests/main-set/011-refs/119-tag-create-unborn.json b/apps/tests/main-set/011-refs/119-tag-create-unborn.json index bb77ca04..259e7b1f 100644 --- a/apps/tests/main-set/011-refs/119-tag-create-unborn.json +++ b/apps/tests/main-set/011-refs/119-tag-create-unborn.json @@ -1,12 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "tag name", "expected": [ 2, "", - [ - "usage: cov tag [-h] [ [] | -d ... | -l [...]]", - "cov tag: error: HEAD refers to branch with no reports\n" - ] + "cov tag: error: HEAD refers to branch with no reports\n" ], "prepare": [ "cd $TMP", diff --git a/apps/tests/main-set/011-refs/120-tag-delete-tag.json b/apps/tests/main-set/011-refs/120-tag-delete-tag.json index a6d69073..56fe3f78 100644 --- a/apps/tests/main-set/011-refs/120-tag-delete-tag.json +++ b/apps/tests/main-set/011-refs/120-tag-delete-tag.json @@ -1,10 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "tag -d H", - "expected": [ - 0, - "", - "" - ], + "expected": [0, "", ""], "prepare": [ "unpack $DATA/revparse.tar $TMP", "cd $TMP/revparse" diff --git a/apps/tests/main-set/011-refs/121-tag-delete-nonexisting.json b/apps/tests/main-set/011-refs/121-tag-delete-nonexisting.json index d787a09a..13431949 100644 --- a/apps/tests/main-set/011-refs/121-tag-delete-nonexisting.json +++ b/apps/tests/main-set/011-refs/121-tag-delete-nonexisting.json @@ -1,12 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "tag -d nonexisting", "expected": [ 2, "", - [ - "usage: cov tag [-h] [ [] | -d ... | -l [...]]", - "cov tag: error: requested object could not be found\n" - ] + "cov tag: error: requested object could not be found\n" ], "prepare": [ "unpack $DATA/revparse.tar $TMP", diff --git a/apps/tests/main-set/011-refs/122-branch-create-invalid.json b/apps/tests/main-set/011-refs/122-branch-create-invalid.json index dd807f5f..63424a9f 100644 --- a/apps/tests/main-set/011-refs/122-branch-create-invalid.json +++ b/apps/tests/main-set/011-refs/122-branch-create-invalid.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch \"bad:name\"", "expected": [ 2, diff --git a/apps/tests/main-set/011-refs/123-tag-create-invalid.json b/apps/tests/main-set/011-refs/123-tag-create-invalid.json index 945e9428..ed0d27d2 100644 --- a/apps/tests/main-set/011-refs/123-tag-create-invalid.json +++ b/apps/tests/main-set/011-refs/123-tag-create-invalid.json @@ -1,5 +1,6 @@ { - "args": "tag \"bad:name\"", + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "tag bad\\:name", "expected": [ 2, "", diff --git a/apps/tests/main-set/011-refs/124-branch-list-detached.json b/apps/tests/main-set/011-refs/124-branch-list-detached.json index d36502b5..0de9b4ca 100644 --- a/apps/tests/main-set/011-refs/124-branch-list-detached.json +++ b/apps/tests/main-set/011-refs/124-branch-list-detached.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "branch", "expected": [ 0, diff --git a/apps/tests/main-set/012-checkout/125-checkout-no-args.json b/apps/tests/main-set/012-checkout/125-checkout-no-args.json index 6f370426..620a9701 100644 --- a/apps/tests/main-set/012-checkout/125-checkout-no-args.json +++ b/apps/tests/main-set/012-checkout/125-checkout-no-args.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "checkout", "expected": [ 2, @@ -8,7 +9,5 @@ "cov checkout: error: at least one argument is needed\n" ] ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/main-set/012-checkout/126-checkout-no-cov.json b/apps/tests/main-set/012-checkout/126-checkout-no-cov.json index 905fa7e1..fafd80c5 100644 --- a/apps/tests/main-set/012-checkout/126-checkout-no-cov.json +++ b/apps/tests/main-set/012-checkout/126-checkout-no-cov.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "checkout main", "expected": [ 2, @@ -8,7 +9,5 @@ "cov checkout: error: Cannot find a Cov repository in $TMP\n" ] ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/main-set/012-checkout/127-checkout-help.json b/apps/tests/main-set/012-checkout/127-checkout-help.json index f0df4acd..a04852e5 100644 --- a/apps/tests/main-set/012-checkout/127-checkout-help.json +++ b/apps/tests/main-set/012-checkout/127-checkout-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "checkout -h", "expected": [ 0, @@ -21,7 +22,5 @@ ], "" ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/main-set/012-checkout/128-checkout-two-names.json b/apps/tests/main-set/012-checkout/128-checkout-two-names.json index 1e8bee2d..54e26cd8 100644 --- a/apps/tests/main-set/012-checkout/128-checkout-two-names.json +++ b/apps/tests/main-set/012-checkout/128-checkout-two-names.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "checkout two names", "expected": [ 2, diff --git a/apps/tests/main-set/012-checkout/129-checkout-too-many-for-branching.json b/apps/tests/main-set/012-checkout/129-checkout-too-many-for-branching.json index e8eca639..0e8ce3b9 100644 --- a/apps/tests/main-set/012-checkout/129-checkout-too-many-for-branching.json +++ b/apps/tests/main-set/012-checkout/129-checkout-too-many-for-branching.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "checkout -b result starting-point something else", "expected": [ 2, diff --git a/apps/tests/main-set/012-checkout/130-checkout-too-little-for-branching.json b/apps/tests/main-set/012-checkout/130-checkout-too-little-for-branching.json index dd45c94a..3c409e0a 100644 --- a/apps/tests/main-set/012-checkout/130-checkout-too-little-for-branching.json +++ b/apps/tests/main-set/012-checkout/130-checkout-too-little-for-branching.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "checkout -b", "expected": [ 2, diff --git a/apps/tests/main-set/012-checkout/131-checkout-two-commands.json b/apps/tests/main-set/012-checkout/131-checkout-two-commands.json index 6c13a08c..41d68267 100644 --- a/apps/tests/main-set/012-checkout/131-checkout-two-commands.json +++ b/apps/tests/main-set/012-checkout/131-checkout-two-commands.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "checkout -b --orphan", "expected": [ 2, diff --git a/apps/tests/main-set/012-checkout/132-checkout-branch.json b/apps/tests/main-set/012-checkout/132-checkout-branch.json index 90bdd5fa..597caec9 100644 --- a/apps/tests/main-set/012-checkout/132-checkout-branch.json +++ b/apps/tests/main-set/012-checkout/132-checkout-branch.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "checkout -b branch-name", "post": [ "branch --show-current", diff --git a/apps/tests/main-set/012-checkout/133-checkout-orphan.json b/apps/tests/main-set/012-checkout/133-checkout-orphan.json index f743b440..20157490 100644 --- a/apps/tests/main-set/012-checkout/133-checkout-orphan.json +++ b/apps/tests/main-set/012-checkout/133-checkout-orphan.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "checkout --orphan branch-name", "post": [ "branch --show-current", @@ -7,10 +8,7 @@ "expected": [ 2, "branch-name\n", - [ - "usage: cov log [-h] [] [-n ] [--oneline] [--format ] [--abbrev-hash] [--no-abbrev-hash] [--prop-names] [--no-prop-names] [--color ] [--decorate ]", - "cov log: error: HEAD refers to branch with no reports\n" - ] + "cov log: error: HEAD refers to branch with no reports\n" ], "prepare": [ "unpack $DATA/revparse.tar $TMP", diff --git a/apps/tests/main-set/012-checkout/134-checkout-orphan-main.json b/apps/tests/main-set/012-checkout/134-checkout-orphan-main.json index 9a70fe9f..739a7122 100644 --- a/apps/tests/main-set/012-checkout/134-checkout-orphan-main.json +++ b/apps/tests/main-set/012-checkout/134-checkout-orphan-main.json @@ -1,12 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "checkout --orphan main", "expected": [ 2, "", - [ - "usage: cov checkout [-h] [--detach|-b|--orphan] []", - "cov checkout: git::category error: Object exists preventing operation\n" - ] + "cov checkout: git::category error: Object exists preventing operation\n" ], "prepare": [ "unpack $DATA/revparse.tar $TMP", diff --git a/apps/tests/main-set/012-checkout/135-checkout-orphan-invalid-spec.json b/apps/tests/main-set/012-checkout/135-checkout-orphan-invalid-spec.json index dd95f83e..afe8af85 100644 --- a/apps/tests/main-set/012-checkout/135-checkout-orphan-invalid-spec.json +++ b/apps/tests/main-set/012-checkout/135-checkout-orphan-invalid-spec.json @@ -1,12 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "checkout --orphan 'invalid:spec'", "expected": [ 2, "", - [ - "usage: cov checkout [-h] [--detach|-b|--orphan] []", - "cov checkout: git::category error: Name/ref spec was not in a valid format\n" - ] + "cov checkout: git::category error: Name/ref spec was not in a valid format\n" ], "prepare": [ "unpack $DATA/revparse.tar $TMP", diff --git a/apps/tests/main-set/012-checkout/136-checkout-existing.json b/apps/tests/main-set/012-checkout/136-checkout-existing.json index 4dfd1ff6..f40e0c1f 100644 --- a/apps/tests/main-set/012-checkout/136-checkout-existing.json +++ b/apps/tests/main-set/012-checkout/136-checkout-existing.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "checkout feat/cov-3", "post": [ "branch --show-current", diff --git a/apps/tests/main-set/012-checkout/137-checkout-tag.json b/apps/tests/main-set/012-checkout/137-checkout-tag.json index 97ee05b7..ba3fa42a 100644 --- a/apps/tests/main-set/012-checkout/137-checkout-tag.json +++ b/apps/tests/main-set/012-checkout/137-checkout-tag.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "checkout G", "post": [ "branch --show-current", diff --git a/apps/tests/main-set/012-checkout/138-checkout-without-HEAD.json b/apps/tests/main-set/012-checkout/138-checkout-without-HEAD.json index 7d32e838..729a258e 100644 --- a/apps/tests/main-set/012-checkout/138-checkout-without-HEAD.json +++ b/apps/tests/main-set/012-checkout/138-checkout-without-HEAD.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "checkout --detach", "expected": [ 2, diff --git a/apps/tests/main-set/012-checkout/139-checkout-start-point.json b/apps/tests/main-set/012-checkout/139-checkout-start-point.json index 62b1c57f..ce937554 100644 --- a/apps/tests/main-set/012-checkout/139-checkout-start-point.json +++ b/apps/tests/main-set/012-checkout/139-checkout-start-point.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "checkout -b branch-name G~", "post": [ "branch --show-current", diff --git a/apps/tests/main-set/013-reset/140-reset-help.json b/apps/tests/main-set/013-reset/140-reset-help.json index d86b8c78..69308e85 100644 --- a/apps/tests/main-set/013-reset/140-reset-help.json +++ b/apps/tests/main-set/013-reset/140-reset-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "reset -h", "expected": [ 0, @@ -13,7 +14,5 @@ ], "" ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/main-set/013-reset/141-reset-no-args.json b/apps/tests/main-set/013-reset/141-reset-no-args.json index 2b838b42..6aaca174 100644 --- a/apps/tests/main-set/013-reset/141-reset-no-args.json +++ b/apps/tests/main-set/013-reset/141-reset-no-args.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "reset", "expected": [ 2, @@ -10,6 +11,7 @@ ], "prepare": [ "cd '$TMP'", + "git init", "cov init" ] } diff --git a/apps/tests/main-set/013-reset/142-reset-no-cov-HEAD.json b/apps/tests/main-set/013-reset/142-reset-no-cov-HEAD.json index b4aba35a..68740711 100644 --- a/apps/tests/main-set/013-reset/142-reset-no-cov-HEAD.json +++ b/apps/tests/main-set/013-reset/142-reset-no-cov-HEAD.json @@ -1,12 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "reset HEAD", "expected": [ 2, "", - [ - "usage: cov reset [-h] ", - "cov reset: error: HEAD refers to branch with no reports\n" - ] + "cov reset: error: HEAD refers to branch with no reports\n" ], "prepare": [ "mkdirs '$TMP/reset-empty'", diff --git a/apps/tests/main-set/013-reset/143-reset-switch-to-a-tag.json b/apps/tests/main-set/013-reset/143-reset-switch-to-a-tag.json index 3c9a5be3..edff5598 100644 --- a/apps/tests/main-set/013-reset/143-reset-switch-to-a-tag.json +++ b/apps/tests/main-set/013-reset/143-reset-switch-to-a-tag.json @@ -1,14 +1,15 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "reset F", "expected": [ 0, [ "Tip of 'main' branch now points to ebadfd936a9b1a7822bbe35de47122b879fa48bb", - "\u001b[31m[main ebadfd936]\u001b[m Commit F", + "\u001b[31m[main $REPORT]\u001b[m Commit F", " 0 files, \u001b[31m 0%\u001b[m (0/0)", - " \u001b[2;37mbased on\u001b[m \u001b[2;33m366f0b00e@main\u001b[m", - " parent 3dda7ce58", - " \u001b[2;37mcontains e7a2b8532:\u001b[m \u001b[31m 0%\u001b[m\n" + " \u001b[2;37mbased on\u001b[m \u001b[2;33m$HEAD@main\u001b[m", + " parent $PARENT", + " \u001b[2;37mcontains $BUILD:\u001b[m \u001b[31m 0%\u001b[m\n" ], "" ], diff --git a/apps/tests/main-set/013-reset/144-reset-switch-detached-HEAD.json b/apps/tests/main-set/013-reset/144-reset-switch-detached-HEAD.json index cb27a564..b4effd94 100644 --- a/apps/tests/main-set/013-reset/144-reset-switch-detached-HEAD.json +++ b/apps/tests/main-set/013-reset/144-reset-switch-detached-HEAD.json @@ -1,14 +1,15 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "reset F", "expected": [ 0, [ "Detached HEAD now points to ebadfd936a9b1a7822bbe35de47122b879fa48bb", - "\u001b[31m[detached HEAD ebadfd936]\u001b[m Commit F", + "\u001b[31m[detached HEAD $REPORT]\u001b[m Commit F", " 0 files, \u001b[31m 0%\u001b[m (0/0)", - " \u001b[2;37mbased on\u001b[m \u001b[2;33m366f0b00e@main\u001b[m", - " parent 3dda7ce58", - " \u001b[2;37mcontains e7a2b8532:\u001b[m \u001b[31m 0%\u001b[m\n" + " \u001b[2;37mbased on\u001b[m \u001b[2;33m$HEAD@main\u001b[m", + " parent $PARENT", + " \u001b[2;37mcontains $BUILD:\u001b[m \u001b[31m 0%\u001b[m\n" ], "" ], diff --git a/apps/tests/main-set/014-show/145-show-help.json b/apps/tests/main-set/014-show/145-show-help.json index 83f28660..daabef6f 100644 --- a/apps/tests/main-set/014-show/145-show-help.json +++ b/apps/tests/main-set/014-show/145-show-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show -h", "expected": [ 0, @@ -21,7 +22,5 @@ ], "" ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/main-set/014-show/146-show-no-args-unborn.json b/apps/tests/main-set/014-show/146-show-no-args-unborn.json index a0701bf1..2bcfd13b 100644 --- a/apps/tests/main-set/014-show/146-show-no-args-unborn.json +++ b/apps/tests/main-set/014-show/146-show-no-args-unborn.json @@ -1,12 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show", "expected": [ 2, "", - [ - "usage: cov show [-h] [] [-m ] [--format ] [--abbrev-hash] [--no-abbrev-hash] [--prop-names] [--no-prop-names] [--color ] [--decorate ]", - "cov show: error: HEAD refers to branch with no reports\n" - ] + "cov show: error: HEAD refers to branch with no reports\n" ], "prepare": [ "mkdirs '$TMP/dirname'", diff --git a/apps/tests/main-set/014-show/147-show-no-args.json b/apps/tests/main-set/014-show/147-show-no-args.json index 4173d870..25105c0f 100644 --- a/apps/tests/main-set/014-show/147-show-no-args.json +++ b/apps/tests/main-set/014-show/147-show-no-args.json @@ -1,9 +1,9 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show", "patches": { "report [0-9a-f]{40}": "report $OID", - "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1", - "Added: .*": "Added: $NOW" + "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1" }, "expected": [ 0, @@ -14,7 +14,7 @@ "GitBranch: main", "Coverage: [ 67%] 2/3 (fail)", "Author: Johnny Appleseeed ", - "Added: $NOW", + "Added: $DATE", "", " second", "", diff --git a/apps/tests/main-set/014-show/148-show-no-args-module.json b/apps/tests/main-set/014-show/148-show-no-args-module.json index 2dfaf1b2..89cffb54 100644 --- a/apps/tests/main-set/014-show/148-show-no-args-module.json +++ b/apps/tests/main-set/014-show/148-show-no-args-module.json @@ -1,10 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show --color=always", "patches": { "\u001b\\[33mreport [0-9a-f]{40}\u001b\\[m": "\u001b[33mreport $REPORT_HASH\u001b[m", "commit [0-9a-f]{40}": "commit $COMMIT_HASH", - "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1", - "Added: .*": "Added: $NOW" + "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1" }, "expected": [ 0, @@ -15,7 +15,7 @@ "GitBranch: main", "Coverage: [ 86%] \u001b[2;37m6/7\u001b[m \u001b[2;33m(incomplete)\u001b[m", "Author: Johnny Appleseed ", - "Added: $NOW", + "Added: $DATE", "", " reported files", "", diff --git a/apps/tests/main-set/014-show/149-show-directory.json b/apps/tests/main-set/014-show/149-show-directory.json index 9814f661..29e06be5 100644 --- a/apps/tests/main-set/014-show/149-show-directory.json +++ b/apps/tests/main-set/014-show/149-show-directory.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show HEAD:src", "expected": [ 0, diff --git a/apps/tests/main-set/014-show/150-A-show-file.json b/apps/tests/main-set/014-show/150-A-show-file.json index 2c881744..4c8c5c87 100644 --- a/apps/tests/main-set/014-show/150-A-show-file.json +++ b/apps/tests/main-set/014-show/150-A-show-file.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show HEAD:src/main.cc", "expected": [ 0, diff --git a/apps/tests/main-set/014-show/150-B-show-file-color.json b/apps/tests/main-set/014-show/150-B-show-file-color.json index 359ab8bd..e9c73537 100644 --- a/apps/tests/main-set/014-show/150-B-show-file-color.json +++ b/apps/tests/main-set/014-show/150-B-show-file-color.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show HEAD:src/main.cc --color always", "expected": [ 0, diff --git a/apps/tests/main-set/014-show/150-C-show-file-many-chunks.json b/apps/tests/main-set/014-show/150-C-show-file-many-chunks.json index 24300efe..911e7e86 100644 --- a/apps/tests/main-set/014-show/150-C-show-file-many-chunks.json +++ b/apps/tests/main-set/014-show/150-C-show-file-many-chunks.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show HEAD:src/main.cc --color always", "expected": [ 0, diff --git a/apps/tests/main-set/014-show/151-show-module.json b/apps/tests/main-set/014-show/151-show-module.json index 3b7365f0..1de21584 100644 --- a/apps/tests/main-set/014-show/151-show-module.json +++ b/apps/tests/main-set/014-show/151-show-module.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show -m MAIN", "expected": [ 0, diff --git a/apps/tests/main-set/014-show/152-show-range.json b/apps/tests/main-set/014-show/152-show-range.json index 52763cf9..7abab82a 100644 --- a/apps/tests/main-set/014-show/152-show-range.json +++ b/apps/tests/main-set/014-show/152-show-range.json @@ -1,10 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show then..", "patches": { "report [0-9a-f]{40}": "report $REPORT_HASH", "commit [0-9a-f]{40}": "commit $COMMIT_HASH", - "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1", - "Added: .*": "Added: $NOW" + "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1" }, "expected": [ 0, @@ -15,7 +15,7 @@ "GitBranch: main", "Coverage: [ 86%] 6/7 (incomplete)", "Author: Johnny Appleseed ", - "Added: $NOW", + "Added: $DATE", "", " reported files", "", diff --git a/apps/tests/main-set/014-show/153-show-not-a-covmodule.json b/apps/tests/main-set/014-show/153-show-not-a-covmodule.json index 7ca8e6e7..58591b36 100644 --- a/apps/tests/main-set/014-show/153-show-not-a-covmodule.json +++ b/apps/tests/main-set/014-show/153-show-not-a-covmodule.json @@ -1,10 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show", "patches": { "report [0-9a-f]{40}": "report $REPORT_HASH", "commit [0-9a-f]{40}": "commit $COMMIT_HASH", - "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1", - "Added: .*": "Added: $NOW" + "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1" }, "expected": [ 0, @@ -15,7 +15,7 @@ "GitBranch: main", "Coverage: [ 86%] 6/7 (incomplete)", "Author: Johnny Appleseed ", - "Added: $NOW", + "Added: $DATE", "", " reported files", "", diff --git a/apps/tests/main-set/014-show/154-show-HEAD..HEAD.json b/apps/tests/main-set/014-show/154-show-HEAD..HEAD.json index 00dca68f..2d8f286a 100644 --- a/apps/tests/main-set/014-show/154-show-HEAD..HEAD.json +++ b/apps/tests/main-set/014-show/154-show-HEAD..HEAD.json @@ -1,10 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show same..sies", "patches": { "report [0-9a-f]{40}": "report $REPORT_HASH", "commit [0-9a-f]{40}": "commit $COMMIT_HASH", - "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1", - "Added: .*": "Added: $NOW" + "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1" }, "expected": [ 0, @@ -15,7 +15,7 @@ "GitBranch: main", "Coverage: [ 86%] 6/7 (incomplete)", "Author: Johnny Appleseed ", - "Added: $NOW", + "Added: $DATE", "", " reported files", "", diff --git a/apps/tests/main-set/014-show/155-show-module-props.json b/apps/tests/main-set/014-show/155-show-module-props.json index 9111ec0d..89074a27 100644 --- a/apps/tests/main-set/014-show/155-show-module-props.json +++ b/apps/tests/main-set/014-show/155-show-module-props.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show -m MAIN", "expected": [ 0, diff --git a/apps/tests/main-set/014-show/156-show-module-props-colors.json b/apps/tests/main-set/014-show/156-show-module-props-colors.json index 5f58225d..d03e88d3 100644 --- a/apps/tests/main-set/014-show/156-show-module-props-colors.json +++ b/apps/tests/main-set/014-show/156-show-module-props-colors.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show -m MAIN --color always", "expected": [ 0, diff --git a/apps/tests/main-set/014-show/157-show-module-props-colors-empty.json b/apps/tests/main-set/014-show/157-show-module-props-colors-empty.json index 4f0c8ef3..256b25a4 100644 --- a/apps/tests/main-set/014-show/157-show-module-props-colors-empty.json +++ b/apps/tests/main-set/014-show/157-show-module-props-colors-empty.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show -m MAIN --color always", "expected": [ 0, diff --git a/apps/tests/main-set/014-show/158-show-functions.json b/apps/tests/main-set/014-show/158-show-functions.json index 9d356bf1..5c1f9f77 100644 --- a/apps/tests/main-set/014-show/158-show-functions.json +++ b/apps/tests/main-set/014-show/158-show-functions.json @@ -1,10 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show HEAD:src/main.cc --color always", "patches": { "report [0-9a-f]{40}": "report $REPORT_HASH", "commit [0-9a-f]{40}": "commit $COMMIT_HASH", - "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1", - "Added: .*": "Added: $NOW" + "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1" }, "expected": [ 0, diff --git a/apps/tests/main-set/014-show/159-show-functions-log.json b/apps/tests/main-set/014-show/159-show-functions-log.json index 567b06a6..908b7c6d 100644 --- a/apps/tests/main-set/014-show/159-show-functions-log.json +++ b/apps/tests/main-set/014-show/159-show-functions-log.json @@ -1,10 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show --color always", "patches": { "\u001b\\[33mreport [0-9a-f]{40}\u001b\\[m": "\u001b[33mreport $REPORT_HASH\u001b[m", "commit [0-9a-f]{40}": "commit $COMMIT_HASH", - "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1", - "Added: .*": "Added: $NOW" + "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1" }, "expected": [ 0, @@ -16,7 +16,7 @@ "Coverage: [ 67%] \u001b[2;37m6/9\u001b[m \u001b[2;31m(fail)\u001b[m", "Functions: [ 50%] \u001b[2;37m1/2\u001b[m \u001b[2;31m(fail)\u001b[m", "Author: Johnny Appleseed ", - "Added: $NOW", + "Added: $DATE", "", " function coverage (1)", "", diff --git a/apps/tests/main-set/014-show/160-show-functions-log.json b/apps/tests/main-set/014-show/160-show-functions-log.json index 5b337e5a..d596d0f1 100644 --- a/apps/tests/main-set/014-show/160-show-functions-log.json +++ b/apps/tests/main-set/014-show/160-show-functions-log.json @@ -1,10 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show --color always HEAD:src", "patches": { "\u001b\\[33mreport [0-9a-f]{40}\u001b\\[m": "\u001b[33mreport $REPORT_HASH\u001b[m", "commit [0-9a-f]{40}": "commit $COMMIT_HASH", - "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1", - "Added: .*": "Added: $NOW" + "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1" }, "expected": [ 0, diff --git a/apps/tests/main-set/014-show/161-show-no-chunks.json b/apps/tests/main-set/014-show/161-show-no-chunks.json index c66d53b8..ee5c4903 100644 --- a/apps/tests/main-set/014-show/161-show-no-chunks.json +++ b/apps/tests/main-set/014-show/161-show-no-chunks.json @@ -1,10 +1,10 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show HEAD:src/main.cc", "patches": { "\u001b\\[33mreport [0-9a-f]{40}\u001b\\[m": "\u001b[33mreport $REPORT_HASH\u001b[m", "commit [0-9a-f]{40}": "commit $COMMIT_HASH", - "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1", - "Added: .*": "Added: $NOW" + "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1" }, "expected": [ 0, diff --git a/apps/tests/main-set/014-show/162-show-module-props-indented.json b/apps/tests/main-set/014-show/162-show-module-props-indented.json index 94f09e24..a40389ec 100644 --- a/apps/tests/main-set/014-show/162-show-module-props-indented.json +++ b/apps/tests/main-set/014-show/162-show-module-props-indented.json @@ -1,15 +1,13 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show $BUILD --format=fuller --prop-names --decorate short", - "patches": { - "build [0-9a-f]{40}": "build $BUILD_HASH", - "Added: .*": "Added: $NOW" - }, + "patches": {"build [0-9a-f]{40}": "build $BUILD_HASH"}, "expected": [ 0, [ "build $BUILD_HASH", "Coverage: [ 67%] 2/3 (fail)", - "Added: $NOW", + "Added: $DATE", "Config: build_type: Debug,", " flag: on,", " os: qnx", diff --git a/apps/tests/main-set/014-show/163-show-file-in-build.json b/apps/tests/main-set/014-show/163-show-file-in-build.json index 9377208d..2b712d96 100644 --- a/apps/tests/main-set/014-show/163-show-file-in-build.json +++ b/apps/tests/main-set/014-show/163-show-file-in-build.json @@ -1,4 +1,5 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show $BUILD:src/main.cc", "expected": [ 0, diff --git a/apps/tests/main-set/014-show/163-show-file-in-files.json b/apps/tests/main-set/014-show/163-show-file-in-files.json index 8eaaff86..44dc757e 100644 --- a/apps/tests/main-set/014-show/163-show-file-in-files.json +++ b/apps/tests/main-set/014-show/163-show-file-in-files.json @@ -1,9 +1,7 @@ { + "$schema": "../../../../build/downloads/runner-schema.json", "args": "show $FILES:src/main.cc", - "patches": { - "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1", - "Added: .*": "Added: $NOW" - }, + "patches": {"build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1"}, "expected": [ 0, [ diff --git a/apps/tests/main-set/015-filters-A/01-strip-excludes.json b/apps/tests/main-set/015-filters-A/01-strip-excludes.json new file mode 100644 index 00000000..6babc0c9 --- /dev/null +++ b/apps/tests/main-set/015-filters-A/01-strip-excludes.json @@ -0,0 +1,68 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "report -f strip-excludes '$DATA/extensions/report-strip.json'", + "post": ["show HEAD:main.cc"], + "patches": { + "\u001b\\[31m\\[main [0-9a-fA-F]+\\]\u001b\\[m (.*)": "\u001b[31m[main $REPORT]\u001b[m \\1", + " \u001b\\[2;37mcontains [0-9a-fA-F]+:\u001b\\[m (.+)": " \u001b[2;37mcontains $BUILD:\u001b[m \\1" + }, + "expected": [ + 0, + [ + "\u001b[31m[main $REPORT]\u001b[m Commit \"extensions\"", + " one file, \u001b[31m 33%\u001b[m (5/15, -10), Fn\u001b[31m 40%\u001b[m (2/5)", + " \u001b[2;37mbased on\u001b[m \u001b[2;33m$HEAD@main\u001b[m", + " parent $PARENT", + " \u001b[2;37mcontains $BUILD:\u001b[m \u001b[31m 33%\u001b[m", + "", + "file main.cc", + "blob 8413b254a0751500a3f50ecb1dd93ae4fbf12658", + "functions 2efee5e14da1591abe9e2af91e246c6e38199117", + "lines 6277d9678915703ea7af2326a3e214fc053e8cb6", + "stats 40 15 5, 5 2, 0 0", + "", + "+-----------+--------------+---------+--------------+---------+----------+---------+------------+", + "| Name | % Funcs | Missing | % Lines | Visited | Relevant | Missing | Line count |", + "+-----------+--------------+---------+--------------+---------+----------+---------+------------+", + "| main.cc | 40.00 +40.00 | 3 +3 | 33.33 +33.33 | 5 +5 | 15 +15 | 10 +10 | 40 +40 |", + "+-----------+--------------+---------+--------------+---------+----------+---------+------------+", + "", + " 1 | | module;", + " 2 | | import std;", + " 3 | | ", + " 0x | partial_function_exclusion1", + " 4 | 0x | -void partial_function_exclusion() /*after*/ {", + " 5 | 0x | - line1();", + " 6 | 0x | -// GCOV_EXCL_START[OS]", + " 7 | 0x | - line2();", + " 8 | 0x | - line3();", + " 9 | 0x | - line4();", + " 10 | 0x | -}", + " 11 | | ", + " 12 | | // GCOV_EXCL_STOP", + " 13 | | ", + " 0x | sep1", + " 14 | 4x | +void sep1() {}", + " 15 | | // GCOV_EXCL_START", + " 16 | | ", + " 0x | partial_function_exclusion2", + " 17 | | void partial_function_exclusion1() /*before*/ {", + " 18 | | line1();", + " 19 | | line2();", + " 20 | | // GCOV_EXCL_STOP", + " 21 | 0x | - line3();", + " 22 | 0x | - line4();", + " 23 | 0x | -}", + " 24 | | ", + " 1x | sep2", + " 25 | 5x | +void sep2() {}", + " 26 | | ", + "\n" + ], + "strip-excludes: excluded 10 lines, 1 function\n" + ], + "prepare": [ + "unpack $DATA/extensions.tar $TMP", + "cd '$TMP/extensions'" + ] +} diff --git a/apps/tests/main-set/015-filters-A/02-strip-excludes-osOS.json b/apps/tests/main-set/015-filters-A/02-strip-excludes-osOS.json new file mode 100644 index 00000000..7672a1fd --- /dev/null +++ b/apps/tests/main-set/015-filters-A/02-strip-excludes-osOS.json @@ -0,0 +1,64 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "report -f strip-excludes '$DATA/extensions/report-strip.json' -- --os OS", + "post": ["show HEAD:main.cc"], + "expected": [ + 0, + [ + "\u001b[31m[main $REPORT]\u001b[m Commit \"extensions\"", + " one file, \u001b[31m 50%\u001b[m (5/10, -5), Fn\u001b[31m 40%\u001b[m (2/5)", + " \u001b[2;37mbased on\u001b[m \u001b[2;33m$HEAD@main\u001b[m", + " parent $PARENT", + " \u001b[2;37mcontains $BUILD:\u001b[m \u001b[31m 50%\u001b[m", + "", + "file main.cc", + "blob 8413b254a0751500a3f50ecb1dd93ae4fbf12658", + "functions 2efee5e14da1591abe9e2af91e246c6e38199117", + "lines b0815db9957ba028c131708a2bb7df42d5084aaf", + "stats 40 10 5, 5 2, 0 0", + "", + "+-----------+--------------+---------+--------------+---------+----------+---------+------------+", + "| Name | % Funcs | Missing | % Lines | Visited | Relevant | Missing | Line count |", + "+-----------+--------------+---------+--------------+---------+----------+---------+------------+", + "| main.cc | 40.00 +40.00 | 3 +3 | 50.00 +50.00 | 5 +5 | 10 +10 | 5 +5 | 40 +40 |", + "+-----------+--------------+---------+--------------+---------+----------+---------+------------+", + "", + " 1 | | module;", + " 2 | | import std;", + " 3 | | ", + " 0x | partial_function_exclusion1", + " 4 | 0x | -void partial_function_exclusion() /*after*/ {", + " 5 | 0x | - line1();", + " 6 | | // GCOV_EXCL_START[OS]", + " 7 | | line2();", + " 8 | | line3();", + " 9 | | line4();", + " 10 | | }", + " 11 | | ", + " 12 | | // GCOV_EXCL_STOP", + " 13 | | ", + " 0x | sep1", + " 14 | 4x | +void sep1() {}", + " 15 | | // GCOV_EXCL_START", + " 16 | | ", + " 0x | partial_function_exclusion2", + " 17 | | void partial_function_exclusion1() /*before*/ {", + " 18 | | line1();", + " 19 | | line2();", + " 20 | | // GCOV_EXCL_STOP", + " 21 | 0x | - line3();", + " 22 | 0x | - line4();", + " 23 | 0x | -}", + " 24 | | ", + " 1x | sep2", + " 25 | 5x | +void sep2() {}", + " 26 | | ", + "\n" + ], + "strip-excludes: excluded 15 lines, 1 function\n" + ], + "prepare": [ + "unpack $DATA/extensions.tar $TMP", + "cd '$TMP/extensions'" + ] +} diff --git a/apps/tests/main-set/015-filters-A/03-A-strip-excludes-llvm.json b/apps/tests/main-set/015-filters-A/03-A-strip-excludes-llvm.json new file mode 100644 index 00000000..ea9130ec --- /dev/null +++ b/apps/tests/main-set/015-filters-A/03-A-strip-excludes-llvm.json @@ -0,0 +1,44 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "report -f strip-excludes '$DATA/extensions/report-llvm.json' -- --os OS --compiler llvm", + "post": "show", + "patches": { + "report [0-9a-f]{40}": "report $OID", + "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1" + }, + "expected": [ + 0, + [ + "\u001b[31m[main $REPORT]\u001b[m Commit \"extensions\"", + " 2 files, \u001b[31m 0%\u001b[m (0/0), Fn\u001b[31m 0%\u001b[m (0/2)", + " \u001b[2;37mbased on\u001b[m \u001b[2;33m$HEAD@main\u001b[m", + " parent $PARENT", + " \u001b[2;37mcontains $BUILD:\u001b[m \u001b[31m 0%\u001b[m", + "", + "report $OID", + "commit 98d4978ecbddf677c1e6bf5a40da84bd0b337340", + "build $BUILD_HASH [ 0%]", + "GitBranch: main", + "Coverage: [ 0%] 0/0 (fail)", + "Functions: [ 0%] 0/2 (fail)", + "Author: Johnny Appleseed ", + "Added: $DATE", + "", + " Commit \"extensions\"", + "", + "+------------+---------+---------+---------+---------+----------+------------+", + "| Name | % Funcs | Missing | % Lines | Visited | Relevant | Line count |", + "+------------+---------+---------+---------+---------+----------+------------+", + "| clang.cc | 0.00 | 1 +1 | 0.00 | 0 | 0 | 8 +8 |", + "| llvm.cc | 0.00 | 1 +1 | 0.00 | 0 | 0 | 8 +8 |", + "+------------+---------+---------+---------+---------+----------+------------+", + "| > TOTAL | 0.00 | 2 +2 | 0.00 | 0 | 0 | 16 +16 |", + "+------------+---------+---------+---------+---------+----------+------------+\n" + ], + "strip-excludes: excluded 8 lines\n" + ], + "prepare": [ + "unpack $DATA/extensions.tar $TMP", + "cd '$TMP/extensions'" + ] +} diff --git a/apps/tests/main-set/015-filters-A/03-B-strip-excludes-clang.json b/apps/tests/main-set/015-filters-A/03-B-strip-excludes-clang.json new file mode 100644 index 00000000..5b68375f --- /dev/null +++ b/apps/tests/main-set/015-filters-A/03-B-strip-excludes-clang.json @@ -0,0 +1,44 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "report -f strip-excludes '$DATA/extensions/report-llvm.json' -- --os OS --compiler clang", + "post": "show", + "patches": { + "report [0-9a-f]{40}": "report $OID", + "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1" + }, + "expected": [ + 0, + [ + "\u001b[31m[main $REPORT]\u001b[m Commit \"extensions\"", + " 2 files, \u001b[31m 0%\u001b[m (0/0), Fn\u001b[31m 0%\u001b[m (0/2)", + " \u001b[2;37mbased on\u001b[m \u001b[2;33m$HEAD@main\u001b[m", + " parent $PARENT", + " \u001b[2;37mcontains $BUILD:\u001b[m \u001b[31m 0%\u001b[m", + "", + "report $OID", + "commit 98d4978ecbddf677c1e6bf5a40da84bd0b337340", + "build $BUILD_HASH [ 0%]", + "GitBranch: main", + "Coverage: [ 0%] 0/0 (fail)", + "Functions: [ 0%] 0/2 (fail)", + "Author: Johnny Appleseed ", + "Added: $DATE", + "", + " Commit \"extensions\"", + "", + "+------------+---------+---------+---------+---------+----------+------------+", + "| Name | % Funcs | Missing | % Lines | Visited | Relevant | Line count |", + "+------------+---------+---------+---------+---------+----------+------------+", + "| clang.cc | 0.00 | 1 +1 | 0.00 | 0 | 0 | 8 +8 |", + "| llvm.cc | 0.00 | 1 +1 | 0.00 | 0 | 0 | 8 +8 |", + "+------------+---------+---------+---------+---------+----------+------------+", + "| > TOTAL | 0.00 | 2 +2 | 0.00 | 0 | 0 | 16 +16 |", + "+------------+---------+---------+---------+---------+----------+------------+\n" + ], + "strip-excludes: excluded 8 lines\n" + ], + "prepare": [ + "unpack $DATA/extensions.tar $TMP", + "cd '$TMP/extensions'" + ] +} diff --git a/apps/tests/main-set/015-filters-A/04-strip-excludes-compilerA-OS.json b/apps/tests/main-set/015-filters-A/04-strip-excludes-compilerA-OS.json new file mode 100644 index 00000000..91925153 --- /dev/null +++ b/apps/tests/main-set/015-filters-A/04-strip-excludes-compilerA-OS.json @@ -0,0 +1,64 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "report -f strip-excludes '$DATA/extensions/report-strip.json' -- --os OS --compiler compilerA", + "post": ["show HEAD:main.cc"], + "expected": [ + 0, + [ + "\u001b[31m[main $REPORT]\u001b[m Commit \"extensions\"", + " one file, \u001b[31m 29%\u001b[m (2/7, -5), Fn\u001b[31m 25%\u001b[m (1/4)", + " \u001b[2;37mbased on\u001b[m \u001b[2;33m$HEAD@main\u001b[m", + " parent $PARENT", + " \u001b[2;37mcontains $BUILD:\u001b[m \u001b[31m 29%\u001b[m", + "", + "file main.cc", + "blob 8413b254a0751500a3f50ecb1dd93ae4fbf12658", + "functions 14f0e1233783a10d5073ddcd060500201cf35c29", + "lines 901e3f666456f35c146cbd93cb9b95b2ad1fa5c0", + "stats 40 7 2, 4 1, 0 0", + "", + "+-----------+--------------+---------+--------------+---------+----------+---------+------------+", + "| Name | % Funcs | Missing | % Lines | Visited | Relevant | Missing | Line count |", + "+-----------+--------------+---------+--------------+---------+----------+---------+------------+", + "| main.cc | 25.00 +25.00 | 3 +3 | 28.57 +28.57 | 2 +2 | 7 +7 | 5 +5 | 40 +40 |", + "+-----------+--------------+---------+--------------+---------+----------+---------+------------+", + "", + " 1 | | module;", + " 2 | | import std;", + " 3 | | ", + " 0x | partial_function_exclusion1", + " 4 | 0x | -void partial_function_exclusion() /*after*/ {", + " 5 | 0x | - line1();", + " 6 | | // GCOV_EXCL_START[OS]", + " 7 | | line2();", + " 8 | | line3();", + " 9 | | line4();", + " 10 | | }", + " 11 | | ", + " 12 | | // GCOV_EXCL_STOP", + " 13 | | ", + " 0x | sep1", + " 14 | 4x | +void sep1() {}", + " 15 | | // GCOV_EXCL_START", + " 16 | | ", + " 0x | partial_function_exclusion2", + " 17 | | void partial_function_exclusion1() /*before*/ {", + " 18 | | line1();", + " 19 | | line2();", + " 20 | | // GCOV_EXCL_STOP", + " 21 | 0x | - line3();", + " 22 | 0x | - line4();", + " 23 | 0x | -}", + " 24 | | ", + " 1x | sep2", + " 25 | 5x | +void sep2() {}", + " 26 | | ", + "\n" + ], + "strip-excludes: excluded 18 lines, 2 functions\n" + ], + "prepare": [ + "unpack $DATA/extensions.tar $TMP", + "cd '$TMP/extensions'" + ] +} diff --git a/apps/tests/main-set/015-filters-A/05-strip-excludes-multi.json b/apps/tests/main-set/015-filters-A/05-strip-excludes-multi.json new file mode 100644 index 00000000..68455d31 --- /dev/null +++ b/apps/tests/main-set/015-filters-A/05-strip-excludes-multi.json @@ -0,0 +1,54 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "report -f strip-excludes '$DATA/extensions/report-multi.json'", + "post": "show", + "patches": { + "report [0-9a-f]{40}": "report $OID", + "build [0-9a-f]{40} (.*)": "build $BUILD_HASH \\1" + }, + "expected": [ + 0, + [ + "\u001b[31m[main $REPORT]\u001b[m Commit \"extensions\"", + " 4 files, \u001b[32m100%\u001b[m (9/9), Fn\u001b[32m100%\u001b[m (4/4)", + " \u001b[2;37mbased on\u001b[m \u001b[2;33m$HEAD@main\u001b[m", + " parent $PARENT", + " \u001b[2;37mcontains $BUILD:\u001b[m \u001b[32m100%\u001b[m", + "", + "report $OID", + "commit 98d4978ecbddf677c1e6bf5a40da84bd0b337340", + "build $BUILD_HASH [100%]", + "GitBranch: main", + "Coverage: [100%] 9/9 (pass)", + "Functions: [100%] 4/4 (pass)", + "Author: Johnny Appleseed ", + "Added: $DATE", + "", + " Commit \"extensions\"", + "", + "+--------------------+----------------+----------------+---------+----------+------------+", + "| Name | % Funcs | % Lines | Visited | Relevant | Line count |", + "+--------------------+----------------+----------------+---------+----------+------------+", + "| no-exclusions.cc | 100.00 +100.00 | 100.00 +100.00 | 6 +6 | 6 +6 | 6 +6 |", + "| start-end.cc | 100.00 +100.00 | 100.00 +100.00 | 1 +1 | 1 +1 | 8 +8 |", + "| start-start.cc | 100.00 +100.00 | 100.00 +100.00 | 1 +1 | 1 +1 | 8 +8 |", + "| start.cc | 100.00 +100.00 | 100.00 +100.00 | 1 +1 | 1 +1 | 7 +7 |", + "+--------------------+----------------+----------------+---------+----------+------------+", + "| > TOTAL | 100.00 +100.00 | 100.00 +100.00 | 9 +9 | 9 +9 | 29 +29 |", + "+--------------------+----------------+----------------+---------+----------+------------+\n" + ], + [ + "\u001b[1;37m$TMP/extensions/start-start.cc:7:8:\u001b[m \u001b[1;35mwarning:\u001b[m double start: found GCOV_EXCL_START", + "\u001b[1;37m$TMP/extensions/start-start.cc:2:8:\u001b[m \u001b[1;36mnote:\u001b[m see previous start", + "\u001b[1;37m$TMP/extensions/start-start.cc:2:8:\u001b[m \u001b[1;35mwarning:\u001b[m GCOV_EXCL_START not matched with GCOV_EXCL_STOP", + "\u001b[1;37m$TMP/extensions/start-end.cc:7:8:\u001b[m \u001b[1;35mwarning:\u001b[m found GCOV_EXCL_END; did you mean GCOV_EXCL_STOP?", + "\u001b[1;37m$TMP/extensions/start-end.cc:2:8:\u001b[m \u001b[1;35mwarning:\u001b[m GCOV_EXCL_START not matched with GCOV_EXCL_STOP", + "\u001b[1;37m$TMP/extensions/start.cc:2:8:\u001b[m \u001b[1;35mwarning:\u001b[m GCOV_EXCL_START not matched with GCOV_EXCL_STOP", + "strip-excludes: excluded 15 lines\n" + ] + ], + "prepare": [ + "unpack $DATA/extensions.tar $TMP", + "cd '$TMP/extensions'" + ] +} diff --git a/apps/tests/main-set/015-filters-A/06-strip-excludes-no-such.json b/apps/tests/main-set/015-filters-A/06-strip-excludes-no-such.json new file mode 100644 index 00000000..fd469d56 --- /dev/null +++ b/apps/tests/main-set/015-filters-A/06-strip-excludes-no-such.json @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "report -f strip-excludes '$DATA/extensions/report-no-such.json'", + "expected": [ + 1, + "", + "cov report: error: cannot find no-such-file.cc\n" + ], + "prepare": [ + "unpack $DATA/extensions.tar $TMP", + "cd '$TMP/extensions'" + ] +} diff --git a/apps/tests/main-set/015-filters-A/07-A-strip-excludes-broken.json b/apps/tests/main-set/015-filters-A/07-A-strip-excludes-broken.json new file mode 100644 index 00000000..139b6939 --- /dev/null +++ b/apps/tests/main-set/015-filters-A/07-A-strip-excludes-broken.json @@ -0,0 +1,16 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "report -f strip-excludes '$DATA/extensions/report-broken-A.json'", + "expected": [ + 2, + "", + [ + "cov report: /files[0]/name: undefined", + "cov report: error: there were issues with $DATA/extensions/report-broken-A.json processed by strip-excludes filter\n" + ] + ], + "prepare": [ + "unpack $DATA/extensions.tar $TMP", + "cd '$TMP/extensions'" + ] +} diff --git a/apps/tests/main-set/015-filters-A/07-B-strip-excludes-broken.json b/apps/tests/main-set/015-filters-A/07-B-strip-excludes-broken.json new file mode 100644 index 00000000..79cf4c9c --- /dev/null +++ b/apps/tests/main-set/015-filters-A/07-B-strip-excludes-broken.json @@ -0,0 +1,16 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "report -f strip-excludes '$DATA/extensions/report-broken-B.json'", + "expected": [ + 2, + "", + [ + "cov report: /files[0]/digest (main.cc): undefined", + "cov report: error: there were issues with $DATA/extensions/report-broken-B.json processed by strip-excludes filter\n" + ] + ], + "prepare": [ + "unpack $DATA/extensions.tar $TMP", + "cd '$TMP/extensions'" + ] +} diff --git a/apps/tests/main-set/015-filters-A/07-C-strip-excludes-broken.json b/apps/tests/main-set/015-filters-A/07-C-strip-excludes-broken.json new file mode 100644 index 00000000..8bbc6b3e --- /dev/null +++ b/apps/tests/main-set/015-filters-A/07-C-strip-excludes-broken.json @@ -0,0 +1,16 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "report -f strip-excludes '$DATA/extensions/report-broken-C.json'", + "expected": [ + 2, + "", + [ + "cov report: /files[0]/line_coverage (main.cc): undefined", + "cov report: error: there were issues with $DATA/extensions/report-broken-C.json processed by strip-excludes filter\n" + ] + ], + "prepare": [ + "unpack $DATA/extensions.tar $TMP", + "cd '$TMP/extensions'" + ] +} diff --git a/apps/tests/main-set/015-filters-A/07-C-strip-excludes-brokenjson b/apps/tests/main-set/015-filters-A/07-C-strip-excludes-brokenjson new file mode 100644 index 00000000..c2e08758 --- /dev/null +++ b/apps/tests/main-set/015-filters-A/07-C-strip-excludes-brokenjson @@ -0,0 +1,16 @@ +{ + "$schema": "../../runner-schema.json", + "args": "report -f strip-excludes '$DATA/extensions/report-broken-C.json'", + "expected": [ + 2, + "", + [ + "cov report: /files[0]/line_coverage (main.cc): undefined", + "cov report: error: there were issues with $DATA/extensions/report-broken-C.json processed by strip-excludes filter\n" + ] + ], + "prepare": [ + "unpack $DATA/extensions.tar $TMP", + "cd '$TMP/extensions'" + ] +} diff --git a/apps/tests/main-set/016-collect/01-collect-help.json b/apps/tests/main-set/016-collect/01-collect-help.json new file mode 100644 index 00000000..1a76d1a0 --- /dev/null +++ b/apps/tests/main-set/016-collect/01-collect-help.json @@ -0,0 +1,18 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "collect -h", + "expected": [ + 0, + [ + "usage: cov-collect [-h] [-c ARG] [--clean] [--observe]", + "", + "optional arguments:", + " -h, --help show this help message and exit", + " -c, --config ARG ", + " --clean ", + " --observe \n" + ], + "" + ], + "prepare": ["cd '$TMP'"] +} diff --git a/apps/tests/main-set/016-collect/02-collect-unborn.json b/apps/tests/main-set/016-collect/02-collect-unborn.json new file mode 100644 index 00000000..d0f4ceac --- /dev/null +++ b/apps/tests/main-set/016-collect/02-collect-unborn.json @@ -0,0 +1,15 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "collect", + "expected": [ + 1, + "", + "[git] called on unborn branch\n" + ], + "prepare": [ + "mkdirs '$TMP/unborn'", + "cd '$TMP/unborn'", + "git init", + "cov init" + ] +} diff --git a/apps/tests/main-set/016-collect/03-collect-no-HEAD.json b/apps/tests/main-set/016-collect/03-collect-no-HEAD.json new file mode 100644 index 00000000..dd6fb7ac --- /dev/null +++ b/apps/tests/main-set/016-collect/03-collect-no-HEAD.json @@ -0,0 +1,18 @@ +{ + "$schema": "../../runner-schema.json", + "disabled": true, + "args": "collect", + "expected": [ + 1, + "", + "[git] called on unborn branch\n" + ], + "prepare": [ + "mkdirs '$TMP/unborn'", + "cd '$TMP/unborn'", + "git init", + "cov init", + "mkdirs .git/refs/heads", + "touch .git/refs/heads/main 'ref: refs/heads/main\n'" + ] +} diff --git a/apps/tests/main-set/016-collect/04-collect-too-many-args.json b/apps/tests/main-set/016-collect/04-collect-too-many-args.json new file mode 100644 index 00000000..57b1ed4d --- /dev/null +++ b/apps/tests/main-set/016-collect/04-collect-too-many-args.json @@ -0,0 +1,19 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "collect ctest -C Debug .", + "expected": [ + 2, + "", + [ + "usage: cov-collect [-h] [-c ARG] [--clean] [--observe]", + "cov-collect: error: unrecognized argument: ctest\n" + ] + ], + "prepare": [ + "mkdirs '$TMP/observe'", + "cd '$TMP/observe'", + "git init", + "cov init", + "git commit -m 'initial' --allow-empty" + ] +} diff --git a/apps/tests/main-set/016-collect/05-collect-too-little-args.json b/apps/tests/main-set/016-collect/05-collect-too-little-args.json new file mode 100644 index 00000000..2b4924fd --- /dev/null +++ b/apps/tests/main-set/016-collect/05-collect-too-little-args.json @@ -0,0 +1,19 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "collect --observe", + "expected": [ + 2, + "", + [ + "usage: cov-collect [-h] [-c ARG] [--clean] [--observe]", + "cov-collect: error: argument --observe: missing tool name and arguments\n" + ] + ], + "prepare": [ + "mkdirs '$TMP/observe'", + "cd '$TMP/observe'", + "git init", + "cov init", + "git commit -m 'initial' --allow-empty" + ] +} diff --git a/apps/tests/main-set/016-collect/06-collect-observe.json b/apps/tests/main-set/016-collect/06-collect-observe.json new file mode 100644 index 00000000..ef90ceb3 --- /dev/null +++ b/apps/tests/main-set/016-collect/06-collect-observe.json @@ -0,0 +1,23 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "linear": true, + "args": "collect --observe ctest -C Debug .", + "check": { + "stderr": "end" + }, + "expected": [ + 0, + "Test project $TMP/observe\n", + "No tests were found!!!\n" + ], + "prepare": [ + "mkdirs '$TMP/observe'", + "cd '$TMP/observe'", + "git init", + "cov init", + "git commit -m 'initial' --allow-empty", + "mock hal9000-lcars-GLaDOS-g++-17 g++", + "mock hal9000-lcars-GLaDOS-gcov-17 gcov", + "generate '$DATA/covcollect/ini' .covcollect 'COMPILER=$TMP/mocks/g++'" + ] +} diff --git a/apps/tests/main-set/016-collect/07-collect-clang.json b/apps/tests/main-set/016-collect/07-collect-clang.json new file mode 100644 index 00000000..9bbe1905 --- /dev/null +++ b/apps/tests/main-set/016-collect/07-collect-clang.json @@ -0,0 +1,38 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "collect", + "patches": { + "\\[([^\\]]+)\\] [0-9.]+ s": "[\\1] #.## s", + "(.+)\\.exe": "\\1" + }, + "expected": [ + 0, + "", + [ + "[toolkit] LLVM 17.0.1 (from $TMP/clang/.covcollect)", + " [merge] $TMP/mocks/llvm-profdata-17", + " [cov] $TMP/mocks/llvm-cov-17", + " [data] $TMP/clang/build/llvm", + "[preprocess] start...", + "[preprocess] #.## s", + "[report] start...", + "[0/0] finished ", + "[report] #.## s", + "[files] 0", + "[lines] 0 / 0", + "[functions] 0 / 0", + "[i/o] $TMP/clang/build/cov-collect.json\n" + ] + ], + "prepare": [ + "mkdirs '$TMP/clang'", + "cd '$TMP/clang'", + "git init", + "cov init", + "git commit -m 'initial' --allow-empty", + "mock clang++-17 clang++-17", + "mock llvm-cov-17 llvm-cov-17", + "mock llvm-profdata-17 llvm-profdata-17", + "generate '$DATA/covcollect/ini' .covcollect 'COMPILER=$TMP/mocks/clang++-17'" + ] +} diff --git a/apps/tests/main-set/016-collect/08-collect-gcc.json b/apps/tests/main-set/016-collect/08-collect-gcc.json new file mode 100644 index 00000000..2329f412 --- /dev/null +++ b/apps/tests/main-set/016-collect/08-collect-gcc.json @@ -0,0 +1,34 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "collect", + "patches": { + "\\[([^\\]]+)\\] [0-9.]+ s": "[\\1] #.## s", + "(.+)\\.exe": "\\1" + }, + "expected": [ + 0, + "", + [ + "[toolkit] GNU 17.0.1 (from $TMP/collect/.covcollect)", + " [gcov] $TMP/mocks/gcov-17.0.1", + "[report] start...", + "[0/0] finished ", + "[report] #.## s", + "[files] 0", + "[lines] 0 / 0", + "[functions] 0 / 0", + "[i/o] $TMP/collect/build/cov-collect.json\n" + ] + ], + "prepare": [ + "mkdirs '$TMP/collect'", + "cd '$TMP/collect'", + "git init", + "cov init", + "git commit -m 'initial' --allow-empty", + "mock hal9000-lcars-GLaDOS-g++-17 g++", + "mock hal9000-lcars-GLaDOS-gcov-17 gcov", + "mock hal9000-lcars-GLaDOS-gcov-17 gcov-17.0.1", + "generate '$DATA/covcollect/ini' .covcollect 'COMPILER=$TMP/mocks/g++'" + ] +} diff --git a/apps/tests/main-set/016-collect/09-collect-gcc-triple.json b/apps/tests/main-set/016-collect/09-collect-gcc-triple.json new file mode 100644 index 00000000..66a30f64 --- /dev/null +++ b/apps/tests/main-set/016-collect/09-collect-gcc-triple.json @@ -0,0 +1,19 @@ +{ + "$schema": "../../runner-schema.json", + "args": "collect", + "disabled": true, + "patches": { + "\\[([^]]+)\\] [0-9.]+ s": "[\\1] #.## s" + }, + "expected": null, + "prepare": [ + "mkdirs '$TMP/collect'", + "cd '$TMP/collect'", + "git init", + "cov init", + "git commit -m 'initial' --allow-empty", + "mock hal9000-lcars-GLaDOS-g++-17 hal9000-lcars-GLaDOS-g++-17", + "mock hal9000-lcars-GLaDOS-gcov-17 hal9000-lcars-GLaDOS-gcov", + "generate '$DATA/covcollect/ini' .covcollect 'COMPILER=$TMP/mocks/hal9000-lcars-GLaDOS-g++-17'" + ] +} diff --git a/apps/tests/main-set/016-collect/10-collect-clean.json b/apps/tests/main-set/016-collect/10-collect-clean.json new file mode 100644 index 00000000..0dd99c30 --- /dev/null +++ b/apps/tests/main-set/016-collect/10-collect-clean.json @@ -0,0 +1,15 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "collect --clean", + "expected": [0, "", ""], + "prepare": [ + "mkdirs '$TMP/collect'", + "cd '$TMP/collect'", + "git init", + "cov init", + "git commit -m 'initial' --allow-empty", + "mock hal9000-lcars-GLaDOS-g++-17 g++", + "mock hal9000-lcars-GLaDOS-gcov-17 gcov", + "generate '$DATA/covcollect/ini' .covcollect 'COMPILER=$TMP/mocks/g++'" + ] +} diff --git a/apps/tests/main-set/016-collect/11-collect-no-output.json b/apps/tests/main-set/016-collect/11-collect-no-output.json new file mode 100644 index 00000000..31ff8daa --- /dev/null +++ b/apps/tests/main-set/016-collect/11-collect-no-output.json @@ -0,0 +1,34 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "collect", + "patches": { + "\\[([^\\]]+)\\] [0-9.]+ s": "[\\1] #.## s", + "(.+)\\.exe": "\\1" + }, + "expected": [ + 1, + "", + [ + "[toolkit] GNU 17.0.1 (from $TMP/dir/.covcollect)", + " [gcov] $TMP/mocks/gcov", + "[report] start...", + "[0/0] finished ", + "[report] #.## s", + "[files] 0", + "[lines] 0 / 0", + "[functions] 0 / 0", + "[i/o] cannot open $TMP/dir/build/cov-collect.json\n" + ] + ], + "prepare": [ + "mkdirs '$TMP/dir'", + "cd '$TMP/dir'", + "git init", + "cov init", + "git commit -m 'initial' --allow-empty", + "mkdirs build/cov-collect.json", + "mock hal9000-lcars-GLaDOS-g++-17 g++", + "mock hal9000-lcars-GLaDOS-gcov-17 gcov", + "generate '$DATA/covcollect/ini' .covcollect 'COMPILER=$TMP/mocks/g++'" + ] +} diff --git a/apps/tests/main-set/016-collect/12-collect-msvc.json b/apps/tests/main-set/016-collect/12-collect-msvc.json new file mode 100644 index 00000000..84c01c27 --- /dev/null +++ b/apps/tests/main-set/016-collect/12-collect-msvc.json @@ -0,0 +1,38 @@ +{ + "$schema": "../../../../build/downloads/runner-schema.json", + "args": "collect", + "patches": { + "\\[([^\\]]+)\\] [0-9.]+ s": "[\\1] #.## s", + " \\[occ\\] C:\\\\+Program Files(:? \\(x86\\))?\\\\+OpenCppCoverage\\\\+OpenCppCoverage\\.exe": " [occ] $TMP/mocks/OpenCppCoverage", + " \\[occ\\] \\$TMP\\/+mocks\\/+OpenCppCoverage\\.exe": " [occ] $TMP/mocks/OpenCppCoverage", + " \\[outfile\\] \\\\OpenCppCoverage\\\\cobertura\\.xml": " [outfile] /OpenCppCoverage/cobertura.xml" + }, + "expected": [ + 0, + "", + [ + "[toolkit] MSVC 17.01.74656 (from $TMP/dir/.covcollect)", + " [occ] $TMP/mocks/OpenCppCoverage", + " [outfile] /OpenCppCoverage/cobertura.xml", + "[report] start...", + "[report] #.## s", + "[files] 1", + "[lines] 2 / 2", + "[functions] 0 / 0", + "[i/o] $TMP/dir/build/cov-collect.json\n" + ] + ], + "prepare": [ + "mkdirs '$TMP/dir'", + "cd '$TMP/dir'", + "git init", + "cov init", + "git commit -m 'initial' --allow-empty", + "mkdirs build/OpenCppCoverage", + "touch src/main.cc", + "mock cl.exe cl.exe", + "mock OpenCppCoverage OpenCppCoverage", + "generate '$DATA/covcollect/ini' .covcollect 'COMPILER=$TMP/mocks/cl.exe'", + "generate '$DATA/covcollect/OpenCppCoverage.xml' build/OpenCppCoverage/cobertura.xml 'SOURCES=$TMP/dir'" + ] +} diff --git a/apps/tests/messages-pl/001-cov/001-version.json b/apps/tests/messages-pl/001-cov/001-version.json index 13e8c814..45fc2efc 100644 --- a/apps/tests/messages-pl/001-cov/001-version.json +++ b/apps/tests/messages-pl/001-cov/001-version.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "--version", "expected": [ diff --git a/apps/tests/messages-pl/001-cov/002-help.json b/apps/tests/messages-pl/001-cov/002-help.json index 8c6d7fac..94d6c405 100644 --- a/apps/tests/messages-pl/001-cov/002-help.json +++ b/apps/tests/messages-pl/001-cov/002-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "--help", "expected": [ diff --git a/apps/tests/messages-pl/001-cov/003-unknown-option.json b/apps/tests/messages-pl/001-cov/003-unknown-option.json index 21ece8bd..e391e7d8 100644 --- a/apps/tests/messages-pl/001-cov/003-unknown-option.json +++ b/apps/tests/messages-pl/001-cov/003-unknown-option.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "--versions", "expected": [ diff --git a/apps/tests/messages-pl/001-cov/004-alias.json b/apps/tests/messages-pl/001-cov/004-alias.json index 7d652362..28e26eac 100644 --- a/apps/tests/messages-pl/001-cov/004-alias.json +++ b/apps/tests/messages-pl/001-cov/004-alias.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "-C recurse first -D", "expected": [ diff --git a/apps/tests/messages-pl/001-cov/005-alias-short-help.json b/apps/tests/messages-pl/001-cov/005-alias-short-help.json index 400c1880..526be30f 100644 --- a/apps/tests/messages-pl/001-cov/005-alias-short-help.json +++ b/apps/tests/messages-pl/001-cov/005-alias-short-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "-C recurse second -h", "expected": [ diff --git a/apps/tests/messages-pl/001-cov/007-alias-to-nothing.json b/apps/tests/messages-pl/001-cov/007-alias-to-nothing.json index 43851181..6327f39f 100644 --- a/apps/tests/messages-pl/001-cov/007-alias-to-nothing.json +++ b/apps/tests/messages-pl/001-cov/007-alias-to-nothing.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "-C bad-recurse start-bad", "expected": [ diff --git a/apps/tests/messages-pl/001-cov/008-bad-alias.json b/apps/tests/messages-pl/001-cov/008-bad-alias.json index 729b479a..cd856c50 100644 --- a/apps/tests/messages-pl/001-cov/008-bad-alias.json +++ b/apps/tests/messages-pl/001-cov/008-bad-alias.json @@ -1,24 +1,14 @@ { + "$schema": "../../runner-schema.json", "args": "-C bad-recurse first -D", - "check": { - "stderr": "begin" - }, + "check": {"stderr": "begin"}, "expected": [ 1, "", [ "użycie: cov [-h] [-C ] []", "cov: „fourth” nie jest poleceniem cov", - "", - "typowe polecenia:", - " init tworzy nowe repozytorium cov", - " config pokazuje i/lub ustawia różne ustawienia", - " module definiuje grupy plików", - " report dołącza raport do repozytorium", - " remove usuwa określony raport z repozytorium", - " log drukuje listę raportów", - " show pokazuje konkretny raport", - " serve uruchamia lokalny serwer WWW raportów\n" + "\n" ] ], "prepare": [ diff --git a/apps/tests/messages-pl/001-cov/013-bad-alias-full-output.json b/apps/tests/messages-pl/001-cov/013-bad-alias-full-output.json index 40d5b57a..83211276 100644 --- a/apps/tests/messages-pl/001-cov/013-bad-alias-full-output.json +++ b/apps/tests/messages-pl/001-cov/013-bad-alias-full-output.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "definitely-not-a-tool", "expected": [ diff --git a/apps/tests/messages-pl/002-init/015-init-no-args.json b/apps/tests/messages-pl/002-init/015-init-no-args.json index 3e664961..31c8b6b7 100644 --- a/apps/tests/messages-pl/002-init/015-init-no-args.json +++ b/apps/tests/messages-pl/002-init/015-init-no-args.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "init", "expected": [ diff --git a/apps/tests/messages-pl/002-init/020-init-no-gitrepo.json b/apps/tests/messages-pl/002-init/020-init-no-gitrepo.json index 1ecc6a30..2c3c9b6f 100644 --- a/apps/tests/messages-pl/002-init/020-init-no-gitrepo.json +++ b/apps/tests/messages-pl/002-init/020-init-no-gitrepo.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "init --git-dir '$TMP/repo.git' '$TMP/repo.covdata'", "expected": [ diff --git a/apps/tests/messages-pl/002-init/021-init-no-args-no-cwd.json b/apps/tests/messages-pl/002-init/021-init-no-args-no-cwd.json index ec27f440..2030e7bf 100644 --- a/apps/tests/messages-pl/002-init/021-init-no-args-no-cwd.json +++ b/apps/tests/messages-pl/002-init/021-init-no-args-no-cwd.json @@ -1,9 +1,8 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "init", - "patches": { - "cov init: błąd: argument -C: .*": "cov init: błąd: argument -C: ERROR MESSAGE" - }, + "patches": {"cov init: błąd: argument -C: .*": "cov init: błąd: argument -C: ERROR MESSAGE"}, "expected": [ 2, "", diff --git a/apps/tests/messages-pl/002-init/023-init-no-force.json b/apps/tests/messages-pl/002-init/023-init-no-force.json index a9c8b8f5..21fb1877 100644 --- a/apps/tests/messages-pl/002-init/023-init-no-force.json +++ b/apps/tests/messages-pl/002-init/023-init-no-force.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "init", "expected": [ diff --git a/apps/tests/messages-pl/002-init/024-init-force.json b/apps/tests/messages-pl/002-init/024-init-force.json index 44e5ee91..32733c6b 100644 --- a/apps/tests/messages-pl/002-init/024-init-force.json +++ b/apps/tests/messages-pl/002-init/024-init-force.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "init --force", "expected": [ diff --git a/apps/tests/messages-pl/003-config/025-config-no-args.json b/apps/tests/messages-pl/003-config/025-config-no-args.json index 926890ff..90b171bd 100644 --- a/apps/tests/messages-pl/003-config/025-config-no-args.json +++ b/apps/tests/messages-pl/003-config/025-config-no-args.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "config", "expected": [ diff --git a/apps/tests/messages-pl/003-config/031-config-list-after-get.json b/apps/tests/messages-pl/003-config/031-config-list-after-get.json index 92861f2f..0264914a 100644 --- a/apps/tests/messages-pl/003-config/031-config-list-after-get.json +++ b/apps/tests/messages-pl/003-config/031-config-list-after-get.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "env": { "HOME": "$TMP/home", diff --git a/apps/tests/messages-pl/003-config/032-config-unset-after-list.json b/apps/tests/messages-pl/003-config/032-config-unset-after-list.json index 87d06ef0..6b9b64cc 100644 --- a/apps/tests/messages-pl/003-config/032-config-unset-after-list.json +++ b/apps/tests/messages-pl/003-config/032-config-unset-after-list.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "env": { "HOME": "$TMP/home", diff --git a/apps/tests/messages-pl/003-config/033-config-add-after-unset-all.json b/apps/tests/messages-pl/003-config/033-config-add-after-unset-all.json index 2358fa58..14a77207 100644 --- a/apps/tests/messages-pl/003-config/033-config-add-after-unset-all.json +++ b/apps/tests/messages-pl/003-config/033-config-add-after-unset-all.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "env": { "HOME": "$TMP/home", diff --git a/apps/tests/messages-pl/003-config/034-config-get-all-after-get.json b/apps/tests/messages-pl/003-config/034-config-get-all-after-get.json index 8bf0530c..34d73732 100644 --- a/apps/tests/messages-pl/003-config/034-config-get-all-after-get.json +++ b/apps/tests/messages-pl/003-config/034-config-get-all-after-get.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "env": { "HOME": "$TMP/home", diff --git a/apps/tests/messages-pl/003-config/035-config-list-local-no-cov.json b/apps/tests/messages-pl/003-config/035-config-list-local-no-cov.json index 6a2607c2..062d13b9 100644 --- a/apps/tests/messages-pl/003-config/035-config-list-local-no-cov.json +++ b/apps/tests/messages-pl/003-config/035-config-list-local-no-cov.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "config --local --list", "expected": [ diff --git a/apps/tests/messages-pl/003-config/036-config-add-one-arg.json b/apps/tests/messages-pl/003-config/036-config-add-one-arg.json index 0a611b6a..1bb897b4 100644 --- a/apps/tests/messages-pl/003-config/036-config-add-one-arg.json +++ b/apps/tests/messages-pl/003-config/036-config-add-one-arg.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "config --file $TMP/config --add group.key", "expected": [ diff --git a/apps/tests/messages-pl/003-config/037-config-unset-too-many.json b/apps/tests/messages-pl/003-config/037-config-unset-too-many.json index f0fe6832..62ffbb24 100644 --- a/apps/tests/messages-pl/003-config/037-config-unset-too-many.json +++ b/apps/tests/messages-pl/003-config/037-config-unset-too-many.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "config --file $TMP/config --unset group.key value", "expected": [ diff --git a/apps/tests/messages-pl/003-config/038-config-set-but-multivar.json b/apps/tests/messages-pl/003-config/038-config-set-but-multivar.json index 8f107f2e..d079933c 100644 --- a/apps/tests/messages-pl/003-config/038-config-set-but-multivar.json +++ b/apps/tests/messages-pl/003-config/038-config-set-but-multivar.json @@ -1,15 +1,11 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "config --file $TMP/config group.key 'value 3'", "expected": [ 2, "", - [ - "użycie: cov config [-h] [] [ [] | --unset | -l | --list]", - "cov config: błąd: wpis nie jest unikalny ze względu na to, że jest zmienną wielokrotną\n" - ] + "cov config: błąd: wpis nie jest unikalny ze względu na to, że jest zmienną wielokrotną\n" ], - "prepare": [ - "touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'" - ] + "prepare": ["touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'"] } diff --git a/apps/tests/messages-pl/003-config/039-config-set-but-include.json b/apps/tests/messages-pl/003-config/039-config-set-but-include.json index 43cad013..d9688e39 100644 --- a/apps/tests/messages-pl/003-config/039-config-set-but-include.json +++ b/apps/tests/messages-pl/003-config/039-config-set-but-include.json @@ -1,13 +1,11 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "config --file $TMP/config group.key 'value 3'", "expected": [ 2, "", - [ - "użycie: cov config [-h] [] [ [] | --unset | -l | --list]", - "cov config: błąd: wpis nie jest unikalny ze względu na to, że został załączony\n" - ] + "cov config: błąd: wpis nie jest unikalny ze względu na to, że został załączony\n" ], "prepare": [ "touch '$TMP/config.inc' '[group]\nkey = value 1'", diff --git a/apps/tests/messages-pl/003-config/040-config-unset-but-multivar.json b/apps/tests/messages-pl/003-config/040-config-unset-but-multivar.json index 596c2a3b..5ded5a24 100644 --- a/apps/tests/messages-pl/003-config/040-config-unset-but-multivar.json +++ b/apps/tests/messages-pl/003-config/040-config-unset-but-multivar.json @@ -1,15 +1,11 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "config --file $TMP/config --unset group.key", "expected": [ 2, "", - [ - "użycie: cov config [-h] [] [ [] | --unset | -l | --list]", - "cov config: błąd: wpis nie jest unikalny ze względu na to, że jest zmienną wielokrotną\n" - ] + "cov config: błąd: wpis nie jest unikalny ze względu na to, że jest zmienną wielokrotną\n" ], - "prepare": [ - "touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'" - ] + "prepare": ["touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'"] } diff --git a/apps/tests/messages-pl/003-config/041-config-unset-but-not-there.json b/apps/tests/messages-pl/003-config/041-config-unset-but-not-there.json index da1fd5b8..ddffef59 100644 --- a/apps/tests/messages-pl/003-config/041-config-unset-but-not-there.json +++ b/apps/tests/messages-pl/003-config/041-config-unset-but-not-there.json @@ -1,15 +1,11 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "config --file $TMP/config --unset group.other", "expected": [ 2, "", - [ - "użycie: cov config [-h] [] [ [] | --unset | -l | --list]", - "cov config: błąd: nie można odnaleźć klucza „group.other” do usunięcia\n" - ] + "cov config: błąd: nie można odnaleźć klucza „group.other” do usunięcia\n" ], - "prepare": [ - "touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'" - ] + "prepare": ["touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'"] } diff --git a/apps/tests/messages-pl/003-config/042-config-unset-but-no-a-key.json b/apps/tests/messages-pl/003-config/042-config-unset-but-no-a-key.json index b2e46952..e198fff4 100644 --- a/apps/tests/messages-pl/003-config/042-config-unset-but-no-a-key.json +++ b/apps/tests/messages-pl/003-config/042-config-unset-but-no-a-key.json @@ -1,15 +1,11 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "config --file $TMP/config --unset group-other", "expected": [ 2, "", - [ - "użycie: cov config [-h] [] [ [] | --unset | -l | --list]", - "cov config: błąd: nieprawidłowa nazwa elementu konfiguracji „group-other”\n" - ] + "cov config: błąd: nieprawidłowa nazwa elementu konfiguracji „group-other”\n" ], - "prepare": [ - "touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'" - ] + "prepare": ["touch '$TMP/config' '[group]\nkey = value 1\nkey = value 2'"] } diff --git a/apps/tests/messages-pl/004-module/047-module-help.json b/apps/tests/messages-pl/004-module/047-module-help.json index 349f0426..c4027273 100644 --- a/apps/tests/messages-pl/004-module/047-module-help.json +++ b/apps/tests/messages-pl/004-module/047-module-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "module --help", "expected": [ diff --git a/apps/tests/messages-pl/004-module/051-module-modify-in-bare-git.json b/apps/tests/messages-pl/004-module/051-module-modify-in-bare-git.json index 28399599..0d149ee9 100644 --- a/apps/tests/messages-pl/004-module/051-module-modify-in-bare-git.json +++ b/apps/tests/messages-pl/004-module/051-module-modify-in-bare-git.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "module --remove-all \"doesn't matter\"", "expected": [ diff --git a/apps/tests/messages-pl/004-module/052-module-add-duplicate.json b/apps/tests/messages-pl/004-module/052-module-add-duplicate.json index c997c6eb..b11571e2 100644 --- a/apps/tests/messages-pl/004-module/052-module-add-duplicate.json +++ b/apps/tests/messages-pl/004-module/052-module-add-duplicate.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "module --add module 'dir 1'", "expected": [ diff --git a/apps/tests/messages-pl/004-module/053-module-remove-from-nothing.json b/apps/tests/messages-pl/004-module/053-module-remove-from-nothing.json index b9db4ca7..af2aab9b 100644 --- a/apps/tests/messages-pl/004-module/053-module-remove-from-nothing.json +++ b/apps/tests/messages-pl/004-module/053-module-remove-from-nothing.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "module --remove some-name 'dir 1'", "expected": [ diff --git a/apps/tests/messages-pl/004-module/056-module-open_from_ref-range.json b/apps/tests/messages-pl/004-module/056-module-open_from_ref-range.json index 058e98c1..644489dd 100644 --- a/apps/tests/messages-pl/004-module/056-module-open_from_ref-range.json +++ b/apps/tests/messages-pl/004-module/056-module-open_from_ref-range.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "module v1.0.0..HEAD", "expected": [ diff --git a/apps/tests/messages-pl/004-module/057-module-open_from_ref-tree.json b/apps/tests/messages-pl/004-module/057-module-open_from_ref-tree.json index 9c0fff76..78b14052 100644 --- a/apps/tests/messages-pl/004-module/057-module-open_from_ref-tree.json +++ b/apps/tests/messages-pl/004-module/057-module-open_from_ref-tree.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "module 2559ccfa6", "expected": [ diff --git a/apps/tests/messages-pl/004-module/058-module-no-git.json b/apps/tests/messages-pl/004-module/058-module-no-git.json index aa7eed55..e704a773 100644 --- a/apps/tests/messages-pl/004-module/058-module-no-git.json +++ b/apps/tests/messages-pl/004-module/058-module-no-git.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "module", "expected": [ diff --git a/apps/tests/messages-pl/004-module/059-module-excl-show.json b/apps/tests/messages-pl/004-module/059-module-excl-show.json index 779681cd..c457d867 100644 --- a/apps/tests/messages-pl/004-module/059-module-excl-show.json +++ b/apps/tests/messages-pl/004-module/059-module-excl-show.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "module HEAD --add module dir", "expected": [ diff --git a/apps/tests/messages-pl/004-module/060-module-excl-show-sep.json b/apps/tests/messages-pl/004-module/060-module-excl-show-sep.json index dbdbb60c..f0a03fd8 100644 --- a/apps/tests/messages-pl/004-module/060-module-excl-show-sep.json +++ b/apps/tests/messages-pl/004-module/060-module-excl-show-sep.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "module --show-sep --add module dir", "expected": [ diff --git a/apps/tests/messages-pl/004-module/061-module-too-many-refs.json b/apps/tests/messages-pl/004-module/061-module-too-many-refs.json index 3cf68d9d..2d9f6a11 100644 --- a/apps/tests/messages-pl/004-module/061-module-too-many-refs.json +++ b/apps/tests/messages-pl/004-module/061-module-too-many-refs.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "module HEAD ANOTHER_HEAD main v1.0.0", "expected": [ diff --git a/apps/tests/messages-pl/004-module/062-module-too-little-args.json b/apps/tests/messages-pl/004-module/062-module-too-little-args.json index d9adc26d..ab17fa77 100644 --- a/apps/tests/messages-pl/004-module/062-module-too-little-args.json +++ b/apps/tests/messages-pl/004-module/062-module-too-little-args.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "module --add module", "expected": [ diff --git a/apps/tests/messages-pl/004-module/062-module-too-many-args.json b/apps/tests/messages-pl/004-module/062-module-too-many-args.json index 47a823b6..b5deb0f4 100644 --- a/apps/tests/messages-pl/004-module/062-module-too-many-args.json +++ b/apps/tests/messages-pl/004-module/062-module-too-many-args.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "module --remove-all module dir", "expected": [ diff --git a/apps/tests/messages-pl/004-module/064-module-no-covmodule-add.json b/apps/tests/messages-pl/004-module/064-module-no-covmodule-add.json index 9dc82bf8..d3edb0b8 100644 --- a/apps/tests/messages-pl/004-module/064-module-no-covmodule-add.json +++ b/apps/tests/messages-pl/004-module/064-module-no-covmodule-add.json @@ -1,13 +1,11 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "module --add module dir", "expected": [ 2, "", - [ - "użycie: cov module [-h] [--show-sep []] | [--set-sep | --add | --remove | --remove-all ] | []", - "cov module: błąd: requested file is a directory\n" - ] + "cov module: błąd: requested file is a directory\n" ], "prepare": [ "git init $TMP/repo", diff --git a/apps/tests/messages-pl/005-report/066-report-no-args.json b/apps/tests/messages-pl/005-report/066-report-no-args.json index 566e8437..ef0d2a91 100644 --- a/apps/tests/messages-pl/005-report/066-report-no-args.json +++ b/apps/tests/messages-pl/005-report/066-report-no-args.json @@ -1,11 +1,12 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "report", "expected": [ 2, "", [ - "użycie: cov report [-h] [-f ] [-p = ...] [--amend]", + "użycie: cov report [-h] [-f ] [-p = ...] [--amend] [-o ]", "cov report: błąd: argument jest wymagany\n" ] ] diff --git a/apps/tests/messages-pl/005-report/067-report-help.json b/apps/tests/messages-pl/005-report/067-report-help.json index 95b63fb1..883b23d5 100644 --- a/apps/tests/messages-pl/005-report/067-report-help.json +++ b/apps/tests/messages-pl/005-report/067-report-help.json @@ -1,19 +1,21 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "report -h", "expected": [ 0, [ - "użycie: cov report [-h] [-f ] [-p = ...] [--amend]", + "użycie: cov report [-h] [-f ] [-p = ...] [--amend] [-o ]", "", "argumenty pozycyjne:", " wybiera raport do zaimportowania", "", "argumenty opcjonalne:", " -h, --help pokazuje ten komunikat pomocy i wychodzi", - " -f, --filter filtruje inne formaty raportów do wewnętrznego formatu cov; znane filtry to: ‘cobertura’ i ‘coveralls’", + " -f, --filter filtruje inne formaty raportów do wewnętrznego formatu cov; znane filtry to: ‘cobertura’, ‘coveralls’ i ‘strip-excludes’", " -p, --prop = dodaje właściwość do tego raportu z kompilacji; jeśli jest jedną z „true”, „false”, „on” or „off”, będzie traktowana jako wartość logiczna, jeśli wygląda jak liczba całkowita, będzie traktowana jako liczba, w przeciwnym razie będzie traktowana jako ciąg znaków; dobrymi nazwami właściwości mogą być „os”, „arch”, „build_type” lub „compiler”", - " --amend zastępuje końcówkę bieżącej gałęzi, tworząc nowy zapis\n" + " --amend zastępuje końcówkę bieżącej gałęzi, tworząc nowy zapis", + " -o, --out \n" ], "" ] diff --git a/apps/tests/messages-pl/005-report/068-report-no-cov.json b/apps/tests/messages-pl/005-report/068-report-no-cov.json index b3fc1240..5bacbfb9 100644 --- a/apps/tests/messages-pl/005-report/068-report-no-cov.json +++ b/apps/tests/messages-pl/005-report/068-report-no-cov.json @@ -1,11 +1,12 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "report $DATA/basic-coverage.json", "expected": [ 2, "", [ - "użycie: cov report [-h] [-f ] [-p = ...] [--amend]", + "użycie: cov report [-h] [-f ] [-p = ...] [--amend] [-o ]", "cov report: błąd: Nie można znaleźć repozytorium Cov w $TMP\n" ] ], diff --git a/apps/tests/messages-pl/005-report/069-report-no-commit.json b/apps/tests/messages-pl/005-report/069-report-no-commit.json index ea86d916..fe0134de 100644 --- a/apps/tests/messages-pl/005-report/069-report-no-commit.json +++ b/apps/tests/messages-pl/005-report/069-report-no-commit.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "report $DATA/basic-coverage.json", "expected": [ diff --git a/apps/tests/messages-pl/005-report/070-report-failing-filter.json b/apps/tests/messages-pl/005-report/070-report-failing-filter.json index 98b0e799..7e9fe0cc 100644 --- a/apps/tests/messages-pl/005-report/070-report-failing-filter.json +++ b/apps/tests/messages-pl/005-report/070-report-failing-filter.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "report $DATA/basic-coverage.json -f echo-to-stderr", "expected": [ diff --git a/apps/tests/messages-pl/005-report/071-report-no-filter.json b/apps/tests/messages-pl/005-report/071-report-no-filter.json index 6037eba7..cfdfbf1d 100644 --- a/apps/tests/messages-pl/005-report/071-report-no-filter.json +++ b/apps/tests/messages-pl/005-report/071-report-no-filter.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "report $DATA/basic-coverage.json -f no-filter", "expected": [ diff --git a/apps/tests/messages-pl/005-report/072-report-not-the-json.json b/apps/tests/messages-pl/005-report/072-report-not-the-json.json index a1f52e2f..05d76b78 100644 --- a/apps/tests/messages-pl/005-report/072-report-not-the-json.json +++ b/apps/tests/messages-pl/005-report/072-report-not-the-json.json @@ -1,11 +1,12 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "report $DATA/no-git-coverage.json", "expected": [ 2, "", [ - "użycie: cov report [-h] [-f ] [-p = ...] [--amend]", + "cov report: /git: undefined", "cov report: błąd: wystąpiły problemy z $DATA/no-git-coverage.json\n" ] ], diff --git a/apps/tests/messages-pl/005-report/073-report-not-the-json-filtered.json b/apps/tests/messages-pl/005-report/073-report-not-the-json-filtered.json index 5f5161d7..6f156b2f 100644 --- a/apps/tests/messages-pl/005-report/073-report-not-the-json-filtered.json +++ b/apps/tests/messages-pl/005-report/073-report-not-the-json-filtered.json @@ -1,12 +1,13 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "report $DATA/no-git-coverage.json -f echo-to-stdout", "expected": [ 2, "", [ - "użycie: cov report [-h] [-f ] [-p = ...] [--amend]", - "cov report: błąd: wystąpiły problemy z $DATA/no-git-coverage.json przetworzonym przez filtr echo-to-stdout" + "cov report: /git: undefined", + "cov report: błąd: wystąpiły problemy z $DATA/no-git-coverage.json przetworzonym przez filtr echo-to-stdout\n" ] ], "prepare": [ diff --git a/apps/tests/messages-pl/005-report/074-report-no-file.json b/apps/tests/messages-pl/005-report/074-report-no-file.json index c37f55d3..0ba9c8fd 100644 --- a/apps/tests/messages-pl/005-report/074-report-no-file.json +++ b/apps/tests/messages-pl/005-report/074-report-no-file.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "report $DATA/basic-coverage.json -f replace-head", "expected": [ diff --git a/apps/tests/messages-pl/005-report/075-report-empty.json b/apps/tests/messages-pl/005-report/075-report-empty.json index 5f60c00a..2220da74 100644 --- a/apps/tests/messages-pl/005-report/075-report-empty.json +++ b/apps/tests/messages-pl/005-report/075-report-empty.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "report $DATA/empty-coverage.json -f replace-head", "patches": { diff --git a/apps/tests/messages-pl/005-report/076-report-one-file.json b/apps/tests/messages-pl/005-report/076-report-one-file.json index d7d46c21..f0c8ec0b 100644 --- a/apps/tests/messages-pl/005-report/076-report-one-file.json +++ b/apps/tests/messages-pl/005-report/076-report-one-file.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "report $DATA/build-coverage.json -f create-report", "patches": { diff --git a/apps/tests/messages-pl/005-report/077-report-amend-on-empty.json b/apps/tests/messages-pl/005-report/077-report-amend-on-empty.json index cd786bee..7f078e48 100644 --- a/apps/tests/messages-pl/005-report/077-report-amend-on-empty.json +++ b/apps/tests/messages-pl/005-report/077-report-amend-on-empty.json @@ -1,15 +1,14 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "report $DATA/build-coverage.json -f create-report --amend", - "patches": { - "\u001b\\[31m\\[main [0-9a-fA-F]+\\]\u001b\\[m second": "\u001b[31m[main $REPORT]\u001b[m second" - }, + "patches": {"\u001b\\[31m\\[main [0-9a-fA-F]+\\]\u001b\\[m second": "\u001b[31m[main $REPORT]\u001b[m second"}, "expected": [ 2, "", [ "[ADD] src/main.cc", - "użycie: cov report [-h] [-f ] [-p = ...] [--amend]", + "użycie: cov report [-h] [-f ] [-p = ...] [--amend] [-o ]", "cov report: błąd: nie masz nic do poprawienia\n" ] ], diff --git a/apps/tests/messages-pl/005-report/078-D-report-with-props.json b/apps/tests/messages-pl/005-report/078-D-report-with-props.json index 2a57b8e1..4c508145 100644 --- a/apps/tests/messages-pl/005-report/078-D-report-with-props.json +++ b/apps/tests/messages-pl/005-report/078-D-report-with-props.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "args": "report $DATA/build-coverage.json -f create-report -pos=qnx -pdebug=false -pcompiler=javac", "patches": { "\u001b\\[31m\\[main [0-9a-fA-F]+\\]\u001b\\[m second": "\u001b[31m[main $REPORT]\u001b[m second", diff --git a/apps/tests/messages-pl/005-report/078-report-amend-on-parentless.json b/apps/tests/messages-pl/005-report/078-report-amend-on-parentless.json index 46f002d7..331fb567 100644 --- a/apps/tests/messages-pl/005-report/078-report-amend-on-parentless.json +++ b/apps/tests/messages-pl/005-report/078-report-amend-on-parentless.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "report $DATA/build-coverage.json -f create-report --amend", "patches": { diff --git a/apps/tests/messages-pl/005-report/079-B-report-the-same-twice.json b/apps/tests/messages-pl/005-report/079-B-report-the-same-twice.json index e5a48851..4e215834 100644 --- a/apps/tests/messages-pl/005-report/079-B-report-the-same-twice.json +++ b/apps/tests/messages-pl/005-report/079-B-report-the-same-twice.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "report $DATA/build-coverage.json -f create-report", "expected": [ diff --git a/apps/tests/messages-pl/005-report/079-report-with-parent.json b/apps/tests/messages-pl/005-report/079-report-with-parent.json index 937805e4..f3971057 100644 --- a/apps/tests/messages-pl/005-report/079-report-with-parent.json +++ b/apps/tests/messages-pl/005-report/079-report-with-parent.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "report $DATA/build-coverage-2-files.json -f create-report", "patches": { diff --git a/apps/tests/messages-pl/005-report/080-report-on-detached.json b/apps/tests/messages-pl/005-report/080-report-on-detached.json index c5516835..9eb1786d 100644 --- a/apps/tests/messages-pl/005-report/080-report-on-detached.json +++ b/apps/tests/messages-pl/005-report/080-report-on-detached.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "report $DATA/build-coverage-2-files.json -f create-report", "patches": { diff --git a/apps/tests/messages-pl/005-report/081-report-hash-bang-less.json b/apps/tests/messages-pl/005-report/081-report-hash-bang-less.json index 307e3303..d2c00f68 100644 --- a/apps/tests/messages-pl/005-report/081-report-hash-bang-less.json +++ b/apps/tests/messages-pl/005-report/081-report-hash-bang-less.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "report $DATA/build-coverage-2-files.json -f hash-bang-less", "expected": [ diff --git a/apps/tests/messages-pl/006-log/082-log-no-args.json b/apps/tests/messages-pl/006-log/082-log-no-args.json index 4d5de384..888d6042 100644 --- a/apps/tests/messages-pl/006-log/082-log-no-args.json +++ b/apps/tests/messages-pl/006-log/082-log-no-args.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "log", "expected": [ @@ -9,7 +10,5 @@ "cov log: błąd: Nie można znaleźć repozytorium Cov w $TMP\n" ] ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/messages-pl/006-log/083-log-no-args-inited.json b/apps/tests/messages-pl/006-log/083-log-no-args-inited.json index 5e3d2a93..fe6434d3 100644 --- a/apps/tests/messages-pl/006-log/083-log-no-args-inited.json +++ b/apps/tests/messages-pl/006-log/083-log-no-args-inited.json @@ -1,13 +1,11 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "log", "expected": [ 2, "", - [ - "użycie: cov log [-h] [] [-n ] [--oneline] [--format ] [--abbrev-hash] [--no-abbrev-hash] [--prop-names] [--no-prop-names] [--color ] [--decorate ]", - "cov log: błąd: HEAD odnosi się do gałęzi bez raportów\n" - ] + "cov log: błąd: HEAD odnosi się do gałęzi bez raportów\n" ], "prepare": [ "git init '$TMP/repo'", diff --git a/apps/tests/messages-pl/006-log/084-log-no-args-Dmain.json b/apps/tests/messages-pl/006-log/084-log-no-args-Dmain.json index adb595d5..4ab7e093 100644 --- a/apps/tests/messages-pl/006-log/084-log-no-args-Dmain.json +++ b/apps/tests/messages-pl/006-log/084-log-no-args-Dmain.json @@ -1,9 +1,8 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "log", - "patches": { - "Added: .*": "Added: $DATE" - }, + "patches": {"Added: .*": "Added: $DATE"}, "expected": [ 0, [ diff --git a/apps/tests/messages-pl/006-log/092-log-help.json b/apps/tests/messages-pl/006-log/092-log-help.json index 635b5281..e45893cf 100644 --- a/apps/tests/messages-pl/006-log/092-log-help.json +++ b/apps/tests/messages-pl/006-log/092-log-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "log --help", "expected": [ diff --git a/apps/tests/messages-pl/006-log/093-log-two-HEADs.json b/apps/tests/messages-pl/006-log/093-log-two-HEADs.json index 88354cc4..93776e06 100644 --- a/apps/tests/messages-pl/006-log/093-log-two-HEADs.json +++ b/apps/tests/messages-pl/006-log/093-log-two-HEADs.json @@ -1,13 +1,11 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "log ..", "expected": [ 2, "", - [ - "użycie: cov log [-h] [] [-n ] [--oneline] [--format ] [--abbrev-hash] [--no-abbrev-hash] [--prop-names] [--no-prop-names] [--color ] [--decorate ]", - "cov log: błąd: nieprawidłowy wzorzec '..'\n" - ] + "cov log: błąd: nieprawidłowy wzorzec '..'\n" ], "prepare": [ "unpack $DATA/revparse.tar $TMP", diff --git a/apps/tests/messages-pl/006-log/094-log-123456789.json b/apps/tests/messages-pl/006-log/094-log-123456789.json index e2040883..a6baff15 100644 --- a/apps/tests/messages-pl/006-log/094-log-123456789.json +++ b/apps/tests/messages-pl/006-log/094-log-123456789.json @@ -1,13 +1,11 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "log 123456789", "expected": [ 2, "", - [ - "użycie: cov log [-h] [] [-n ] [--oneline] [--format ] [--abbrev-hash] [--no-abbrev-hash] [--prop-names] [--no-prop-names] [--color ] [--decorate ]", - "cov log: błąd: nie można odnaleźć żądanego obiektu\n" - ] + "cov log: błąd: nie można odnaleźć żądanego obiektu\n" ], "prepare": [ "unpack $DATA/revparse.tar $TMP", diff --git a/apps/tests/messages-pl/006-log/095-log-try-non-report.json b/apps/tests/messages-pl/006-log/095-log-try-non-report.json index ade8455f..786f3ff9 100644 --- a/apps/tests/messages-pl/006-log/095-log-try-non-report.json +++ b/apps/tests/messages-pl/006-log/095-log-try-non-report.json @@ -1,12 +1,10 @@ { + "$schema": "../../runner-schema.json", "args": "log $BUILD --prop-names --decorate short", "expected": [ 2, "", - [ - "usage: cov log [-h] [] [-n ] [--oneline] [--format ] [--abbrev-hash] [--no-abbrev-hash] [--prop-names] [--no-prop-names] [--color ] [--decorate ]", - "cov log: cov::category error: Object cannot be cast to required type\n" - ] + "cov log: cov::category error: Object cannot be cast to required type\n" ], "prepare": [ "unpack $DATA/repo.git.tar $TMP", diff --git a/apps/tests/messages-pl/007-refs/095-branch-no-args.json b/apps/tests/messages-pl/007-refs/095-branch-no-args.json index 019c5da6..46a6c490 100644 --- a/apps/tests/messages-pl/007-refs/095-branch-no-args.json +++ b/apps/tests/messages-pl/007-refs/095-branch-no-args.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "branch", "expected": [ @@ -9,7 +10,5 @@ "cov branch: błąd: Nie można znaleźć repozytorium Cov w $TMP\n" ] ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/messages-pl/007-refs/096-tag-no-args.json b/apps/tests/messages-pl/007-refs/096-tag-no-args.json index 5876c7a1..9e1e7222 100644 --- a/apps/tests/messages-pl/007-refs/096-tag-no-args.json +++ b/apps/tests/messages-pl/007-refs/096-tag-no-args.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "tag", "expected": [ @@ -9,7 +10,5 @@ "cov tag: błąd: Nie można znaleźć repozytorium Cov w $TMP\n" ] ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/messages-pl/007-refs/097-branch-help.json b/apps/tests/messages-pl/007-refs/097-branch-help.json index 8ce3b786..325e2c6d 100644 --- a/apps/tests/messages-pl/007-refs/097-branch-help.json +++ b/apps/tests/messages-pl/007-refs/097-branch-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "branch --help", "expected": [ @@ -21,7 +22,5 @@ ], "" ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/messages-pl/007-refs/098-tag-help.json b/apps/tests/messages-pl/007-refs/098-tag-help.json index e4c27b19..cbe137d2 100644 --- a/apps/tests/messages-pl/007-refs/098-tag-help.json +++ b/apps/tests/messages-pl/007-refs/098-tag-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "tag --help", "expected": [ @@ -18,7 +19,5 @@ ], "" ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/messages-pl/007-refs/104-branch-force-delete.json b/apps/tests/messages-pl/007-refs/104-branch-force-delete.json index 9702f5c6..e5a7e1d6 100644 --- a/apps/tests/messages-pl/007-refs/104-branch-force-delete.json +++ b/apps/tests/messages-pl/007-refs/104-branch-force-delete.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "branch --delete main --force", "expected": [ diff --git a/apps/tests/messages-pl/007-refs/105-branch-two-commands.json b/apps/tests/messages-pl/007-refs/105-branch-two-commands.json index 92ae2616..83bd9565 100644 --- a/apps/tests/messages-pl/007-refs/105-branch-two-commands.json +++ b/apps/tests/messages-pl/007-refs/105-branch-two-commands.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "branch --show-current -l feat/cov-*", "expected": [ diff --git a/apps/tests/messages-pl/007-refs/108-branch-create-existing.json b/apps/tests/messages-pl/007-refs/108-branch-create-existing.json index 8a73b1b2..8fb1a585 100644 --- a/apps/tests/messages-pl/007-refs/108-branch-create-existing.json +++ b/apps/tests/messages-pl/007-refs/108-branch-create-existing.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "branch cov-separate", "expected": [ diff --git a/apps/tests/messages-pl/007-refs/109-branch-create-three-at-a-time.json b/apps/tests/messages-pl/007-refs/109-branch-create-three-at-a-time.json index 4a2806d6..09bda280 100644 --- a/apps/tests/messages-pl/007-refs/109-branch-create-three-at-a-time.json +++ b/apps/tests/messages-pl/007-refs/109-branch-create-three-at-a-time.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "branch branch-1 branch-2 branch-3", "expected": [ diff --git a/apps/tests/messages-pl/007-refs/110-branch-create-unborn.json b/apps/tests/messages-pl/007-refs/110-branch-create-unborn.json index 712a6434..b397c22a 100644 --- a/apps/tests/messages-pl/007-refs/110-branch-create-unborn.json +++ b/apps/tests/messages-pl/007-refs/110-branch-create-unborn.json @@ -1,13 +1,11 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "branch name", "expected": [ 2, "", - [ - "użycie: cov branch [-h] [ [] | -d ... | -l [...] | --show-current]", - "cov branch: błąd: HEAD odnosi się do gałęzi bez raportów\n" - ] + "cov branch: błąd: HEAD odnosi się do gałęzi bez raportów\n" ], "prepare": [ "cd $TMP", diff --git a/apps/tests/messages-pl/007-refs/111-branch-delete-nothing.json b/apps/tests/messages-pl/007-refs/111-branch-delete-nothing.json index c908b28e..d0321ebb 100644 --- a/apps/tests/messages-pl/007-refs/111-branch-delete-nothing.json +++ b/apps/tests/messages-pl/007-refs/111-branch-delete-nothing.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "branch --delete", "expected": [ diff --git a/apps/tests/messages-pl/007-refs/112-branch-delete-main.json b/apps/tests/messages-pl/007-refs/112-branch-delete-main.json index 55baa773..4df382a6 100644 --- a/apps/tests/messages-pl/007-refs/112-branch-delete-main.json +++ b/apps/tests/messages-pl/007-refs/112-branch-delete-main.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "branch -d main", "expected": [ diff --git a/apps/tests/messages-pl/007-refs/114-branch-delete-nonexisting.json b/apps/tests/messages-pl/007-refs/114-branch-delete-nonexisting.json index f5fe5a3b..3bd2b421 100644 --- a/apps/tests/messages-pl/007-refs/114-branch-delete-nonexisting.json +++ b/apps/tests/messages-pl/007-refs/114-branch-delete-nonexisting.json @@ -1,13 +1,11 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "branch -d feat/cov-22", "expected": [ 2, "", - [ - "użycie: cov branch [-h] [ [] | -d ... | -l [...] | --show-current]", - "cov branch: błąd: nie można odnaleźć żądanego obiektu\n" - ] + "cov branch: błąd: nie można odnaleźć żądanego obiektu\n" ], "prepare": [ "unpack $DATA/revparse.tar $TMP", diff --git a/apps/tests/messages-pl/007-refs/117-tag-create-existing.json b/apps/tests/messages-pl/007-refs/117-tag-create-existing.json index fe9b9876..7738be99 100644 --- a/apps/tests/messages-pl/007-refs/117-tag-create-existing.json +++ b/apps/tests/messages-pl/007-refs/117-tag-create-existing.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "tag H", "expected": [ diff --git a/apps/tests/messages-pl/007-refs/118-tag-create-three-at-a-time.json b/apps/tests/messages-pl/007-refs/118-tag-create-three-at-a-time.json index 98af328b..ae89649b 100644 --- a/apps/tests/messages-pl/007-refs/118-tag-create-three-at-a-time.json +++ b/apps/tests/messages-pl/007-refs/118-tag-create-three-at-a-time.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "tag tag-1 tag-2 tag-3", "expected": [ diff --git a/apps/tests/messages-pl/007-refs/119-tag-create-unborn.json b/apps/tests/messages-pl/007-refs/119-tag-create-unborn.json index 68cf2223..8a3ce27f 100644 --- a/apps/tests/messages-pl/007-refs/119-tag-create-unborn.json +++ b/apps/tests/messages-pl/007-refs/119-tag-create-unborn.json @@ -1,13 +1,11 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "tag name", "expected": [ 2, "", - [ - "użycie: cov tag [-h] [ [] | -d ... | -l [...]]", - "cov tag: błąd: HEAD odnosi się do gałęzi bez raportów\n" - ] + "cov tag: błąd: HEAD odnosi się do gałęzi bez raportów\n" ], "prepare": [ "cd $TMP", diff --git a/apps/tests/messages-pl/007-refs/121-tag-delete-nonexisting.json b/apps/tests/messages-pl/007-refs/121-tag-delete-nonexisting.json index e79f4b2f..98826b7e 100644 --- a/apps/tests/messages-pl/007-refs/121-tag-delete-nonexisting.json +++ b/apps/tests/messages-pl/007-refs/121-tag-delete-nonexisting.json @@ -1,13 +1,11 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "tag -d nonexisting", "expected": [ 2, "", - [ - "użycie: cov tag [-h] [ [] | -d ... | -l [...]]", - "cov tag: błąd: nie można odnaleźć żądanego obiektu\n" - ] + "cov tag: błąd: nie można odnaleźć żądanego obiektu\n" ], "prepare": [ "unpack $DATA/revparse.tar $TMP", diff --git a/apps/tests/messages-pl/007-refs/122-branch-create-invalid.json b/apps/tests/messages-pl/007-refs/122-branch-create-invalid.json index 563afab3..f2faa146 100644 --- a/apps/tests/messages-pl/007-refs/122-branch-create-invalid.json +++ b/apps/tests/messages-pl/007-refs/122-branch-create-invalid.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "branch \"bad:name\"", "expected": [ diff --git a/apps/tests/messages-pl/007-refs/123-tag-create-invalid.json b/apps/tests/messages-pl/007-refs/123-tag-create-invalid.json index 34b63681..f9331f3f 100644 --- a/apps/tests/messages-pl/007-refs/123-tag-create-invalid.json +++ b/apps/tests/messages-pl/007-refs/123-tag-create-invalid.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "tag \"bad:name\"", "expected": [ diff --git a/apps/tests/messages-pl/012-checkout/125-checkout-no-args.json b/apps/tests/messages-pl/012-checkout/125-checkout-no-args.json index 2b2778fe..2cbec13a 100644 --- a/apps/tests/messages-pl/012-checkout/125-checkout-no-args.json +++ b/apps/tests/messages-pl/012-checkout/125-checkout-no-args.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "checkout", "expected": [ @@ -9,7 +10,5 @@ "cov checkout: błąd: potrzebny jest co najmniej jeden argument\n" ] ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/messages-pl/012-checkout/126-checkout-no-cov.json b/apps/tests/messages-pl/012-checkout/126-checkout-no-cov.json index 2a4993d2..bb4d3e14 100644 --- a/apps/tests/messages-pl/012-checkout/126-checkout-no-cov.json +++ b/apps/tests/messages-pl/012-checkout/126-checkout-no-cov.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "checkout main", "expected": [ @@ -9,7 +10,5 @@ "cov checkout: błąd: Nie można znaleźć repozytorium Cov w $TMP\n" ] ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/messages-pl/012-checkout/127-checkout-help.json b/apps/tests/messages-pl/012-checkout/127-checkout-help.json index cd067158..c39da17b 100644 --- a/apps/tests/messages-pl/012-checkout/127-checkout-help.json +++ b/apps/tests/messages-pl/012-checkout/127-checkout-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "checkout -h", "expected": [ @@ -22,7 +23,5 @@ ], "" ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/messages-pl/012-checkout/128-checkout-two-names.json b/apps/tests/messages-pl/012-checkout/128-checkout-two-names.json index 6dbf7529..cf690fc3 100644 --- a/apps/tests/messages-pl/012-checkout/128-checkout-two-names.json +++ b/apps/tests/messages-pl/012-checkout/128-checkout-two-names.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "checkout two names", "expected": [ diff --git a/apps/tests/messages-pl/012-checkout/129-checkout-too-many-for-branching.json b/apps/tests/messages-pl/012-checkout/129-checkout-too-many-for-branching.json index 9d61fa5b..95924871 100644 --- a/apps/tests/messages-pl/012-checkout/129-checkout-too-many-for-branching.json +++ b/apps/tests/messages-pl/012-checkout/129-checkout-too-many-for-branching.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "checkout -b result starting-point something else", "expected": [ diff --git a/apps/tests/messages-pl/012-checkout/130-checkout-too-little-for-branching.json b/apps/tests/messages-pl/012-checkout/130-checkout-too-little-for-branching.json index c9a8be8b..055c1382 100644 --- a/apps/tests/messages-pl/012-checkout/130-checkout-too-little-for-branching.json +++ b/apps/tests/messages-pl/012-checkout/130-checkout-too-little-for-branching.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "checkout -b", "expected": [ diff --git a/apps/tests/messages-pl/012-checkout/131-checkout-two-commands.json b/apps/tests/messages-pl/012-checkout/131-checkout-two-commands.json index ce13dcb7..531eb28a 100644 --- a/apps/tests/messages-pl/012-checkout/131-checkout-two-commands.json +++ b/apps/tests/messages-pl/012-checkout/131-checkout-two-commands.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "checkout -b --orphan", "expected": [ diff --git a/apps/tests/messages-pl/012-checkout/133-checkout-orphan.json b/apps/tests/messages-pl/012-checkout/133-checkout-orphan.json index 15709055..027478c1 100644 --- a/apps/tests/messages-pl/012-checkout/133-checkout-orphan.json +++ b/apps/tests/messages-pl/012-checkout/133-checkout-orphan.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "checkout --orphan branch-name", "post": [ @@ -8,10 +9,7 @@ "expected": [ 2, "branch-name\n", - [ - "użycie: cov log [-h] [] [-n ] [--oneline] [--format ] [--abbrev-hash] [--no-abbrev-hash] [--prop-names] [--no-prop-names] [--color ] [--decorate ]", - "cov log: błąd: HEAD odnosi się do gałęzi bez raportów\n" - ] + "cov log: błąd: HEAD odnosi się do gałęzi bez raportów\n" ], "prepare": [ "unpack $DATA/revparse.tar $TMP", diff --git a/apps/tests/messages-pl/012-checkout/134-checkout-orphan-main.json b/apps/tests/messages-pl/012-checkout/134-checkout-orphan-main.json index cc430cfc..86b573bd 100644 --- a/apps/tests/messages-pl/012-checkout/134-checkout-orphan-main.json +++ b/apps/tests/messages-pl/012-checkout/134-checkout-orphan-main.json @@ -1,13 +1,11 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "checkout --orphan main", "expected": [ 2, "", - [ - "użycie: cov checkout [-h] [--detach|-b|--orphan] []", - "cov checkout: błąd git::category: Object exists preventing operation\n" - ] + "cov checkout: błąd git::category: Object exists preventing operation\n" ], "prepare": [ "unpack $DATA/revparse.tar $TMP", diff --git a/apps/tests/messages-pl/012-checkout/135-checkout-orphan-invalid-spec.json b/apps/tests/messages-pl/012-checkout/135-checkout-orphan-invalid-spec.json index 1e16af27..96355bc0 100644 --- a/apps/tests/messages-pl/012-checkout/135-checkout-orphan-invalid-spec.json +++ b/apps/tests/messages-pl/012-checkout/135-checkout-orphan-invalid-spec.json @@ -1,13 +1,11 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "checkout --orphan 'invalid:spec'", "expected": [ 2, "", - [ - "użycie: cov checkout [-h] [--detach|-b|--orphan] []", - "cov checkout: błąd git::category: Name/ref spec was not in a valid format\n" - ] + "cov checkout: błąd git::category: Name/ref spec was not in a valid format\n" ], "prepare": [ "unpack $DATA/revparse.tar $TMP", diff --git a/apps/tests/messages-pl/012-checkout/138-checkout-without-HEAD.json b/apps/tests/messages-pl/012-checkout/138-checkout-without-HEAD.json index 7262a27d..a9aec701 100644 --- a/apps/tests/messages-pl/012-checkout/138-checkout-without-HEAD.json +++ b/apps/tests/messages-pl/012-checkout/138-checkout-without-HEAD.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "checkout --detach", "expected": [ diff --git a/apps/tests/messages-pl/014-show/145-show-help.json b/apps/tests/messages-pl/014-show/145-show-help.json index 2662cbbc..df3419fb 100644 --- a/apps/tests/messages-pl/014-show/145-show-help.json +++ b/apps/tests/messages-pl/014-show/145-show-help.json @@ -1,4 +1,5 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "show -h", "expected": [ @@ -22,7 +23,5 @@ ], "" ], - "prepare": [ - "cd '$TMP'" - ] + "prepare": ["cd '$TMP'"] } diff --git a/apps/tests/messages-pl/014-show/146-show-no-args-unborn.json b/apps/tests/messages-pl/014-show/146-show-no-args-unborn.json index c3c90ff8..69bc4a50 100644 --- a/apps/tests/messages-pl/014-show/146-show-no-args-unborn.json +++ b/apps/tests/messages-pl/014-show/146-show-no-args-unborn.json @@ -1,13 +1,11 @@ { + "$schema": "../../runner-schema.json", "lang": "pl", "args": "show", "expected": [ 2, "", - [ - "użycie: cov show [-h] [] [-m ] [--format ] [--abbrev-hash] [--no-abbrev-hash] [--prop-names] [--no-prop-names] [--color ] [--decorate ]", - "cov show: błąd: HEAD odnosi się do gałęzi bez raportów\n" - ] + "cov show: błąd: HEAD odnosi się do gałęzi bez raportów\n" ], "prepare": [ "mkdirs '$TMP/dirname'", diff --git a/apps/tests/runner-schema.json b/apps/tests/runner-schema.json new file mode 120000 index 00000000..551bcd98 --- /dev/null +++ b/apps/tests/runner-schema.json @@ -0,0 +1 @@ +../../build/downloads/runner-schema.json \ No newline at end of file diff --git a/cmake/builtins.cmake b/cmake/builtins.cmake index 9b7b8844..1418444e 100644 --- a/cmake/builtins.cmake +++ b/cmake/builtins.cmake @@ -37,50 +37,3 @@ file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gen/cov/builtins.hh CONTENT ${__content} ) - -function(add_cov_tool TARGET) - cmake_parse_arguments(PARSE_ARGV 1 COV "" "" "") - add_executable(${TARGET} ${COV_UNPARSED_ARGUMENTS}) - target_link_libraries(${TARGET} PRIVATE app_main) - if (WIN32) - target_link_options(${TARGET} PRIVATE /ENTRY:wmainCRTStartup) - endif() -endfunction() - -set(CORE_EXT) - -function(setup_cov_ext COV_TOOL) - set(__CORE ${CORE_EXT} ${COV_TOOL}) - set(CORE_EXT ${__CORE} PARENT_SCOPE) - - set_target_properties(cov-${COV_TOOL} PROPERTIES FOLDER apps/core) - target_link_options(cov-${COV_TOOL} PRIVATE ${ADDITIONAL_LINK_FLAGS}) - target_compile_options(cov-${COV_TOOL} PRIVATE ${ADDITIONAL_WALL_FLAGS}) - - set_target_properties(cov-${COV_TOOL} PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CORE_DIR}" - ) - - foreach(BUILD_TYPE DEBUG RELEASE RELWITHDEBINFO MINSIZEREL) - set_target_properties(cov-${COV_TOOL} PROPERTIES - RUNTIME_OUTPUT_DIRECTORY_${BUILD_TYPE} "${CMAKE_BINARY_DIR}/${CORE_DIR}" - ) - endforeach() -endfunction() - -macro(add_cov_ext COV_TOOL) - add_cov_tool(cov-${COV_TOOL} cov-${COV_TOOL}.cc) - source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES cov-${COV_TOOL}.cc) - - setup_cov_ext(${COV_TOOL}) - install(TARGETS cov-${COV_TOOL} - RUNTIME DESTINATION ${CORE_DIR} - COMPONENT tools - ) -endmacro() - -function(print_cov_ext) - set(__CORE ${CORE_EXT}) - string(REPLACE ";" ", " CORE_LIST "${CORE_EXT}") - message(STATUS "Core extensions: ${CORE_LIST}") -endfunction() diff --git a/cmake/cov.cmake b/cmake/cov.cmake new file mode 100644 index 00000000..add79d06 --- /dev/null +++ b/cmake/cov.cmake @@ -0,0 +1,132 @@ +function(fix_vs_modules TARGET) + set(__TOOLS ${VS_MODS} ${TARGET}) + set(VS_MODS ${__TOOLS} PARENT_SCOPE) + + if (MSVC_VERSION GREATER_EQUAL 1936 AND MSVC_IDE) + set_target_properties(${TARGET} PROPERTIES VS_USER_PROPS "${PROJECT_SOURCE_DIR}/cmake/VS17.NoModules.props") + endif() +endfunction() + +set(COV_LIBS) +set(COV_TOOLS) +set(VS_MODS) + +function(add_cov_tool TARGET) + set(__TOOLS ${COV_TOOLS} ${TARGET}) + set(COV_TOOLS ${__TOOLS} PARENT_SCOPE) + + cmake_parse_arguments(PARSE_ARGV 1 COV "" "" "") + add_executable(${TARGET} ${COV_UNPARSED_ARGUMENTS}) + target_compile_options(${TARGET} PRIVATE ${ADDITIONAL_WALL_FLAGS}) + target_link_options(${TARGET} PRIVATE ${ADDITIONAL_LINK_FLAGS}) + if (WIN32) + target_link_options(${TARGET} PRIVATE /ENTRY:wmainCRTStartup) + fix_vs_modules(${TARGET}) + set(VS_MODS ${VS_MODS} PARENT_SCOPE) + endif() +endfunction() + +function(add_cov_library TARGET) + set(__LIBS ${COV_LIBS} ${TARGET}) + set(COV_LIBS ${__LIBS} PARENT_SCOPE) + + cmake_parse_arguments(PARSE_ARGV 1 COV "" "" "") + add_library(${TARGET} STATIC ${COV_UNPARSED_ARGUMENTS}) + target_compile_options(${TARGET} PRIVATE ${ADDITIONAL_WALL_FLAGS}) + target_link_options(${TARGET} PRIVATE ${ADDITIONAL_LINK_FLAGS}) + fix_vs_modules(${TARGET}) + set(VS_MODS ${VS_MODS} PARENT_SCOPE) + if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include") + target_include_directories(${TARGET} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include) + elseif (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src") + target_include_directories(${TARGET} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src) + endif() +endfunction() + +set(CORE_EXT) + +function(setup_cov_ext COV_TOOL) + set(__CORE ${CORE_EXT} ${COV_TOOL}) + set(CORE_EXT ${__CORE} PARENT_SCOPE) + + set_target_properties(cov-${COV_TOOL} PROPERTIES FOLDER apps/core) + target_link_options(cov-${COV_TOOL} PRIVATE ${ADDITIONAL_LINK_FLAGS}) + target_compile_options(cov-${COV_TOOL} PRIVATE ${ADDITIONAL_WALL_FLAGS}) + + set_target_properties(cov-${COV_TOOL} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CORE_DIR}" + ) + + foreach(BUILD_TYPE DEBUG RELEASE RELWITHDEBINFO MINSIZEREL) + set_target_properties(cov-${COV_TOOL} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY_${BUILD_TYPE} "${CMAKE_BINARY_DIR}/${CORE_DIR}" + ) + endforeach() +endfunction() + +macro(add_cov_ext COV_TOOL) + string(REPLACE "-" "_" __COV_TOOL_SAFE_NAME "${COV_TOOL}") + if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/cov_${__COV_TOOL_SAFE_NAME}.cc) + set(${COV_TOOL}_CODE cov_${__COV_TOOL_SAFE_NAME}.cc) + elseif(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${__COV_TOOL_SAFE_NAME}/main.cc) + set(${COV_TOOL}_CODE ${__COV_TOOL_SAFE_NAME}/main.cc) + elseif(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${__COV_TOOL_SAFE_NAME}/${__COV_TOOL_SAFE_NAME}.cc) + set(${COV_TOOL}_CODE ${__COV_TOOL_SAFE_NAME}/${__COV_TOOL_SAFE_NAME}.cc) + else() + message(FATAL_ERROR "Cannot find code for ${COV_TOOL}'s tool() function") + endif() + add_cov_tool(cov-${COV_TOOL} ${${COV_TOOL}_CODE}) + source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${${COV_TOOL}_CODE}) + + setup_cov_ext(${COV_TOOL}) + install(TARGETS cov-${COV_TOOL} + RUNTIME DESTINATION ${CORE_DIR} + COMPONENT tools + ) +endmacro() + +function(print_cov_ext) + set(__CORE ${CORE_EXT}) + string(REPLACE ";" ", " CORE_LIST "${CORE_EXT}") + message(STATUS "Core extensions: ${CORE_LIST}") +endfunction() + +function(add_cov_test TARGET) + cmake_parse_arguments(PARSE_ARGV 1 COV "" "" "") + add_executable(${TARGET}-test ${COV_UNPARSED_ARGUMENTS}) + set_target_properties(${TARGET}-test PROPERTIES FOLDER tests) + target_compile_options(${TARGET}-test PRIVATE ${ADDITIONAL_WALL_FLAGS}) + target_link_options(${TARGET}-test PRIVATE ${ADDITIONAL_LINK_FLAGS}) + target_include_directories(${TARGET}-test + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/tests + ${CMAKE_CURRENT_BINARY_DIR}) + fix_vs_modules(${TARGET}-test) + set(VS_MODS ${VS_MODS} PARENT_SCOPE) +endfunction() + +function(add_win32_icon TARGET NAME) + set(RESNAME "${PROJECT_SOURCE_DIR}/data/icons/${NAME}") + set(RC "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.dir/icon.rc") + set(__content "// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) +// This file was autogenerated. Do not edit. + +LANGUAGE 9, 1 + +100 ICON \"${RESNAME}\" +") + + file(GENERATE OUTPUT ${RC} CONTENT ${__content}) + target_sources(${TARGET} PRIVATE "${RC}" "${RESNAME}") +endfunction() + +macro(set_parent_scope) + set(CORE_EXT ${CORE_EXT} PARENT_SCOPE) + set(COV_LIBS ${COV_LIBS} PARENT_SCOPE) + set(COV_TOOLS ${COV_TOOLS} PARENT_SCOPE) + set(VS_MODS ${VS_MODS} PARENT_SCOPE) +endmacro() diff --git a/conanfile.txt b/conanfile.txt index e54c7cc7..c9509ede 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -3,6 +3,8 @@ mbits-args/0.12.3 fmt/9.1.0 gtest/cci.20210126 mbits-lngs/0.7.6 +ctre/3.7.2 +expat/2.2.10 zlib/1.2.12 libcurl/8.1.2 diff --git a/data/icons/.gitignore b/data/icons/.gitignore index 3a5e4374..4a2c700d 100644 --- a/data/icons/.gitignore +++ b/data/icons/.gitignore @@ -1,2 +1,3 @@ *.png appicon-win32* +plugin-win32* diff --git a/data/icons/plugin.ico b/data/icons/plugin.ico new file mode 100644 index 00000000..f76b0367 Binary files /dev/null and b/data/icons/plugin.ico differ diff --git a/docs/building.md b/docs/building.md index e8526c15..235fb066 100644 --- a/docs/building.md +++ b/docs/building.md @@ -29,7 +29,9 @@ Currently, there are few specialized flows: **config**, **build**, **report** an |**Build**|Builds the project.|✓||✓|✓|✓| |**Test**|Either directly calls `ctest`, or builds the self-coverage gathering target.|✓|||✓|✓| |**Report**|Puts the latest coverage into local instance of **cov**.||||✓|✓| +|**Sign**|On Windows, signs installable binaries|✓||||✓| |**Pack**|Creates archives and installers|✓||||✓| +|**SignPackages**|On Windows, signs the MSI package|✓||||✓| |**Store**|Copies packages from **Pack** to `./build/artifacts`.|✓||||✓| |**BinInst**|Extracts the ZIP/TAR.GZ to `./build/.local/`. Adding `./build/.local/bin` to $PATH should help with running latest build from the get-go.|||||✓| |**DevInst**|Extracts the ZIP/TAR.GZ to `./build/.user/`.|||||✓| diff --git a/docs/roadmap.md b/docs/roadmap.md index dc3b5d6e..a6f1c11d 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -78,7 +78,10 @@ - [ ] Look into tortoise and a hare (mzdun/cov#62) - [ ] Major release - Update schema id and references in filters + - [ ] change `SHARE_DIR` to `share/cov-${PROJECT_VERSION_MAJOR}` - [ ] Freeze translation IDs (mzdun/cov#63) +- [ ] Post-release issues + - [ ] support `cov show --oneline` (see `matrix.py`'s Report) - [ ] `cov serve` - [ ] Boost.Beast WS - [ ] React frontend diff --git a/extensions/CMakeLists.txt b/extensions/CMakeLists.txt new file mode 100644 index 00000000..9e03bc5b --- /dev/null +++ b/extensions/CMakeLists.txt @@ -0,0 +1,33 @@ +add_subdirectory(libs) +add_subdirectory(tests) + +set(NATIVE_FILTERS + strip-excludes +) + +set(NATIVE_FILTERS ${NATIVE_FILTERS} PARENT_SCOPE) + +foreach(NAME ${NATIVE_FILTERS}) + string(REPLACE "-" "_" SAFE_NAME "${NAME}") + add_cov_tool(${NAME} filters/${SAFE_NAME}.cc) + add_win32_icon(${NAME} "plugin.ico") + set_target_properties(${NAME} PROPERTIES FOLDER apps) + + set_target_properties(${NAME} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${SHARE_DIR}/filters" + ) + + foreach(BUILD_TYPE DEBUG RELEASE RELWITHDEBINFO MINSIZEREL) + set_target_properties(${NAME} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY_${BUILD_TYPE} "${CMAKE_BINARY_DIR}/${SHARE_DIR}/filters" + ) + endforeach() +endforeach() + +target_link_libraries(strip-excludes PRIVATE excludes) + +add_cov_ext(collect) +add_win32_icon(cov-collect "plugin.ico") +target_link_libraries(cov-collect PRIVATE collect-api) + +set_parent_scope() diff --git a/extensions/cov_collect.cc b/extensions/cov_collect.cc new file mode 100644 index 00000000..ccb1493f --- /dev/null +++ b/extensions/cov_collect.cc @@ -0,0 +1,217 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::literals; +using namespace cov::app; +using namespace cov; + +git::repository open_here() { + std::error_code ec{}; + auto const common = git::repository::discover("."sv, ec); + if (ec) return {}; + auto repo = git::repository::open(common, ec); + if (ec) return {}; + return repo; +} + +bool repo_head(git::repository const& repo, + json::string& branch, + git::oid& ref) { + std::error_code ec{}; + auto head = repo.head(ec); + if (ec == git::errc::unbornbranch || ec == git::errc::notfound) { + fmt::print(stderr, "[git] called on unborn branch\n"); + return false; + } + + auto branch_name = head.shorthand(); + if (branch_name) branch = to_u8(branch_name); + + auto resolved = head.resolve(ec); + if (!ec) { + auto oid = git_reference_target(resolved.raw()); + if (oid) { + ref.assign(*oid); + return true; + } + } + // GCOV_EXCL_START + fmt::print(stderr, "[git] cannot resolve HEAD to a commit\n"); + return false; + // GCOV_EXCL_STOP +} + +enum class command { clean, observe, collect }; + +template +using cmd_val = std::integral_constant; + +struct params { + std::optional config{std::nullopt}; + command cmd{command::collect}; + args::args_view observed; +}; + +params parse_arguments(args::args_view const& arguments) { + params result{}; + + args::null_translator tr{}; + args::parser p{{}, arguments, &tr}; + p.arg(result.config, "c", "config").opt(); + p.set>(result.cmd, "clean").opt(); + p.set>(result.cmd, "observe").opt(); + + auto rest = p.parse(args::parser::allow_subcommands); + if (!rest.empty() && result.cmd != command::observe) { + p.error(tr(args::lng::unrecognized, rest[0], {})); + } + + if (!rest.empty()) { + result.observed.progname = rest[0]; + result.observed.args = rest.shift(); + } else if (result.cmd == command::observe) { + p.error("argument --observe: missing tool name and arguments"); + } + + return result; +} + +std::filesystem::path workdir(git::repository const& repo) { + auto const dir_view = repo.workdir(); + if (!dir_view) return std::filesystem::current_path(); + return make_u8path(*dir_view); +} + +void find_config(git::repository const& repo, + std::optional& config) { + auto root = workdir(repo); + + for (auto const& dirname : {"build/debug"sv, "build"sv, "debug"sv, ""sv}) { + auto path = root / dirname / ".covcollect"sv; + if (std::filesystem::is_regular_file(path)) { + config = get_u8path(path); + return; + } + } +} + +template Callback> +int measure(std::string_view label, Callback&& cb) { + using namespace std::chrono; + + fmt::print(stderr, "[{}] start...\n", label); + + auto const then = steady_clock::now(); + auto const ret = cb(); + auto const now = steady_clock::now(); + auto const ms = duration_cast(now - then); + auto const cent_s = (ms.count() + 5) / 10; + fmt::print(stderr, "[{}] {}.{:02} s\n", label, cent_s / 100, cent_s % 100); + + return ret; +} + +int tool(args::args_view const& arguments) { + git::init memory{}; + + auto params = parse_arguments(arguments); + + auto repo = open_here(); + if (!repo) return 1; + json::string branch{}; + git::oid ref{}; + if (!repo_head(repo, branch, ref)) return 1; + + if (!params.config) { + find_config(repo, params.config); + if (!params.config) return 1; + } + + collect::config cfg{}; + cfg.load(*params.config); + + auto const toolkit = collect::recognize_toolkit(cfg); + if (!toolkit) return 1; + + if (params.cmd == command::clean) { + toolkit->clean(cfg); + return 0; + } + + if (params.cmd == command::observe) { + return toolkit->observe(cfg, params.observed.progname, + params.observed.args); + } + + fmt::print(stderr, "[toolkit] {} {} (from {})\n", toolkit->label(), + toolkit->version(), get_u8path(*params.config)); + toolkit->hello(cfg); + collect::report cvg{cfg}; + + if (toolkit->needs_preprocess()) { + auto const ret = + measure("preprocess"sv, [&] { return toolkit->preprocess(cfg); }); + if (ret) return ret; + } + + auto ret = measure("report"sv, [&] { return toolkit->report(cfg, cvg); }); + if (ret) return ret; + + cvg.post_process(); + auto const summary = cvg.summary(); + + fmt::print(stderr, "[files] {}\n", summary.lines_total); + fmt::print(stderr, "[lines] {} / {}\n", summary.lines.visited, + summary.lines.relevant); + fmt::print(stderr, "[functions] {} / {}\n", summary.functions.visited, + summary.functions.relevant); + + { + struct output : json::output { + output(std::filesystem::path path) : file{io::fopen(path, "w")} {} + void write(json::string_view view) override { + file.store(view.data(), view.size()); + } + void write(byte_type ch) override { file.store(&ch, 1); } + io::file file; + }; + + auto outname = cfg.bin_dir / cfg.output; + + { + std::error_code ignore; + std::filesystem::create_directories(outname.parent_path(), ignore); + } + + output results{outname}; + if (!results.file) { + fmt::print(stderr, "[i/o] cannot open {}\n", get_u8path(outname)); + return 1; + } + auto node = cvg.get_json(branch, ref); + json::write_json(results, node); + fmt::print(stderr, "[i/o] {}\n", get_u8path(outname)); + } + return 0; +} diff --git a/extensions/filters/strip_excludes.cc b/extensions/filters/strip_excludes.cc new file mode 100644 index 00000000..8a112791 --- /dev/null +++ b/extensions/filters/strip_excludes.cc @@ -0,0 +1,227 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::literals; + +namespace cov::app::strip { + exclude_result find_excl_blocks( + std::span valid_markers, + std::string_view path, + std::filesystem::path src_dir) { + std::error_code ec{}; + + auto const full_path = src_dir / make_u8path(path); + auto const opened = io::fopen(full_path, "rb"); + if (opened) { + auto stg = opened.read(); + excludes builder{.path = get_u8path(full_path), + .valid_markers = valid_markers}; + + auto const chars = std::string_view{ + reinterpret_cast(stg.data()), stg.size()}; + split(chars, '\n', [&](unsigned line_no, std::string_view text) { + builder.on_line(line_no, text); + }); + builder.after_lines(); + auto tmp = exclude_result{std::move(builder.result), + std::move(builder.empties)}; + return tmp; + } + + return {}; + } + + enum which_lines { soft = false, hard = true }; + + bool erase_line(json::map& line_coverage, + unsigned line, + which_lines intensity) { + auto const str = fmt::format("{}", line); + auto key = to_u8s(str); + auto it = line_coverage.find(key); + if (it != line_coverage.end()) { + if (intensity == hard || it->second == json::node{0}) { + line_coverage.erase(it); + return true; + } + } + return false; + } + + excl_block mask_range(json::map const& range, + std::span blocks) { + auto json_range_start = cast(range, u8"start_line"); + auto json_range_end = cast(range, u8"end_line"); + if (!json_range_start) return {.start = 1, .end = 0}; + auto start_line = *json_range_start; + auto end_line = json_range_end ? *json_range_end : start_line; + + for (auto const& block : blocks) { + auto const block_start = static_cast(block.start); + auto const block_end = static_cast(block.end); + + if (end_line < block_start || start_line > block_end) continue; + if (start_line >= block_start) start_line = block_end + 1; + if (end_line <= block_end) end_line = block_start - 1; + if (end_line < start_line) break; + } + return {.start = static_cast(start_line), + .end = static_cast(end_line)}; + } + + size_t filter_blocks(json::array* array, + std::span blocks) { + json::array visible{}; + size_t counter{}; + visible.reserve(array->size()); + for (auto& node_range : *array) { + auto json_range = cast(node_range); + if (!json_range) { + ++counter; // GCOV_EXCL_LINE -- oh, c'mon, continue is + // counted... + continue; + } + auto const block = mask_range(*json_range, blocks); + if (block.start > block.end) { + ++counter; + continue; + } + visible.emplace_back(std::move(node_range)); + } + *array = std::move(visible); + return counter; + } + + int tool(args::args_view const& arguments) { + parser p{arguments, {platform::locale_dir(), ::lngs::system_locales()}}; + p.parse(); + + std::vector valid_markers{*p.os, *p.compiler}; + if (*p.compiler == "clang"sv) + valid_markers.push_back("llvm"sv); + else if (*p.compiler == "llvm"sv) + valid_markers.push_back("clang"sv); + + auto const text = platform::read_input(); + auto root = json::read_json({text.data(), text.size()}); + auto cvg = json::cast(root); + if (!cvg) return 1; + + git::init memory{}; + + // TODO: after first major release, add code for compatibility check + // through $schema + auto head = json::cast_from_json(root, u8"git.head"sv); + auto files = json::cast_from_json(root, u8"files"sv); + if (!files || !head) return 1; + + auto const src_dir = + std::filesystem::weakly_canonical(make_u8path(p.src_dir)); + + size_t line_counter = 0, fn_counter = 0, br_counter = 0; + + for (auto& file_node : *files) { + auto file = json::cast(file_node); + if (!file) continue; + + auto json_file_name = json::cast(file, u8"name"); + auto json_file_digest = json::cast(file, u8"digest"); + auto json_file_lines = + json::cast(file, u8"line_coverage"); + + if (!json_file_name || !json_file_digest || !json_file_lines) + continue; + + auto [excludes, empties] = find_excl_blocks( + valid_markers, from_u8(*json_file_name), src_dir); + if (excludes.empty() && empties.empty()) continue; + + std::sort(excludes.begin(), excludes.end()); + + // LINES + // =========================================================== + for (auto const [start, stop] : excludes) { + for (auto line = start; line <= stop; ++line) { + if (erase_line(*json_file_lines, line, hard)) + ++line_counter; + } + } + + for (auto const empty : empties) { + if (erase_line(*json_file_lines, empty, soft)) ++line_counter; + } + + // FUNCTIONS + // =========================================================== + + if (auto json_array = json::cast(file, u8"functions"); + json_array) { + fn_counter += filter_blocks(json_array, excludes); + } + + // BRANCHES + // =========================================================== + + if (auto json_array = json::cast(file, u8"branches"); + json_array) { + // GCOV_EXCL_START -- TODO: [BRANCHES] Enable for next task + br_counter += filter_blocks(json_array, excludes); + // GCOV_EXCL_STOP + } + } + + // SUMMARY + // =========================================================== + if (line_counter || fn_counter || br_counter) { + bool first = true; + fmt::print(stderr, "strip-excludes: excluded "); + if (line_counter) { + first = false; + fmt::print(stderr, "{} {}", line_counter, + line_counter == 1 ? "line"sv : "lines"sv); + } + if (fn_counter) { + if (!first) fmt::print(stderr, ", "); + first = false; + fmt::print(stderr, "{} {}", fn_counter, + fn_counter == 1 ? "function"sv : "functions"sv); + } + // GCOV_EXCL_START -- TODO: [BRANCHES] Enable for next task + if (br_counter) { + if (!first) fmt::print(stderr, ", "); + first = false; + fmt::print(stderr, "{} {}", br_counter, + br_counter == 1 ? "branch"sv : "branches"sv); + } + // GCOV_EXCL_STOP + fmt::print(stderr, "\n"); + } + + json::write_json(stdout, root, json::concise); + return 0; + } +}; // namespace cov::app::strip + +int tool(args::args_view const& arguments) { + return cov::app::strip::tool(arguments); +} diff --git a/extensions/libs/CMakeLists.txt b/extensions/libs/CMakeLists.txt new file mode 100644 index 00000000..b3bf543c --- /dev/null +++ b/extensions/libs/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(native) +add_subdirectory(collect-api) +add_subdirectory(excludes) + +set_parent_scope() diff --git a/extensions/libs/collect-api/CMakeLists.txt b/extensions/libs/collect-api/CMakeLists.txt new file mode 100644 index 00000000..5ec0f4ea --- /dev/null +++ b/extensions/libs/collect-api/CMakeLists.txt @@ -0,0 +1,24 @@ +set(SOURCES + src/gnu.cc + src/llvm.cc + src/msvc.cc + src/occ_xml.cc + src/occ_xml.hh + src/queue.hh + src/report.cc + src/report.hh + src/toolkit.cc + src/toolkit.hh + src/task_watcher.cc + src/task_watcher.hh + src/thread_pool.cc + src/thread_pool.hh + src/walk.cc + src/walk.hh +) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES}) + +add_cov_library(collect-api ${SOURCES}) +target_link_libraries(collect-api PUBLIC ext_native) + +set_parent_scope() diff --git a/extensions/libs/collect-api/src/gnu.cc b/extensions/libs/collect-api/src/gnu.cc new file mode 100644 index 00000000..656f5374 --- /dev/null +++ b/extensions/libs/collect-api/src/gnu.cc @@ -0,0 +1,250 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::literals; + +// Alpine: g++ (GCC) 13.1.1 20230429 +// Jammy: g++ (Ubuntu 12.1.0-2ubuntu1~22.04) 12.1.0 +// MINGW-w64: g++.exe (Rev7, Built by MSYS2 project) 13.1.0 +#define GXX "g\\+\\+" +#define WORD "[a-zA-Z0-9_]" +#define COMMENT R"(\([^)]+\))" +#define VERSION "[0-9.]+" +static constexpr auto GNU = + ctre::match<"^(?:" GXX "(?:.exe)?|" WORD "+-" WORD "+-" WORD "+-" GXX + "(?:-" VERSION ")?) " COMMENT " (" VERSION ")(?: [0-9]+)?$">; + +static constexpr auto TRIPLET = ctre::match< + // the triplet + "^(" WORD "+-" WORD "+-" WORD "+-)" GXX "(?:-" VERSION ")? \\(.*$">; + +namespace cov::app::collect { + static unsigned u(long long val) { + static constexpr long long max_uint = + std::numeric_limits::max(); + return std::max(0u, static_cast(std::min(max_uint, val))); + } + + struct gnu_toolkit : toolkit { + gnu_toolkit(std::filesystem::path const& compiler, + std::string&& ver, + std::string&& triplet) + : ver_{std::move(ver)} + , triplet_{std::move(triplet)} + , compiler_{compiler} {} + std::string_view label() const noexcept override { return "GNU"sv; } + std::string_view version() const noexcept override { return ver_; } + + void hello(config const&) const override { + fmt::print(stderr, " [gcov] {}\n", get_u8path(gcov_)); + } + + void clean(config const& cfg) const override { + remove_all(cfg.bin_dir / "**/*.gcda"); + } + + int observe(config const&, + std::string_view command, + args::arglist arguments) const override { + return platform::call(make_u8path(command), arguments); + } + + int report(config const& cfg, coverage& cvg) override { + using namespace std::filesystem; + std::unordered_map> paths; + walk(cfg.bin_dir / "**/*.gcno", + [&](std::filesystem::path const& path) { + paths[path.parent_path()].insert(path.filename()); + }); + + closure state{.cfg = cfg, .cvg = cvg}; + + for (auto const& [dirname, filenames] : paths) { + state.push( + [&, this, &dirname = dirname, &filenames = filenames]() { + std::vector nodes; + + if (state.set_return_code( + load_directory(dirname, filenames, nodes))) + return; + + for (auto& node : nodes) { + state.push([this, &state, node = std::move(node)] { + auto const ret = + analyze_node(state.cfg, state.cvg, node); + state.task_finished(); + if (!ret) state.set_return_code(1); + }); + } + state.task_finished(); + }); + } + + return state.wait(); + } + + bool find_tools() { + auto const hint = compiler_.parent_path(); + auto const ver = split(ver_, '.'); + return find_tool(gcov_, hint, fmt::format("{}gcov", triplet_), ver); + } + + private: + int load_directory( + std::filesystem::path const& dirname, + std::unordered_set const& filenames, + std::vector& nodes); + + bool analyze_node(config const& cfg, + coverage& cvg, + json::node const& root); + + std::string ver_; + std::string triplet_; + std::filesystem::path compiler_; + std::filesystem::path gcov_; + }; + + std::unique_ptr gnu(config const& cfg, + std::span lines) { + if (auto matched = GNU(lines[0]); matched) { + auto triplet_matched = TRIPLET(lines[0]); + auto triplet = triplet_matched + ? triplet_matched.get<1>().to_string() + : std::string{}; + auto tk = std::make_unique( + cfg.compiler, matched.get<1>().to_string(), std::move(triplet)); + if (!tk->find_tools()) { + fmt::print(stderr, "[gcc] cannot configure the toolkit\n"); + tk.reset(); + } + return tk; + } + return {}; + } + + int gnu_toolkit::load_directory( + std::filesystem::path const& dirname, + std::unordered_set const& filenames, + std::vector& nodes) { + std::vector args{"--branch-probabilities"s, "--branch-counts"s, + "--json-format"s, "--stdout"s, + "--object-directory"s, get_u8path(dirname)}; + args.reserve(args.size() + filenames.size()); + for (auto const& gcno : filenames) + args.push_back(get_u8path(dirname / gcno)); + + auto proc = platform::run(get_u8path(gcov_), arguments{args}); + if (proc.return_code) { + std::fwrite(proc.error.data(), 1, proc.error.size(), stderr); + return proc.return_code; + } + + auto view = json::string_view{ + reinterpret_cast(proc.output.data()), + proc.output.size()}; + while (!view.empty()) { + size_t read{}; + auto root = + json::read_json(view, {}, json::read_mode::serialized, &read); + if (!read || std::holds_alternative(root)) break; + view = view.substr(read); + + nodes.push_back(std::move(root)); + } + + return 0; + } + + bool gnu_toolkit::analyze_node(config const& cfg, + coverage& cvg, + json::node const& json_root) { + auto root = cast(json_root, u8"files"); + if (!root) return false; + for (auto const& json_file : *root) { + auto json_name = cast(json_file, u8"file"); + auto json_lines = cast(json_file, u8"lines"); + auto json_functions = cast(json_file, u8"functions"); + + if (!json_name || !json_lines) continue; + auto filename = std::filesystem::path{*json_name}; + if (!filename.is_absolute()) filename = cfg.bin_dir / filename; + + auto sink = cvg.get_file_mt(get_u8path(filename)); + if (!sink) { + filename = std::filesystem::weakly_canonical(filename); + sink = cvg.get_file_mt(get_u8path(filename)); + if (!sink) { + continue; + } + } + + for (auto const& json_line : *json_lines) { + auto json_line_number = + cast(json_line, u8"line_number"); + auto json_count = cast(json_line, u8"count"); + + if (!json_line_number || !json_count) continue; + sink->on_visited(u(*json_line_number), u(*json_count)); + } + + if (json_functions) { + for (auto const& json_function : *json_functions) { + auto json_start_line = + cast(json_function, u8"start_line"); + auto json_end_line = + cast(json_function, u8"end_line"); + auto json_start_column = + cast(json_function, u8"start_column"); + auto json_end_column = + cast(json_function, u8"end_column"); + auto json_execution_count = + cast(json_function, u8"execution_count"); + auto json_fn_name = + cast(json_function, u8"name"); + auto json_demangled_name = + cast(json_function, u8"demangled_name"); + + if (!json_start_line || !json_execution_count || + !(json_fn_name && json_demangled_name)) + continue; + + text_pos start{ + .line = u(*json_start_line), + .col = u(json_start_column ? *json_start_column : 0)}; + text_pos stop{ + .line = u(json_end_line ? *json_end_line + : *json_start_line), + .col = u(json_end_column ? *json_end_column : 0)}; + unsigned count = u(*json_execution_count); + std::string name = from_u8s( + *(json_fn_name ? json_fn_name : json_demangled_name)); + std::string demangled; + if (json_fn_name && json_demangled_name) + demangled = from_u8s(*json_demangled_name); + + sink->on_function(start, stop, count, name, demangled); + } + } + + cvg.handover_mt(std::move(sink)); + } + return true; + } +} // namespace cov::app::collect diff --git a/extensions/libs/collect-api/src/llvm.cc b/extensions/libs/collect-api/src/llvm.cc new file mode 100644 index 00000000..5d1fd4f2 --- /dev/null +++ b/extensions/libs/collect-api/src/llvm.cc @@ -0,0 +1,446 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std::literals; + +// Ubuntu clang version () +// clang version -- on windows +static constexpr auto CLANG = + ctre::match; + +#ifdef _WIN32 +int setenv(const char* name, const char* value, int) { + return _putenv_s(name, value); +} +#endif + +namespace cov::app::collect { + struct execution { + unsigned line; + std::optional count; + }; + + static unsigned u(long long val) { + static constexpr long long max_uint = + std::numeric_limits::max(); + return std::max(0u, static_cast(std::min(max_uint, val))); + } + + struct region_ref { + text_pos start; + text_pos end; + + static std::optional encompassing_function( + json::array const& regions) { + std::optional result{std::nullopt}; + + for (auto const& json_region : regions) { + auto region = cast(json_region); + if (!region || region->size() < 8) return std::nullopt; + auto start_line = cast((*region)[0]); + auto start_col = cast((*region)[1]); + auto end_line = cast((*region)[2]); + auto end_col = cast((*region)[3]); + auto file_id = cast((*region)[5]); + auto type = cast((*region)[7]); + + if (!start_line || !start_col || !end_line || !end_col || + !type || !file_id) + return std::nullopt; + if (*type || *file_id) continue; + + text_pos start{.line = u(*start_line), .col = u(*start_col)}; + text_pos end{.line = u(*end_line), .col = u(*end_col)}; + + if (!result) { + result = region_ref{.start = start, .end = end}; + continue; + } + + if (result->start > start) result->start = start; + if (result->end < end) result->end = end; + } + + return result; + } + }; + + struct cvg_segment { + text_pos pos; + unsigned count; + bool has_count; + bool is_entry; + bool is_gap; + + bool is_start_of_region() const { + return !is_gap && has_count && is_entry; + } + + static std::optional from(json::array const& arr) { + // [24, 37, 21, true, true, false] + if (arr.size() < 6) return std::nullopt; + auto line = cast(arr[0]); + auto column = cast(arr[1]); + auto count = cast(arr[2]); + auto has_count = cast(arr[3]); + auto is_entry = cast(arr[4]); + auto is_gap = cast(arr[5]); + + if (!line || !column || !count || !has_count || !is_entry || + !is_gap) + return std::nullopt; + + return cvg_segment{.pos = {.line = u(*line), .col = u(*column)}, + .count = u(*count), + .has_count = *has_count, + .is_entry = *is_entry, + .is_gap = *is_gap}; + } + + static execution make_stats( + std::span const& line_segments, + cvg_segment const* wrapped_segment, + unsigned line) { + execution result{line, std::nullopt}; + + unsigned min_region_count = 0; + for (auto const& segment : line_segments) { + if (min_region_count > 1) break; + if (segment->is_start_of_region()) ++min_region_count; + } + + auto const start_of_skipped_region = + (!line_segments.empty() && line_segments[0]->has_count && + line_segments[0]->is_entry); + + auto mapped = !start_of_skipped_region && + ((wrapped_segment && wrapped_segment->has_count) || + (min_region_count > 0)); + if (!mapped) return result; + + if (wrapped_segment) result.count = wrapped_segment->count; + + for (auto const& segment : line_segments) { + if (segment->is_start_of_region()) + result.count = std::max(*result.count, segment->count); + } + + return result; + } + + static void run_coverage(std::span const& coverage, + coverage_file& sink) { + cvg_segment const* wrapped = nullptr; + auto line = coverage.empty() ? 0u : coverage[0].pos.line; + + std::vector segments{}; + segments.reserve(coverage.size()); + + auto const end_index = coverage.size(); + decltype(coverage.size()) index = 0; + + while (index < end_index) { + if (!segments.empty()) wrapped = segments.back(); + segments.clear(); + + while (index < end_index && coverage[index].pos.line == line) { + segments.push_back(&coverage[index]); + ++index; + } + + auto const mark = make_stats(segments, wrapped, line); + ++line; + + if (mark.count) sink.on_visited(mark.line, *mark.count); + } + } + }; + + struct llvm_toolkit : toolkit { + llvm_toolkit(std::filesystem::path const& compiler, std::string&& ver) + : ver_{std::move(ver)}, compiler_{compiler} {} + + bool load_config(config const& settings); + bool find_tools() { + auto const hint = compiler_.parent_path(); + auto const ver = split(ver_, '.'); + return find_tool(merger_, hint, "llvm-profdata"sv, ver) && + find_tool(exporter_, hint, "llvm-cov"sv, ver); + } + + std::string_view label() const noexcept override { return "LLVM"sv; } + std::string_view version() const noexcept override { return ver_; } + + void hello(config const&) const override { + fmt::print(stderr, " [merge] {}\n", get_u8path(merger_)); + fmt::print(stderr, " [cov] {}\n", get_u8path(exporter_)); + if (!raw_ext_.empty()) + fmt::print(stderr, " [raw] {}\n", raw_ext_); + if (!prof_data_dir_.empty()) + fmt::print(stderr, " [data] {}\n", + get_u8path(prof_data_dir_)); + if (!exec_.empty()) { + fmt::print(stderr, " [exe]\n"); + for (auto const& exe : exec_) { + fmt::print(stderr, " - {}\n", get_u8path(exe)); + } + } + } + + auto merged() const { return prof_data_dir_ / "merged.db"sv; } + + void clean(config const& cfg) const override { + std::error_code ec{}; + remove_all(cfg.bin_dir / ("**/*" + raw_ext_)); + std::filesystem::remove_all(cfg.bin_dir / prof_data_dir_, ec); + if (ec) return; + std::filesystem::create_directories(cfg.bin_dir / prof_data_dir_, + ec); + } + + int observe(config const& cfg, + std::string_view command, + args::arglist arguments) const override { + auto const file_mask = get_u8path(cfg.bin_dir / prof_data_dir_ / + "raw"sv / ("%p" + raw_ext_)); + setenv("LLVM_PROFILE_FILE", file_mask.c_str(), 1); + return platform::call(make_u8path(command), arguments); + } + + bool needs_preprocess() const override { return true; } + int preprocess(config const& cfg) override; + int report(config const& cfg, coverage& cvg) override { + closure state{.cfg = cfg, .cvg = cvg, .pool{}}; + + for (auto const& exe : exec_) { + state.push([&, exe] { + state.set_return_code( + report_exe(state.cfg, state.cvg, exe)); + state.task_finished(); + }); + } + + return state.wait(); + } + + int report_exe(config const& cfg, + coverage& cvg, + std::filesystem::path const& exe); + + private: + std::string ver_; + std::filesystem::path compiler_; + std::filesystem::path merger_; + std::filesystem::path exporter_; + std::vector exec_; + std::filesystem::path prof_data_dir_; + std::string raw_ext_; + }; + + std::unique_ptr llvm(config const& cfg, + std::span lines) { + if (auto matched = CLANG(lines[0]); matched) { + auto tk = std::make_unique( + cfg.compiler, matched.get<1>().to_string()); + if (!tk->load_config(cfg) || !tk->find_tools()) { + fmt::print(stderr, "[clang] cannot configure the toolkit\n"); + tk.reset(); + } + return tk; + } + return {}; + } + + bool llvm_toolkit::load_config(config const& settings) { + std::set patterns; + std::set execs; + + auto cfg = git::config::create(); + cfg.add_file_ondisk(settings.cfg_dir / ".covcollect"sv); + cfg.foreach_entry( + [this, &patterns](git_config_entry const* entry) -> int { + auto name = std::string_view{entry->name}; + static constexpr auto prefix = "clang."sv; +#ifdef _WIN32 + auto const ext = ".exe"s; +#endif + + if (!name.starts_with(prefix)) return 0; + auto key = name.substr(prefix.length()); + + if (key == "profdata"sv) { + prof_data_dir_ = make_u8path(entry->value); + } else if (key == "exec"sv) { +#ifdef _WIN32 + patterns.insert(make_u8path(entry->value + ext)); +#else + patterns.insert(make_u8path(entry->value)); +#endif + } else if (key == "raw"sv) { + raw_ext_ = entry->value; + } + return 0; + }); + + prof_data_dir_ = std::filesystem::weakly_canonical( + prof_data_dir_.empty() ? settings.bin_dir / "llvm"sv + : settings.bin_dir / prof_data_dir_); + for (auto& pattern : patterns) { + auto const path = + std::filesystem::weakly_canonical(settings.bin_dir / pattern); + walk(path, [&execs](auto const& found) { + auto f = io::fopen(found, "rb"); + if (f) { + char hash_bang[2]; + if (f.load(hash_bang, 2) == 2) { + if (hash_bang[0] == '#' && hash_bang[1] == '!') { + // it's a script! + return; + } + } + } + execs.insert(found); + }); + } + + exec_.clear(); + exec_.insert(exec_.begin(), execs.begin(), execs.end()); + + return true; + } + + int llvm_toolkit::preprocess(config const& cfg) { + std::error_code ec{}; + using namespace std::filesystem; + + auto const db = merged(); + create_directories(prof_data_dir_, ec); + if (ec) return 1; + remove(db, ec); + if (ec) { + std::cerr << "error: " << ec.message() << '\n'; + return 1; + } + + std::vector stg{"merge"s, "-sparse"s}; + + walk(cfg.bin_dir / ("**/*" + raw_ext_), + [&stg](auto const& path) { stg.push_back(get_u8path(path)); }); + + stg.reserve(stg.size() + 2); + stg.push_back("-o"s); + stg.push_back(get_u8path(db)); + + return platform::call(merger_, arguments{stg}); + } + + int llvm_toolkit::report_exe(config const&, + coverage& cvg, + std::filesystem::path const& exe) { + std::array args{"export"s, "-format"s, + "text"s, "-skip-expansions"s, + "-instr-profile"s, get_u8path(merged()), + get_u8path(exe)}; + auto proc = platform::run(get_u8path(exporter_), arguments{args}); + if (proc.return_code) { + std::fwrite(proc.error.data(), 1, proc.error.size(), stderr); + return proc.return_code; + } + + auto root = json::read_json( + {reinterpret_cast(proc.output.data()), + proc.output.size()}); + + auto root_map = cast(root); + if (!root_map) return 1; + + { + auto root_version = cast(root_map, u8"version"); + auto version = split(from_u8(*root_version), '.'); + if (version.empty() || version[0] != "2"sv) return 1; + } + + if (auto root_data = cast(root_map, u8"data"); root_data) { + for (auto const& json_data : *root_data) { + auto files = cast(json_data, u8"files"); + auto functions = cast(json_data, u8"functions"); + if (files) { + for (auto const& json_file : *files) { + auto filename = + cast(json_file, u8"filename"); + auto segments = + cast(json_file, u8"segments"); + auto branches = + cast(json_file, u8"branches"); + + if (!filename || (!segments && !branches)) continue; + + auto sink = cvg.get_file_mt(from_u8(*filename)); + if (!sink) continue; + + if (segments) { + std::vector data{}; + data.reserve(segments->size()); + for (auto const& json_segment : *segments) { + auto segment = cast(json_segment); + if (!segment) return 1; + auto cvg_seg = cvg_segment::from(*segment); + if (!cvg_seg) return 1; + data.push_back(*cvg_seg); + } + + cvg_segment::run_coverage(data, *sink); + } + + cvg.handover_mt(std::move(sink)); + } + } + if (functions) { + for (auto const& json_function : *functions) { + auto filenames = + cast(json_function, u8"filenames"); + auto regions = + cast(json_function, u8"regions"); + auto name = cast(json_function, u8"name"); + auto count = cast(json_function, u8"count"); + + if (!filenames || filenames->empty() || !regions || + regions->empty() || !name || !count) + continue; + + auto region = + region_ref::encompassing_function(*regions); + if (!region) continue; + + auto filename = cast((*filenames)[0]); + auto sink = cvg.get_file_mt(from_u8(*filename)); + if (sink) { + sink->on_function(region->start, region->end, + u(*count), get_u8path(*name), {}); + cvg.handover_mt(std::move(sink)); + } + } + } + } + } + return 0; + } + +} // namespace cov::app::collect diff --git a/extensions/libs/collect-api/src/msvc.cc b/extensions/libs/collect-api/src/msvc.cc new file mode 100644 index 00000000..fe60e774 --- /dev/null +++ b/extensions/libs/collect-api/src/msvc.cc @@ -0,0 +1,202 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::literals; + +namespace cov::app::collect { + std::string dos_path(std::filesystem::path const& path) { + auto copy = path; + copy.make_preferred(); + return get_u8path(copy); + } + + std::string dos_path(std::filesystem::path&& path) { + path.make_preferred(); + return get_u8path(path); + } + + struct msvc_toolkit : toolkit { + msvc_toolkit(std::filesystem::path const& compiler) + : compiler_{compiler} {} + + bool load_config(config const& settings); + bool get_version(config const& cfg); + bool find_tools() { + auto const hint = compiler_.parent_path(); + auto const ver = split(ver_, '.'); + if (!find_tool(occ_, "C:/Program Files/OpenCppCoverage"sv, + "OpenCppCoverage"sv, {})) + return false; + occ_.make_preferred(); + return true; + } + + std::string_view label() const noexcept override { return "MSVC"sv; } + std::string_view version() const noexcept override { return ver_; } + + void hello(config const&) const override { + fmt::print(stderr, " [occ] {}\n", get_u8path(occ_)); + fmt::print(stderr, " [outfile] {}\n", + get_u8path("" / occ_dir_ / occ_output_)); + } + + void clean(config const&) const override {} + + int observe(config const& cfg, + std::string_view command, + ::args::arglist args) const override { + if (occ_.empty()) { + return platform::call(make_u8path(command), args); + } + + std::vector occ_args{ + "-q"s, + "--working_dir"s, + dos_path(cfg.src_dir), + "--export_type"s, + fmt::format("{}:{}", occ_filter_, + dos_path(cfg.bin_dir / occ_dir_ / occ_output_)), + }; + // +2 for "--cover_children --"; +1 for the observed command + occ_args.reserve(occ_args.size() + 2 * cfg.include.size() + 2 + 1 + + args.size()); + + for (auto const& include : cfg.include) { + std::error_code ec{}; + auto src = std::filesystem::absolute(cfg.src_dir / include, ec); + if (ec) continue; + occ_args.push_back("--source"s); + occ_args.push_back(dos_path(std::move(src))); + } + occ_args.push_back("--cover_children"s); + occ_args.push_back("--"s); + + std::filesystem::path cmd{}; + if (!find_tool(cmd, {}, command, {})) { + cmd = make_u8path(command); + } + cmd.make_preferred(); + occ_args.push_back(get_u8path(cmd)); + for (auto index = 0u; index < args.size(); ++index) { + auto arg = args[index]; + occ_args.push_back({arg.data(), arg.size()}); + } + + return platform::call(occ_, arguments{occ_args}); + } + + int report(config const&, coverage&) override; + + private: + std::string ver_; + std::filesystem::path compiler_; + std::filesystem::path occ_; + std::filesystem::path occ_dir_{"OpenCppCoverage"sv}; + std::string occ_filter_{"cobertura"sv}; + std::filesystem::path occ_output_{"cobertura.xml"sv}; + }; + + std::unique_ptr msvc(config const& cfg) { + auto tk = std::make_unique(cfg.compiler); + if (!tk->load_config(cfg) || !tk->get_version(cfg) || + !tk->find_tools()) { + fmt::print(stderr, "[msvc] cannot configure the toolkit\n"); + tk.reset(); + } + return tk; + } + + bool msvc_toolkit::load_config(config const& settings) { + auto cfg = git::config::create(); + cfg.add_file_ondisk(settings.cfg_dir / ".covcollect"sv); + cfg.foreach_entry([this](git_config_entry const* entry) -> int { + auto name = std::string_view{entry->name}; + static constexpr auto prefix = "msvc."sv; +#ifdef _WIN32 + auto const ext = ".exe"s; +#endif + + if (!name.starts_with(prefix)) return 0; + auto key = name.substr(prefix.length()); + + if (key == "output-dir"sv) { + occ_dir_ = make_u8path(entry->value); + } else if (key == "filter"sv) { + occ_filter_ = entry->value; + } else if (key == "output"sv) { + occ_output_ = entry->value; + } + return 0; + }); + + if (occ_dir_.empty()) occ_dir_ = "OpenCppCoverage"sv; + if (occ_filter_.empty()) occ_filter_ = "cobertura"sv; + if (occ_output_.empty()) occ_output_ = "cobertura.xml"sv; + + return true; + } + + bool msvc_toolkit::get_version(config const& cfg) { + static constexpr auto MSC_VER = "_MSC_VER\n_MSC_FULL_VER"sv; + std::error_code ec{}; + auto const msc_ver = cfg.bin_dir / occ_dir_ / "msc_ver.c"; + std::filesystem::create_directories(msc_ver.parent_path(), ec); + if (ec) return false; + { + auto file = io::fopen(msc_ver, "w"); + if (!file) return false; + if (file.store(MSC_VER.data(), MSC_VER.size()) != MSC_VER.size()) + return false; + } + std::vector cl_args = {"/nologo"s, "/EP"s, + get_u8path(msc_ver.filename())}; + auto proc = platform::run(cfg.compiler, arguments{cl_args}, + msc_ver.parent_path(), {}); + if (proc.return_code) { + std::fwrite(proc.error.data(), 1, proc.error.size(), stderr); + return false; + } + + auto const ver_text = + trim({reinterpret_cast(proc.output.data()), + proc.output.size()}); + auto const ver_lines = split(ver_text, '\n'); + auto const first_line = trim(ver_lines.front()); + + auto major = first_line.substr(0, 2); + auto minor = first_line.size() > 2 ? first_line.substr(2) : "00"sv; + + if (ver_lines.size() > 1) { + auto const second_line = trim(ver_lines[1]); + if (second_line.size() > first_line.size()) { + auto const patch = second_line.substr(first_line.size()); + + ver_ = fmt::format("{}.{}.{}", major, minor, patch); + return true; + } + } + ver_ = fmt::format("{}.{}", major, minor); + return true; + } + + int msvc_toolkit::report(config const& cfg, coverage& cvg) { + auto const report_path = cfg.bin_dir / occ_dir_ / occ_output_; + return occ_load(report_path, cfg, cvg) ? 0 : 1; + } +} // namespace cov::app::collect diff --git a/extensions/libs/collect-api/src/occ_xml.cc b/extensions/libs/collect-api/src/occ_xml.cc new file mode 100644 index 00000000..48d223cc --- /dev/null +++ b/extensions/libs/collect-api/src/occ_xml.cc @@ -0,0 +1,201 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#include "occ_xml.hh" +#include +#include +#include +#include +#include + +// define DEBUG_IGNORE + +#define SIMPLE_PARENT(PARENT, ...) \ + struct PARENT##_handler : handler_impl<__VA_ARGS__> { \ + using handler_impl<__VA_ARGS__>::handler_impl; \ + static std::string_view name() noexcept { return #PARENT##sv; } \ + }; + +using namespace std::literals; + +namespace cov::app::collect { + std::optional parse_uint(std::string_view text) { + unsigned value{}; + auto first = text.data(); + auto last = first + text.size(); + auto [end, err] = std::from_chars(first, last, value); + if (end != last || err != std::errc{}) return std::nullopt; + return value; + } + + struct line_handler : handler_interface { + using handler_interface::handler_interface; + static std::string_view name() noexcept { return "line"sv; } + void onElement(char const** attrs) override { + if (!ctx->sink) return; + + std::optional number, hits; + + for (auto attr = attrs; *attr; attr += 2) { + auto const name = std::string_view{attr[0]}; + if (name == "number"sv) { + number = parse_uint(attr[1]); + } else if (name == "hits"sv) { + hits = parse_uint(attr[1]); + } + if (number && hits) break; + } + + if (number && hits) ctx->sink->on_visited(*number, *hits); + } + }; + + SIMPLE_PARENT(lines, line_handler); + SIMPLE_PARENT(methods); + + struct class_handler : handler_interface { + using handler_interface::handler_interface; + static std::string_view name() noexcept { return "class"sv; } + void onElement(char const** attrs) override { + for (auto attr = attrs; *attr; attr += 2) { + auto const name = std::string_view{attr[0]}; + if (name == "filename"sv) { + auto path = get_u8path(make_u8path(ctx->source) / + make_u8path(attr[1])); + ctx->sink = ctx->cvg.get_file_mt(path); + break; + } + } + } + + void onStop() override { + if (ctx->sink) ctx->cvg.handover_mt(std::move(ctx->sink)); + ctx->sink.reset(); + } + + std::unique_ptr onChild( + std::string_view name) override { + if (ctx->sink) + return find_helper::find(name, + ctx); + return {}; + } + }; + + struct source_handler : handler_interface { + using handler_interface::handler_interface; + static std::string_view name() noexcept { return "source"sv; } + void onElement(char const**) override { ctx->source.clear(); } + + void onCharacter(std::string_view chunk) override { + ctx->source.append(chunk); + } + + void onStop() override { + if (ctx->source.empty() || ctx->source.back() != '/') + ctx->source.push_back('/'); + } + }; + + SIMPLE_PARENT(classes, class_handler); + SIMPLE_PARENT(package, classes_handler); + SIMPLE_PARENT(packages, package_handler); + SIMPLE_PARENT(sources, source_handler) + SIMPLE_PARENT(coverage, packages_handler, sources_handler); + SIMPLE_PARENT(document, coverage_handler); + + class parser : public xml::ExpatBase { + std::stack> handlers_{}; + int ignore_depth_{0}; + + public: + parser(msvc_context* ctx) { + handlers_.push(std::make_unique(ctx)); + } + + static bool load(std::filesystem::path const& filename, + config const& cfg, + coverage& cvg) { + auto file = io::fopen(filename); + if (!file) return false; + + msvc_context ctx{.cfg = cfg, .cvg = cvg}; + parser ldr{&ctx}; + + if (!ldr.create()) return false; + ldr.enableElementHandler(); + ldr.enableCdataSectionHandler(); + ldr.enableCharacterDataHandler(); + + char buffer[8196]; + while (auto const byte_count = file.load(buffer, sizeof(buffer))) { + ldr.parse(buffer, static_cast(byte_count), false); + } + ldr.parse(buffer, 0, true); + + return true; + } + + void onStartElement(char const* name, char const** attrs) { + if (!ignore_depth_) { + auto const tag = std::string_view{name}; + auto current = handlers_.top().get(); + auto next = current->onChild(tag); + if (next) { + next->onElement(attrs); + handlers_.push(std::move(next)); + return; + } + } +#if defined(DEBUG_IGNORE) + if (!ignore_depth_) { + fmt::print(stderr, "[{}] >>> {}", ignore_depth_, name); + for (auto attr = attrs; *attr; attr += 2) { + fmt::print(stderr, " {}=\"{}\"", attr[0], attr[1]); + } + fmt::print(stderr, "\n"); + } +#endif + + ++ignore_depth_; + } + + void onEndElement([[maybe_unused]] char const* name) { + if (ignore_depth_) { + --ignore_depth_; +#if defined(DEBUG_IGNORE) + if (!ignore_depth_) + fmt::print(stderr, "[{}] <<< {}\n", ignore_depth_, name); +#endif + return; + } + + auto current = std::move(handlers_.top()); + handlers_.pop(); + current->onStop(); + } + + void onCharacterData(char const* data, int length) { + if (ignore_depth_) return; + auto current = handlers_.top().get(); + current->onCharacter( + std::string_view{data, static_cast(length)}); + } + }; + + handler_interface::~handler_interface() = default; + void handler_interface::onElement(char const**) {} + std::unique_ptr handler_interface::onChild( + std::string_view) { + return {}; + } + void handler_interface::onCharacter(std::string_view) {} + void handler_interface::onStop() {} + + bool occ_load(std::filesystem::path const& filename, + config const& cfg, + coverage& cvg) { + return parser::load(filename, cfg, cvg); + } + +} // namespace cov::app::collect diff --git a/extensions/libs/collect-api/src/occ_xml.hh b/extensions/libs/collect-api/src/occ_xml.hh new file mode 100644 index 00000000..676d9976 --- /dev/null +++ b/extensions/libs/collect-api/src/occ_xml.hh @@ -0,0 +1,62 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#pragma once + +#include +#include +#include +#include +#include + +namespace cov::app::collect { + struct msvc_context { + config const& cfg; + coverage& cvg; + std::string source{}; + std::unique_ptr sink{}; + }; + + struct handler_interface { + msvc_context* ctx{}; + handler_interface(msvc_context* ctx) : ctx{ctx} {} + virtual ~handler_interface(); + virtual void onElement(char const** attrs); + virtual std::unique_ptr onChild(std::string_view); + virtual void onCharacter(std::string_view); + virtual void onStop(); + }; + + template + struct find_helper; + + template <> + struct find_helper<> { + static std::unique_ptr find(std::string_view, + msvc_context*) { + return {}; + } + }; + + template + struct find_helper { + static std::unique_ptr find(std::string_view name, + msvc_context* ctx) { + if (name == First::name()) return std::make_unique(ctx); + return find_helper::find(name, ctx); + } + }; + + template + struct handler_impl : handler_interface { + using handler_interface::handler_interface; + std::unique_ptr onChild( + std::string_view name) override { + return find_helper::find(name, this->ctx); + } + }; + + bool occ_load(std::filesystem::path const& filename, + config const& cfg, + coverage& cvg); +} // namespace cov::app::collect diff --git a/extensions/libs/collect-api/src/queue.hh b/extensions/libs/collect-api/src/queue.hh new file mode 100644 index 00000000..4c04f87f --- /dev/null +++ b/extensions/libs/collect-api/src/queue.hh @@ -0,0 +1,80 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#pragma once + +#include +#include +#include +#include +#include + +namespace cov { + template + class mt_queue { + public: + mt_queue() = default; + mt_queue(mt_queue const& other) { + std::lock_guard lock{other.m_}; + items_ = other.items_; + } + mt_queue& operator=(mt_queue const& other) { + std::unique_lock self{m_, std::defer_lock}; + std::unique_lock lock{other.m_, std::defer_lock}; + std::lock(self, lock); + items_ = other.items_; + return *this; + } + + bool empty() const { + std::lock_guard lock{m_}; + return items_.empty(); + } + + size_t size() const { + std::lock_guard lock{m_}; + return items_.size(); + } + + void wake() { cv_.notify_all(); } + + void push(Element const& value) { + std::lock_guard lock{m_}; + items_.push(value); + ++total_; + // fmt::print(stderr, + // "...new item (size: {}, total: {}, popped: {})\n", + // items_.size(), total_, popped_); + cv_.notify_one(); + } + void push(Element&& value) { + std::lock_guard lock{m_}; + items_.push(std::move(value)); + ++total_; + // fmt::print(stderr, + // "...new item (size: {}, total: {}, popped: {});\n", + // items_.size(), total_, popped_); + cv_.notify_one(); + } + + bool wait_and_pop(Element& result, std::stop_token tok) { + std::unique_lock lock{m_}; + cv_.wait(lock, [this, &tok] { + return !items_.empty() || tok.stop_requested(); + }); + if (!items_.empty()) { + result = std::move(items_.front()); + items_.pop(); + ++popped_; + } + return !tok.stop_requested(); + } + + private: + mutable std::mutex m_{}; + std::condition_variable cv_{}; + size_t total_{}; + size_t popped_{}; + std::queue items_{}; + }; +} // namespace cov diff --git a/extensions/libs/collect-api/src/report.cc b/extensions/libs/collect-api/src/report.cc new file mode 100644 index 00000000..8b03c0b3 --- /dev/null +++ b/extensions/libs/collect-api/src/report.cc @@ -0,0 +1,200 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#include "report.hh" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::literals; + +namespace cov::app::collect { + std::unique_ptr report::get_file_mt(std::string_view path) { + static constexpr auto sep = + static_cast(std::filesystem::path::preferred_separator); + if (!path.starts_with(src_prefix_)) return nullptr; + path = path.substr(src_prefix_.length()); + for (auto const& excl_path : cfg_.exclude) { + auto excl = get_u8path(excl_path); + if (!path.starts_with(excl)) continue; + auto rest = path.substr(excl.size()); + if (rest.empty() || rest.front() == sep) return nullptr; + } + for (auto const& incl_path : cfg_.include) { + auto incl = get_u8path(incl_path); + if (!path.starts_with(incl)) continue; + auto rest = path.substr(incl.size()); + if (rest.empty() || rest.front() == sep) { + auto key = std::string{path.data(), path.size()}; +#ifdef _WIN32 + std::replace(key.begin(), key.end(), '\\', '/'); +#endif + return std::make_unique(key); + } + } + return nullptr; + } + + void report::handover_mt(std::unique_ptr&& returned) { + std::lock_guard lock{m_}; + std::unique_ptr src{static_cast(returned.release())}; + + auto key = src->filename(); + auto it = files_.find(key); + if (it == files_.end()) { + bool ignore = false; + std::tie(it, ignore) = files_.insert({key, std::move(src)}); + return; + } + it->second->merge(*src); + } + + void report::post_process() { + std::vector to_remove{}; + to_remove.reserve(files_.size()); + for (auto& [name, data] : files_) { + if (!data->post_process(cfg_.src_dir / make_u8path(name))) + to_remove.push_back(name); + } + for (auto const& name : to_remove) { + files_.erase(name); + } + } + + io::v1::coverage_stats report::summary() const { + auto result = io::v1::coverage_stats::init(); + result.lines_total = static_cast(files_.size()); // reuse + for (auto& [name, data] : files_) { + result.lines.relevant += + static_cast(data->lines().size()); + for (auto const& [_, count] : data->lines()) { + if (count) ++result.lines.visited; + } + + result.functions.relevant += + static_cast(data->functions().size()); + for (auto const& fun : data->functions()) { + if (fun.count) ++result.functions.visited; + } + } + return result; + } + + json::node report::get_json(json::string const& branch, git::oid_view ref) { + json::array json_files{}; + json_files.reserve(files_.size()); + for (auto const& [filename, file] : files_) { + json_files.push_back(file->get_json(filename)); + } + + return json::map{ + {u8"$schema"s, + to_u8s(fmt::format("https://raw.githubusercontent.com/mzdun/cov/" + "v{}/apps/report-schema.json", + cov::version::string))}, + { + u8"git"s, + json::map{{u8"branch"s, branch}, + {u8"head"s, to_u8s(ref.str())}}, + }, + {u8"files"s, std::move(json_files)}, + }; + } + + std::string report::build_prefix() const { + return fmt::format( + "{}{}", get_u8path(cfg_.src_dir), + static_cast(std::filesystem::path::preferred_separator)); + } + + json::node report::function_cvg::get_json() const { + json::map item{ + {u8"name"s, to_u8s(name)}, + {u8"count"s, count}, + {u8"start_line"s, start.line}, + }; + if (start.col) item[u8"start_column"s] = start.col; + if (stop.line) item[u8"end_line"s] = stop.line; + if (stop.col) item[u8"end_column"s] = stop.col; + if (!demangled.empty()) item[u8"demangled"s] = to_u8s(demangled); + return {std::move(item)}; + } + + void report::file::on_visited(unsigned line, unsigned count) { + lines_[line] += count; + } + + void report::file::on_function(text_pos const& start, + text_pos const& stop, + unsigned count, + std::string const& name, + std::string const& demangled) { + functions_.push_back({.start = start, + .stop = stop, + .count = count, + .name = name, + .demangled = demangled}); + } + + void report::file::merge(file& src) { + for (auto const& [line, count] : src.lines_) + lines_[line] += count; + functions_.insert(functions_.end(), + std::make_move_iterator(src.functions_.begin()), + std::make_move_iterator(src.functions_.end())); + } + + bool report::file::post_process(std::filesystem::path const& path) { + auto input = cov::io::fopen(path, "rb"); + if (!input) { + hash_.clear(); + return false; + } + + std::byte buffer[8192]; + hash::sha1 m{}; + + while (auto size = input.load(buffer, sizeof(buffer))) { + m.update({buffer, size}); + } + hash_ = fmt::format("sha1:{}", m.finalize().str()); + + std::sort(functions_.begin(), functions_.end()); + return true; + } + + json::node report::file::get_json(std::string_view filename) const { + json::map result{{u8"name"s, to_u8s(filename)}, + {u8"digest"s, to_u8s(hash_)}, + {u8"line_coverage"s, get_lines()}}; + if (!functions_.empty()) { + result[u8"functions"s] = get_functions(); + } + + return {std::move(result)}; + } + + json::node report::file::get_lines() const { + json::map result{}; + for (auto const& [line, count] : lines_) { + result[to_u8s(fmt::format("{}", line))] = count; + } + return {std::move(result)}; + } + + json::node report::file::get_functions() const { + json::array result{}; + result.reserve(functions_.size()); + + for (auto const& function : functions_) { + result.emplace_back(function.get_json()); + } + return {result}; + } +} // namespace cov::app::collect diff --git a/extensions/libs/collect-api/src/report.hh b/extensions/libs/collect-api/src/report.hh new file mode 100644 index 00000000..50f40c9f --- /dev/null +++ b/extensions/libs/collect-api/src/report.hh @@ -0,0 +1,81 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cov::app::collect { + struct report : coverage { + report(config const& cfg) : cfg_{cfg} {} + std::unique_ptr get_file_mt( + std::string_view path) override; + void handover_mt(std::unique_ptr&&) override; + + void post_process(); + io::v1::coverage_stats summary() const; + json::node get_json(json::string const& branch, git::oid_view ref); + + private: + std::string build_prefix() const; + + struct function_cvg { + cov::app::collect::text_pos start; + cov::app::collect::text_pos stop; + unsigned count; + std::string name; + std::string demangled; + + json::node get_json() const; + auto operator<=>(function_cvg const&) const noexcept = default; + }; + + struct file : coverage_file { + file(std::string const& filename) : filename_{filename} {} + + void on_visited(unsigned line, unsigned count) override; + void on_function(text_pos const& start, + text_pos const& stop, + unsigned count, + std::string const& name, + std::string const& demangled) override; + + void merge(file& src); + + std::string const& filename() const noexcept { return filename_; } + + std::map const& lines() const noexcept { + return lines_; + } + std::vector const& functions() const noexcept { + return functions_; + } + + bool post_process(std::filesystem::path const& path); + json::node get_json(std::string_view filename) const; + json::node get_lines() const; + json::node get_functions() const; + + private: + std::string filename_; + std::map lines_{}; + std::vector functions_{}; + std::string hash_{}; + }; + + std::mutex m_; + config const& cfg_; + std::string src_prefix_{build_prefix()}; + std::map> files_{}; + }; +} // namespace cov::app::collect diff --git a/extensions/libs/collect-api/src/task_watcher.cc b/extensions/libs/collect-api/src/task_watcher.cc new file mode 100644 index 00000000..0065efff --- /dev/null +++ b/extensions/libs/collect-api/src/task_watcher.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#include "task_watcher.hh" +#include +#include + +using namespace std::literals; + +namespace cov::app::collect { + void task_watcher::print() { + auto const now = clock::now(); + auto const duration = now - last_print_; + if (duration < 1s) return; + last_print_ = now; + fmt::print(stderr, "[{}/{}] collecting...\r", finished_, total_); + std::fflush(stderr); + } + + void task_watcher::task_prepared() { + std::lock_guard lock{m_}; + if (run_all_.stop_requested()) return; + ++counter_; + ++total_; + print(); + } + + void task_watcher::task_finished() { + std::lock_guard lock{m_}; + if (run_all_.stop_requested()) return; + if (counter_) { + ++finished_; + --counter_; + if (!counter_) cv_.notify_one(); + print(); + } + } + + void task_watcher::abort() { + run_all_.request_stop(); + cv_.notify_one(); + } + + void task_watcher::wait() { + std::unique_lock lock{m_}; + cv_.wait(lock, + [this] { return counter_ == 0 || run_all_.stop_requested(); }); + fmt::print(stderr, "[{}/{}] finished \n", finished_, total_); + } + + bool closure::set_return_code(int ret) { + if (!ret) return false; + int zero{}; + if (return_code.compare_exchange_weak(zero, ret, + std::memory_order_relaxed)) + fmt::print(stderr, "[report] aborting...\n"); + watcher.abort(); + return true; + } + + void closure::task_finished() { watcher.task_finished(); } + void closure::push(std::function const& task) { + watcher.task_prepared(); + pool.push(task); + } + + int closure::wait() { + watcher.wait(); + return return_code; + } +} // namespace cov::app::collect diff --git a/extensions/libs/collect-api/src/task_watcher.hh b/extensions/libs/collect-api/src/task_watcher.hh new file mode 100644 index 00000000..f61fff5f --- /dev/null +++ b/extensions/libs/collect-api/src/task_watcher.hh @@ -0,0 +1,45 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace cov::app::collect { + class task_watcher { + using clock = std::chrono::steady_clock; + std::mutex m_; + size_t counter_{}; + size_t total_{}; + size_t finished_{}; + clock::time_point last_print_{}; + std::condition_variable cv_{}; + std::stop_source run_all_{}; + + public: + void print(); + void task_prepared(); + void task_finished(); + void abort(); + void wait(); + }; + + struct closure { + config const& cfg; + coverage& cvg; + thread_pool pool{std::thread::hardware_concurrency() * 2}; + std::atomic return_code{0}; + task_watcher watcher{}; + + bool set_return_code(int ret); + void task_finished(); + void push(std::function const& task); + int wait(); + }; + +} // namespace cov::app::collect diff --git a/extensions/libs/collect-api/src/thread_pool.cc b/extensions/libs/collect-api/src/thread_pool.cc new file mode 100644 index 00000000..129bed3b --- /dev/null +++ b/extensions/libs/collect-api/src/thread_pool.cc @@ -0,0 +1,57 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#include +#include + +using namespace std::literals; + +// define REPORT_TIMES 1 + +namespace cov { + thread_pool::thread_pool(size_t size) { + if (!size) size = 1; + threads_.reserve(size); + for (size_t index = 0; index < size; ++index) + threads_.push_back(std::jthread{thread_proc, std::ref(tasks_)}); + } + thread_pool::~thread_pool() { + for (auto& thread : threads_) + thread.request_stop(); + tasks_.wake(); + } + + std::atomic counter{1}; + void thread_pool::thread_proc(std::stop_token tok, + mt_queue>& tasks) { +#if REPORT_TIMES + using namespace std::chrono; + size_t id{1}, internal{}; + steady_clock::duration runtime{}; + while (!counter.compare_exchange_weak(id, id + 1, + std::memory_order_relaxed)) { + } +#endif + while (!tok.stop_requested()) { + std::function task; + if (tasks.wait_and_pop(task, tok)) { +#if REPORT_TIMES + ++internal; + auto const then = steady_clock::now(); +#endif + task(); +#if REPORT_TIMES + auto const now = steady_clock::now(); + runtime += now - then; +#endif + } + } + +#if REPORT_TIMES + auto const ms = duration_cast(runtime); + auto const cent_s = (ms.count() + 5) / 10; + fmt::print(stderr, "[#{}] {} call{}, {}.{:02} s\n", id, internal, + internal == 1 ? ""sv : "s"sv, cent_s / 100, cent_s % 100); +#endif + } +} // namespace cov diff --git a/extensions/libs/collect-api/src/thread_pool.hh b/extensions/libs/collect-api/src/thread_pool.hh new file mode 100644 index 00000000..8b07b66e --- /dev/null +++ b/extensions/libs/collect-api/src/thread_pool.hh @@ -0,0 +1,25 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#pragma once + +#include +#include +#include +#include + +namespace cov { + class thread_pool { + public: + thread_pool(size_t size = std::thread::hardware_concurrency()); + ~thread_pool(); + + void push(std::function const& task) { tasks_.push(task); } + + private: + static void thread_proc(std::stop_token, + mt_queue>& tasks); + mt_queue> tasks_{}; + std::vector threads_{}; + }; +} // namespace cov diff --git a/extensions/libs/collect-api/src/toolkit.cc b/extensions/libs/collect-api/src/toolkit.cc new file mode 100644 index 00000000..22faec61 --- /dev/null +++ b/extensions/libs/collect-api/src/toolkit.cc @@ -0,0 +1,143 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#include "toolkit.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::literals; + +namespace cov::app::collect { + std::unique_ptr llvm(config const& cfg, + std::span lines); + std::unique_ptr gnu(config const& cfg, + std::span lines); + std::unique_ptr msvc(config const& cfg); + + static constexpr std::array toolkits = {gnu, llvm}; + + coverage_file::~coverage_file() = default; + coverage::~coverage() = default; + toolkit::~toolkit() = default; + + bool toolkit::needs_preprocess() const { return false; } + int toolkit::preprocess(config const&) { return 0; } + + bool toolkit::find_tool(std::filesystem::path& dst, + std::filesystem::path const& hint, + std::string_view tool, + std::span version) { + std::vector names{}; + names.reserve(version.size() + 1); + while (!version.empty()) { + names.push_back( + fmt::format("{}-{}", tool, fmt::join(version, "."))); + version = version.subspan(0, version.size() - 1); + } + names.push_back(fmt::format("{}", tool)); + auto result = platform::find_program(names, hint); + if (!result) return false; + dst = std::move(*result); + return true; + } + + void toolkit::remove_all(std::filesystem::path const& mask) { + walk(mask, [&](std::filesystem::path const& path) { + std::error_code ignore{}; + // fmt::print(stderr, "{}\n", get_u8path(path)); + std::filesystem::remove(path, ignore); + }); + } + + std::unique_ptr recognize_toolkit(config const& cfg) { + if (cfg.compiler.filename() == L"cl.exe"sv) return msvc(cfg); + char ver[] = "--version"; + char* v_args[] = {ver, nullptr}; + auto const proc = platform::run(cfg.compiler, {1, v_args}); + if (proc.return_code) return {}; + + auto view = trim({reinterpret_cast(proc.output.data()), + proc.output.size()}); + auto lines = split(view, '\n'); + + for (auto toolkit : toolkits) { + auto result = toolkit(cfg, lines); + if (result) return result; + } + + fmt::print( + stderr, + "[toolkit] could not find toolkit compatible with the compiler;\n" + "[toolkit] called: `{} --version`\n", + get_u8path(cfg.compiler)); + if (!lines.empty() && !lines.front().empty()) + fmt::print(stderr, "[toolkit] first line: `{}`\n", lines.front()); + + return {}; + } + + void config::load(std::filesystem::path const& filename) { + cfg_dir = std::filesystem::weakly_canonical(filename.parent_path()); + compiler.clear(); + bin_dir.clear(); + src_dir.clear(); + include.clear(); + exclude.clear(); + output.clear(); + + auto cfg = git::config::create(); + cfg.add_file_ondisk(filename); + cfg.foreach_entry([this](git_config_entry const* entry) -> int { + auto view = std::string_view{entry->name}; + if (view.starts_with("collect."sv)) { + auto key = view.substr("collect."sv.length()); + + if (key == "compiler"sv) { + this->compiler = make_u8path(entry->value); + return 0; + } + + if (key == "output"sv) { + this->output = make_u8path(entry->value); + return 0; + } + + if (key == "bin-dir"sv) { + this->bin_dir = std::filesystem::weakly_canonical( + this->cfg_dir / make_u8path(entry->value)); + return 0; + } + + if (key == "src-dir"sv) { + this->src_dir = std::filesystem::weakly_canonical( + this->cfg_dir / make_u8path(entry->value)); + return 0; + } + + if (key == "include"sv) { + this->include.push_back(make_u8path(entry->value)); + return 0; + } + + if (key == "exclude"sv) { + this->exclude.push_back(make_u8path(entry->value)); + return 0; + } + } + + return 0; + }); + + if (src_dir.empty()) src_dir = cfg_dir; + if (bin_dir.empty()) bin_dir = src_dir; + if (output.empty()) output = "cov-collect.json"; + } +} // namespace cov::app::collect diff --git a/extensions/libs/collect-api/src/toolkit.hh b/extensions/libs/collect-api/src/toolkit.hh new file mode 100644 index 00000000..9119a8fe --- /dev/null +++ b/extensions/libs/collect-api/src/toolkit.hh @@ -0,0 +1,88 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cov::app::collect { + struct config { + std::filesystem::path cfg_dir; + std::filesystem::path compiler; + std::filesystem::path bin_dir; + std::filesystem::path src_dir; + std::filesystem::path output; + std::vector include; + std::vector exclude; + + void load(std::filesystem::path const& filename); + }; + + struct text_pos { + unsigned line{0}; + unsigned col{0}; + auto operator<=>(text_pos const&) const noexcept = default; + }; + + struct coverage_file { + virtual ~coverage_file(); + virtual void on_visited(unsigned line, unsigned count) = 0; + virtual void on_function(text_pos const& start, + text_pos const& stop, + unsigned count, + std::string const& name, + std::string const& demangled) = 0; + }; + + struct coverage { + virtual ~coverage(); + virtual std::unique_ptr get_file_mt( + std::string_view path) = 0; + virtual void handover_mt(std::unique_ptr&&) = 0; + }; + + struct toolkit { + virtual ~toolkit(); + virtual std::string_view label() const noexcept = 0; + virtual std::string_view version() const noexcept = 0; + virtual void hello(config const&) const = 0; + virtual void clean(config const& cfg) const = 0; + virtual int observe(config const& cfg, + std::string_view command, + args::arglist arguments) const = 0; + virtual bool needs_preprocess() const; + virtual int preprocess(config const&); + virtual int report(config const&, coverage&) = 0; + + static bool find_tool(std::filesystem::path& dst, + std::filesystem::path const& hint, + std::string_view tool, + std::span version); + static void remove_all(std::filesystem::path const& mask); + }; + + struct arguments { + explicit arguments(std::span stg) { + mem_.reserve(stg.size()); + std::transform(stg.begin(), stg.end(), std::back_inserter(mem_), + [](auto& s) { return s.data(); }); + } + + operator ::args::arglist() noexcept { + return {static_cast(mem_.size()), mem_.data()}; + } + + public: + std::vector mem_; + }; + + std::unique_ptr recognize_toolkit(config const& cfg); +} // namespace cov::app::collect diff --git a/extensions/libs/collect-api/src/walk.cc b/extensions/libs/collect-api/src/walk.cc new file mode 100644 index 00000000..a698ce96 --- /dev/null +++ b/extensions/libs/collect-api/src/walk.cc @@ -0,0 +1,188 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#include "walk.hh" + +#include +#include +#include + +#ifdef WIN32 +#include +#pragma comment(lib, "shlwapi.lib") +namespace cov::app::platform { + bool glob_matches(std::filesystem::path const& match, + std::filesystem::path const name) { + return !!PathMatchSpecW(name.c_str(), match.c_str()); + } +} // namespace cov::app::platform +#else +#include +int fnmatch(const char* pattern, const char* string, int flags); +namespace cov::app::platform { + bool glob_matches(std::filesystem::path const& match, + std::filesystem::path const name) { + return !fnmatch(match.c_str(), name.c_str(), FNM_PATHNAME); + } +} // namespace cov::app::platform +#endif + +using namespace std::literals; + +namespace cov::app::collect { + std::vector> split_path( + std::filesystem::path const& path) { + std::vector> tmp{}; + + for (auto const& part : path) { + auto const u8part = get_u8path(part); + auto const kind = + u8part == "**"sv ? walk_type::recursive + : u8part.find_first_of("*?"sv) != std::string::npos + ? walk_type::directory_glob + : walk_type::directory; + tmp.push_back({kind, part}); + } + + if (!tmp.empty()) { + switch (tmp.back().first) { + case walk_type::directory: + tmp.back().first = walk_type::filename; + break; + case walk_type::directory_glob: + tmp.back().first = walk_type::filename_glob; + break; + default: + break; + } + } + std::vector> rewrite{}; + for (auto& [kind, part] : tmp) { + if (!rewrite.empty() && rewrite.back().first == kind && + (kind == walk_type::directory || + kind == walk_type::recursive)) { + rewrite.back().second /= part; + continue; + } + + rewrite.push_back({kind, part}); + } + + return rewrite; + } + + static void walk_impl( + std::filesystem::path const& root, + std::span const> stack, + std::function const& cb); + + static void notify( + std::filesystem::path const& path, + std::function const& cb) { + std::error_code ec{}; + auto exists = std::filesystem::is_regular_file(path, ec); + if (!ec && exists) cb(path); + } + + static void walk_notify( + std::filesystem::path const& path, + std::filesystem::path const& mask, + std::function const& cb) { + using namespace std::filesystem; + + std::error_code ec{}; + directory_iterator entries{path, ec}; + if (ec) return; + + for (auto const& entry : entries) { + if (!is_regular_file(entry.status())) continue; + if (!platform::glob_matches(mask, entry.path().filename())) + continue; + cb(entry.path()); + } + } + + static void walk_directory( + std::filesystem::path const& path, + std::filesystem::path const& mask, + std::span const> stack, + std::function const& cb) { + using namespace std::filesystem; + + std::error_code ec{}; + directory_iterator entries{path, ec}; + if (ec) return; + + for (auto const& entry : entries) { + if (!is_directory(entry.status())) continue; + if (!platform::glob_matches(mask, entry.path().filename())) + continue; + + walk_impl(entry.path(), stack, cb); + } + } + + static void recurse( + std::filesystem::path const& root, + std::span const> stack, + std::function const& cb) { + using namespace std::filesystem; + + // first, `.`: + walk_impl(root, stack, cb); + + std::error_code ec{}; + recursive_directory_iterator entries{root, ec}; + if (ec) return; + + for (auto const& entry : entries) { + if (!is_directory(entry.status())) continue; + walk_impl(entry.path(), stack, cb); + } + } + + static void walk_impl( + std::filesystem::path const& root, + std::span const> stack, + std::function const& cb) { + if (stack.empty()) return; + auto [type, chunk] = stack.front(); + stack = stack.subspan(1); + + switch (type) { + case walk_type::filename: + notify(root / chunk, cb); + return; + case walk_type::filename_glob: + walk_notify(root, chunk, cb); + return; + case walk_type::directory: + walk_impl(root / chunk, stack, cb); + return; + case walk_type::directory_glob: + walk_directory(root, chunk, stack, cb); + return; + case walk_type::recursive: + if (stack.empty()) return; + recurse(root, stack, cb); + return; + } + } + + void walk_split( + std::span const> stack, + std::function const& cb) { + std::filesystem::path root{"."sv}; + if (!stack.empty() && stack.front().first == walk_type::directory) { + root = stack.front().second; + stack = stack.subspan(1); + } + + walk_impl(root, stack, cb); + } + + void walk(std::filesystem::path const& path, + std::function const& cb) { + walk_split(split_path(path), cb); + } +} // namespace cov::app::collect diff --git a/extensions/libs/collect-api/src/walk.hh b/extensions/libs/collect-api/src/walk.hh new file mode 100644 index 00000000..00852ef3 --- /dev/null +++ b/extensions/libs/collect-api/src/walk.hh @@ -0,0 +1,30 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#pragma once + +#include +#include +#include +#include +#include + +namespace cov::app::collect { + enum class walk_type { + directory, + directory_glob, + filename, + filename_glob, + recursive, + }; + + std::vector> split_path( + std::filesystem::path const& path); + + void walk_split( + std::span const>, + std::function const&); + + void walk(std::filesystem::path const& path, + std::function const&); +} // namespace cov::app::collect diff --git a/extensions/libs/excludes/CMakeLists.txt b/extensions/libs/excludes/CMakeLists.txt new file mode 100644 index 00000000..5fe36f11 --- /dev/null +++ b/extensions/libs/excludes/CMakeLists.txt @@ -0,0 +1,12 @@ +set(SOURCES + src/excludes.cc + src/excludes.hh + src/parser.cc + src/parser.hh +) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES}) + +add_cov_library(excludes ${SOURCES}) +target_link_libraries(excludes PUBLIC ext_native) + +set_parent_scope() diff --git a/extensions/libs/excludes/src/excludes.cc b/extensions/libs/excludes/src/excludes.cc new file mode 100644 index 00000000..1b73ae77 --- /dev/null +++ b/extensions/libs/excludes/src/excludes.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#include "excludes.hh" +#include +#include +#include +#include + +using namespace std::literals; + +namespace cov::app::strip { + static constexpr auto matches_tags = + ctre::search<"(?:G|L|GR)COV_EXCL_(?:START|LINE)\\[([^\\]]+)\\]">; + static constexpr auto matches_line = + ctre::search<"(?:G|L|GR)COV_EXCL_LINE">; + static constexpr auto matches_start = + ctre::search<"(?:G|L|GR)COV_EXCL_START">; + static constexpr auto matches_stop = + ctre::search<"(?:G|L|GR)COV_EXCL_STOP">; + static constexpr auto matches_end = ctre::search<"(?:G|L|GR)COV_EXCL_END">; + + bool excludes::has_any_marker(std::string_view markers) { + bool has = false; + split(markers, ',', [&](unsigned, std::string_view marker) { + if (has) return; + auto const key = tolower(trim(marker)); + for (auto const& known : valid_markers) { + if (known == key) { + has = true; + break; + } + } + }); + return has; + } + + template + unsigned column_from(std::string_view text, Match const& matcher) { + auto const group0 = matcher.template get<0>().to_view(); + auto const diff = group0.data() - text.data(); + decltype(diff) zero = 0; + return static_cast(std::max(zero, diff) + 1); + } + + void excludes::on_line(unsigned line_no, std::string_view text) { + if (trim(text).empty()) { + empties.insert(line_no); + return; + } + + if (auto const has_tags = matches_tags(text); has_tags) { + auto tags = has_tags.get<1>().to_view(); + if (!has_any_marker(tags)) return; + } + + auto switch_off = false; + if (matches_stop(text)) { + switch_off = true; + } else if (auto end_match = matches_end(text); end_match) { + if (inside_exclude) { + auto const end = end_match.get<0>().to_view(); + warn(line_no, column_from(text, end_match), + fmt::format("found {}; did you mean {}?"sv, end, + stop_for(end, "_END"sv))); + } + } else if (auto start_match = matches_start(text); start_match) { + if (inside_exclude) { + warn(line_no, column_from(text, start_match), + fmt::format("double start: found {}"sv, + start_match.get<0>().to_view())); + note(std::get<0>(last_start), std::get<1>(last_start), + "see previous start"); + } else { + last_start = {line_no, column_from(text, start_match), + start_match.get<0>().to_string()}; + } + inside_exclude = true; + } + + if (inside_exclude || matches_line(text)) { + exclude(line_no); + } + + if (switch_off) { + inside_exclude = false; + } + } + + void excludes::after_lines() { + if (inside_exclude) { + std::string_view start = std::get<2>(last_start); + warn(std::get<0>(last_start), std::get<1>(last_start), + fmt::format("{} not matched with {}"sv, start, + stop_for(start, "_START"sv))); + } + } + + void excludes::exclude(unsigned line) { + if (!result.empty() && result.back().end + 1 == line) { + result.back().end = line; + return; + } + result.push_back({line, line}); + } + + void excludes::warn(unsigned line, unsigned column, std::string_view msg) { + message(line, column, "\033[1;35mwarning"sv, msg); + } + + void excludes::note(unsigned line, unsigned column, std::string_view msg) { + message(line, column, "\033[1;36mnote"sv, msg); + } + + void excludes::message(unsigned line, + unsigned column, + std::string_view tag, + std::string_view msg) { + fmt::print(stderr, "\033[1;37m{}:{}:{}:\033[m {}:\033[m {}\n", path, + line, column, tag, msg); + } + + std::string excludes::stop_for(std::string_view start, + std::string_view suffix) { + return fmt::format("{}_STOP", + start.substr(0, start.length() - suffix.length())); + } +} // namespace cov::app::strip diff --git a/extensions/libs/excludes/src/excludes.hh b/extensions/libs/excludes/src/excludes.hh new file mode 100644 index 00000000..4f2d3837 --- /dev/null +++ b/extensions/libs/excludes/src/excludes.hh @@ -0,0 +1,47 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cov::app::strip { + struct excl_block { + unsigned start{}; + unsigned end{}; // GCOV_EXCL_LINE + auto operator<=>(excl_block const&) const noexcept = default; + }; + + using exclude_result = + std::pair, std::set>; + + struct excludes { + std::string path; + std::span valid_markers; + bool inside_exclude{false}; + std::tuple last_start{}; + std::vector result{}; + std::set empties{}; + + bool has_any_marker(std::string_view markers); + void on_line(unsigned line_no, std::string_view text); + void after_lines(); + void exclude(unsigned line); + void warn(unsigned line, unsigned column, std::string_view msg); + void note(unsigned line, unsigned column, std::string_view msg); + void message(unsigned line, + unsigned column, + std::string_view tag, + std::string_view msg); + static std::string stop_for(std::string_view start, + std::string_view suffix); + }; + +} // namespace cov::app::strip diff --git a/extensions/libs/excludes/src/parser.cc b/extensions/libs/excludes/src/parser.cc new file mode 100644 index 00000000..d65ac459 --- /dev/null +++ b/extensions/libs/excludes/src/parser.cc @@ -0,0 +1,28 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#include "parser.hh" + +namespace cov::app::strip { + parser::parser(::args::args_view const& arguments, + str::translator_open_info const& langs) + : base_parser{langs, arguments} { + using namespace str; + + parser_.arg(src_dir, "src").meta(tr_(args::lng::DIR_META)).opt(); + parser_.arg(compiler, "c", "compiler").meta(tr_(args::lng::NAME_META)); + parser_.arg(os, "os").meta(tr_(args::lng::NAME_META)); + } + + void parser::parse() { + parser_.parse(); + + if (!compiler) + compiler = + std::string{default_compiler.data(), default_compiler.size()}; + if (!os) os = std::string{platform.data(), platform.size()}; + + os = tolower(trim(*os)); + compiler = tolower(trim(*compiler)); + } +} // namespace cov::app::strip diff --git a/extensions/libs/excludes/src/parser.hh b/extensions/libs/excludes/src/parser.hh new file mode 100644 index 00000000..7523b455 --- /dev/null +++ b/extensions/libs/excludes/src/parser.hh @@ -0,0 +1,46 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::literals; + +namespace cov::app { + using errlng = str::errors::lng; + using ErrorsStrings = str::errors::Strings; + + template <> + struct lngs_traits + : base_lngs_traits {}; +}; // namespace cov::app + +namespace cov::app::strip { +#ifdef _WIN32 + static constexpr auto default_compiler = "msvc"sv; + static constexpr auto platform = "win32"sv; +#endif +#ifdef __linux__ + static constexpr auto default_compiler = "gcc"sv; + static constexpr auto platform = "posix"sv; +#endif + + struct parser : base_parser { + parser(::args::args_view const& arguments, + str::translator_open_info const& langs); + + void parse(); + + std::string src_dir{".", 1}; + std::optional compiler; + std::optional os; + }; +} // namespace cov::app::strip diff --git a/extensions/libs/native/CMakeLists.txt b/extensions/libs/native/CMakeLists.txt new file mode 100644 index 00000000..fa9edbc6 --- /dev/null +++ b/extensions/libs/native/CMakeLists.txt @@ -0,0 +1,15 @@ +set(SOURCES + src/platform.cc + include/native/expat.hh + include/native/platform.hh + include/native/str.hh +) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES}) + +add_cov_library(ext_native ${SOURCES}) +target_include_directories(ext_native PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include/native +) +target_link_libraries(ext_native PUBLIC ctre::ctre expat::expat app_main) + +set_parent_scope() diff --git a/extensions/libs/native/include/native/expat.hh b/extensions/libs/native/include/native/expat.hh new file mode 100644 index 00000000..9d4451f9 --- /dev/null +++ b/extensions/libs/native/include/native/expat.hh @@ -0,0 +1,337 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#pragma once + +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#endif +// GCOV_EXCL_START +namespace xml { + template + class ExpatBase { + public: + ExpatBase() : parser_(nullptr) {} + ~ExpatBase() { destroy(); } + + bool create(const XML_Char* encoding = nullptr, + const XML_Char* sep = nullptr) { + destroy(); + if (encoding != nullptr && encoding[0] == 0) encoding = nullptr; + + if (sep != nullptr && sep[0] == 0) sep = nullptr; + + parser_ = XML_ParserCreate_MM(encoding, nullptr, sep); + if (parser_ == nullptr) return false; + + Final* pThis = static_cast(this); + pThis->onPostCreate(); + + XML_SetUserData(parser_, static_cast(pThis)); + return true; + } + + bool isCreated() const { return parser_ != nullptr; } + + void destroy() { + if (parser_) XML_ParserFree(parser_); + parser_ = nullptr; + } + + bool parse(const char* buffer, int length = -1, bool isFinal = true) { + if (length < 0) length = static_cast(std::strlen(buffer)); + + return XML_Parse(parser_, buffer, length, isFinal) != 0; + } + + bool parseBuffer(int length, bool isFinal = true) { + return XML_ParseBuffer(parser_, length, isFinal) != 0; + } + + void* getBuffer(int length) { return XML_GetBuffer(parser_, length); } + + void enableStartElementHandler(bool enable = true) { + XML_SetStartElementHandler(parser_, + enable ? startElementHandler : nullptr); + } + + void enableEndElementHandler(bool enable = true) { + XML_SetEndElementHandler(parser_, + enable ? endElementHandler : nullptr); + } + + void enableElementHandler(bool enable = true) { + enableStartElementHandler(enable); + enableEndElementHandler(enable); + } + + void enableCharacterDataHandler(bool enable = true) { + XML_SetCharacterDataHandler( + parser_, enable ? characterDataHandler : nullptr); + } + + void enableProcessingInstructionHandler(bool enable = true) { + XML_SetProcessingInstructionHandler( + parser_, enable ? ProcessingInstructionHandler : nullptr); + } + + void enableCommentHandler(bool enable = true) { + XML_SetCommentHandler(parser_, enable ? commentHandler : nullptr); + } + + void enableStartCdataSectionHandler(bool enable = true) { + XML_SetStartCdataSectionHandler( + parser_, enable ? startCdataSectionHandler : nullptr); + } + + void enableEndCdataSectionHandler(bool enable = true) { + XML_SetEndCdataSectionHandler( + parser_, enable ? endCdataSectionHandler : nullptr); + } + + void enableCdataSectionHandler(bool enable = true) { + enableStartCdataSectionHandler(enable); + enableEndCdataSectionHandler(enable); + } + + void enableDefaultHandler(bool enable = true, bool expand = true) { + if (expand) { + XML_SetDefaultHandlerExpand(parser_, + enable ? defaultHandler : nullptr); + } else { + XML_SetDefaultHandler(parser_, + enable ? defaultHandler : nullptr); + } + } + + void enableExternalEntityRefHandler(bool enable = true) { + XML_SetExternalEntityRefHandler( + parser_, enable ? externalEntityRefHandler : nullptr); + } + + void enableUnknownEncodingHandler(bool enable = true) { + Final* pThis = static_cast(this); + XML_SetUnknownEncodingHandler( + parser_, enable ? unknownEncodingHandler : nullptr, + enable ? static_cast(pThis) : nullptr); + } + + void enableStartNamespaceDeclHandler(bool enable = true) { + XML_SetStartNamespaceDeclHandler( + parser_, enable ? startNamespaceDeclHandler : nullptr); + } + + void enableEndNamespaceDeclHandler(bool enable = true) { + XML_SetEndNamespaceDeclHandler( + parser_, enable ? endNamespaceDeclHandler : nullptr); + } + + void enableNamespaceDeclHandler(bool enable = true) { + enableStartNamespaceDeclHandler(enable); + enableEndNamespaceDeclHandler(enable); + } + + void enableXmlDeclHandler(bool enable = true) { + XML_SetXmlDeclHandler(parser_, enable ? xmlDeclHandler : nullptr); + } + + void enableStartDoctypeDeclHandler(bool enable = true) { + XML_SetStartDoctypeDeclHandler( + parser_, enable ? startDoctypeDeclHandler : nullptr); + } + + void enableEndDoctypeDeclHandler(bool enable = true) { + XML_SetEndDoctypeDeclHandler( + parser_, enable ? endDoctypeDeclHandler : nullptr); + } + + void enableDoctypeDeclHandler(bool enable = true) { + enableStartDoctypeDeclHandler(enable); + enableEndDoctypeDeclHandler(enable); + } + + enum XML_Error getErrorCode() { return XML_GetErrorCode(parser_); } + + long getCurrentByteIndex() { return XML_GetCurrentByteIndex(parser_); } + + int getCurrentLineNumber() { return XML_GetCurrentLineNumber(parser_); } + + int getCurrentColumnNumber() { + return XML_GetCurrentColumnNumber(parser_); + } + + int getCurrentByteCount() { return XML_GetCurrentByteCount(parser_); } + + const char* getInputContext(int* offset, int* size) { + return XML_GetInputContext(parser_, offset, size); + } + + const XML_LChar* getErrorString() { + return XML_ErrorString(getErrorCode()); + } + + static const XML_LChar* getExpatVersion() { return XML_ExpatVersion(); } + + static XML_Expat_Version getExpatVersionInfo() { + return XML_ExpatVersionInfo(); + } + + static const XML_LChar* getErrorString(enum XML_Error error) { + return XML_ErrorString(error); + } + + void onStartElement(const XML_Char* name, const XML_Char** attrs) {} + + void onEndElement(const XML_Char* name) {} + + void onCharacterData(const XML_Char* data, int length) {} + + void onProcessingInstruction(const XML_Char* target, + const XML_Char* data) {} + + void onComment(const XML_Char* data) {} + + void onStartCdataSection() {} + + void onEndCdataSection() {} + + void onDefault(const XML_Char* data, int length) {} + + bool onExternalEntityRef(const XML_Char* context, + const XML_Char* base, + const XML_Char* systemID, + const XML_Char* publicID) { + return false; + } + + bool onUnknownEncoding(const XML_Char* name, XML_Encoding* info) { + return false; + } + + void onStartNamespaceDecl(const XML_Char* prefix, const XML_Char* uri) { + } + + void onEndNamespaceDecl(const XML_Char* prefix) {} + + void onXmlDecl(const XML_Char* version, + const XML_Char* encoding, + bool standalone) {} + + void onStartDoctypeDecl(const XML_Char* doctypeName, + const XML_Char* systemId, + const XML_Char* publicID, + bool hasInternalSubset) {} + + void onEndDoctypeDecl() {} + + protected: + void onPostCreate() {} + + static auto ptr(void* userData) { + return static_cast(userData); + } + + template + static auto bool2(boolean_like result) { + return result ? true_value : false_value; + } + + static void startElementHandler(void* userData, + const XML_Char* name, + const XML_Char** attrs) { + ptr(userData)->onStartElement(name, attrs); + } + + static void endElementHandler(void* userData, const XML_Char* name) { + ptr(userData)->onEndElement(name); + } + + static void characterDataHandler(void* userData, + const XML_Char* data, + int length) { + ptr(userData)->onCharacterData(data, length); + } + + static void ProcessingInstructionHandler(void* userData, + const XML_Char* target, + const XML_Char* data) { + ptr(userData)->onProcessingInstruction(target, data); + } + + static void commentHandler(void* userData, const XML_Char* data) { + ptr(userData)->onComment(data); + } + + static void startCdataSectionHandler(void* userData) { + ptr(userData)->onStartCdataSection(); + } + + static void endCdataSectionHandler(void* userData) { + ptr(userData)->onEndCdataSection(); + } + + static void defaultHandler(void* userData, + const XML_Char* data, + int length) { + ptr(userData)->onDefault(data, length); + } + + static int externalEntityRefHandler(XML_Parser parser, + const XML_Char* context, + const XML_Char* base, + const XML_Char* systemID, + const XML_Char* publicID) { + auto const userData = XML_GetUserData(parser); + return bool2<1, 0>(ptr(userData)->onExternalEntityRef( + context, base, systemID, publicID)); + } + + static int unknownEncodingHandler(void* userData, + const XML_Char* name, + XML_Encoding* info) { + return bool2( + ptr(userData)->onUnknownEncoding(name, info)); + } + + static void startNamespaceDeclHandler(void* userData, + const XML_Char* prefix, + const XML_Char* uri) { + ptr(userData)->onStartNamespaceDecl(prefix, uri); + } + + static void endNamespaceDeclHandler(void* userData, + const XML_Char* prefix) { + ptr(userData)->onEndNamespaceDecl(prefix); + } + + static void xmlDeclHandler(void* userData, + const XML_Char* version, + const XML_Char* encoding, + int standalone) { + ptr(userData)->onXmlDecl(version, encoding, standalone != 0); + } + + static void startDoctypeDeclHandler(void* userData, + const XML_Char* doctypeName, + const XML_Char* systemId, + const XML_Char* publicID, + int hasInternalSubset) { + ptr(userData)->onStartDoctypeDecl(doctypeName, systemId, publicID, + hasInternalSubset != 0); + } + + static void endDoctypeDeclHandler(void* userData) { + ptr(userData)->onEndDoctypeDecl(); + } + + XML_Parser parser_; + }; +} // namespace xml +// GCOV_EXCL_STOP +#if defined(__clang__) +#pragma clang diagnostic pop +#endif diff --git a/extensions/libs/native/include/native/platform.hh b/extensions/libs/native/include/native/platform.hh new file mode 100644 index 00000000..d71e5c76 --- /dev/null +++ b/extensions/libs/native/include/native/platform.hh @@ -0,0 +1,13 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#pragma once + +#include +#include + +namespace cov::app::platform { + std::filesystem::path const& sys_root(); + std::filesystem::path locale_dir(); + std::vector read_input(); +}; // namespace cov::app::platform diff --git a/extensions/libs/native/include/native/str.hh b/extensions/libs/native/include/native/str.hh new file mode 100644 index 00000000..e5aef6e0 --- /dev/null +++ b/extensions/libs/native/include/native/str.hh @@ -0,0 +1,75 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#pragma once + +#include +#include +#include +#include +#include + +namespace cov::app { + inline std::u8string_view to_u8(std::string_view str) { + return {reinterpret_cast(str.data()), str.size()}; + } + + inline std::u8string to_u8s(std::string_view str) { + return {reinterpret_cast(str.data()), str.size()}; + } + + inline std::string_view from_u8(std::u8string_view str) { + return {reinterpret_cast(str.data()), str.size()}; + } + + inline std::string from_u8s(std::u8string_view str) { + return {reinterpret_cast(str.data()), str.size()}; + } + + inline std::string_view trim(std::string_view s) { + while (!s.empty() && + std::isspace(static_cast(s.front()))) + s = s.substr(1); + while (!s.empty() && std::isspace(static_cast(s.back()))) + s = s.substr(0, s.size() - 1); + return s; + } + + inline std::string tolower(std::string_view s) { + std::string result{}; + result.assign(s); + for (auto& c : result) { + c = static_cast(std::tolower(static_cast(c))); + } + return result; + } // GCOV_EXCL_LINE[GCC] + + template + inline void split(std::string_view text, char sep, Callback&& cb) { + auto pos = text.find(sep); + decltype(pos) prev = 0; + + unsigned block = 0; + while (pos != std::string_view::npos) { + auto const block_end = pos; + auto const block_start = prev; + ++block; + prev = pos + 1; + pos = text.find(sep, prev); + cb(block, text.substr(block_start, block_end - block_start)); + } + ++block; + cb(block, text.substr(prev)); + } + + inline std::vector split(std::string_view text, + char sep) { + size_t length{}; + split(text, sep, [&length](auto, auto const&) { ++length; }); + std::vector result{}; + result.reserve(length); + split(text, sep, + [&result](auto, auto view) { result.push_back(view); }); + return result; + } // GCOV_EXCL_LINE[GCC] +}; // namespace cov::app diff --git a/extensions/libs/native/src/platform.cc b/extensions/libs/native/src/platform.cc new file mode 100644 index 00000000..c7ef2909 --- /dev/null +++ b/extensions/libs/native/src/platform.cc @@ -0,0 +1,35 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#include "platform.hh" +#include +#include + +using namespace std::literals; + +namespace cov::app::platform { + std::filesystem::path const& sys_root() { + static auto const root = [] { + auto const dir = exec_path().parent_path(); + // share/cov-XY/filters + return dir.parent_path().parent_path().parent_path(); + }(); + + return root; + } + + std::filesystem::path locale_dir() { + return sys_root() / directory_info::share / "locale"sv; + } // GCOV_EXCL_LINE + + std::vector read_input() { + std::vector result{}; + char8_t buffer[8192]; + + while (auto read = std::fread(buffer, 1, sizeof(buffer), stdin)) { + result.insert(result.end(), buffer, buffer + read); + } + + return result; + } // GCOV_EXCL_LINE +}; // namespace cov::app::platform diff --git a/extensions/tests/CMakeLists.txt b/extensions/tests/CMakeLists.txt new file mode 100644 index 00000000..7636b0fb --- /dev/null +++ b/extensions/tests/CMakeLists.txt @@ -0,0 +1,32 @@ +macro(mock_ex name source linkname) + add_executable(mock-${name} mock_${source}.cc) + set_target_properties(mock-${name} PROPERTIES OUTPUT_NAME ${linkname}) + target_link_libraries(mock-${name} PRIVATE fmt::fmt mbits::args app) + set_target_properties(mock-${name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/mocks") + fix_vs_modules(mock-${name}) + + foreach(BUILD_TYPE DEBUG RELEASE RELWITHDEBINFO MINSIZEREL) + set_target_properties( + mock-${name} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY_${BUILD_TYPE} "${CMAKE_BINARY_DIR}/mocks" + ) + endforeach() +endmacro() + +macro(mock name linkname) + string(REPLACE "-" "_" SAFE ${name}) + mock_ex(${name} ${SAFE} ${linkname}) +endmacro() + +if (COV_TESTING) + mock(gcc hal9000-lcars-GLaDOS-g++-17) + mock(gcov hal9000-lcars-GLaDOS-gcov-17) + mock(clang clang++-17) + mock(llvm-cov llvm-cov-17) + mock(llvm-profdata llvm-profdata-17) + mock(cl cl) + mock(occ OpenCppCoverage) + + set_target_properties(mock-cl PROPERTIES SUFFIX ".exe") +endif() diff --git a/extensions/tests/mock_cl.cc b/extensions/tests/mock_cl.cc new file mode 100644 index 00000000..d7a76199 --- /dev/null +++ b/extensions/tests/mock_cl.cc @@ -0,0 +1,10 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#include + +int main(int, char**) { + fmt::print( + "1701\n" + "170174656\n"); +} diff --git a/extensions/tests/mock_clang.cc b/extensions/tests/mock_clang.cc new file mode 100644 index 00000000..58c63a42 --- /dev/null +++ b/extensions/tests/mock_clang.cc @@ -0,0 +1,16 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#include +#include + +using namespace std::literals; + +int main(int, char**) { + static constexpr auto version = "17.0.1"sv; + fmt::print( + "Chell clang version {}\n" + "Target: hal9000-lcars-GLaDOS\n" + "Thread model: Elim Garak\n", + version); +} diff --git a/extensions/tests/mock_gcc.cc b/extensions/tests/mock_gcc.cc new file mode 100644 index 00000000..72f4932c --- /dev/null +++ b/extensions/tests/mock_gcc.cc @@ -0,0 +1,19 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#include +#include +#include +#include + +using namespace std::literals; + +int main(int argc, char** argv) { + using namespace cov::app; + + auto const args = ::args::from_main(argc, argv); + static constexpr auto version = "17.0.1"sv; + auto progname = get_u8path(make_u8path(args.progname).filename()); + + fmt::print("{0} (Chell {1}-D) {1}\n", progname, version); +} diff --git a/extensions/tests/mock_gcov.cc b/extensions/tests/mock_gcov.cc new file mode 100644 index 00000000..234f17e2 --- /dev/null +++ b/extensions/tests/mock_gcov.cc @@ -0,0 +1,4 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +int main() {} diff --git a/extensions/tests/mock_llvm_cov.cc b/extensions/tests/mock_llvm_cov.cc new file mode 100644 index 00000000..234f17e2 --- /dev/null +++ b/extensions/tests/mock_llvm_cov.cc @@ -0,0 +1,4 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +int main() {} diff --git a/extensions/tests/mock_llvm_profdata.cc b/extensions/tests/mock_llvm_profdata.cc new file mode 100644 index 00000000..234f17e2 --- /dev/null +++ b/extensions/tests/mock_llvm_profdata.cc @@ -0,0 +1,4 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +int main() {} diff --git a/extensions/tests/mock_occ.cc b/extensions/tests/mock_occ.cc new file mode 100644 index 00000000..234f17e2 --- /dev/null +++ b/extensions/tests/mock_occ.cc @@ -0,0 +1,4 @@ +// Copyright (c) 2023 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +int main() {} diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index f813e044..6ccc80fa 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -7,3 +7,8 @@ add_subdirectory(date) add_subdirectory(json) set_target_properties(json PROPERTIES FOLDER libs) + +fix_vs_modules(date-tz) +fix_vs_modules(json) + +set_parent_scope() diff --git a/external/json b/external/json index 424d468d..111bcdc5 160000 --- a/external/json +++ b/external/json @@ -1 +1 @@ -Subproject commit 424d468df53020a47f2dd8f1c585b4231a125004 +Subproject commit 111bcdc5c2f88976d84993ccc0bc8212faf47299 diff --git a/flow.py b/flow.py index 73076783..d6bba687 100755 --- a/flow.py +++ b/flow.py @@ -31,4 +31,7 @@ def list_flows(): sys.exit(0) args = [sys.executable, os.path.join(__flow__, f"{sys.argv[1]}.py"), *sys.argv[2:]] -sys.exit(subprocess.run(args, shell=False).returncode) +try: + sys.exit(subprocess.run(args, shell=False).returncode) +except KeyboardInterrupt: + sys.exit(1) diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index 510d5116..5535bf47 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -8,3 +8,5 @@ add_subdirectory(hilite) add_subdirectory(cov-api) add_subdirectory(app) add_subdirectory(cov-rt) + +set_parent_scope() diff --git a/libs/app/CMakeLists.txt b/libs/app/CMakeLists.txt index b91e743a..b2bd2bdf 100644 --- a/libs/app/CMakeLists.txt +++ b/libs/app/CMakeLists.txt @@ -9,18 +9,26 @@ set(LOCALE_DIR "${PROJECT_BINARY_DIR}/${SHARE_DIR}/locale") set(SOURCES src/args.cc + src/path_env.hh src/path.cc src/tr.cc include/cov/app/args.hh include/cov/app/path.hh + include/cov/app/run.hh include/cov/app/tr.hh ) if (UNIX) - list(APPEND SOURCES src/posix.cc) + list(APPEND SOURCES + src/posix/exec_path.cc + src/posix/run.cc + ) elseif(WIN32) - list(APPEND SOURCES src/win32.cc) + list(APPEND SOURCES + src/win32/exec_path.cc + src/win32/run.cc + ) endif() set(GEN_SOURCES @@ -56,17 +64,10 @@ source_group(TREE ${CMAKE_CURRENT_BINARY_DIR} FILES ${GEN_SOURCES} PREFIX gen) source_group(TREE "${PROJECT_BINARY_DIR}/${SHARE_DIR}" FILES ${SHARE_SOURCES} PREFIX gen/share) source_group(TREE ${DATA_DIR} FILES ${DATA_SOURCES} PREFIX data) -add_library(app STATIC ${SOURCES} ${GEN_SOURCES} ${SHARE_SOURCES} ${DATA_SOURCES}) -target_compile_options(app PRIVATE ${ADDITIONAL_WALL_FLAGS}) -target_link_options(app PRIVATE ${ADDITIONAL_LINK_FLAGS}) -target_include_directories(app PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_BINARY_DIR}/include) +add_cov_library(app ${SOURCES} ${GEN_SOURCES} ${SHARE_SOURCES} ${DATA_SOURCES}) target_link_libraries(app PUBLIC cov-api lighter mbits::liblngs mbits::args) -add_library(app_main STATIC src/main.cc) -target_compile_options(app_main PRIVATE ${ADDITIONAL_WALL_FLAGS}) -target_link_options(app_main PRIVATE ${ADDITIONAL_LINK_FLAGS}) +add_cov_library(app_main src/main.cc) target_link_libraries(app_main PUBLIC app) if (WIN32) @@ -145,3 +146,5 @@ cpack_add_component(libapp DISPLAY_NAME "libapp" GROUP devel ) + +set_parent_scope() diff --git a/libs/app/include/cov/app/run.hh b/libs/app/include/cov/app/run.hh new file mode 100644 index 00000000..6a5a1f39 --- /dev/null +++ b/libs/app/include/cov/app/run.hh @@ -0,0 +1,48 @@ +// Copyright (c) 2022 Marcin Zdun +// This code is licensed under MIT license (see LICENSE for details) + +#pragma once + +#include +#include +#include +#include +#include + +namespace cov::app::platform { + int run_tool(std::filesystem::path const& tooldir, + std::string_view tool, + args::arglist args); + + struct captured_output { + std::vector output{}; + std::vector error{}; + int return_code{}; + }; + + captured_output run_filter(std::filesystem::path const& filter_dir, + std::filesystem::path const& cwd, + std::string_view filter, + args::arglist args, + std::vector const& input); + + std::optional find_program( + std::span names, + std::filesystem::path const& hint = {}); + + captured_output run(std::filesystem::path const& exec, + args::arglist args, + std::filesystem::path const& cwd, + std::vector const& input); + captured_output run(std::filesystem::path const& exec, + args::arglist args, + std::vector const& input = {}); + int call(std::filesystem::path const& exec, + args::arglist args, + std::filesystem::path const& cwd, + std::vector const& input); + + int call(std::filesystem::path const& exec, + args::arglist args, + std::vector const& input = {}); +} // namespace cov::app::platform diff --git a/libs/app/src/args.cc b/libs/app/src/args.cc index eec141fd..3eb0e4bd 100644 --- a/libs/app/src/args.cc +++ b/libs/app/src/args.cc @@ -73,7 +73,6 @@ namespace cov::app { str::errors::Strings const& tr, str::args::Strings const& args) const { auto const msg = message(ec, tr, args); - parser_.short_help(stderr); std::fputs(msg.c_str(), stderr); std::fputc('\n', stderr); std::exit(2); diff --git a/libs/cov-rt/src/path_env.hh b/libs/app/src/path_env.hh similarity index 100% rename from libs/cov-rt/src/path_env.hh rename to libs/app/src/path_env.hh diff --git a/libs/app/src/posix.cc b/libs/app/src/posix/exec_path.cc similarity index 100% rename from libs/app/src/posix.cc rename to libs/app/src/posix/exec_path.cc diff --git a/libs/cov-rt/src/posix.cc b/libs/app/src/posix/run.cc similarity index 73% rename from libs/cov-rt/src/posix.cc rename to libs/app/src/posix/run.cc index 96b8cc9a..24af66f1 100644 --- a/libs/cov-rt/src/posix.cc +++ b/libs/app/src/posix/run.cc @@ -4,11 +4,14 @@ #include #include #include -#include +#include +#include #include #include #include -#include "path_env.hh" +#include "../path_env.hh" + +// define STDOUT_DUMP #ifdef RUNNING_GCOV extern "C" { @@ -35,6 +38,8 @@ namespace cov::app::platform { std::filesystem::path where(std::filesystem::path const& bin, char const* environment_variable, std::string const& program) { + if (program.find('/') != std::string::npos) return program; + auto path_str = env(environment_variable); auto dirs = split(bin.native(), path_str); @@ -140,6 +145,40 @@ namespace cov::app::platform { this, std::ref(src)); } +#ifdef STDOUT_DUMP + static void dump(std::span buffer) { + static constexpr auto len = 20zu; + char line[len * 4 + 2]; + line[len * 4] = '\n'; + line[len * 4 + 1] = 0; + auto index = 0zu; + for (auto b : buffer) { + if (index == len) { + fputs(line, stdout); + index = 0; + } + + static constexpr char alphabet[] = "0123456789ABCDEF"; + auto const c = static_cast(b); + line[index * 3] = alphabet[(c >> 4) & 0xF]; + line[index * 3 + 1] = alphabet[c & 0xF]; + line[index * 3 + 2] = ' '; + line[len * 3 + index] = + std::isprint(c) ? static_cast(c) : '.'; + ++index; + } + if (index < len) { + for (; index < len; ++index) { + line[index * 3] = ' '; + line[index * 3 + 1] = ' '; + line[index * 3 + 2] = ' '; + line[len * 3 + index] = ' '; + } + fputs(line, stdout); + } + } +#endif + std::thread async_read(std::vector& dst) { return std::thread( [](int fd, std::vector& bytes) { @@ -149,6 +188,9 @@ namespace cov::app::platform { auto const actual = ::read(fd, buffer, std::size(buffer)); if (actual <= 0) break; +#ifdef STDOUT_DUMP + dump({buffer, buffer + actual}); +#endif bytes.insert(bytes.end(), buffer, buffer + actual); } }, @@ -280,9 +322,50 @@ namespace cov::app::platform { captured_output run_filter(std::filesystem::path const& filter_dir, std::filesystem::path const& cwd, std::string_view filter, + args::arglist args, std::vector const& input) { return execute(filter_dir, "COV_FILTER_PATH", - std::string(filter.data(), filter.size()), {}, &input, + std::string(filter.data(), filter.size()), args, &input, &cwd, true); } + + std::optional find_program( + std::span names, + std::filesystem::path const& hint) { + for (auto const& name : names) { + auto candidate = where(hint, "PATH", name); + if (!candidate.empty()) return candidate; + } + return std::nullopt; + } + + captured_output run(std::filesystem::path const& exec, + args::arglist args, + std::filesystem::path const& cwd, + std::vector const& input) { + return execute({}, "PATH", get_u8path(exec), args, &input, &cwd, true); + } + + captured_output run(std::filesystem::path const& exec, + args::arglist args, + std::vector const& input) { + return execute({}, "PATH", get_u8path(exec), args, &input, nullptr, + true); + } + + int call(std::filesystem::path const& exec, + args::arglist args, + std::filesystem::path const& cwd, + std::vector const& input) { + return execute({}, "PATH", get_u8path(exec), args, &input, &cwd, false) + .return_code; + } + + int call(std::filesystem::path const& exec, + args::arglist args, + std::vector const& input) { + return execute({}, "PATH", get_u8path(exec), args, &input, nullptr, + false) + .return_code; + } } // namespace cov::app::platform diff --git a/libs/app/src/win32.cc b/libs/app/src/win32/exec_path.cc similarity index 100% rename from libs/app/src/win32.cc rename to libs/app/src/win32/exec_path.cc diff --git a/libs/cov-rt/src/win32.cc b/libs/app/src/win32/run.cc similarity index 87% rename from libs/cov-rt/src/win32.cc rename to libs/app/src/win32/run.cc index e4659c19..fbb21161 100644 --- a/libs/cov-rt/src/win32.cc +++ b/libs/app/src/win32/run.cc @@ -6,15 +6,15 @@ #include #include #include -#include +#include #include #include #include #include #include #include +#include "../path_env.hh" #include "fmt/format.h" -#include "path_env.hh" namespace cov::app::platform { namespace { @@ -370,21 +370,38 @@ namespace cov::app::platform { return !ec && std::filesystem::is_regular_file(status); } + std::vector all_lower( + std::span items) { + std::vector result{}; + result.reserve(items.size()); + for (auto view : items) { + std::wstring arg; + arg.assign(view); + CharLowerW(arg.data()); + result.push_back(std::move(arg)); + } + return result; + } + std::filesystem::path where(std::filesystem::path const& bin, wchar_t const* environment_variable, std::wstring const& program) { + auto ext_str = env(L"PATHEXT"); + auto path_ext = all_lower(split(std::wstring{}, ext_str)); + + if (program.find_first_of(L"\\/"sv) != std::string::npos) { + return program; + } + auto path_str = env(environment_variable); auto dirs = split(bin.native(), path_str); - auto ext_str = env(L"PATHEXT"); - auto path_ext = split(std::wstring{}, ext_str); - for (auto const& dir : dirs) { for (auto const ext : path_ext) { auto path = std::filesystem::path{dir} / filename(program, ext); if (file_exists(path)) { - return std::filesystem::canonical(path); + return path; } } } @@ -522,8 +539,50 @@ namespace cov::app::platform { captured_output run_filter(std::filesystem::path const& filter_dir, std::filesystem::path const& cwd, std::string_view filter, + args::arglist args, std::vector const& input) { - return execute(filter_dir, L"COV_FILTER_PATH", from_utf8(filter), {}, + return execute(filter_dir, L"COV_FILTER_PATH", from_utf8(filter), args, &input, &cwd, true); } + + std::optional find_program( + std::span names, + std::filesystem::path const& hint) { + for (auto const& name : names) { + auto candidate = where(hint, L"PATH", from_utf8(name)); + if (!candidate.empty()) return candidate; + } + return std::nullopt; + } + + captured_output run(std::filesystem::path const& exec, + args::arglist args, + std::filesystem::path const& cwd, + std::vector const& input) { + return execute({}, L"PATH", exec.native(), args, &input, &cwd, true); + } + + captured_output run(std::filesystem::path const& exec, + args::arglist args, + std::vector const& input) { + return execute({}, L"PATH", exec.native(), args, &input, nullptr, true); + } + + int call(std::filesystem::path const& exec, + args::arglist args, + std::filesystem::path const& cwd, + std::vector const& input) { + auto input_ptr = input.empty() ? nullptr : &input; + return execute({}, L"PATH", exec.native(), args, input_ptr, &cwd, false) + .return_code; + } + + int call(std::filesystem::path const& exec, + args::arglist args, + std::vector const& input) { + auto input_ptr = input.empty() ? nullptr : &input; + return execute({}, L"PATH", exec.native(), args, input_ptr, nullptr, + false) + .return_code; + } } // namespace cov::app::platform diff --git a/libs/cov-api/CMakeLists.txt b/libs/cov-api/CMakeLists.txt index fd3c4bef..d14f08ae 100644 --- a/libs/cov-api/CMakeLists.txt +++ b/libs/cov-api/CMakeLists.txt @@ -106,12 +106,7 @@ set(COV_SOURCES ) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${GIT2_SOURCES} ${COV_SOURCES}) -add_library(cov-api STATIC ${GIT2_SOURCES} ${COV_SOURCES}) -target_compile_options(cov-api PRIVATE ${ADDITIONAL_WALL_FLAGS}) -target_link_options(cov-api PRIVATE ${ADDITIONAL_LINK_FLAGS}) -target_include_directories(cov-api PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_BINARY_DIR}/include) +add_cov_library(cov-api ${GIT2_SOURCES} ${COV_SOURCES}) target_link_libraries(cov-api PUBLIC libgit2::libgit2 fmt::fmt date::date-tz mbits::args json PRIVATE hilite openssl::openssl) set_target_properties(cov-api PROPERTIES FOLDER libs) @@ -139,45 +134,18 @@ if (COV_TESTING) ${COV_TEST_SRCS_CXX} ${COV_STRESS_TEST_SRCS_CC}) - add_executable(git2-test ${GIT2_TEST_SRCS_CC} ${GIT2_TEST_SRCS_CPP} ${GIT2_TEST_SRCS_CXX}) - add_executable(git2-stress-test ${GIT2_STRESS_TEST_SRCS_CC} tests/git2-c++/setup.cc) - add_executable(cov-test ${COV_TEST_SRCS_CC} ${COV_TEST_SRCS_CPP} ${COV_TEST_SRCS_CXX}) - add_executable(cov-stress-test ${COV_STRESS_TEST_SRCS_CC} tests/cov/setup.cc tests/git2-c++/stress/new.cc) + add_cov_test(git2 ${GIT2_TEST_SRCS_CC} ${GIT2_TEST_SRCS_CPP} ${GIT2_TEST_SRCS_CXX}) + add_cov_test(git2-stress ${GIT2_STRESS_TEST_SRCS_CC} tests/git2-c++/setup.cc) + add_cov_test(cov ${COV_TEST_SRCS_CC} ${COV_TEST_SRCS_CPP} ${COV_TEST_SRCS_CXX}) + add_cov_test(cov-stress ${COV_STRESS_TEST_SRCS_CC} tests/cov/setup.cc tests/git2-c++/stress/new.cc) - set_target_properties(git2-test git2-stress-test cov-test cov-stress-test PROPERTIES FOLDER tests) - - target_compile_options(git2-test PRIVATE ${ADDITIONAL_WALL_FLAGS}) - target_link_options(git2-test PRIVATE ${ADDITIONAL_LINK_FLAGS}) target_link_libraries(git2-test PRIVATE cov-api testing-lib) - target_include_directories(git2-test - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/tests - ${CMAKE_CURRENT_BINARY_DIR}) - - target_compile_options(git2-stress-test PRIVATE ${ADDITIONAL_WALL_FLAGS}) - target_link_options(git2-stress-test PRIVATE ${ADDITIONAL_LINK_FLAGS}) target_link_libraries(git2-stress-test PRIVATE cov-api testing-lib) - target_include_directories(git2-stress-test - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/tests - ${CMAKE_CURRENT_BINARY_DIR}) - - target_compile_options(cov-test PRIVATE ${ADDITIONAL_WALL_FLAGS}) - target_link_options(cov-test PRIVATE ${ADDITIONAL_LINK_FLAGS}) - target_link_libraries(cov-test PRIVATE cov-api testing-lib mbits::args) - target_include_directories(cov-test - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/tests - ${CMAKE_CURRENT_BINARY_DIR}) - - target_compile_options(cov-stress-test PRIVATE ${ADDITIONAL_WALL_FLAGS}) - target_link_options(cov-stress-test PRIVATE ${ADDITIONAL_LINK_FLAGS}) + target_link_libraries(cov-test PRIVATE cov-api mbits::args testing-lib) target_link_libraries(cov-stress-test PRIVATE cov-api testing-lib) target_include_directories(cov-stress-test PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/tests - ${CMAKE_CURRENT_SOURCE_DIR}/../cov-api/tests/git2-c++/stress - ${CMAKE_CURRENT_BINARY_DIR}) + ${CMAKE_CURRENT_SOURCE_DIR}/tests/git2-c++/stress) if (COV_CUTDOWN_OS) message(STATUS "cov-api tests: removing some tests on cutdown OS") @@ -191,6 +159,13 @@ if (COV_TESTING) add_test(NAME git2-stress COMMAND git2-stress-test "--gtest_output=xml:${TEST_REPORT_DIR}/libgit2-c++-stress/${TEST_REPORT_FILE}") add_test(NAME cov COMMAND cov-test "--gtest_output=xml:${TEST_REPORT_DIR}/libcov/${TEST_REPORT_FILE}") add_test(NAME cov-stress COMMAND cov-stress-test "--gtest_output=xml:${TEST_REPORT_DIR}/libcov-stress/${TEST_REPORT_FILE}") + + if (CMAKE_GENERATOR MATCHES "Visual Studio" AND TARGET cov_coveralls_test) + add_dependencies(cov_coveralls_test + git2-test git2-stress-test + cov-test cov-stress-test + ) + endif() endif() install( @@ -209,3 +184,5 @@ cpack_add_component(libcov DEPENDS lighter GROUP devel ) + +set_parent_scope() diff --git a/libs/cov-api/include/cov/git2/repository.hh b/libs/cov-api/include/cov/git2/repository.hh index bb2761cc..b895303b 100644 --- a/libs/cov-api/include/cov/git2/repository.hh +++ b/libs/cov-api/include/cov/git2/repository.hh @@ -25,6 +25,10 @@ namespace git { return git::create_handle(ec, git_reference_resolve, this->get()); } + + char const* shorthand() const noexcept { + return git_reference_shorthand(this->get()); + } }; template diff --git a/libs/cov-api/include/cov/io/types.hh b/libs/cov-api/include/cov/io/types.hh index 86498153..47c8e2b3 100644 --- a/libs/cov-api/include/cov/io/types.hh +++ b/libs/cov-api/include/cov/io/types.hh @@ -295,7 +295,7 @@ namespace cov::io { // would not overflow an uintmax... return {static_cast(out / divider), static_cast(out % divider), digits}; - // GCOV_EXCL_END + // GCOV_EXCL_STOP } }; diff --git a/libs/cov-api/src/cov/format/facades.cc b/libs/cov-api/src/cov/format/facades.cc index 451a0845..d137cc8c 100644 --- a/libs/cov-api/src/cov/format/facades.cc +++ b/libs/cov-api/src/cov/format/facades.cc @@ -12,9 +12,7 @@ namespace cov::placeholder { class file_facade : public object_facade { public: - file_facade(cov::files::entry const* data, - cov::repository const* repo) - : data_{data}, repo_{repo} {} + file_facade(cov::files::entry const* data) : data_{data} {} std::string_view name() const noexcept override { return "blob"sv; } std::string_view secondary_label() const noexcept override { @@ -51,14 +49,11 @@ namespace cov::placeholder { private: cov::files::entry const* data_{}; - cov::repository const* repo_{}; }; class files_facade : public object_facade { public: - files_facade(ref_ptr const& data, - cov::repository const* repo) - : data_{data}, repo_{repo} {} + files_facade(ref_ptr const& data) : data_{data} {} std::string_view name() const noexcept override { return "files"sv; @@ -70,7 +65,6 @@ namespace cov::placeholder { private: ref_ptr data_{}; - cov::repository const* repo_{}; }; class build_facade : public object_facade { @@ -380,13 +374,13 @@ namespace cov::placeholder { std::unique_ptr object_facade::present_files( ref_ptr const& data, - cov::repository const* repo) { - return std::make_unique(data, repo); + cov::repository const*) { + return std::make_unique(data); } std::unique_ptr object_facade::present_file( cov::files::entry const* data, - cov::repository const* repo) { - return std::make_unique(data, repo); + cov::repository const*) { + return std::make_unique(data); } } // namespace cov::placeholder diff --git a/libs/cov-api/src/cov/io/build.cc b/libs/cov-api/src/cov/io/build.cc index 7dfdc79e..ec0f95eb 100644 --- a/libs/cov-api/src/cov/io/build.cc +++ b/libs/cov-api/src/cov/io/build.cc @@ -75,16 +75,10 @@ namespace cov::io::handlers { propset, build.stats); } -#if defined(__GNUC__) -// The warning is legit, since as_a<> can return nullptr, if there is no -// cov::build in type tree branch, but this should be called from within -// db_object::store, which is guarded by report::recognized -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnull-dereference" -#endif bool build::store(ref_ptr const& value, write_stream& out) const { auto const obj = as_a(static_cast(value.get())); + if (!obj) return false; auto stg = [obj] { strings_builder strings{}; strings.insert(obj->props_json()); @@ -117,9 +111,6 @@ namespace cov::io::handlers { return true; } -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif } // namespace cov::io::handlers namespace cov { diff --git a/libs/cov-api/src/cov/io/files.cc b/libs/cov-api/src/cov/io/files.cc index 75a57e9a..d9c83af0 100644 --- a/libs/cov-api/src/cov/io/files.cc +++ b/libs/cov-api/src/cov/io/files.cc @@ -215,6 +215,7 @@ namespace cov::io::handlers { bool files::store(ref_ptr const& value, write_stream& out) const { auto const obj = as_a(static_cast(value.get())); + if (!obj) return false; auto entries = obj->entries(); auto only_lines = true; diff --git a/libs/cov-api/src/cov/io/function_coverage.cc b/libs/cov-api/src/cov/io/function_coverage.cc index a5db69bd..2405c3a8 100644 --- a/libs/cov-api/src/cov/io/function_coverage.cc +++ b/libs/cov-api/src/cov/io/function_coverage.cc @@ -134,6 +134,7 @@ namespace cov::io::handlers { write_stream& out) const { auto const obj = as_a( static_cast(value.get())); + if (!obj) return false; auto entries = obj->entries(); auto stg = [&] { @@ -183,9 +184,6 @@ namespace cov::io::handlers { return true; } -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif } // namespace cov::io::handlers namespace cov { diff --git a/libs/cov-api/src/cov/io/line_coverage.cc b/libs/cov-api/src/cov/io/line_coverage.cc index a6c5ff45..0d4724c0 100644 --- a/libs/cov-api/src/cov/io/line_coverage.cc +++ b/libs/cov-api/src/cov/io/line_coverage.cc @@ -34,17 +34,11 @@ namespace cov::io::handlers { return cov::line_coverage::create(std::move(result)); } -#if defined(__GNUC__) -// The warning is legit, since as_a<> can return nullptr, if there is no -// cov::line_coverage in type tree branch, but this should be called from within -// db_object::store, which is guarded by line_coverage::recognized -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnull-dereference" -#endif bool line_coverage::store(ref_ptr const& value, write_stream& out) const { auto const obj = as_a(static_cast(value.get())); + if (!obj) return false; auto const& items = obj->coverage(); v1::line_coverage hdr{.line_count = static_cast(items.size())}; @@ -52,9 +46,6 @@ namespace cov::io::handlers { if (!out.store(items)) return false; return true; } -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif } // namespace cov::io::handlers namespace cov { diff --git a/libs/cov-api/src/cov/io/report.cc b/libs/cov-api/src/cov/io/report.cc index f2615c5a..cfa72ae7 100644 --- a/libs/cov-api/src/cov/io/report.cc +++ b/libs/cov-api/src/cov/io/report.cc @@ -229,16 +229,10 @@ namespace cov::io::handlers { header.added.to_seconds(), header.stats, std::move(builds)); } -#if defined(__GNUC__) -// The warning is legit, since as_a<> can return nullptr, if there is no -// cov::report in type tree branch, but this should be called from within -// db_object::store, which is guarded by report::recognized -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnull-dereference" -#endif bool report::store(ref_ptr const& value, write_stream& out) const { auto const obj = as_a(static_cast(value.get())); + if (!obj) return false; auto stg = [obj] { strings_builder strings{}; @@ -303,9 +297,6 @@ namespace cov::io::handlers { return true; } -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif } // namespace cov::io::handlers namespace cov { diff --git a/libs/cov-api/tests/cov/diff-test.cc b/libs/cov-api/tests/cov/diff-test.cc index 94674de6..dc6113b7 100644 --- a/libs/cov-api/tests/cov/diff-test.cc +++ b/libs/cov-api/tests/cov/diff-test.cc @@ -142,17 +142,17 @@ namespace cov::testing { ASSERT_FALSE(ec) << ec.message(); static constexpr quick_stat initial_files[] = { - {"main.cc"sv, {5, 1, 0, 0, 0, 0, 0}}, + {"main.cc"sv, {5, {1, 0}, {0, 0}, {0, 0}}}, }; static constexpr quick_stat middle_files[] = { - {"src/main.cc"sv, {5, 1, 0, 0, 0, 0, 0}}, - {"src/old-name.cc"sv, {6, 1, 0, 0, 0, 0, 0}}, + {"src/main.cc"sv, {5, {1, 0}, {0, 0}, {0, 0}}}, + {"src/old-name.cc"sv, {6, {1, 0}, {0, 0}, {0, 0}}}, }; static constexpr quick_stat current_files[] = { - {"src/main.cc"sv, {5, 1, 1, 0, 0, 0, 0}}, - {"src/greetings.cc"sv, {8, 3, 3, 0, 0, 0, 0}}, + {"src/main.cc"sv, {5, {1, 1}, {0, 0}, {0, 0}}}, + {"src/greetings.cc"sv, {8, {3, 3}, {0, 0}, {0, 0}}}, }; git::oid initial{}, middle{}, current{}; diff --git a/libs/cov-api/tests/cov/format/date-test.cc b/libs/cov-api/tests/cov/format/date-test.cc index ddf3240f..f661dcf0 100644 --- a/libs/cov-api/tests/cov/format/date-test.cc +++ b/libs/cov-api/tests/cov/format/date-test.cc @@ -64,6 +64,7 @@ namespace cov::testing { {1250, {300, 299}, {0, 0}, {0, 0}}, {}); ASSERT_TRUE(report); + fmt::print("locale: {}\n", env.locale); auto facade = ph::object_facade::present_report(report, nullptr); auto actual = fmt.format(facade.get(), env); ASSERT_EQ(expected, actual); @@ -82,8 +83,14 @@ namespace cov::testing { static constexpr auto feb29 = sys_days{2000_y / feb / 29} + 12h + 34min + 56s; + // If you'll get locale::facet::_S_create_c_locale name not valid exceptions + // on some of those tests, on Ubuntu: + // ``` + // sudo locale-gen "pl_PL.UTF-8" "en_US.UTF-8" "en_GB.UTF-8" + // ``` + static date_test const tests[] = { -#ifndef CUTDOWN_OS +#if !defined(_WIN32) || !defined(CUTDOWN_OS) { "Date in Poland/Polish"sv, "%rd"sv, @@ -108,7 +115,7 @@ namespace cov::testing { .time_zone = "Europe/Warsaw"sv, .locale = "en_GB.UTF-8"sv}, }, -#endif // CUTDOWN_OS +#endif // !_WIN32 || !CUTDOWN_OS { "Date in Poland/US English"sv, "%rd"sv, @@ -121,7 +128,7 @@ namespace cov::testing { .time_zone = "Europe/Warsaw"sv, .locale = "en_US.UTF-8"sv}, }, -#ifndef CUTDOWN_OS +#if !defined(_WIN32) || !defined(CUTDOWN_OS) { "Date in Labrador/Polish"sv, "%rd"sv, @@ -146,7 +153,7 @@ namespace cov::testing { .time_zone = "America/St_Johns"sv, .locale = "en_GB.UTF-8"sv}, }, -#endif // CUTDOWN_OS +#endif // !_WIN32 || !CUTDOWN_OS { "Date in Labrador/US English"sv, "%rd"sv, diff --git a/libs/cov-api/tests/cov/format/facade-test.cc b/libs/cov-api/tests/cov/format/facade-test.cc index 0bab408f..a6cc510d 100644 --- a/libs/cov-api/tests/cov/format/facade-test.cc +++ b/libs/cov-api/tests/cov/format/facade-test.cc @@ -339,7 +339,7 @@ namespace cov::placeholder::testing { ASSERT_FALSE(ec) << ec.message(); auto const report_id = "92b91e27801bbd1ee4e2cc456b81be767a03fbbf"_oid; - git::oid zero{}, build_id{}, files_id{}; + git::oid zero{}, build_id{}; io::v1::coverage_stats const stats{1250, {300, 299}, {1, 1}, {0, 0}}; diff --git a/libs/cov-api/tests/cov/init-test.cc b/libs/cov-api/tests/cov/init-test.cc index 3ecec2d3..8710e95e 100644 --- a/libs/cov-api/tests/cov/init-test.cc +++ b/libs/cov-api/tests/cov/init-test.cc @@ -136,19 +136,6 @@ namespace cov::testing { }, }, -#ifdef WIN32 - { - "outside_git_win32"sv, - { - "outside_git/.covdata"sv, - "Z:/1234567890aswedferckarek/project/.git"sv, - }, - make_setup(remove_all("outside_git"sv), - create_directories("outside_git"sv)), - {"Z:/1234567890aswedferckarek/project/.git"sv}, - }, -#endif - { "beside_git"sv, {"beside_git/.covdata"sv, "beside_git/project/.git"sv}, diff --git a/libs/cov-rt/CMakeLists.txt b/libs/cov-rt/CMakeLists.txt index ea7c1993..694accb9 100644 --- a/libs/cov-rt/CMakeLists.txt +++ b/libs/cov-rt/CMakeLists.txt @@ -5,7 +5,6 @@ set(SOURCES src/cvg_info.cc src/line_printer.cc src/module.cc - src/path_env.hh src/report_command.cc src/report.cc src/root_command.cc @@ -37,19 +36,9 @@ set(SOURCES include/cov/app/tools.hh ) -if (UNIX) - list(APPEND SOURCES src/posix.cc) -elseif(WIN32) - list(APPEND SOURCES src/win32.cc) -endif() source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES}) -add_library(cov-rt STATIC ${SOURCES}) -target_compile_options(cov-rt PRIVATE ${ADDITIONAL_WALL_FLAGS}) -target_link_options(cov-rt PRIVATE ${ADDITIONAL_LINK_FLAGS}) -target_include_directories(cov-rt PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_BINARY_DIR}/include) +add_cov_library(cov-rt ${SOURCES}) target_link_libraries(cov-rt PUBLIC app) set_target_properties(cov-rt PROPERTIES FOLDER libs) @@ -76,27 +65,14 @@ if (COV_TESTING) ${STRESS_TEST_SRCS_CC} ) - add_executable(cov-rt-test ${TEST_SRCS_CC} ${TEST_SRCS_CPP} ${TEST_SRCS_CXX}) - add_executable(cov-rt-stress-test ${STRESS_TEST_SRCS_CC} tests/setup.cc ../cov-api/tests/git2-c++/stress/new.cc) - - set_target_properties(cov-rt-test cov-rt-stress-test PROPERTIES FOLDER tests) + add_cov_test(cov-rt ${TEST_SRCS_CC} ${TEST_SRCS_CPP} ${TEST_SRCS_CXX}) + add_cov_test(cov-rt-stress ${STRESS_TEST_SRCS_CC} tests/setup.cc ../cov-api/tests/git2-c++/stress/new.cc) - target_compile_options(cov-rt-test PRIVATE ${ADDITIONAL_WALL_FLAGS}) - target_link_options(cov-rt-test PRIVATE ${ADDITIONAL_LINK_FLAGS}) target_link_libraries(cov-rt-test PRIVATE cov-rt testing-lib) - target_include_directories(cov-rt-test - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/tests - ${CMAKE_CURRENT_BINARY_DIR}) - - target_compile_options(cov-rt-stress-test PRIVATE ${ADDITIONAL_WALL_FLAGS}) - target_link_options(cov-rt-stress-test PRIVATE ${ADDITIONAL_LINK_FLAGS}) target_link_libraries(cov-rt-stress-test PRIVATE cov-rt testing-lib) target_include_directories(cov-rt-stress-test PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/tests - ${CMAKE_CURRENT_SOURCE_DIR}/../cov-api/tests/git2-c++/stress - ${CMAKE_CURRENT_BINARY_DIR}) + ${CMAKE_CURRENT_SOURCE_DIR}/../cov-api/tests/git2-c++/stress) add_test(NAME cov-rt COMMAND cov-rt-test "--gtest_output=xml:${TEST_REPORT_DIR}/libcov-rt/${TEST_REPORT_FILE}") add_test(NAME cov-rt-stress COMMAND cov-rt-stress-test "--gtest_output=xml:${TEST_REPORT_DIR}/libcov-rt-stress/${TEST_REPORT_FILE}") @@ -123,3 +99,5 @@ cpack_add_component(librt DISPLAY_NAME "libcov-rt" GROUP devel ) + +set_parent_scope() diff --git a/libs/cov-rt/include/cov/app/report.hh b/libs/cov-rt/include/cov/app/report.hh index 5120fb5f..5cc387c1 100644 --- a/libs/cov-rt/include/cov/app/report.hh +++ b/libs/cov-rt/include/cov/app/report.hh @@ -53,7 +53,25 @@ namespace cov::app::report { std::string digest{}; std::map line_coverage{}; std::vector function_coverage{}; - auto operator<=>(file_info const&) const noexcept = default; + bool operator==(file_info const& rhs) const noexcept = default; + auto operator<=>(file_info const& rhs) const noexcept { + // without this definition, clang 16 does not like the + // function_coverage' spaceship... + if (auto const cmp = name <=> rhs.name; cmp != 0) { + return cmp; + } + if (auto const cmp = algorithm <=> rhs.algorithm; cmp != 0) { + return cmp; + } + if (auto const cmp = digest <=> rhs.digest; cmp != 0) { + return cmp; + } + if (auto const cmp = line_coverage <=> rhs.line_coverage; + cmp != 0) { + return cmp; + } + return function_coverage <=> rhs.function_coverage; + } coverage_info expand_coverage(size_t line_count) const; }; @@ -67,7 +85,8 @@ namespace cov::app::report { git_info git{}; std::vector files{}; - auto operator<=>(report_info const&) const noexcept = default; + bool operator==(report_info const&) const noexcept = default; + auto operator<=>(report_info const& rhs) const noexcept = default; bool load_from_text(std::string_view u8_encoded); }; diff --git a/libs/cov-rt/include/cov/app/report_command.hh b/libs/cov-rt/include/cov/app/report_command.hh index 566713ff..26cd9a69 100644 --- a/libs/cov-rt/include/cov/app/report_command.hh +++ b/libs/cov-rt/include/cov/app/report_command.hh @@ -32,7 +32,8 @@ namespace cov::app::builtin::report { }; parse_results parse(); - std::string report_contents(git::repository_handle repo) const; + std::string report_contents(git::repository_handle repo, + ::args::arglist args) const; std::string_view report_path() const noexcept { return report_; } std::string_view report_filter() const noexcept { @@ -90,6 +91,7 @@ namespace cov::app::builtin::report { // "private : std::vector" std::vector filter(std::vector const& contents, std::string_view filter, + ::args::arglist args, std::filesystem::path const& cwd) const; // visual space @@ -116,6 +118,7 @@ namespace cov::app::builtin::report { std::optional filter_{}; std::vector props_{}; bool amend_{}; + std::optional output_{}; }; struct stored_file { diff --git a/libs/cov-rt/include/cov/app/tools.hh b/libs/cov-rt/include/cov/app/tools.hh index 77a1f378..bea5c558 100644 --- a/libs/cov-rt/include/cov/app/tools.hh +++ b/libs/cov-rt/include/cov/app/tools.hh @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -48,21 +49,4 @@ namespace cov::app { git::config cfg_; std::span builtins_; }; - - namespace platform { - int run_tool(std::filesystem::path const& tooldir, - std::string_view tool, - args::arglist args); - - struct captured_output { - std::vector output{}; - std::vector error{}; - int return_code{}; - }; - - captured_output run_filter(std::filesystem::path const& filter_dir, - std::filesystem::path const& cwd, - std::string_view filter, - std::vector const& input); - } // namespace platform } // namespace cov::app diff --git a/libs/cov-rt/src/cvg_info.cc b/libs/cov-rt/src/cvg_info.cc index 6fd9ad34..2ce0de69 100644 --- a/libs/cov-rt/src/cvg_info.cc +++ b/libs/cov-rt/src/cvg_info.cc @@ -156,7 +156,7 @@ namespace cov::app { max_width -= std::min(max_width, widths.line_no_width); max_width -= std::min(max_width, margins); max_width = std::max(max_width, MAGIC); - // GCOV_EXCL_END + // GCOV_EXCL_STOP } auto name = fn.label; @@ -167,7 +167,7 @@ namespace cov::app { // GCOV_EXCL_START -- TODO: Add pty to test_driver backing.assign(name.substr(0, max_width - 3)); name = backing; - // GCOV_EXCL_END + // GCOV_EXCL_STOP } auto const count_column = fmt::format("{}x", fn.count); @@ -178,8 +178,10 @@ namespace cov::app { } return fmt::format( + // GCOV_EXCL_START[gcc] "{} {:>{}} |{} {}", prefix, count_column, widths.line_no_width + 3 + widths.count_width + 1, suffix, + // GCOV_EXCL_STOP line_printer::to_string(total_count, name, shorten, use_color)); } @@ -193,9 +195,11 @@ namespace cov::app { auto const count_column = count ? fmt::format("{}x", *count) : " "s; return fmt::format( + // GCOV_EXCL_START[gcc] " {:>{}} | {:>{}} | {}", line_no + 1, widths.line_no_width, count_column, widths.count_width + 1, line_printer::to_string(count, line_text, line.contents, + // GCOV_EXCL_STOP syntax.dict, use_color)); } } // namespace cov::app diff --git a/libs/cov-rt/src/line_printer.cc b/libs/cov-rt/src/line_printer.cc index 425bb3e1..019700a5 100644 --- a/libs/cov-rt/src/line_printer.cc +++ b/libs/cov-rt/src/line_printer.cc @@ -304,11 +304,11 @@ namespace cov::app::line_printer { } ctx.paint_color(); // EOL reset if (shortened) { - // GCOV_EXCL_START -- TODO: Add pty to test_driver + // LCOV_EXCL_START -- TODO: Add pty to test_driver if (use_color) ctx.result.append("\033[2;49;39m"sv); ctx.result.append("..."sv); if (use_color) ctx.result.append("\033[m"sv); - // GCOV_EXCL_END + // GCOV_EXCL_STOP } return ctx.result; diff --git a/libs/cov-rt/src/report.cc b/libs/cov-rt/src/report.cc index 377db24f..a3ad7292 100644 --- a/libs/cov-rt/src/report.cc +++ b/libs/cov-rt/src/report.cc @@ -290,11 +290,26 @@ namespace cov::app::report { auto const json_git_head = cast_from_json(json_git, u8"head"sv); - if (!json_git_branch || !json_git_head || !json_files) return false; + if (!json_git_branch || !json_git_head || !json_files) { + if (!json_git) { + fmt::print(stderr, "cov report: /git: undefined\n"); + } else { + if (!json_git_branch) + fmt::print(stderr, "cov report: /git/branch: undefined\n"); + if (!json_git_head) + fmt::print(stderr, "cov report: /git/head: undefined\n"); + } + if (!json_files) + fmt::print(stderr, "cov report: /files: undefined\n"); + return false; + } files.reserve(json_files->size()); + auto file_index{std::numeric_limits::max()}; for (auto const& file_node : *json_files) { + ++file_index; + auto const json_file_name = cast_from_json(file_node, u8"name"sv); auto const json_file_digest = @@ -306,6 +321,25 @@ namespace cov::app::report { if (!json_file_name || !json_file_digest || !json_file_line_coverage) { + std::string format = "cov report: /files[{}]/{}"; + if (json_file_name) format += " ({})"; + format += ": undefined\n"; + if (!json_file_name) + fmt::print(stderr, fmt::runtime(format), file_index, + "name"sv, + json_file_name ? from_json(*json_file_name) + : std::string_view{}); + if (!json_file_digest) + fmt::print(stderr, fmt::runtime(format), file_index, + "digest"sv, + json_file_name ? from_json(*json_file_name) + : std::string_view{}); + if (!json_file_line_coverage) + fmt::print(stderr, fmt::runtime(format), file_index, + "line_coverage"sv, + json_file_name ? from_json(*json_file_name) + : std::string_view{}); + [[unlikely]]; files.clear(); return false; @@ -315,6 +349,8 @@ namespace cov::app::report { auto const pos = digest_view.find(':'); auto const algorithm = lookup_digest(digest_view.substr(0, pos)); if (pos == std::string_view::npos || algorithm == digest::unknown) { + fmt::print(stderr, "cov report: /file[{}]/digest ({}): '{}'\n", + file_index, from_json(*json_file_name), digest_view); [[unlikely]]; files.clear(); return false; @@ -333,6 +369,12 @@ namespace cov::app::report { unsigned line{}; if (!hits || !conv_rebase(node_key, line)) { + json::string val; + json::write_json(val, node_value, json::concise); + fmt::print(stderr, + "cov report: /file[{}]/line_coverage[{}]: {}\n", + file_index, from_json(*json_file_name), + from_json(node_key), from_json(val)); [[unlikely]]; files.clear(); return false; @@ -343,7 +385,10 @@ namespace cov::app::report { if (json_file_functions_coverage) { auto& cvg = dst.function_coverage; cvg.reserve(json_file_functions_coverage->size()); + + auto function_index{std::numeric_limits::max()}; for (auto const& node_file : *json_file_functions_coverage) { + ++function_index; auto const name = cast_from_json(node_file, u8"name"sv); auto const count = @@ -351,7 +396,36 @@ namespace cov::app::report { auto const start_line = cast_from_json(node_file, u8"start_line"sv); - if (!name || !count || !start_line) continue; + if (!name || !count || !start_line) { + std::string format = "cov report: /files[{}]/{}"; + if (json_file_name) format += " ({})"; + format += ": undefined\n"; + if (!name) + fmt::print( + stderr, + "cov report: /files[{}]/functions[{}]/name " + "({}): undefined", + file_index, function_index, + from_json(*json_file_name)); + if (!count) + fmt::print( + stderr, + "cov report: /files[{}]/functions[{}]/count " + "({}): undefined", + file_index, function_index, + from_json(*json_file_name)); + if (!start_line) + fmt::print(stderr, + "cov report: " + "/files[{}]/functions[{}]/start_line " + "({}): undefined", + file_index, function_index, + from_json(*json_file_name)); + + [[unlikely]]; + files.clear(); + return false; + } auto const demangled = cast_from_json( node_file, u8"demangled"sv); diff --git a/libs/cov-rt/src/report_command.cc b/libs/cov-rt/src/report_command.cc index bad67815..11924794 100644 --- a/libs/cov-rt/src/report_command.cc +++ b/libs/cov-rt/src/report_command.cc @@ -54,8 +54,8 @@ namespace cov::app::builtin::report { parser::parser(::args::args_view const& arguments, str::translator_open_info const& langs) : base_parser{langs, arguments} { - static constexpr std::string_view filters[] = {"cobertura"sv, - "coveralls"sv}; + static constexpr std::string_view filters[] = { + "cobertura"sv, "coveralls"sv, "strip-excludes"sv}; parser_.arg(report_) .meta(tr_(replng::REPORT_FILE_META)) @@ -70,28 +70,58 @@ namespace cov::app::builtin::report { parser_.set(amend_, "amend") .help(tr_(replng::AMEND_DESCRIPTION)) .opt(); + parser_.arg(output_, "o", "out").opt(); } parser::parse_results parser::parse() { using namespace str; - parser_.parse(); + auto rest = parser_.parse(::args::parser::allow_subcommands); + if (!rest.empty()) { + if (rest[0] != "--"sv) { + error(fmt::format(fmt::runtime(tr_(args::lng::UNRECOGNIZED)), + rest[0])); + } + rest = rest.shift(); + } parse_results result{open_here(*this, tr_)}; - if (!result.report.load_from_text(report_contents(result.repo.git()))) { + if (output_) { + if (!filter_) error("--out requires --filter"); + if (amend_) error("--out cannot be used with --amend"); + + auto const text = report_contents(result.repo.git(), rest); + if (*output_ == "-") { + fputs(text.c_str(), stdout); + } else { + auto filename = make_u8path(*output_); + std::error_code ec{}; + std::filesystem::create_directories(filename.parent_path(), ec); + if (ec) error(ec, tr_); + auto file = io::fopen(filename, "wb"); + if (file) file.store(text.data(), text.size()); + std::exit(0); + } + } + + if (!result.report.load_from_text( + report_contents(result.repo.git(), rest))) { if (filter_) { - error(tr_.format(replng::ERROR_FILTERED_REPORT_ISSUES, report_, - *filter_)); + simple_error(tr_, parser_.program(), + tr_.format(replng::ERROR_FILTERED_REPORT_ISSUES, + report_, *filter_)); } else { // GCOV_EXCL_LINE[WIN32] - error(tr_.format(replng::ERROR_REPORT_ISSUES, report_)); + simple_error(tr_, parser_.program(), + tr_.format(replng::ERROR_REPORT_ISSUES, report_)); } } result.props = cov::report::builder::properties(props_); return result; } // GCOV_EXCL_LINE[GCC] -- and now it wants th throw something... - std::string parser::report_contents(git::repository_handle repo) const { + std::string parser::report_contents(git::repository_handle repo, + ::args::arglist args) const { auto source = io::fopen(make_u8path(report_)); if (!source) error(tr_.format(str::args::lng::FILE_NOT_FOUND, report_)); @@ -99,7 +129,7 @@ namespace cov::app::builtin::report { if (filter_) { auto dir = repo.workdir(); if (!dir) dir = repo.commondir(); - content = filter(content, *filter_, make_u8path(*dir)); + content = filter(content, *filter_, args, make_u8path(*dir)); } return {reinterpret_cast(content.data()), content.size()}; @@ -108,10 +138,11 @@ namespace cov::app::builtin::report { std::vector parser::filter( std::vector const& contents, std::string_view filter, + ::args::arglist args, std::filesystem::path const& cwd) const { auto output = platform::run_filter( platform::sys_root() / directory_info::share / "filters"sv, cwd, - filter, contents); + filter, args, contents); if (!output.error.empty()) fwrite(output.error.data(), 1, output.error.size(), stderr); diff --git a/libs/cov-rt/src/show.cc b/libs/cov-rt/src/show.cc index fe797f60..1214d81e 100644 --- a/libs/cov-rt/src/show.cc +++ b/libs/cov-rt/src/show.cc @@ -82,7 +82,7 @@ namespace cov::app::show { placeholder::color::faint_red); }, show_column::other, with_branches_missing}, - // GCOV_EXCL_END + // GCOV_EXCL_STOP column{"% Funcs"sv, [](environment const& env, cell_row& cells, diff --git a/libs/cov-rt/tests/tools-test.cc b/libs/cov-rt/tests/tools-test.cc index eafec06e..db62f40c 100644 --- a/libs/cov-rt/tests/tools-test.cc +++ b/libs/cov-rt/tests/tools-test.cc @@ -60,6 +60,7 @@ namespace cov::app::testing { { // add missing externals here... (this test will fail with every // new external added) + "collect"sv, }, actual); } diff --git a/libs/helpers/CMakeLists.txt b/libs/helpers/CMakeLists.txt index b2966f48..5f2d29de 100644 --- a/libs/helpers/CMakeLists.txt +++ b/libs/helpers/CMakeLists.txt @@ -1 +1,3 @@ add_subdirectory(testing-lib) + +set_parent_scope() diff --git a/libs/helpers/testing-lib/CMakeLists.txt b/libs/helpers/testing-lib/CMakeLists.txt index 9cc44cae..03fe6cf7 100644 --- a/libs/helpers/testing-lib/CMakeLists.txt +++ b/libs/helpers/testing-lib/CMakeLists.txt @@ -7,10 +7,9 @@ if (COV_TESTING) ) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES}) - add_library(testing-lib STATIC ${SOURCES}) - target_compile_options(testing-lib PRIVATE ${ADDITIONAL_WALL_FLAGS}) - target_link_options(testing-lib PRIVATE ${ADDITIONAL_LINK_FLAGS}) - target_include_directories(testing-lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) + add_cov_library(testing-lib ${SOURCES}) target_link_libraries(testing-lib PUBLIC GTest::gmock_main fmt::fmt) set_target_properties(testing-lib PROPERTIES FOLDER tests/libs) endif() + +set_parent_scope() diff --git a/libs/hilite/CMakeLists.txt b/libs/hilite/CMakeLists.txt index 97cae336..314a4cfb 100644 --- a/libs/hilite/CMakeLists.txt +++ b/libs/hilite/CMakeLists.txt @@ -8,3 +8,5 @@ add_subdirectory(hilite-cxx) add_subdirectory(hilite-py3) add_subdirectory(hilite-ts) add_subdirectory(lighter) + +set_parent_scope() diff --git a/libs/hilite/hilite-cxx/CMakeLists.txt b/libs/hilite/hilite-cxx/CMakeLists.txt index 59d206b2..f830c327 100644 --- a/libs/hilite/hilite-cxx/CMakeLists.txt +++ b/libs/hilite/hilite-cxx/CMakeLists.txt @@ -4,10 +4,7 @@ set(SOURCES ) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES}) -add_library(hilite-cxx STATIC ${SOURCES}) -target_compile_options(hilite-cxx PRIVATE ${ADDITIONAL_WALL_FLAGS}) -target_link_options(hilite-cxx PRIVATE ${ADDITIONAL_LINK_FLAGS}) -target_include_directories(hilite-cxx PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) +add_cov_library(hilite-cxx ${SOURCES}) target_link_libraries(hilite-cxx PUBLIC hilite) set_target_properties(hilite-cxx PROPERTIES FOLDER libs/hilite) @@ -28,3 +25,5 @@ cpack_add_component(hilite_cxx GROUP devel HIDDEN ) + +set_parent_scope() diff --git a/libs/hilite/hilite/CMakeLists.txt b/libs/hilite/hilite/CMakeLists.txt index 0a608892..37980c54 100644 --- a/libs/hilite/hilite/CMakeLists.txt +++ b/libs/hilite/hilite/CMakeLists.txt @@ -24,10 +24,7 @@ set(SOURCES ) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES}) -add_library(hilite STATIC ${SOURCES}) -target_compile_options(hilite PRIVATE ${ADDITIONAL_WALL_FLAGS}) -target_link_options(hilite PRIVATE ${ADDITIONAL_LINK_FLAGS}) -target_include_directories(hilite PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) +add_cov_library(hilite ${SOURCES}) set_target_properties(hilite PROPERTIES FOLDER libs/hilite) install( @@ -45,3 +42,5 @@ cpack_add_component(hilite DISPLAY_NAME "libhilite" GROUP devel ) + +set_parent_scope() diff --git a/libs/hilite/lighter/CMakeLists.txt b/libs/hilite/lighter/CMakeLists.txt index 0f0d0d55..96b28168 100644 --- a/libs/hilite/lighter/CMakeLists.txt +++ b/libs/hilite/lighter/CMakeLists.txt @@ -18,10 +18,7 @@ string(REPLACE ";" ", " DEP_LIST "${DEPS}") endif() -add_library(lighter STATIC ${SOURCES}) -target_compile_options(lighter PRIVATE ${ADDITIONAL_WALL_FLAGS}) -target_link_options(lighter PRIVATE ${ADDITIONAL_LINK_FLAGS}) -target_include_directories(lighter PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) +add_cov_library(lighter ${SOURCES}) target_link_libraries(lighter PUBLIC ${DEPS}) set_target_properties(lighter PROPERTIES FOLDER libs/hilite) @@ -38,31 +35,24 @@ if (COV_TESTING) ${TEST_SRCS_CXX} ${STRESS_TEST_SRCS_CC}) - add_executable(lighter-test ${TEST_SRCS_CC} ${TEST_SRCS_CPP} ${TEST_SRCS_CXX}) - add_executable(lighter-stress-test ${STRESS_TEST_SRCS_CC} + add_cov_test(lighter ${TEST_SRCS_CC} ${TEST_SRCS_CPP} ${TEST_SRCS_CXX}) + add_cov_test(lighter-stress ${STRESS_TEST_SRCS_CC} ../../cov-api/tests/git2-c++/stress/new.cc) - set_target_properties(lighter-test lighter-stress-test PROPERTIES FOLDER tests) - - target_compile_options(lighter-test PRIVATE ${ADDITIONAL_WALL_FLAGS}) - target_link_options(lighter-test PRIVATE ${ADDITIONAL_LINK_FLAGS}) - target_link_libraries(lighter-test lighter GTest::gmock_main fmt::fmt) - target_include_directories(lighter-test - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/tests - ${CMAKE_CURRENT_BINARY_DIR}) - - target_compile_options(lighter-stress-test PRIVATE ${ADDITIONAL_WALL_FLAGS}) - target_link_options(lighter-stress-test PRIVATE ${ADDITIONAL_LINK_FLAGS}) + target_link_libraries(lighter-test PRIVATE lighter GTest::gmock_main fmt::fmt) target_link_libraries(lighter-stress-test PRIVATE lighter GTest::gmock_main) target_include_directories(lighter-stress-test PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/tests - ${CMAKE_CURRENT_SOURCE_DIR}/../../cov-api/tests/git2-c++/stress - ${CMAKE_CURRENT_BINARY_DIR}) + ${CMAKE_CURRENT_SOURCE_DIR}/../../cov-api/tests/git2-c++/stress) add_test(NAME lighter COMMAND lighter-test "--gtest_output=xml:${TEST_REPORT_DIR}/liblighter/${TEST_REPORT_FILE}") add_test(NAME lighter-stress COMMAND lighter-stress-test "--gtest_output=xml:${TEST_REPORT_DIR}/liblighter-stress/${TEST_REPORT_FILE}") + + if (CMAKE_GENERATOR MATCHES "Visual Studio" AND TARGET cov_coveralls_test) + add_dependencies(cov_coveralls_test + lighter-test lighter-stress-test + ) + endif() endif() install( @@ -88,3 +78,5 @@ cpack_add_component(lighter DEPENDS ${_DEPS} GROUP devel ) + +set_parent_scope() diff --git a/packages/wix/cpack.cmake b/packages/wix/cpack.cmake index ec0a7936..e3bb7d1d 100644 --- a/packages/wix/cpack.cmake +++ b/packages/wix/cpack.cmake @@ -7,6 +7,7 @@ list(REMOVE_ITEM CPACK_COMPONENTS_ALL hilite hilite_cxx lighter + libexts ) foreach(COMP ${CPACK_COMPONENTS_ALL}) diff --git a/runner.chai b/runner.chai new file mode 100644 index 00000000..532cccb6 --- /dev/null +++ b/runner.chai @@ -0,0 +1,75 @@ +import("fs"); + +global proj = project("cov"); +proj.allow("git"); + +proj.install_component("main_exec"); +proj.install_component("tools"); + +proj.datasets(fs.abspath("apps/tests"), "main-set"); +proj.environment("DATA", fs.abspath("apps/tests/data")); + +proj.register_patch( + re_escape("\033[31m[") + "(.+) [0-9a-fA-F]+" + re_escape("]\033[m ") + "(.+)", + "\033[31m[\\1 $REPORT]\033[m \\2"); +proj.register_patch( + re_escape("[") + "(.+) [0-9a-fA-F]+" + re_escape("] ") + "(.+)", + "[\\1 $REPORT] \\2"); +proj.register_patch( + re_escape(" \033[2;37mcontains ") + "[0-9a-fA-F]+" + re_escape(":\033[m ") + "(.+)", + " \033[2;37mcontains $BUILD:\033[m \\1"); +proj.register_patch(" contains [0-9a-fA-F]+: (.+)", " contains $BUILD: \\1"); +proj.register_patch( + re_escape(" \033[2;37mbased on\033[m \033[2;33m") + "[0-9a-fA-F]+@(.+)" + re_escape("\033[m"), + " \033[2;37mbased on\033[m \033[2;33m$HEAD@\\1\033[m"); +proj.register_patch(" based on [0-9a-fA-F]+@(.+)", " based on $HEAD@\\1"); +proj.register_patch("(\\s+)parent [0-9a-fA-F]+", "\\1parent $PARENT"); + +proj.register_patch("CommitDate:(\\s+).*", "CommitDate:\\1$DATE"); +proj.register_patch("Added:(\\s+).*", "Added:\\1$DATE"); + + +def text_from(path) { + var file = path.open(); + if (!file) { + return ""; + } + var text = file.read(); + file.close(); + return text; +} + +def detach(test, args) { + var dirname = test.path(args[0]); + var HEAD = fs.join(dirname, "HEAD"); + var branch = text_from(HEAD).trim_right(); + if (!branch.starts_with("ref: ")) { + return true; + } + branch = branch.substr(5, branch.size()); + + var commit = text_from(fs.join(dirname, branch)); + if (commit.trim().empty()) { return false; } + var file = HEAD.open("w"); + file.write(commit); + return true; +} +proj.handle("detach", 1, detach); + +def cov_install(copy_dir, rt) { + var filters_target = fs.join(copy_dir, "additional-filters"); + + rt.append("COV_FILTER_PATH", filters_target); + rt.append("COV_PATH", fs.join(rt.build_dir, "dont-copy")); + rt.append("COV_PATH", fs.join(rt.build_dir, "elsewhere/libexec/cov")); + + fs.create_directories(filters_target); + var filters = fs.directory_iterator("apps/tests/test-filters"); + for (entry: filters) { + if (fs.extension(entry.path) != ".py") { + continue; + } + var installed_path = fs.join(filters_target, fs.stem(entry.path)); + fs.copy(entry.path, installed_path); + } +} diff --git a/tools/archives/__init__.py b/tools/archives/__init__.py new file mode 100644 index 00000000..4c540a80 --- /dev/null +++ b/tools/archives/__init__.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# Copyright (c) 2023 Marcin Zdun +# This code is licensed under MIT license (see LICENSE for details) + +import zipfile +import tarfile +import os +from typing import Dict, Callable, Tuple, List + + +def _untar(src, dst): + with tarfile.open(src) as TAR: + + def is_within_directory(directory, target): + abs_directory = os.path.abspath(directory) + abs_target = os.path.abspath(target) + + prefix = os.path.commonprefix([abs_directory, abs_target]) + + return prefix == abs_directory + + for member in TAR.getmembers(): + member_path = os.path.join(dst, member.name) + if not is_within_directory(dst, member_path): + raise Exception(f"Attempted path traversal in Tar file: {member.name}") + + TAR.extractall(dst) + + +def _unzip(src, dst): + with zipfile.ZipFile(src) as ZIP: + ZIP.extractall(dst) + + +_tar = (_untar, ["tar", "-xf"]) + +Unpacker = Callable[[str, str], None] +UnpackInfo = Tuple[Unpacker, List[str]] + +ARCHIVES: Dict[str, UnpackInfo] = { + ".tar": _tar, + ".tar.gz": _tar, + ".zip": (_unzip, ["unzip"]), +} + + +def locate_unpack(archive: str) -> UnpackInfo: + reminder, ext = os.path.splitext(archive) + _, mid = os.path.splitext(reminder) + if mid == ".tar": + ext = ".tar" + return ARCHIVES[ext] + + +del _tar +del _unzip +del _untar diff --git a/tools/download-latest.py b/tools/download-latest.py new file mode 100644 index 00000000..b41c9be1 --- /dev/null +++ b/tools/download-latest.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# Copyright (c) 2023 Marcin Zdun +# This code is licensed under MIT license (see LICENSE for details) + +import argparse +import os +import sys +import subprocess + +from archives import locate_unpack +from github.api import API +from github.cmake import get_version +from github.runner import Environment, print_args + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +GITHUB_ORG = "mzdun" + + +parser = argparse.ArgumentParser(usage="Download the latest release from GitHub") +parser.add_argument( + "--dry-run", + action="store_true", + required=False, + help="print commands, change nothing", +) + +args = parser.parse_args() +Environment.DRY_RUN = args.dry_run + +version_tag = get_version().tag() +repo_name = get_version().name.value +api = API(GITHUB_ORG, repo_name) + +proc = subprocess.run( + [sys.executable, os.path.join(ROOT, "packages", "system.py"), "platform"], + shell=False, + capture_output=True, +) +platform = None if proc.returncode != 0 else proc.stdout.strip().decode("UTF-8") + + +release = api.get_release(platform=platform) + +if release is None: + sys.exit(0) + +unpack, msg = locate_unpack(release) +print_args((*msg, release.replace(os.path.sep, "/"), "build/latest")) +if not Environment.DRY_RUN: + unpack(release, "build/latest") diff --git a/tools/download-runner.py b/tools/download-runner.py new file mode 100755 index 00000000..95ef7f72 --- /dev/null +++ b/tools/download-runner.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python +# Copyright (c) 2023 Marcin Zdun +# This code is licensed under MIT license (see LICENSE for details) + +import hashlib +import io +import os +import sys +from typing import Dict + +import requests + +TOOL_DIR = "build/downloads" +TOOL_NAME = "json-runner" +if os.name == "nt": + ARCHIVE_EXT = "zip" + ARCHIVE_ARCH = "windows-x86_64" + EXEC_EXT = ".exe" +else: + ARCHIVE_EXT = "tar.gz" + ARCHIVE_ARCH = "ubuntu-22.04-x86_64" + EXEC_EXT = "" + + +def _hash(filename: str) -> str: + sha = hashlib.sha256() + with open(filename, "rb") as data: + for block in iter(lambda: data.read(io.DEFAULT_BUFFER_SIZE), b""): + sha.update(block) + return sha.hexdigest() + + +def _download(url: str, filename: str, store: bool = True): + response = requests.get(url, allow_redirects=True) + if response.status_code // 100 != 2: + print( + f"{url}: download failed: {response.status_code}, {response.reason}", + file=sys.stderr, + ) + return None + if not store: + return response.content + + with open(filename, "wb") as file: + file.write(response.content) + return True + + +if os.name == "nt": + import zipfile + + def _extract(path: str, entry: str, target_path: str): + with zipfile.ZipFile(path) as zip: + try: + info = zip.getinfo(entry) + except KeyError: + print( + f"{path}: extract failed: {entry} not found", + file=sys.stderr, + ) + return False + + entry_data = zip.open(info) + try: + with open(target_path, "wb") as binary_dest: + for block in iter( + lambda: entry_data.read(io.DEFAULT_BUFFER_SIZE), b"" + ): + binary_dest.write(block) + finally: + entry_data.close() + return True + +else: + import tarfile + + def _extract(path: str, entry: str, target_path: str): + with tarfile.open(path) as tar: + try: + info = tar.getmember(entry) + except KeyError: + print( + f"{path}: extract failed: {entry} not found", + file=sys.stderr, + ) + return False + entry_data = tar.extractfile(info) + try: + with open(target_path, "wb") as binary_dest: + for block in iter( + lambda: entry_data.read(io.DEFAULT_BUFFER_SIZE), b"" + ): + binary_dest.write(block) + finally: + entry_data.close() + + if not info.issym(): + try: + os.chmod(target_path, info.mode) + except OSError as e: + print( + f"{target_path}: could not change mode: {e}", + file=sys.stderr, + ) + return False + try: + os.utime(target_path, (info.mtime, info.mtime)) + except OSError as e: + print( + f"{target_path}: could not change modification time: {e}", + file=sys.stderr, + ) + return False + + return True + + +def download_tools(version: str): + package_name = f"{TOOL_NAME}-{version}-{ARCHIVE_ARCH}" + sha_url = f"https://github.com/mzdun/{TOOL_NAME}/releases/download/v{version}/sha256sum.txt" + arch_url = f"https://github.com/mzdun/{TOOL_NAME}/releases/download/v{version}/{package_name}.{ARCHIVE_EXT}" + path = f"{TOOL_DIR}/{package_name}.{ARCHIVE_EXT}" + + os.makedirs(TOOL_DIR, exist_ok=True) + + print(f"[DL] {sha_url}", file=sys.stderr) + sha256sum_text = _download(sha_url, f"{TOOL_DIR}/sha256sum.txt", False) + if not sha256sum_text: + return False + sha256sum: Dict[str, str] = {} + + for hash, filename in [ + line.split(" ", 1) + for line in sha256sum_text.decode("UTF-8").split("\n") + if line != "" + ]: + if filename[:1] != "*": + continue + filename = filename[1:] + sha256sum[filename] = hash + + expected_hash = sha256sum.get(f"{package_name}.{ARCHIVE_EXT}") + if expected_hash is None: + print( + f"{sha_url}: error: cannot locate sha256sum for {package_name}.{ARCHIVE_EXT}", + file=sys.stderr, + ) + return False + + needs_download = True + if os.path.isfile(path): + needs_download = _hash(path) != expected_hash + + if not needs_download: + print(f"[USE] {path}", file=sys.stderr) + + if needs_download: + print(f"[DL] {arch_url}", file=sys.stderr) + if not _download(arch_url, path): + return False + actual_hash = _hash(path) + if actual_hash != expected_hash: + os.remove(path) + print( + f"{arch_url}: download failed: unexpected sh256sum:\n expecting {expected_hash}\n received {actual_hash}", + file=sys.stderr, + ) + return False + + short_version = ".".join(version.split(".")[:2]) + print( + f"[EXTRACT] {package_name}/share/{TOOL_NAME}-{short_version}/schema.json", + file=sys.stderr, + ) + if not _extract( + path, + f"{package_name}/share/{TOOL_NAME}-{short_version}/schema.json", + f"{TOOL_DIR}/{TOOL_NAME}-schema.json", + ): + return False + + print(f"[EXTRACT] {package_name}/bin/{TOOL_NAME}{EXEC_EXT}", file=sys.stderr) + if not _extract( + path, + f"{package_name}/bin/{TOOL_NAME}{EXEC_EXT}", + f"{TOOL_DIR}/{TOOL_NAME}{EXEC_EXT}", + ): + return False + + return True + + +if __name__ == "__main__" and not download_tools( + "0.2.1" if len(sys.argv) < 2 else sys.argv[1] +): + sys.exit(1) diff --git a/tools/flow/lib/matrix.py b/tools/flow/lib/matrix.py index 8435adb8..0c144e5e 100644 --- a/tools/flow/lib/matrix.py +++ b/tools/flow/lib/matrix.py @@ -27,6 +27,9 @@ _conan: Optional[conan] = None _platform_name, _platform_version, _platform_arch = uname() +_collect_version = (0, 22, 0) +_report_version = (0, 20, 0) +_runner_version = (0, 2, 1) platform = _platform_name @@ -275,7 +278,7 @@ def configure_conan(config: dict): CONAN_PROFILE_GEN = "_profile-build_type" profile_gen = f"./{CONAN_DIR}/{CONAN_PROFILE_GEN}-{config['preset']}" - if config.get("--first-build", False): + if config.get("--first-build"): runner.refresh_build_dir("conan") if _conan is None: @@ -307,9 +310,14 @@ def configure_conan(config: dict): @step_call("CMake") def configure_cmake(config: dict): runner.refresh_build_dir(config["preset"]) - cov_COVERALLS = "ON" if config.get("coverage", False) else "OFF" - COV_SANITIZE = "ON" if config.get("sanitizer", False) else "OFF" + cov_COVERALLS = "ON" if config.get("coverage") else "OFF" + COV_SANITIZE = "ON" if config.get("sanitizer") else "OFF" COV_CUTDOWN_OS = "ON" if runner.CUTDOWN_OS else "OFF" + runner.call( + sys.executable, + "tools/download-runner.py", + ".".join(str(x) for x in _runner_version), + ) runner.call( "cmake", "--preset", @@ -324,10 +332,46 @@ def configure_cmake(config: dict): def build(config: dict): runner.call("cmake", "--build", "--preset", config["preset"], "--parallel") + @staticmethod + def get_bin(version: Tuple[int], config: dict): + ext = ".exe" if os.name == "nt" else "" + for dirname in ["latest", "release", config["preset"]]: + path = f"build/{dirname}/bin/cov{ext}" + if not os.path.isfile(path): + continue + proc = subprocess.run([path, "--version"], shell=False, capture_output=True) + if proc.returncode: + continue + exe_version = tuple( + int(x) + for x in proc.stdout.strip() + .split(b" ")[-1] + .split(b"-")[0] + .decode("UTF-8") + .split(".") + ) + if exe_version >= version: + return path + return None + @staticmethod @step_call("Test") def test(config: dict): - if config.get("coverage", False): + has_coverage = config.get("coverage") + cov_exe = None + if has_coverage: + cov_exe = steps.get_bin(_collect_version, config) + + if cov_exe is not None: + runner.call(cov_exe, "collect", "--clean") + runner.call( + cov_exe, "collect", "--observe", "ctest", "--preset", config["preset"] + ) + runner.call(cov_exe, "collect") + # todo: report coverage to github, somehow + return + + if has_coverage: runner.call( "cmake", "--build", @@ -346,8 +390,30 @@ def test(config: dict): _append_coverage(GITHUB_STEP_SUMMARY, json.load(f)) except KeyError: pass - else: - runner.call("ctest", "--preset", config["preset"]) + return + + runner.call("ctest", "--preset", config["preset"]) + + @staticmethod + @step_call("Sign", lambda config: config.get("os") == "windows") + def sign(config: dict): + files_to_sign: List[str] = [] + for root in [ + "bin", + "share", + os.path.join("libexec", "cov"), + ]: + for root_dir, _, files in os.walk( + os.path.join("build", config["preset"], root) + ): + for filename in files: + stem, ext = os.path.splitext(filename) + if stem[-5:] == "-test" or ext.lower() not in [".exe", ".dll"]: + continue + files_to_sign.append( + os.path.join(root_dir, filename).replace("\\", "/") + ) + runner.call("python", "tools/win32/sign.py", *files_to_sign) @staticmethod @step_call("Pack", lambda config: len(config.get("cpack_generator", [])) > 0) @@ -360,6 +426,27 @@ def pack(config: dict): ";".join(config.get("cpack_generator", [])), ) + @staticmethod + @step_call( + "SignPackages", + lambda config: config.get("os") == "windows" + and len(config.get("cpack_generator", [])) > 0, + ) + def sign_packages(config: dict): + files_to_sign: List[str] = [] + for root_dir, dirs, files in os.walk( + os.path.join("build", config["preset"], "packages") + ): + dirs[:] = [] + for filename in files: + _, ext = os.path.splitext(filename) + if ext.lower() != ".msi": + continue + files_to_sign.append( + os.path.join(root_dir, filename).replace("\\", "/") + ) + runner.call("python", "tools/win32/sign.py", *files_to_sign) + @staticmethod @step_call("Store") def store(config: dict): @@ -423,11 +510,16 @@ def _store_tests(config: dict): preset = config["preset"] runner.copy(f"build/{preset}/test-results", "build/artifacts/test-results") - if config.get("coverage", False): + if config.get("coverage"): output = f"build/artifacts/coveralls/{config['report_os']}-{config['report_compiler']}-{config['build_type']}.json" - print_args("cp", f"build/{preset}/coveralls.json", output) - if not runner.DRY_RUN: - copy_file(f"build/{preset}/coveralls.json", output) + if os.path.exists(f"build/{preset}/coveralls.json"): + print_args("cp", f"build/{preset}/coveralls.json", output) + if not runner.DRY_RUN: + copy_file(f"build/{preset}/coveralls.json", output) + else: + print_args("cp", f"build/{preset}/collected.json", output) + if not runner.DRY_RUN: + copy_file(f"build/{preset}/collected.json", output) @staticmethod @step_call( @@ -459,10 +551,12 @@ def dev_inst(config: dict): @step_call( "Report", flags=step_info.VERBOSE, - visible=lambda config: config.get("coverage", False) == True, + visible=lambda config: config.get("coverage") == True, ) def report(config: dict): - reporter = f"build/{config['preset']}/bin/cov" + cov_exe = steps.get_bin(_collect_version, config) + reporter = steps.get_bin(_report_version, config) + try: tag_process = subprocess.run([reporter, "tag"], stdout=subprocess.PIPE) tags = ( @@ -474,15 +568,23 @@ def report(config: dict): except: tags = () version = get_version() - legacy_reporter = os.environ.get("LEGACY_COV") - report = f"build/{config['preset']}/coveralls.json" + report = f"build/{config['preset']}/collected.json" response = f"build/{config['preset']}/report_answers.txt" at_args = [] if os.path.isfile(response): at_args.append(f"@{response}") - runner.call(reporter, "report", "--filter", "coveralls", report, *at_args) - if legacy_reporter is not None: - runner.call(legacy_reporter, "import", "--in", report, "--amend") + if cov_exe is None: + coveralls = f"build/{config['preset']}/coveralls.json" + runner.call( + reporter, + "report", + "--out", + report, + "--filter", + "coveralls", + coveralls, + ) + runner.call(reporter, "report", "--filter", "strip-excludes", report, *at_args) if version.tag() in tags: runner.call( reporter, @@ -500,7 +602,9 @@ def build_steps(): steps.build, steps.test, steps.report, + steps.sign, steps.pack, + steps.sign_packages, steps.store, steps.store_packages, steps.store_tests, diff --git a/tools/flow/lib/runner.py b/tools/flow/lib/runner.py index 18865712..5603111f 100644 --- a/tools/flow/lib/runner.py +++ b/tools/flow/lib/runner.py @@ -15,37 +15,10 @@ from dataclasses import dataclass from typing import Callable, ClassVar, Dict, List, Optional, Tuple +__file_dir__ = os.path.dirname(__file__) +sys.path.append(os.path.dirname(os.path.dirname(__file_dir__))) -def _untar(src, dst): - with tarfile.open(src) as TAR: - - def is_within_directory(directory, target): - abs_directory = os.path.abspath(directory) - abs_target = os.path.abspath(target) - - prefix = os.path.commonprefix([abs_directory, abs_target]) - - return prefix == abs_directory - - for member in TAR.getmembers(): - member_path = os.path.join(dst, member.name) - if not is_within_directory(dst, member_path): - raise Exception(f"Attempted path traversal in Tar file: {member.name}") - - TAR.extractall(dst) - - -def _unzip(src, dst): - with zipfile.ZipFile(src) as ZIP: - ZIP.extractall(dst) - - -_tar = (_untar, ["tar", "-xf"]) -ARCHIVES: Dict[str, Tuple[Callable[[str, str], None], List[str]]] = { - ".tar": _tar, - ".tar.gz": _tar, - ".zip": (_unzip, ["unzip"]), -} +from archives import locate_unpack def copy_file(src, dst): @@ -294,6 +267,7 @@ class runner: DRY_RUN = False CUTDOWN_OS = False GITHUB_ANNOTATE = False + OFFICIAL = False @staticmethod def run_steps(config: dict, keys: list, steps: List[callable]): @@ -370,13 +344,21 @@ def refresh_build_dir(name): os.makedirs(fullname, exist_ok=True) @staticmethod - def call(*args): + def call(*args, **kwargs): print_args(*args) if runner.DRY_RUN: return + env_kwarg = kwargs.get("env", {}) + env = {**os.environ} + for key, value in env_kwarg.items(): + if value is None: + if key in env: + del env[key] + else: + env[key] = value found = shutil.which(args[0]) args = (found if found is not None else args[0], *args[1:]) - proc = subprocess.run(args, check=False) + proc = subprocess.run(args, check=False, env=env) if proc.returncode != 0: sys.exit(1) @@ -395,11 +377,7 @@ def copy(src_dir: str, dst_dir: str, regex: str = ""): def extract(src_dir: str, dst_dir: str, package: str): archive = f"{src_dir}/{package}" - reminder, ext = os.path.splitext(archive) - _, mid = os.path.splitext(reminder) - if mid == ".tar": - ext = ".tar" - unpack, msg = ARCHIVES[ext] + unpack, msg = locate_unpack(archive) print_args(*msg, archive, dst_dir) if not runner.DRY_RUN: diff --git a/tools/flow/run.py b/tools/flow/run.py index fdba46ba..3af0c562 100755 --- a/tools/flow/run.py +++ b/tools/flow/run.py @@ -15,7 +15,17 @@ DEF_STEPS = { "config": ["Conan", "CMake"], "build": ["Build"], - "verify": ["Build", "Test", "Report", "Pack", "Store", "BinInst", "DevInst"], + "verify": [ + "Build", + "Test", + "Report", + "Sign", + "Pack", + "SignPackages", + "Store", + "BinInst", + "DevInst", + ], "report": ["Build", "Test", "Report"], } cmd = os.path.splitext(os.path.basename(sys.argv[0]))[0] @@ -265,6 +275,7 @@ def main(): runner.runner.DRY_RUN = args.dry_run runner.runner.CUTDOWN_OS = args.cutdown_os runner.runner.GITHUB_ANNOTATE = args.github + runner.runner.OFFICIAL = args.official root = os.path.join(os.path.dirname(__file__), "..", "..", ".github", "workflows") paths = [os.path.join(root, "flow.json")] if args.official: @@ -309,4 +320,7 @@ def main(): if __name__ == "__main__": - main() + try: + main() + except KeyboardInterrupt: + sys.exit(1) diff --git a/tools/github-get-artifacts.py b/tools/github-get-artifacts.py index e4b22ad1..30328f04 100755 --- a/tools/github-get-artifacts.py +++ b/tools/github-get-artifacts.py @@ -9,7 +9,7 @@ from github.cmake import get_version from github.runner import Environment -ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) GITHUB_ORG = "mzdun" parser = argparse.ArgumentParser(usage="Download artifacts from GitHub") diff --git a/tools/github/api.py b/tools/github/api.py index 94613bdd..391f6e80 100644 --- a/tools/github/api.py +++ b/tools/github/api.py @@ -4,8 +4,10 @@ import json import os import subprocess +import sys import urllib.parse from typing import Any, List, Optional, Union +import hashlib from .changelog import ChangeLog, format_changelog, read_tag_date from .runner import Environment, capture, checked, checked_capture, print_args @@ -49,12 +51,14 @@ def gh( method: Optional[str] = None, server: Optional[str] = None, capture_output: bool = True, + **kwargs, ): + accept = kwargs.get("accept", "application/vnd.github+json") cmd = [ "gh", "api", "-H", - "Accept: application/vnd.github+json", + f"Accept: {accept}", "-H", "X-GitHub-Api-Version: 2022-11-28", ] @@ -88,6 +92,7 @@ def gh( method: Optional[str] = None, server: Optional[str] = None, capture_output: bool = True, + **kwargs, ): return gh( f"{self.root}{res}", @@ -95,6 +100,7 @@ def gh( method=method, server=server, capture_output=capture_output, + **kwargs, ) def json_from( @@ -252,3 +258,100 @@ def publish_release(self, release_id: int) -> Optional[str]: "make_latest=legacy", method="PATCH", ).get("html_url") + + def get_release( + self, tag: Optional[str] = None, platform: Optional[str] = None + ) -> Optional[str]: + try: + proc = checked_capture("git", "rev-parse", "--show-toplevel") + if proc is None: + return None + root = proc.stdout.decode("UTF-8").strip() + rel_dir = os.path.join(root, "build", "downloads", "releases") + os.makedirs(rel_dir, exist_ok=True) + + proc = self.gh("/releases/latest" if tag is None else f"/releases/tags/{tag}") + if proc is None: + return None + + data = json.loads(proc.stdout.decode("UTF-8")) + name = data.get("name") + version = name[1:] + assets = { + asset.get("name"): ( + asset.get("id"), + asset.get("browser_download_url"), + asset.get("content_type"), + ) + for asset in data.get("assets", []) + } + sha256sum = assets["sha256sum.txt"] + prefix = f"{self.repo}-{version}-" + if platform is not None: + prefix = f"{prefix}{platform}." + exts = [".tar.gz", ".zip"] + filtered = {} + for key, value in assets.items(): + if key[: len(prefix)] != prefix: + continue + cont = True + for ext in exts: + if key[-len(ext) :] == ext: + cont = False + break + if cont: + continue + filtered[key] = value + + if len(filtered) != 1: + return None + + for filename, asset in filtered.items(): + break + + proc = self.gh( + f"/releases/assets/{sha256sum[0]}", + accept="application/octet-stream", + ) + if proc is None: + return None + + sum_lines = proc.stdout.decode("UTF-8").rstrip().split("\n") + sums = {} + for line in sum_lines: + split = line.split(" ", 1) + if len(split) != 2: + continue + digest, file = split + binary = file[:1] == "*" + if binary: + file = file[1:] + sums[file] = (digest, binary) + archive_sum = sums[filename][0] + + proc = self.gh( + f"/releases/assets/{asset[0]}", + accept="application/octet-stream", + ) + if proc is None: + return None + + actual_sum = hashlib.sha256(proc.stdout).hexdigest() + if archive_sum.lower() != actual_sum.lower(): + print( + f"""SHA256 checksum mismatch for file {filename} +Expected: {archive_sum} +Actual: {actual_sum}""", + file=sys.stderr, + ) + return None + + result = os.path.join(rel_dir, filename) + with open(result, "wb") as archive: + archive.write(proc.stdout) + + return os.path.relpath(result, root) + + except subprocess.CalledProcessError as e: + sys.stderr.write(e.stderr.decode("UTF-8")) + sys.exit(e.returncode) diff --git a/apps/tests/driver/__init__.py b/tools/json_tests/driver/__init__.py similarity index 100% rename from apps/tests/driver/__init__.py rename to tools/json_tests/driver/__init__.py diff --git a/apps/tests/driver/commands.py b/tools/json_tests/driver/commands.py similarity index 63% rename from apps/tests/driver/commands.py rename to tools/json_tests/driver/commands.py index a908649f..8254e070 100644 --- a/apps/tests/driver/commands.py +++ b/tools/json_tests/driver/commands.py @@ -5,12 +5,16 @@ import stat import subprocess import sys -import tarfile -import zipfile from typing import Callable, Dict, List, Tuple from . import test +__file_dir__ = os.path.dirname(__file__) +sys.path.append(os.path.dirname(os.path.dirname(__file_dir__))) + +from archives import locate_unpack + + _file_cache = {} _rw_mask = stat.S_IWRITE | stat.S_IWGRP | stat.S_IWOTH _ro_mask = 0o777 ^ _rw_mask @@ -40,44 +44,10 @@ def _make_RW(test: test.Test, args: List[str]): os.chmod(filename, mode) -def _untar(test: test.Test, src, dst): - with tarfile.open(src) as TAR: - - def is_within_directory(directory, target): - abs_directory = os.path.abspath(directory) - abs_target = os.path.abspath(target) - - prefix = os.path.commonprefix([abs_directory, abs_target]) - - return prefix == abs_directory - - def safe_extract(tar, path=".", members=None, *, numeric_owner=False): - for member in tar.getmembers(): - member_path = os.path.join(path, member.name) - if not is_within_directory(path, member_path): - raise Exception("Attempted Path Traversal in Tar File") - - tar.extractall(path, members, numeric_owner=numeric_owner) - - safe_extract(TAR, test.path(dst)) - - -def _unzip(test: test.Test, src, dst): - with zipfile.ZipFile(src) as ZIP: - ZIP.extractall(test.path(dst)) - - -_ARCHIVES = {".tar": _untar, ".zip": _unzip} - - def _unpack(test: test.Test, args: List[str]): archive = args[0] dst = args[1] - reminder, ext = os.path.splitext(archive) - _, mid = os.path.splitext(reminder) - if mid == ".tar": - ext = ".tar" - _ARCHIVES[ext](test, archive, dst) + locate_unpack(archive)[0](archive, test.path(dst)) def _cat(test: test.Test, args: List[str]): @@ -94,14 +64,17 @@ def _shell(test: test.Test, args: List[str]): HANDLERS: Dict[str, Tuple[int, Callable]] = { "mkdirs": (1, lambda test, args: test.makedirs(args[0])), + "cd": (1, lambda test, args: test.chdir(args[0])), "rm": (1, lambda test, args: test.rmtree(args[0])), + "touch": (1, _touch), + "unpack": (2, _unpack), + "store": (2, lambda test, args: test.store_output(args[0], args[1:])), "ro": (1, _make_RO), + "mock": (2, lambda test, args: test.mock(args[0], args[1])), + "generate": (3, lambda test, args: test.generate(args[0], args[1], args[2:])), + "cp": (2, lambda test, args: test.cp(args[0], args[1])), "rw": (1, _make_RW), - "touch": (1, _touch), - "cd": (1, lambda test, args: test.chdir(args[0])), "ls": (1, lambda test, args: test.ls(args[0])), - "unpack": (2, _unpack), "cat": (1, _cat), - "store": (2, lambda test, args: test.store_output(args[0], args[1:])), "shell": (0, _shell), } diff --git a/apps/tests/driver/test.py b/tools/json_tests/driver/test.py similarity index 76% rename from apps/tests/driver/test.py rename to tools/json_tests/driver/test.py index e89fdaca..85acb65f 100644 --- a/apps/tests/driver/test.py +++ b/tools/json_tests/driver/test.py @@ -35,7 +35,7 @@ def _alt_sep(input, value, var): first = split[0] split = split[1:] for index in range(len(split)): - m = re.match(r"(\S+)(\s*.*)", split[index]) + m = re.match(r"^(\S+)((\n|.)*)$", split[index]) if m is None: continue g2 = m.group(2) @@ -58,7 +58,9 @@ def to_lines(stream: str): @dataclass class Env: target: str + build_dir: str data_dir: str + inst_dir: str tempdir: str version: str counter_digits: int @@ -67,10 +69,15 @@ class Env: data_dir_alt: Optional[str] = None tempdir_alt: Optional[str] = None + @property + def mocks_dir(self): + return os.path.join(self.tempdir, "mocks") + def expand(self, input: str, tempdir: str, additional: Dict[str, str] = {}): input = ( input.replace("$TMP", tempdir) .replace("$DATA", self.data_dir) + .replace("$INST", self.inst_dir) .replace("$VERSION", self.version) ) for key, value in additional.items(): @@ -89,17 +96,32 @@ def fix(self, raw_input: bytes, patches: Dict[str, str]): input = _alt_sep(input, self.tempdir_alt, "$TMP") input = _alt_sep(input, self.data_dir_alt, "$DATA") - if not len(patches): - return input + builtins = { + "\x1B\\[31m\\[(.+) [0-9a-fA-F]+\\]\x1B\\[m (.+)": "\x1B[31m[\\1 $REPORT]\x1B[m \\2", + " \x1B\\[2;37mbased on\x1B\\[m \x1B\\[2;33m[0-9a-fA-F]+@(.+)\x1B\\[m": " \x1B[2;37mbased on\x1B[m \x1B[2;33m$HEAD@\\1\x1B[m", + " \x1B\\[2;37mcontains [0-9a-fA-F]+:\x1B\\[m (.+)": " \x1B[2;37mcontains $BUILD:\x1B[m \\1", + " based on [0-9a-fA-F]+@(.+)": " based on $HEAD@\\1", + " contains [0-9a-fA-F]+: (.+)": " contains $BUILD: \\1", + "(\\s+)parent [0-9a-fA-F]+": "\\1parent $PARENT", + "Added:(\\s+).*": "Added:\\1$DATE", + "CommitDate:(\\s+).*": "CommitDate:\\1$DATE", + "\\[(.+) [0-9a-fA-F]+\\] (.+)": "[\\1 $REPORT] \\2", + } lines = input.split("\n") - for patch in patches: - patched = patches[patch] + for patch, replacement in builtins.items(): + pattern = re.compile(patch) + for lineno in range(len(lines)): + m = pattern.match(lines[lineno]) + if m: + lines[lineno] = m.expand(replacement) + + for patch, replacement in patches.items(): pattern = re.compile(patch) for lineno in range(len(lines)): m = pattern.match(lines[lineno]) if m: - lines[lineno] = m.expand(patched) + lines[lineno] = m.expand(replacement) return "\n".join(lines) @@ -135,6 +157,7 @@ def __init__(self, data: dict, filename: str, count: int): self.current_env: Optional[Env] = None self.additional_env: Dict[str, str] = {} self.patches: Dict[str, str] = data.get("patches", {}) + self.needs_occ_path: bool = False self.check = ["all", "all"] for stream in range(len(_streams)): @@ -263,6 +286,7 @@ def run(self, environment: Env): ) root = self.cwd = os.path.join(self.cwd, root) os.makedirs(root, exist_ok=True) + shutil.rmtree(environment.mocks_dir, ignore_errors=True) prep = self.run_cmds(environment, self.prepare, environment.tempdir) if prep is None: @@ -288,6 +312,9 @@ def run(self, environment: Env): _env[key] = environment.expand(value, environment.tempdir) elif key in _env: del _env[key] + if self.needs_occ_path: + _env["PATH"] += os.pathsep + _env["PATH"] += environment.mocks_dir cwd = None if self.linear else self.cwd proc: subprocess.CompletedProcess = subprocess.run( @@ -425,6 +452,9 @@ def ls(self, sub): def rmtree(self, sub): shutil.rmtree(self.path(sub)) + def cp(self, src: str, dst: str): + shutil.copy2(self.path(src), self.path(dst)) + def makedirs(self, sub): os.makedirs(self.path(sub), exist_ok=True) @@ -438,6 +468,76 @@ def store_output(self, name: str, args: List[str]): self.additional_env[name] = proc.stdout.decode("UTF-8").strip() print(f"export {name}={self.additional_env[name]}") + def mock(self, exe, link: str): + ext = ".exe" if os.name == "nt" and os.path.basename(exe) != "cl.exe" else "" + src = os.path.join(self.current_env.build_dir, "mocks", f"{exe}{ext}") + dst = os.path.join(self.current_env.mocks_dir, f"{link}{ext}") + os.makedirs(os.path.dirname(dst), exist_ok=True) + try: + os.remove(dst) + except FileNotFoundError: + pass + os.symlink(src, dst, target_is_directory=os.path.isdir(src)) + if exe == "OpenCppCoverage": + self.needs_occ_path = True + + def generate_cov_collect(self, template, args: List[str]): + with open(template, encoding="UTF-8") as tmplt: + text = tmplt.read().split("$") + + ext = ( + ".exe" if os.name == "nt" and os.path.basename(args[0]) != "cl.exe" else "" + ) + values = { + "COMPILER": os.path.join(self.current_env.mocks_dir, f"{args[0]}{ext}") + } + first = text[0] + text = text[1:] + for index in range(len(text)): + m = re.match(r"^(\S+)(\s*(\n|.)*)$", text[index]) + if m: + key = m.group(1) + value = values.get(key, f"${key}") + text[index] = f"{value}{m.group(2)}" + + with open(os.path.join(self.cwd, ".covcollect"), "w", encoding="UTF-8") as ini: + ini.write("".join([first, *text])) + + def generate(self, template, dst, args: List[str]): + with open(template, encoding="UTF-8") as tmplt: + text = tmplt.read().split("$") + + values = {} + for arg in args: + kv = [a.strip() for a in arg.split("=", 1)] + key = kv[0] + if len(kv) == 1: + values[key] = "" + else: + value = kv[1] + if key in ["COMPILER"]: + ext = ( + ".exe" + if os.name == "nt" and os.path.basename(value) != "cl.exe" + else "" + ) + value = f"{value}{ext}" + values[key] = value + + first = text[0] + text = text[1:] + for index in range(len(text)): + m = re.match(r"^([a-zA-Z0-9_]+)(\s*(\n|.)*)$", text[index]) + if m: + key = m.group(1) + value = values.get(key, f"${key}") + text[index] = f"{value}{m.group(2)}" + + path = self.path(dst) + os.makedirs(os.path.dirname(path), exist_ok=True) + with open(path, "w", encoding="UTF-8") as ini: + ini.write("".join([first, *text])) + @staticmethod def load(filename, count): with open(filename, encoding="UTF-8") as f: diff --git a/apps/tests/driver/testbed.py b/tools/json_tests/driver/testbed.py similarity index 98% rename from apps/tests/driver/testbed.py rename to tools/json_tests/driver/testbed.py index 95823782..d7c806e3 100644 --- a/apps/tests/driver/testbed.py +++ b/tools/json_tests/driver/testbed.py @@ -95,7 +95,9 @@ def task( env2 = test.Env( env1.target, + env1.build_dir, env1.data_dir, + env1.inst_dir, tempdir, env1.version, env1.counter_digits, diff --git a/apps/tests/nullify.py b/tools/json_tests/nullify.py similarity index 56% rename from apps/tests/nullify.py rename to tools/json_tests/nullify.py index 55f3e349..6a743353 100755 --- a/apps/tests/nullify.py +++ b/tools/json_tests/nullify.py @@ -8,10 +8,14 @@ from driver.test import Test -__dir__ = os.path.dirname(__file__) +__file_dir__ = os.path.dirname(__file__) +__root_dir__ = os.path.abspath(os.path.dirname(os.path.dirname(__file_dir__))) +__test_dir__ = os.path.join(__root_dir__, "apps", "tests") parser = argparse.ArgumentParser() -parser.add_argument("--tests", required=True, metavar="DIR") +parser.add_argument( + "--tests", required=True, metavar="DIR", default=[], action="append", nargs="+" +) parser.add_argument( "--lang", metavar="ID", @@ -26,9 +30,10 @@ def _enum_tests(args: argparse.Namespace): testsuite: List[str] = [] - for root, _, files in os.walk(args.tests): - for filename in files: - testsuite.append(os.path.join(root, filename)) + for testdir in args.tests: + for root, _, files in os.walk(testdir): + for filename in files: + testsuite.append(os.path.join(root, filename)) return testsuite @@ -51,9 +56,18 @@ def _load_tests(testsuite: List[str], run: List[str]): def __main__(): args = parser.parse_args() - args.tests = os.path.join(__dir__, args.tests) + args.tests = [testdir for group in args.tests for testdir in group] + for index in range(len(args.tests)): + testdir = args.tests[index] + if not os.path.isdir(f"{__test_dir__}/{testdir}") and os.path.isdir( + f"{__test_dir__}/main-set/{testdir}" + ): + testdir = f"main-set/{testdir}" + args.tests[index] = os.path.join(__test_dir__, testdir) testsuite = _enum_tests(args) - print("tests: ", args.tests) + print("tests:") + for testdir in args.tests: + print(f" - {testdir}") run = args.run diff --git a/apps/tests/test_runner.py b/tools/json_tests/runner.py similarity index 72% rename from apps/tests/test_runner.py rename to tools/json_tests/runner.py index 2bbb711d..9247807b 100755 --- a/apps/tests/test_runner.py +++ b/tools/json_tests/runner.py @@ -7,7 +7,9 @@ import subprocess import sys -__dir__ = os.path.dirname(__file__) +__file_dir__ = os.path.dirname(__file__) +__root_dir__ = os.path.dirname(os.path.dirname(__file_dir__)) +__test_dir__ = os.path.join(__root_dir__, "apps", "tests") parser = argparse.ArgumentParser() parser.add_argument("--bin", required=True, metavar="BINDIR") @@ -26,13 +28,13 @@ if not os.path.isdir(args.bin) and os.path.isdir(f"./build/{args.bin}"): args.bin = f"./build/{args.bin}" -if not os.path.isdir(f"{__dir__}/{args.tests}") and os.path.isdir( - f"{__dir__}/main-set/{args.tests}" +if not os.path.isdir(f"{__test_dir__}/{args.tests}") and os.path.isdir( + f"{__test_dir__}/main-set/{args.tests}" ): args.tests = f"main-set/{args.tests}" if args.version is None: - tools = os.path.join(os.path.dirname(os.path.dirname(__dir__)), "tools") + tools = os.path.join(__root_dir__, "tools") print(tools) sys.path.append(tools) from github.cmake import get_version @@ -44,10 +46,10 @@ (f"--{key}", value) for key, value in { "target": f"{args.bin}/bin/cov{ext}", - "tests": f"{__dir__}/{args.tests}", - "data-dir": f"{__dir__}/data", + "tests": f"{__test_dir__}/{args.tests}", + "data-dir": f"{__test_dir__}/data", "version": args.version, - "install": f"{__dir__}/copy", + "install": f"{__test_dir__}/copy", "install-with": f"{args.bin}/elsewhere/libexec/cov/cov-echo{ext}", }.items() ] @@ -58,7 +60,7 @@ subprocess.run( [ sys.executable, - f"{__dir__}/test_driver.py", + f"{__file_dir__}/test_driver.py", *(item for arg in new_args for item in arg), ], shell=False, diff --git a/tools/json_tests/schema.json b/tools/json_tests/schema.json new file mode 100644 index 00000000..b8436872 --- /dev/null +++ b/tools/json_tests/schema.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "type": "object", + "required": ["args", "expected"], + "properties": { + "$schema": {"type": "string"}, + "linear": {"type": "boolean"}, + "disabled": {"type": ["boolean", "string"]}, + "lang": {"type": "boolean"}, + "args": {"type": "string"}, + "post": {"type": ["string", "array"], "items": {"type": "string"}}, + "expected": {"type": ["null", "array"]}, + "patches": { + "type": "object", + "properties": { + "^$": {"not": {}}, + "^.+$": {"type": "string"} + } + }, + "prepare": { + "type": ["string", "array"], + "items": {"type": ["string", "array"], "items": {"type": "string"}} + }, + "cleanup": { + "type": ["string", "array"], + "items": {"type": ["string", "array"], "items": {"type": "string"}} + } + }, + "additionalProperties": false, + "default": {} +} \ No newline at end of file diff --git a/apps/tests/test_driver.py b/tools/json_tests/test_driver.py similarity index 93% rename from apps/tests/test_driver.py rename to tools/json_tests/test_driver.py index c3e2af32..eb59afd3 100755 --- a/apps/tests/test_driver.py +++ b/tools/json_tests/test_driver.py @@ -18,11 +18,7 @@ if os.name == "nt": sys.stdout.reconfigure(encoding="utf-8") # type: ignore -try: - os.environ["RUN_LINEAR"] - RUN_LINEAR = True -except KeyError: - RUN_LINEAR = False +RUN_LINEAR = os.environ.get("RUN_LINEAR", 0) != 0 parser = argparse.ArgumentParser() parser.add_argument("--target", required=True, metavar="EXE") @@ -126,7 +122,9 @@ def _make_env(args: argparse.Namespace, counter_total: int): return Env( target=target, + build_dir=os.path.dirname(os.path.dirname(target)), data_dir=args.data_dir, + inst_dir=os.path.join(os.path.dirname(args.data_dir), "copy", "bin"), tempdir=tempdir, version=args.version, counter_digits=digits, @@ -149,6 +147,7 @@ def _install(install: Optional[str], install_with: List[str], env: Env): root_dir = os.path.dirname(os.path.dirname(env.target)) filters_target = os.path.join(install, "additional-filters") + filters_source = os.path.join(os.path.dirname(install), "test-filters") _append("COV_FILTER_PATH", filters_target) _append("COV_PATH", os.path.join(os.path.abspath(root_dir), "dont-copy")) @@ -171,7 +170,6 @@ def _install(install: Optional[str], install_with: List[str], env: Env): dirs_exist_ok=True, ) - filters_source = os.path.join(os.path.dirname(__file__), "test-filters") for _, dirs, files in os.walk(filters_source): dirs[:] = [] for filename in files: @@ -256,7 +254,12 @@ def __main__(): counters.report(outcome, test_id, message) shutil.rmtree(tempdir, ignore_errors=True) else: - with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: + hw_concurrency = os.cpu_count() + try: + thread_count = int(os.environ.get("POOL_SIZE")) + except TypeError: + thread_count = min(61, max(1, hw_concurrency * 2)) + with concurrent.futures.ThreadPoolExecutor(thread_count) as executor: futures = [] for test, counter in independent_tests: futures.append(executor.submit(task, env, test, counter)) diff --git a/tools/run_ctest.py b/tools/run_ctest.py new file mode 100755 index 00000000..fb4adba1 --- /dev/null +++ b/tools/run_ctest.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 + +import json +import os +import subprocess +import sys +import shlex +from pprint import pprint +from typing import Dict, List + +from github.cmake import _cmake + + +def _test_directory(preset_name: str): + configs, presets = _load_presets("CMakePresets.json") + stack = [preset_name] + while len(stack): + next = stack[0] + stack = stack[1:] + preset = presets.get(next) + if preset is None: + continue + + if preset[0] is not None: + bin_dir = _get_bin_dir(configs, preset[0]) + if bin_dir is not None: + return bin_dir + stack.extend(reversed(preset[1])) + + return None + + +def _get_bin_dir(configs: Dict[str, tuple], preset_name: str): + stack = [preset_name] + while len(stack): + next = stack[0] + stack = stack[1:] + preset = configs.get(next) + if preset is None: + continue + + if preset[0] is not None: + return preset[0] + + stack.extend(reversed(preset[1])) + + return None + + +def _load_presets(path: str, known: List[str] = []): + path = os.path.abspath(path) + dirname = os.path.dirname(path) + if path in known: + return ({}, {}) + known.append(path) + + with open(path, encoding="UTF-8") as root_json: + presets = json.load(root_json) + + configures: Dict[str, tuple] = {} + for configurePreset in presets.get("configurePresets", []): + name = configurePreset.get("name") + if name is None: + continue + binaryDir = configurePreset.get("binaryDir") + inherits = configurePreset.get("inherits", []) + configures[name] = (binaryDir, inherits) + + tests: Dict[str, tuple] = {} + for testPreset in presets.get("testPresets", []): + name = testPreset.get("name") + if name is None: + continue + configurePreset = testPreset.get("configurePreset") + inherits = testPreset.get("inherits", []) + tests[name] = (configurePreset, inherits) + + includes = presets.get("include", []) + for include in includes: + sub_cfgs, sub_tests = _load_presets(os.path.join(dirname, include), known) + for name, cfg in sub_cfgs.items(): + configures[name] = cfg + for name, test in sub_tests.items(): + tests[name] = test + + return (configures, tests) + + +def _load_tests(dirname: str): + commands = _cmake(os.path.join(dirname, "CTestTestfile.cmake")) + + tests: Dict[str, List[str]] = {} + + for command in commands: + if command.name == "set_tests_properties": + continue + if command.name == "subdirs": + if len(command.args): + sub = _load_tests(os.path.join(dirname, command.args[0].value)) + for name, cmd in sub.items(): + tests[name] = cmd + continue + + if command.name == "add_test": + if len(command.args) > 1: + name = command.args[0].value[3:-3] + cmd = [arg.value for arg in command.args[1:]] + if not len(cmd) or cmd[0] == "NOT_AVAILABLE": + continue + tests[name] = cmd + continue + + if command.name in ["if", "elseif", "else", "endif"]: + continue + + pprint((dirname, command)) + + return tests + + +def _print_arg(arg: str): + cwd = os.getcwd().replace("\\", "/") + cwd += "/" + if arg[: len(cwd)] == cwd: + arg = arg[len(cwd) :] + color = "" + if arg[:1] == "-": + color = "\033[2;37m" + arg = shlex.join([arg.replace("\\", "/")]) + if color == "" and arg[:1] in ["'", '"']: + color = "\033[2;34m" + if color == "": + return arg + return f"{color}{arg}\033[m" + + +def print_args(*args: str): + cmd = shlex.join([args[0]]) + args = " ".join([_print_arg(arg) for arg in args[1:]]) + print(f"\033[33m{cmd}\033[m {args}", file=sys.stderr) + + +bin_dir = _test_directory(sys.argv[1]) +if bin_dir is None: + sys.exit(1) + +bin_dir = bin_dir.replace("${sourceDir}/", "./") +tests = _load_tests(bin_dir) +if len(sys.argv) < 3: + print("known test:", " ".join(tests.keys())) + sys.exit(0) +try: + test = [*tests[sys.argv[2]], *sys.argv[3:]] +except KeyError: + print("known test:", " ".join(tests.keys())) + sys.exit(1) +print_args(*test) +subprocess.run(test, shell=False) diff --git a/tools/win32/icon.ps1 b/tools/win32/icon.ps1 index 4a6087e2..3d28dbfb 100644 --- a/tools/win32/icon.ps1 +++ b/tools/win32/icon.ps1 @@ -1,11 +1,38 @@ -$Icons = "$PSScriptRoot\..\..\data\icons" +$Icons = Resolve-Path "$PSScriptRoot\..\..\data\icons" $sizes = @("16", "24", "32", "48", "256") -$paths = @() -convert -background none $Icons\appicon-win32.svg $Icons\appicon-win32-mask.svg -alpha Off -compose CopyOpacity -composite $Icons\png\win32.png +$null = New-Item -Type Directory -Force "$Icons\png" + +convert -background none ` + $Icons\appicon-win32.svg ` + $Icons\appicon-win32-mask.svg ` + -alpha Off -compose CopyOpacity ` + -composite $Icons\png\win32.png + +$paths = @() foreach($size in $sizes) { $paths += "$Icons\png\win32-$size.png" - convert -background none $Icons\png\win32.png -resize "${size}x${size}" -depth 32 "$Icons\png\win32-$size.png" + convert -background none ` + $Icons\png\win32.png ` + -resize "${size}x${size}" -depth 32 ` + "$Icons\png\win32-$size.png" } convert $paths "$Icons\win32.ico" + +convert -background none ` + $Icons\plugin-win32.svg ` + $Icons\appicon-win32-mask.svg ` + -alpha Off -compose CopyOpacity ` + -composite $Icons\png\plugin.png + +$paths = @() +foreach($size in $sizes) { + $paths += "$Icons\png\plugin-$size.png" + convert -background none ` + $Icons\png\plugin.png ` + -resize "${size}x${size}" -depth 32 ` + "$Icons\png\plugin-$size.png" +} + +convert $paths "$Icons\plugin.ico" diff --git a/tools/icon.py b/tools/win32/icon.py similarity index 72% rename from tools/icon.py rename to tools/win32/icon.py index 4d934a6f..96f69a62 100644 --- a/tools/icon.py +++ b/tools/win32/icon.py @@ -76,6 +76,20 @@ def l(self, p: pt): self.code.append(f"L{self.pos + p}") self.pos += p + def a( + self, + p: pt, + r: Union[pt, number], + x_rot: number = 0, + large_arc: bool = False, + sweep: bool = False, + ): + radius = r if isinstance(r, pt) else pt(r, r) + large_arc_s = "1" if large_arc else "0" + sweep_s = "1" if sweep else "0" + self.code.append(f"A{radius} {x_rot} {large_arc_s} {sweep_s} {self.pos + p}") + self.pos += p + def c(self, dst: pt, c1: pt, c2: pt): self.code.append(f"C{self.pos + c1} {dst - c2} {dst}") self.pos = dst @@ -217,17 +231,68 @@ def trace_mark() -> path: return p +class puzzle_side: + def __init__(self, width: number): + self.width = width + + init_stub = 1 + init_edge = 4 + init_radius = 3 + + scale = width / (2 * (init_edge + init_radius)) + + self.stub = init_stub * scale + self.edge = init_edge * scale + self.radius = init_radius * scale + + def draw(self, p: path, horiz: bool, innie: bool, returning: bool, last=False): + sign = -1 if returning else 1 + stub_sign = -sign if (innie ^ horiz) else sign + main = p.h if horiz else p.v + across = p.v if horiz else p.h + + reach = sign * 2 * self.radius + endpoint = pt(reach, 0) if horiz else pt(0, reach) + + main(sign * self.edge) + across(stub_sign * self.stub) + # main(reach) + p.a(endpoint, self.radius, sweep=not innie) + across(-stub_sign * self.stub) + if not last: + main(sign * self.edge) + + +def trace_puzzle() -> path: + width, _, top, mid = shield_calc() + puzzle_width = width / 2 + + side = puzzle_side(puzzle_width) + + p = path() + p.M(pt(-puzzle_width / 2, top + mid - puzzle_width / 3)) + + side.draw(p, horiz=True, innie=False, returning=False) + side.draw(p, horiz=False, innie=True, returning=False) + side.draw(p, horiz=True, innie=False, returning=True) + side.draw(p, horiz=False, innie=True, returning=True, last=True) + + p.z() + return p + + def trace_glyphs(): - return (trace_shield(), trace_shadow(), trace_fail(), trace_mark()) + return (trace_shield(), trace_shadow(), trace_fail(), trace_mark(), trace_puzzle()) class Shield: def __init__(self): - shield_glyph, shadow, fail, mark = trace_glyphs() + shield_glyph, shadow, fail, mark, puzzle = trace_glyphs() self.shield_glyph = shield_glyph self.shadow = shadow self.fail_glyph = fail self.mark_glyph = mark + self.puzzle = puzzle def _favicon(self, symbol: path, shield_color: str, symbol_color: str): width, height, _, _ = shield_calc() @@ -278,11 +343,32 @@ def appicon_cov(self): def appicon_win32(self): return self._appicon(self.mark_glyph, "#b9e7fc", "#1f88e5", False) + def plugin_win32(self): + shield_color = "#b9e7fc" + symbol_color = "#1f88e5" + height = shield_calc()[1] + result = svg_group(pt(ICON_HEIGHT / 2, ICON_HEIGHT - height)) + result.paths = [ + svg_path(self.shield_glyph, style("fill", shield_color)), + svg_path( + self.puzzle, + style("stroke", symbol_color), + style("fill", symbol_color, opacity=".6"), + ), + svg_path( + self.shadow, + style("fill", "#000"), + style("opacity", ".2"), + ), + svg_path(self.shield_glyph, style("stroke", symbol_color, width="2")), + ] + return svg_root(ICON_HEIGHT, result, px_size=1024) + def appicon_win32_mask(self): height = shield_calc()[1] result = svg_group(pt(ICON_HEIGHT / 2, ICON_HEIGHT - height)) result.paths = [ - f'', + f'', svg_path(self.shield_glyph, style("fill", "#fff")), ] return svg_root(ICON_HEIGHT, result, px_size=1024) @@ -311,12 +397,13 @@ def favicon_outline(self): shield = Shield() -__dir__ = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "..", "data", "icons" -) +__dir_name__ = os.path.dirname(__file__) +__root_dir__ = os.path.dirname(os.path.dirname(__dir_name__)) +__icons__ = os.path.join(__root_dir__, "data", "icons") + for icon in ["good", "bad", "passing", "outline"]: pathname = f"favicon-{icon}.svg" - with open(os.path.join(__dir__, pathname), "wb") as f: + with open(os.path.join(__icons__, pathname), "wb") as f: svg = str(getattr(shield, f"favicon_{icon}")()) f.write(svg.encode("UTF-8")) f.write(b"\n") @@ -326,7 +413,12 @@ def favicon_outline(self): pathname = {"cov": "appicon.svg"}[icon] except KeyError: pathname = f"appicon-{icon.replace('_', '-')}.svg" - with open(os.path.join(__dir__, pathname), "wb") as f: + with open(os.path.join(__icons__, pathname), "wb") as f: svg = str(getattr(shield, f"appicon_{icon}")()) f.write(svg.encode("UTF-8")) f.write(b"\n") + +with open(os.path.join(__icons__, "plugin-win32.svg"), "wb") as f: + svg = str(shield.plugin_win32()) + f.write(svg.encode("UTF-8")) + f.write(b"\n") diff --git a/tools/win32/refresh-cert.ps1 b/tools/win32/refresh-cert.ps1 new file mode 100644 index 00000000..b6e59bc5 --- /dev/null +++ b/tools/win32/refresh-cert.ps1 @@ -0,0 +1,25 @@ +param( + [string]$password, + [string]$filename +) + +$params = @{ + Type = 'Custom' + Container = 'test*' + Subject = 'CN=Marcin Zdun' + TextExtension = @( + '2.5.29.37={text}1.3.6.1.5.5.7.3.3', + '2.5.29.17={text}email=mzdun@midnightbits.com' ) + KeyUsage = 'DigitalSignature' + KeyAlgorithm = 'RSA' + KeyLength = 2048 + NotAfter = (Get-Date).AddMonths(12) + CertStoreLocation = 'Cert:\CurrentUser\My' +} +$cert = New-SelfSignedCertificate @params + +$pwd = ConvertTo-SecureString -String $password -Force -AsPlainText + +$file = Export-PfxCertificate -Cert $cert -FilePath $filename -Password $pwd + +Get-ChildItem Cert:\CurrentUser\My | Where-Object {$_.Thumbprint -match $cert.Thumbprint} | Remove-Item diff --git a/tools/win32/refresh-cert.py b/tools/win32/refresh-cert.py new file mode 100644 index 00000000..e991adbc --- /dev/null +++ b/tools/win32/refresh-cert.py @@ -0,0 +1,53 @@ +# Copyright (c) 2023 Marcin Zdun +# This code is licensed under MIT license (see LICENSE for details) + +import base64 +import json +import os +import random +import string +import subprocess +import sys + +__dir_name__ = os.path.dirname(__file__) +__root_dir__ = os.path.dirname(os.path.dirname(__dir_name__)) + + +def run(*args: str): + proc = subprocess.run(args, shell=False, capture_output=False) + if proc.returncode != 0: + sys.exit(1) + + +letters = string.ascii_letters + string.digits + string.punctuation +weights = ( + ([7] * len(string.ascii_letters)) + + ([5] * len(string.digits)) + + ([2] * len(string.punctuation)) +) +token = "".join(random.choices(letters, weights, k=50)) + +run( + "pwsh", + os.path.join(__dir_name__, "refresh-cert.ps1"), + "-password", + token, + "-filename", + "certificate.pfx", +) + + +def load_binary(filename: str): + with open(filename, "rb") as cert: + return base64.b64encode(cert.read()).decode("UTF-8") + + +data = { + "token": base64.b64encode(token.encode("UTF-8")).decode("UTF-8"), + "secret": load_binary("certificate.pfx"), +} + +os.remove("certificate.pfx") + +with open("signature.key", "w", encoding="UTF-8") as signature: + json.dump(data, signature) diff --git a/tools/win32/sign.py b/tools/win32/sign.py new file mode 100644 index 00000000..86fc496b --- /dev/null +++ b/tools/win32/sign.py @@ -0,0 +1,122 @@ +# Copyright (c) 2023 Marcin Zdun +# This code is licensed under MIT license (see LICENSE for details) + +import base64 +import json +import os +import platform +import subprocess +import sys +import winreg +from typing import List, NamedTuple, Optional, Tuple + +__dir_name__ = os.path.dirname(__file__) +__root_dir__ = os.path.dirname(os.path.dirname(__dir_name__)) + +ENV_KEY = "SIGN_TOKEN" + +Version = Tuple[int, int, int] + +machine = {"ARM64": "arm64", "AMD64": "x64", "X86": "x86"}.get( + platform.machine(), "x86" +) + + +def find_sign_tool() -> Optional[str]: + with winreg.OpenKeyEx( + winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows Kits\Installed Roots" + ) as kits: + try: + kits_root = winreg.QueryValueEx(kits, "KitsRoot10")[0] + except FileNotFoundError: + return None + + versions: List[Tuple[Version, str]] = [] + try: + index = 0 + while True: + ver_str = winreg.EnumKey(kits, index) + ver = tuple(int(chunk) for chunk in ver_str.split(".")) + index += 1 + versions.append((ver, ver_str)) + except OSError: + pass + versions.sort() + versions.reverse() + for _, version in versions: + # C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\signtool.exe + sign_tool = os.path.join(kits_root, "bin", version, machine, "signtool.exe") + if os.path.isfile(sign_tool): + return sign_tool + return None + + +def get_key_(): + env = os.environ.get(ENV_KEY) + if env is not None: + return env + filename = os.path.join(os.path.expanduser("~"), "signature.key") + if os.path.isfile(filename): + with open(filename, encoding="UTF-8") as file: + return file.read().strip() + filename = os.path.join(__root_dir__, "signature.key") + if os.path.isfile(filename): + with open(filename, encoding="UTF-8") as file: + return file.read().strip() + + return None + + +class Key(NamedTuple): + token: str + secret: bytes + + +def get_key() -> Optional[Key]: + key = get_key_() + if key is None: + return None + obj = json.loads(key) + token = obj.get("token") + secret = obj.get("secret") + return Key( + base64.b64decode(token).decode("UTF-8") if token is not None else None, + base64.b64decode(secret) if secret is not None else None, + ) + + +key = get_key() + +if key is None or key.token is None or key.secret is None: + print("sign.py: the key is missing", file=sys.stderr) + sys.exit(0) + +sign_tool = find_sign_tool() +if sign_tool is None: + print("sign.py: signtool.exe not found", file=sys.stderr) + sys.exit(0) + +with open("temp.pfx", "wb") as pfx: + pfx.write(key.secret) +args = [ + sign_tool, + "sign", + # "/v", + # "/debug", + "/f", + "temp.pfx", + "/p", + key.token, + "/tr", + "http://timestamp.digicert.com", + "/fd", + "sha256", + "/td", + "sha256", + *sys.argv[1:], +] +try: + if subprocess.run(args, shell=False).returncode: + sys.exit(1) +finally: + os.remove("temp.pfx")