Skip to content

Commit 5a19ca7

Browse files
fix: more tests
1 parent eac7042 commit 5a19ca7

File tree

6 files changed

+279
-28
lines changed

6 files changed

+279
-28
lines changed

src/input.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import { input } from "@actions-rs-plus/core";
22
import stringArgv from "string-argv";
33

44
// Parsed action input
5-
export interface Input {
5+
export interface ParsedInput {
66
toolchain: string | undefined;
77
args: string[];
88
useCross: boolean;
99
}
1010

11-
export function get(): Input {
11+
export function get(): ParsedInput {
1212
let toolchain: string = input.getInput("toolchain");
1313

1414
if (toolchain.startsWith("+")) {

src/main.ts

+29-22
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Reporter } from "./reporter";
88
import type { AnnotationWithMessageAndLevel, Context, Stats } from "./schema";
99

1010
type Program = Cargo | Cross;
11+
1112
interface ClippyResult {
1213
stats: Stats;
1314
annotations: AnnotationWithMessageAndLevel[];
@@ -51,23 +52,8 @@ async function buildContext(program: Program): Promise<Context> {
5152
return context;
5253
}
5354

54-
async function runClippy(actionInput: input.Input, program: Program): Promise<ClippyResult> {
55-
let args: string[] = [];
56-
57-
// Toolchain selection MUST go first in any condition
58-
if (actionInput.toolchain) {
59-
args.push(`+${actionInput.toolchain}`);
60-
}
61-
62-
args.push("clippy");
63-
64-
// `--message-format=json` should just right after the `cargo clippy`
65-
// because usually people are adding the `-- -D warnings` at the end
66-
// of arguments and it will mess up the output.
67-
args.push("--message-format=json");
68-
69-
args = args.concat(actionInput.args);
70-
55+
async function runClippy(actionInput: input.ParsedInput, program: Program): Promise<ClippyResult> {
56+
const args = buildArgs(actionInput);
7157
const outputParser = new OutputParser();
7258

7359
let exitCode = 0;
@@ -94,13 +80,16 @@ async function runClippy(actionInput: input.Input, program: Program): Promise<Cl
9480
};
9581
}
9682

97-
export async function run(actionInput: input.Input): Promise<void> {
98-
let program: Cargo | Cross;
99-
if (actionInput.useCross) {
100-
program = await Cross.getOrInstall();
83+
function getProgram(useCross: boolean): Promise<Program> {
84+
if (useCross) {
85+
return Cross.getOrInstall();
10186
} else {
102-
program = await Cargo.get();
87+
return Cargo.get();
10388
}
89+
}
90+
91+
export async function run(actionInput: input.ParsedInput): Promise<void> {
92+
const program: Program = await getProgram(actionInput.useCross);
10493

10594
const context = await buildContext(program);
10695

@@ -128,4 +117,22 @@ async function main(): Promise<void> {
128117
}
129118
}
130119

120+
function buildArgs(actionInput: input.ParsedInput): string[] {
121+
const args: string[] = [];
122+
123+
// Toolchain selection MUST go first in any condition
124+
if (actionInput.toolchain) {
125+
args.push(`+${actionInput.toolchain}`);
126+
}
127+
128+
args.push("clippy");
129+
130+
// `--message-format=json` should just right after the `cargo clippy`
131+
// because usually people are adding the `-- -D warnings` at the end
132+
// of arguments and it will mess up the output.
133+
args.push("--message-format=json");
134+
135+
return args.concat(actionInput.args);
136+
}
137+
131138
void main();

src/outputParser.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as core from "@actions/core";
22

3-
import type { AnnotationWithMessageAndLevel, CargoMessage, Stats } from "./schema";
3+
import type { AnnotationWithMessageAndLevel, CargoMessage, MaybeCargoMessage, Stats } from "./schema";
44
import { AnnotationLevel } from "./schema";
55

66
export class OutputParser {
@@ -27,7 +27,7 @@ export class OutputParser {
2727
}
2828

2929
public tryParseClippyLine(line: string): void {
30-
let contents: CargoMessage;
30+
let contents: MaybeCargoMessage;
3131
try {
3232
contents = JSON.parse(line);
3333
} catch (error) {
@@ -40,12 +40,15 @@ export class OutputParser {
4040
return;
4141
}
4242

43-
if (contents.message.code === null) {
43+
if (!contents.message?.code) {
4444
core.debug("Message code is missing, ignoring it");
4545
return;
4646
}
4747

48-
const parsedAnnotation = OutputParser.makeAnnotation(contents);
48+
const cargoMessage = contents as CargoMessage;
49+
50+
const parsedAnnotation = OutputParser.makeAnnotation(cargoMessage);
51+
4952
const key = JSON.stringify(parsedAnnotation);
5053

5154
if (this._uniqueAnnotations.has(key)) {

src/schema.ts

+11
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ export interface AnnotationWithMessageAndLevel {
1212
properties: AnnotationProperties;
1313
}
1414

15+
export interface MaybeCargoMessage {
16+
reason: string;
17+
message?: {
18+
code?: string | null;
19+
level: string;
20+
message: string;
21+
rendered: string;
22+
spans: DiagnosticSpan[];
23+
};
24+
}
25+
1526
export interface CargoMessage {
1627
reason: string;
1728
message: {

src/tests/outputParser.test.ts

+186
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import { OutputParser } from "outputParser";
2+
import { type CargoMessage, type MaybeCargoMessage, type Stats } from "schema";
3+
4+
jest.mock("@actions/core");
5+
6+
describe("outputParser", () => {
7+
const emptyStats: Stats = {
8+
error: 0,
9+
warning: 0,
10+
note: 0,
11+
ice: 0,
12+
help: 0,
13+
};
14+
15+
const defaultMessage: CargoMessage = {
16+
reason: "compiler-message",
17+
message: {
18+
code: "code",
19+
message: "message",
20+
rendered: "rendered",
21+
level: "warning",
22+
spans: [
23+
{
24+
is_primary: true,
25+
column_start: 10,
26+
column_end: 15,
27+
line_start: 30,
28+
line_end: 30,
29+
file_name: "main.rs",
30+
},
31+
],
32+
},
33+
};
34+
35+
it("ignores invalid json", () => {
36+
const outputParser = new OutputParser();
37+
38+
outputParser.tryParseClippyLine("I am not valid json");
39+
40+
expect(outputParser.stats).toEqual(emptyStats);
41+
});
42+
43+
it("ignores non-compiler-messages", () => {
44+
const outputParser = new OutputParser();
45+
46+
const output: MaybeCargoMessage = {
47+
reason: "not-a-compiler-message",
48+
};
49+
50+
outputParser.tryParseClippyLine(JSON.stringify(output));
51+
52+
expect(outputParser.stats).toEqual(emptyStats);
53+
});
54+
55+
it("ignores when compiler-message doesn't have a code", () => {
56+
const outputParser = new OutputParser();
57+
58+
const output: MaybeCargoMessage = {
59+
reason: "compiler-message",
60+
message: {
61+
code: null,
62+
message: "",
63+
rendered: "",
64+
level: "",
65+
spans: [],
66+
},
67+
};
68+
69+
outputParser.tryParseClippyLine(JSON.stringify(output));
70+
71+
expect(outputParser.stats).toEqual(emptyStats);
72+
});
73+
74+
test.each([
75+
["help", undefined],
76+
["note", undefined],
77+
["warning", undefined],
78+
["error", undefined],
79+
["error: internal compiler error", "ice"],
80+
])("bumps %s when message level is %s", (level, test) => {
81+
const outputParser = new OutputParser();
82+
83+
const output: CargoMessage = {
84+
reason: defaultMessage.reason,
85+
message: {
86+
...defaultMessage.message,
87+
level,
88+
},
89+
};
90+
91+
outputParser.tryParseClippyLine(JSON.stringify(output));
92+
93+
expect(outputParser.stats).toEqual({ ...emptyStats, [test ?? level]: 1 });
94+
});
95+
96+
it("ignores when level is not help, note, warning, error, ice", () => {
97+
const outputParser = new OutputParser();
98+
99+
const output: CargoMessage = {
100+
reason: defaultMessage.reason,
101+
message: {
102+
...defaultMessage.message,
103+
level: "it's my birthday",
104+
},
105+
};
106+
107+
outputParser.tryParseClippyLine(JSON.stringify(output));
108+
109+
expect(outputParser.stats).toEqual({ ...emptyStats });
110+
});
111+
112+
it("ignores duplicate", () => {
113+
const outputParser = new OutputParser();
114+
115+
outputParser.tryParseClippyLine(JSON.stringify(defaultMessage));
116+
outputParser.tryParseClippyLine(JSON.stringify(defaultMessage));
117+
118+
expect(outputParser.stats).toEqual({ ...emptyStats, [defaultMessage.message.level]: 1 });
119+
});
120+
121+
it("fails when primary span cannot be found", () => {
122+
const outputParser = new OutputParser();
123+
124+
const output: CargoMessage = {
125+
reason: defaultMessage.reason,
126+
message: {
127+
...defaultMessage.message,
128+
spans: [],
129+
},
130+
};
131+
132+
expect(() => {
133+
outputParser.tryParseClippyLine(JSON.stringify(output));
134+
}).toThrow(/Unable to find primary span for message/);
135+
});
136+
137+
it("parses annotations into AnnotationWithMessageAndLevel", () => {
138+
const outputParser = new OutputParser();
139+
140+
outputParser.tryParseClippyLine(
141+
JSON.stringify({
142+
reason: defaultMessage.reason,
143+
message: {
144+
...defaultMessage.message,
145+
level: "error",
146+
},
147+
}),
148+
);
149+
outputParser.tryParseClippyLine(
150+
JSON.stringify({
151+
reason: defaultMessage.reason,
152+
message: {
153+
...defaultMessage.message,
154+
level: "warning",
155+
},
156+
}),
157+
);
158+
159+
expect(outputParser.annotations).toEqual([
160+
{
161+
level: 0,
162+
message: "rendered",
163+
properties: {
164+
endColumn: 15,
165+
endLine: 30,
166+
file: "main.rs",
167+
startColumn: 10,
168+
startLine: 30,
169+
title: "message",
170+
},
171+
},
172+
{
173+
level: 1,
174+
message: "rendered",
175+
properties: {
176+
endColumn: 15,
177+
endLine: 30,
178+
file: "main.rs",
179+
startColumn: 10,
180+
startLine: 30,
181+
title: "message",
182+
},
183+
},
184+
]);
185+
});
186+
});

src/tests/reporter.test.ts

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Reporter } from "reporter";
2+
import { AnnotationLevel } from "schema";
3+
4+
jest.mock("@actions/core");
5+
6+
describe("reporter", () => {
7+
it("works", async () => {
8+
const r = new Reporter();
9+
10+
await expect(
11+
r.report(
12+
{
13+
error: 0,
14+
help: 0,
15+
ice: 0,
16+
note: 0,
17+
warning: 0,
18+
},
19+
[
20+
{
21+
level: AnnotationLevel.Error,
22+
message: "I'm an error",
23+
properties: {},
24+
},
25+
{
26+
level: AnnotationLevel.Warning,
27+
message: "I'm a warning",
28+
properties: {},
29+
},
30+
{
31+
level: AnnotationLevel.Notice,
32+
message: "I'm a notice",
33+
properties: {},
34+
},
35+
],
36+
{
37+
cargo: "cargo",
38+
clippy: "clippy",
39+
rustc: "rustc",
40+
},
41+
),
42+
).resolves.toBeUndefined();
43+
});
44+
});

0 commit comments

Comments
 (0)