Skip to content

Commit a657c08

Browse files
authored
Update CI and use nextest (#59)
This updates the CI definitions a bit: - Invokes `rustup` directly instead of using an action - Updates the `setup-python` action to the latest version - Adds a separate `doctest` run to run doctests and emit `cargo doc` errors - Switches to `nextest` for running test, which also has builtin junit output - Always run tests with coverage
1 parent ad59f73 commit a657c08

File tree

13 files changed

+103
-113
lines changed

13 files changed

+103
-113
lines changed

.config/nextest.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[profile.default.junit]
2+
path = "core-test-results.xml"

.github/workflows/ci.yml

Lines changed: 41 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ on:
66
- main
77
pull_request:
88

9+
env:
10+
RUSTFLAGS: -Dwarnings
11+
912
concurrency:
1013
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
1114
cancel-in-progress: true
@@ -15,81 +18,58 @@ jobs:
1518
name: Lint (rustfmt + clippy)
1619
runs-on: ubuntu-latest
1720
steps:
18-
- name: Checkout
19-
uses: actions/checkout@v4
20-
- name: Install Rust
21-
uses: dtolnay/rust-toolchain@nightly
22-
with:
23-
toolchain: nightly
24-
components: clippy, rustfmt
25-
- name: Run lint
26-
run: make lint.rust
21+
- uses: actions/checkout@v4
22+
23+
- run: rustup toolchain install nightly --profile minimal --component rustfmt --component clippy --no-self-update
24+
25+
- run: cargo fmt --all -- --check
26+
- run: cargo clippy --all-features --workspace --tests --examples -- -D clippy::all
2727

2828
lint-python:
2929
name: Lint Python (ruff, mypy)
3030
runs-on: ubuntu-latest
3131
steps:
32-
- name: Checkout
33-
uses: actions/checkout@v4
34-
- uses: actions/setup-python@v3
35-
- name: Set up venv
36-
run: make ci.setup_venv
37-
- name: Build and run lint
38-
run: |
32+
- uses: actions/checkout@v4
33+
34+
- uses: actions/setup-python@v5
35+
- run: make ci.setup_venv
36+
37+
- run: |
3938
maturin develop
4039
make lint.python
4140
42-
test:
43-
name: Test
41+
doctest:
42+
name: Documentation (and Tests)
4443
runs-on: ubuntu-latest
45-
strategy:
46-
fail-fast: false
44+
env:
45+
RUSTDOCFLAGS: -Dwarnings
4746
steps:
48-
- name: Checkout
49-
uses: actions/checkout@v4
50-
- name: Install Rust
51-
uses: dtolnay/rust-toolchain@nightly
52-
with:
53-
toolchain: nightly
54-
- name: Run Rust tests
55-
run: |
56-
cargo test
47+
- uses: actions/checkout@v4
5748

58-
- uses: actions/setup-python@v3
59-
- name: Set up venv
60-
run: make ci.setup_venv
61-
- name: Run Python tests
62-
run: |
63-
maturin develop
64-
pytest
65-
66-
# This job runs tests, generates coverage data, and generates JUnit test
67-
# results in a single test invocation and then uploads it all to Codecov.
68-
# However, it doesn't print test results to stdout. If Codecov's failed test
69-
# reporting is solid and we never need to see the results in the CI logs, we
70-
# can delete the "normal" test step and just use this.
71-
test-for-codecov:
72-
name: Test (Upload to Codecov)
49+
- run: rustup toolchain install nightly --profile minimal --no-self-update
50+
51+
- run: cargo test --workspace --all-features --doc
52+
- run: cargo doc --workspace --all-features --document-private-items --no-deps
53+
54+
test:
55+
name: Test
7356
runs-on: ubuntu-latest
74-
strategy:
75-
fail-fast: false
7657
steps:
77-
- name: Checkout
78-
uses: actions/checkout@v4
79-
- name: Install Rust
80-
uses: dtolnay/rust-toolchain@nightly
81-
with:
82-
toolchain: nightly
83-
- name: Install `cargo llvm-cov`
84-
uses: taiki-e/install-action@cargo-llvm-cov
85-
- name: Run tests
86-
run: |
87-
cargo install cargo2junit
88-
cargo llvm-cov --lcov --output-path core.lcov -- -Z unstable-options --format json --report-time | cargo2junit > core-test-results.xml
58+
- uses: actions/checkout@v4
59+
60+
- run: rustup toolchain install nightly --profile minimal --no-self-update
61+
- uses: taiki-e/install-action@cargo-llvm-cov
62+
- uses: taiki-e/install-action@nextest
63+
64+
# FIXME(swatinem): We should pass `--all-targets` to also compile and tests benchmarks
65+
# Though currently `divan` does not support all CLI arguments as used by `nextest`,
66+
# and benchmarks are unbearably slow anyway, so its not feasible to run in debug builds.
67+
- run: cargo llvm-cov nextest --lcov --output-path core.lcov --workspace --all-features
68+
- run: mv target/nextest/default/core-test-results.xml .
69+
70+
- uses: actions/setup-python@v5
71+
- run: make ci.setup_venv
8972

90-
- uses: actions/setup-python@v3
91-
- name: Set up venv
92-
run: make ci.setup_venv
9373
- name: Run Python tests
9474
run: |
9575
# Clear prior profile data

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,5 @@ __pycache__/
2828

2929
.git
3030
lcov.info
31+
/*test-results.xml
32+
/*.lcov

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
[workspace]
2-
32
resolver = "2"
43
members = ["bindings", "core", "test_utils"]
54
default-members = ["core"]

core/benches/pyreport.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::collections::HashMap;
22

33
use codecov_rs::{
44
parsers::pyreport::{chunks, report_json},
5-
report::test::{TestReport, TestReportBuilder},
5+
test_utils::test_report::{TestReport, TestReportBuilder},
66
};
77
use divan::Bencher;
88
use test_utils::fixtures::{read_fixture, FixtureFormat::Pyreport, FixtureSize::Large};

core/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ pub mod parsers;
77
pub mod error;
88

99
#[cfg(any(test, feature = "testing"))]
10-
mod test_utils;
10+
pub mod test_utils;

core/src/parsers/pyreport/chunks.rs

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ use super::{
2020
},
2121
utils,
2222
};
23+
#[cfg(doc)]
24+
use crate::report::models;
2325
use crate::report::{
2426
pyreport::{types::*, CHUNKS_FILE_END_OF_CHUNK, CHUNKS_FILE_HEADER_TERMINATOR},
2527
Report, ReportBuilder,
@@ -28,7 +30,7 @@ use crate::report::{
2830
#[derive(PartialEq, Debug)]
2931
pub struct ChunkCtx {
3032
/// The index of this chunk in the overall sequence of chunks tells us which
31-
/// [`crate::report::models::SourceFile`] this chunk corresponds to.
33+
/// [`SourceFile`](models::SourceFile) this chunk corresponds to.
3234
pub index: usize,
3335

3436
/// Each line in a chunk corresponds to a line in the source file.
@@ -44,7 +46,7 @@ pub struct ParseCtx<R: Report, B: ReportBuilder<R>> {
4446

4547
/// Tracks the labels that we've already added to the report. The key is the
4648
/// identifier for the label inside the chunks file and the value is the
47-
/// ID of the [`crate::report::models::Context`] we created for it in
49+
/// ID of the [`Context`](models::Context) we created for it in
4850
/// the output. If a `"labels_index"` key is present in the chunks file
4951
/// header, this is populated all at once and the key is a numeric ID.
5052
/// Otherwise, this is populated as new labels are encountered and the key
@@ -55,12 +57,12 @@ pub struct ParseCtx<R: Report, B: ReportBuilder<R>> {
5557
pub chunk: ChunkCtx,
5658

5759
/// The output of the report JSON parser includes a map from `chunk_index`
58-
/// to the ID of the [`crate::report::models::SourceFile`] that the
60+
/// to the ID of the [`SourceFile`](models::SourceFile) that the
5961
/// chunk corresponds to.
6062
pub report_json_files: HashMap<usize, i64>,
6163

6264
/// The output of the report JSON parser includes a map from `session_id` to
63-
/// the ID of the [`crate::report::models::Context`] that the session
65+
/// the ID of the [`Context`](models::Context) that the session
6466
/// corresponds to.
6567
pub report_json_sessions: HashMap<usize, i64>,
6668
}
@@ -121,8 +123,8 @@ pub fn coverage<S: StrStream, R: Report, B: ReportBuilder<R>>(
121123
.parse_next(buf)
122124
}
123125

124-
/// Parses the coverage type described by a `ReportLine`. Beware: this field may
125-
/// be inaccurate.
126+
/// Parses the coverage type described by a [`ReportLine`]. Beware: this field
127+
/// may be inaccurate.
126128
///
127129
/// For example, in a chunks file for a Go project, the "coverage type" field is
128130
/// always `null` when some of the values in the "coverage" field indicate the
@@ -141,7 +143,8 @@ pub fn coverage_type<S: StrStream, R: Report, B: ReportBuilder<R>>(
141143
.parse_next(buf)
142144
}
143145

144-
/// Parses value of the "complexity" field in a `ReportLine` or `LineSession`.
146+
/// Parses value of the "complexity" field in a [`ReportLine`] or
147+
/// [`LineSession`].
145148
///
146149
/// Examples: `1`, `3`, `[0, 1]`, `[2, 2]`
147150
pub fn complexity<S: StrStream, R: Report, B: ReportBuilder<R>>(
@@ -218,9 +221,9 @@ where
218221
.parse_next(buf)
219222
}
220223

221-
/// Parses values in the "partials" field of a `LineSession`. These values don't
222-
/// necessarily have to do with partial branch coverage; what they describe is
223-
/// the coverage status of different subspans of a single line.
224+
/// Parses values in the "partials" field of a [`LineSession`]. These values
225+
/// don't necessarily have to do with partial branch coverage; what they
226+
/// describe is the coverage status of different subspans of a single line.
224227
///
225228
/// Examples:
226229
/// - `[null, 10, 0]`: This line was not covered from its start until column 10
@@ -254,7 +257,7 @@ pub fn partial_spans<S: StrStream, R: Report, B: ReportBuilder<R>>(
254257
}
255258

256259
/// Parses a [`LineSession`]. Each [`LineSession`] corresponds to a
257-
/// [`crate::report::models::CoverageSample`] in the output report.
260+
/// [`CoverageSample`](models::CoverageSample) in the output report.
258261
///
259262
/// A [`ReportLine`] has a [`LineSession`] for each upload ("session") sent to
260263
/// us for a commit. The [`LineSession`] contains the coverage measurements for
@@ -346,7 +349,7 @@ pub fn label<S: StrStream, R: Report, B: ReportBuilder<R>>(
346349
///
347350
/// Technically `_coverage_type` is optional, but the way it gets serialized
348351
/// when it's missing is identical to the way we serialize
349-
/// [`crate::report::models::CoverageType::Line`] so there's no way to tell
352+
/// [`CoverageType::Line`] so there's no way to tell
350353
/// which it is when deserializing.
351354
pub fn coverage_datapoint<S: StrStream, R: Report, B: ReportBuilder<R>>(
352355
buf: &mut ReportOutputStream<S, R, B>,
@@ -369,11 +372,12 @@ pub fn coverage_datapoint<S: StrStream, R: Report, B: ReportBuilder<R>>(
369372

370373
/// Parses a [`ReportLine`]. A [`ReportLine`] itself does not correspond to
371374
/// anything in the output, but it's an umbrella that includes all of the data
372-
/// tied to a line/[`CoverageSample`].
375+
/// tied to a line/[`CoverageSample`](models::CoverageSample).
373376
///
374377
/// This parser performs all the writes it can to the output
375-
/// stream and only returns a `ReportLine` for tests. The `report_line_or_empty`
376-
/// parser which wraps this and supports empty lines returns `Ok(())`.
378+
/// stream and only returns a [`ReportLine`] for tests. The
379+
/// `report_line_or_empty` parser which wraps this and supports empty lines
380+
/// returns `Ok(())`.
377381
pub fn report_line<'a, S, R: Report, B: ReportBuilder<R>>(
378382
buf: &mut ReportOutputStream<S, R, B>,
379383
) -> PResult<ReportLine>

core/src/parsers/pyreport/utils.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ fn format_pyreport_branch(branch: &MissingBranch) -> (models::BranchFormat, Stri
5353
}
5454

5555
/// Each [`LineSession`] corresponds to a single [`models::CoverageSample`].
56-
/// Each [`CoverageSample`] _may_ (but won't always) have:
56+
/// Each [`CoverageSample`](models::CoverageSample) _may_ (but won't always)
57+
/// have:
5758
/// - multiple related [`models::BranchesData`] records, one for each specific
5859
/// branch path we have data for
5960
/// - a single related [`models::MethodData`] if the `LineSession` is for a

core/src/report/pyreport/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
* [`shared/reports/types.py`](https://github.com/codecov/shared/blob/main/shared/reports/types.py),
1212
* and [`shared/utils/sessions.py`](https://github.com/codecov/shared/blob/main/shared/utils/sessions.py).
1313
*
14-
* Parsers that will build a [`SQLiteReport`] from these parts live in
14+
* Parsers that will build a [`SqliteReport`] from these parts live in
1515
* [`crate::parsers::pyreport`] but code that will convert a
16-
* [`SQLiteReport`] back into a Pyreport lives here.
16+
* [`SqliteReport`] back into a Pyreport lives here.
1717
*
1818
* # Report JSON
1919
*

core/src/report/pyreport/types.rs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use std::collections::HashMap;
22

33
pub use super::super::models::CoverageType;
44
use crate::parsers::json::JsonVal;
5+
#[cfg(doc)]
6+
use crate::report::models;
57

68
/// Enum representing the possible values of the "coverage" field in a
79
/// ReportLine or LineSession object.
@@ -63,22 +65,22 @@ pub struct Partial {
6365
}
6466

6567
/// Represents the coverage measurements taken for a specific "session". Each
66-
/// `LineSession` will correspond to a
67-
/// [`crate::report::models::CoverageSample`].
68+
/// [`LineSession`] will correspond to a
69+
/// [`CoverageSample`](models::CoverageSample).
6870
///
6971
/// Each upload to our system constitutes a "session" and may be tagged with
7072
/// flags or other context that we want to filter on when viewing report data.
7173
#[derive(Debug, PartialEq)]
7274
pub struct LineSession {
7375
/// This ID indicates which session the measurement was taken in. It can be
7476
/// used as a key in `buf.state.report_json_sessions` to get the ID of a
75-
/// [`crate::report::models::Context`] in order to create a
76-
/// [`crate::report::models::ContextAssoc`].
77+
/// [`Context`](models::Context) in order to create a
78+
/// [`ContextAssoc`](models::ContextAssoc).
7779
pub session_id: usize,
7880

7981
/// The coverage measurement that was taken in this session. The
80-
/// `CoverageType` is "inherited" from the [`ReportLine`] that this
81-
/// `LineSession` is a part of.
82+
/// [`CoverageType`] is "inherited" from the [`ReportLine`] that this
83+
/// [`LineSession`] is a part of.
8284
pub coverage: PyreportCoverage,
8385

8486
/// A list of specific branches/conditions stemming from this line that were
@@ -106,13 +108,13 @@ pub enum RawLabel {
106108
/// A numeric ID that was assigned to this label. The original label can be
107109
/// accessed in the `"labels_index"` key in the chunks file's header.
108110
/// For our parser's purposes, we can access the ID of the
109-
/// [`crate::report::models::Context`] created for this label in
111+
/// [`Context`](models::Context) created for this label in
110112
/// `buf.state.labels_index`.
111113
LabelId(u32),
112114

113115
/// The name of the label. If we have encountered this label before, it
114116
/// should be in `buf.state.labels_index` pointing at the ID for a
115-
/// [`crate::report::models::Context`]. Otherwise, we should create that
117+
/// [`Context`](models::Context). Otherwise, we should create that
116118
/// `Context` + mapping ourselves.
117119
LabelName(String),
118120
}
@@ -124,20 +126,20 @@ pub enum RawLabel {
124126
pub struct CoverageDatapoint {
125127
/// This ID indicates which session the measurement was taken in. It can be
126128
/// used as a key in `buf.state.report_json_sessions` to get the ID of a
127-
/// [`crate::report::models::Context`] in order to create a
128-
/// [`crate::report::models::ContextAssoc`].
129+
/// [`Context`](models::Context) in order to create a
130+
/// [`ContextAssoc`](models::ContextAssoc).
129131
pub session_id: u32,
130132

131133
/// A redundant copy of the coverage measurement for a session. We prefer
132134
/// the value from the [`LineSession`].
133135
pub _coverage: PyreportCoverage,
134136

135-
/// A redundant copy of the `CoverageType`. We use the value from the
137+
/// A redundant copy of the [`CoverageType`]. We use the value from the
136138
/// [`ReportLine`].
137139
///
138140
/// Technically this field is optional, but the way we serialize it when
139141
/// it's missing is identical to the way we serialize
140-
/// [`crate::report::models::CoverageType::Line`] so there's
142+
/// [`CoverageType::Line`] so there's
141143
/// no way to tell which it is when deserializing.
142144
pub _coverage_type: Option<CoverageType>,
143145

@@ -167,7 +169,8 @@ pub struct ReportLine {
167169
pub coverage_type: CoverageType,
168170

169171
/// The list of measurements taken for this line. Each of these corresponds
170-
/// to a [`CoverageSample`] record in a `SqliteReport`.
172+
/// to a [`CoverageSample`](models::CoverageSample) record in a
173+
/// `SqliteReport`.
171174
pub sessions: Vec<LineSession>,
172175

173176
/// Long forgotten field that takes up space.
@@ -177,9 +180,9 @@ pub struct ReportLine {
177180
/// `sessions`.
178181
pub _complexity: Option<Option<Complexity>>,
179182

180-
/// The list of [`CoverageDatapoint`]s for this line. `CoverageDatapoint` is
181-
/// largely redundant but its `labels` field is the only place where label
182-
/// data is recorded (e.g. which test case was running when this
183+
/// The list of [`CoverageDatapoint`]s for this line. [`CoverageDatapoint`]
184+
/// is largely redundant but its `labels` field is the only place where
185+
/// label data is recorded (e.g. which test case was running when this
183186
/// measurement was collected).
184187
pub datapoints: Option<Option<HashMap<u32, CoverageDatapoint>>>,
185188
}

0 commit comments

Comments
 (0)