diff --git a/.github/workflows/dispatch-workflow.yml b/.github/workflows/dispatch-workflow.yml index 90d2f388748..0f0b91ef7ff 100644 --- a/.github/workflows/dispatch-workflow.yml +++ b/.github/workflows/dispatch-workflow.yml @@ -29,6 +29,9 @@ on: bencher: required: true type: boolean + coverage: + required: true + type: boolean jobs: win: @@ -71,6 +74,7 @@ jobs: build-libservo: ${{ inputs.build-libservo }} wpt-args: ${{ inputs.wpt-args }} bencher: ${{ inputs.bencher }} + coverage: ${{ inputs.coverage }} lint: if: ${{ inputs.workflow == 'lint' }} diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 65910e9af61..16de1b15d2b 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -48,6 +48,10 @@ on: required: false default: false type: boolean + coverage: + required: false + default: false + type: boolean workflow_dispatch: inputs: profile: @@ -303,3 +307,137 @@ jobs: - name: Compile libservo with MSRV run: | cargo +${{ steps.msrv.outputs.rust_version }} build -p libservo --locked --all-targets + + # Runs the underlying job (“workload”) on a self-hosted runner if available, + # with the help of a `runner-select` job and a `runner-timeout` job. + runner-select-coverage: + if: inputs.coverage + runs-on: ubuntu-22.04 + outputs: + unique-id: ${{ steps.select.outputs.unique-id }} + selected-runner-label: ${{ steps.select.outputs.selected-runner-label }} + is-self-hosted: ${{ steps.select.outputs.is-self-hosted }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + sparse-checkout: '.github' + - name: Runner select + id: select + uses: ./.github/actions/runner-select + with: + monitor-api-token: ${{ secrets.SERVO_CI_MONITOR_API_TOKEN }} + # Before updating the GH action runner image for the nightly job, ensure + # that the system has a glibc version that is compatible with the one + # used by the wpt.fyi runners. + github-hosted-runner-label: ubuntu-22.04 + self-hosted-image-name: servo-ubuntu2204 + # You can disable self-hosted runners globally by creating a repository variable named + # NO_SELF_HOSTED_RUNNERS with any non-empty value. + # + NO_SELF_HOSTED_RUNNERS: ${{ vars.NO_SELF_HOSTED_RUNNERS }} + # Any other boolean conditions that disable self-hosted runners go here. + force-github-hosted-runner: ${{ inputs.force-github-hosted-runner }} + runner-timeout-coverage: + needs: + - runner-select-coverage + if: inputs.coverage && fromJSON(needs.runner-select-coverage.outputs.is-self-hosted) + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + sparse-checkout: '.github' + - name: Runner timeout + uses: ./.github/actions/runner-timeout + with: + github_token: '${{ secrets.GITHUB_TOKEN }}' + unique-id: '${{ needs.runner-select-coverage.outputs.unique-id }}' + + unit-test-coverage: + if: inputs.coverage + needs: runner-select-coverage + name: Unit Test Coverage [${{ needs.runner-select-coverage.outputs.unique-id }}] + runs-on: ${{ needs.runner-select-coverage.outputs.selected-runner-label }} + continue-on-error: true + steps: + - uses: actions/checkout@v4 + if: ${{ runner.environment != 'self-hosted' && github.event_name != 'pull_request_target' }} + # This is necessary to checkout the pull request if this run was triggered via a + # `pull_request_target` event. + - uses: actions/checkout@v4 + if: ${{ runner.environment != 'self-hosted' && github.event_name == 'pull_request_target' }} + with: + ref: ${{ github.event.pull_request.head.sha }} + # Faster checkout for self-hosted runner that uses prebaked repo. + - if: ${{ runner.environment == 'self-hosted' && github.event_name != 'pull_request_target' }} + run: git fetch --depth=1 origin $GITHUB_SHA + - if: ${{ runner.environment == 'self-hosted' && github.event_name == 'pull_request_target' }} + run: git fetch --depth=1 origin ${{ github.event.pull_request.head.sha }} + - if: ${{ runner.environment == 'self-hosted' }} + # Same as `git switch --detach FETCH_HEAD`, but fixes up dirty working + # trees, in case the runner image was baked with a dirty working tree. + run: | + git switch --detach + git reset --hard FETCH_HEAD + - name: Free Disk Space (Ubuntu) + uses: jlumbroso/free-disk-space@main + if: ${{ runner.environment != 'self-hosted' }} + with: + tool-cache: false + large-packages: false + swap-storage: false + - name: Set LIBCLANG_PATH env # needed for bindgen in mozangle + shell: bash + if: ${{ runner.environment != 'self-hosted' }} + run: echo "LIBCLANG_PATH=/usr/lib/llvm-14/lib" >> $GITHUB_ENV + - name: Setup Python + if: ${{ runner.environment != 'self-hosted' }} + uses: ./.github/actions/setup-python + - name: Change Mirror Priorities + if: ${{ runner.environment != 'self-hosted' }} + uses: ./.github/actions/apt-mirrors + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@v2 + with: + tool: cargo-llvm-cov + - name: Bootstrap dependencies + if: ${{ runner.environment != 'self-hosted' }} + shell: bash + run: | + sudo apt update + ./mach bootstrap --skip-lints + - name: Determine options + id: options + run: | + if [[ ${{ runner.environment }} == 'self-hosted' ]]; + then + CARGO_PROFILE=dev + else + # github hosted runners don't have enough diskspace for the dev profile. + CARGO_PROFILE=coverage + fi + echo "cargo_profile=${CARGO_PROFILE}" | tee $GITHUB_OUTPUT + # We can remove this after upgrading to Rust 1.90, but for now + # installing lld is the easiest way to get it in path. + - name: install lld + run: sudo apt-get install -y lld + - name: Run unit-tests with coverage + shell: bash + env: + RUSTC_BOOTSTRAP: "1" + # lld seems to use less memory and prevents OOM errors during linking. + # We can remove this after upgrading to Rust 1.90, where lld will be the default. + RUSTFLAGS: "-Z linker-features=+lld" + run: | + ./mach test-unit --code-coverage \ + --profile=${{ steps.options.outputs.cargo_profile }} \ + --llvm-cov-option=--codecov \ + --llvm-cov-option=--output-path=codecov.json + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: codecov.json,support/crown/codecov.json + fail_ci_if_error: true + flags: unittests diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1d11033a326..6ae2c77f49f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,7 +35,7 @@ jobs: run: | { echo 'result<> $GITHUB_OUTPUT @@ -59,6 +59,7 @@ jobs: build-args: ${{ matrix.build_args }} number-of-wpt-chunks: ${{ matrix.number_of_wpt_chunks }} bencher: ${{ matrix.bencher }} + coverage: ${{ matrix.coverage }} build-result: name: Result diff --git a/.github/workflows/try.yml b/.github/workflows/try.yml index c3a49af8857..20b1884a29f 100644 --- a/.github/workflows/try.yml +++ b/.github/workflows/try.yml @@ -116,6 +116,7 @@ jobs: wpt-args: ${{ matrix.wpt_args }} number-of-wpt-chunks: ${{ matrix.number_of_wpt_chunks }} bencher: ${{ matrix.bencher }} + coverage: ${{ matrix.coverage }} build-result: name: Result diff --git a/Cargo.toml b/Cargo.toml index 1ecad2a45a1..5f5f8a9ad36 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -200,6 +200,13 @@ opt-level = 1 opt-level = 3 debug-assertions = true +# Disk-space is limited in CI, so we disabled features that are not needed for code coverage. +# Locally using the default `dev` profile is perfectly fine. +[profile.coverage] +inherits = "dev" +incremental = false +debug = false + # A profile between `dev` and `release` which aims to offer a compromise between # fast incremental rebuilds and runtime speed. [profile.medium] diff --git a/python/servo/try_parser.py b/python/servo/try_parser.py index 0a74d8268b6..ac44b2ed309 100644 --- a/python/servo/try_parser.py +++ b/python/servo/try_parser.py @@ -39,6 +39,7 @@ class JobConfig(object): unit_tests: bool = False build_libservo: bool = False bencher: bool = False + coverage: bool = False build_args: str = "" wpt_args: str = "" number_of_wpt_chunks: int = 20 @@ -57,6 +58,7 @@ class JobConfig(object): self.unit_tests |= other.unit_tests self.build_libservo |= other.build_libservo self.bencher |= other.bencher + self.coverage |= other.coverage self.number_of_wpt_chunks = max(self.number_of_wpt_chunks, other.number_of_wpt_chunks) self.update_name() return True @@ -83,6 +85,8 @@ class JobConfig(object): modifier.append("WPT") if self.bencher: modifier.append("Bencher") + if self.coverage: + modifier.append("Coverage") if modifier: self.name += " (" + ", ".join(modifier) + ")" @@ -155,6 +159,8 @@ def handle_modifier(config: Optional[JobConfig], s: str) -> Optional[JobConfig]: config.profile = "production" if "bencher" in s: config.bencher = True + if "coverage" in s: + config.coverage = True elif "wpt" in s: config.wpt = True config.update_name() @@ -204,6 +210,9 @@ class Config(object): words.extend(["linux-production-bencher", "macos-production-bencher", "windows-production-bencher"]) words.extend(["ohos-production-bencher"]) continue # skip over keyword + if word in ["cov", "coverage", "test-coverage"]: + words.extend(["linux-coverage"]) + continue # skip over keyword job = handle_preset(word) job = handle_modifier(job, word) if job is None: @@ -252,6 +261,7 @@ class TestParser(unittest.TestCase): "wpt": False, "wpt_args": "", "build_args": "", + "coverage": False, } ], }, @@ -274,6 +284,7 @@ class TestParser(unittest.TestCase): "bencher": True, "wpt_args": "", "build_args": "", + "coverage": False, }, { "name": "MacOS (Unit Tests, Build libservo)", @@ -286,6 +297,7 @@ class TestParser(unittest.TestCase): "bencher": False, "wpt_args": "", "build_args": "", + "coverage": False, }, { "name": "Windows (Unit Tests, Build libservo)", @@ -298,6 +310,7 @@ class TestParser(unittest.TestCase): "bencher": False, "wpt_args": "", "build_args": "", + "coverage": False, }, { "name": "Android", @@ -310,6 +323,7 @@ class TestParser(unittest.TestCase): "bencher": False, "wpt_args": "", "build_args": "", + "coverage": False, }, { "name": "OpenHarmony", @@ -322,6 +336,7 @@ class TestParser(unittest.TestCase): "bencher": False, "wpt_args": "", "build_args": "", + "coverage": False, }, { "name": "Lint", @@ -334,6 +349,7 @@ class TestParser(unittest.TestCase): "bencher": False, "wpt_args": "", "build_args": "", + "coverage": False, }, ], }, @@ -356,6 +372,7 @@ class TestParser(unittest.TestCase): "wpt": True, "wpt_args": "", "build_args": "", + "coverage": False, } ], },