Skip to content

Commit

Permalink
fix: more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kristof-mattei committed Sep 16, 2023
1 parent eac7042 commit 5a19ca7
Show file tree
Hide file tree
Showing 6 changed files with 279 additions and 28 deletions.
4 changes: 2 additions & 2 deletions src/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { input } from "@actions-rs-plus/core";
import stringArgv from "string-argv";

// Parsed action input
export interface Input {
export interface ParsedInput {
toolchain: string | undefined;
args: string[];
useCross: boolean;
}

export function get(): Input {
export function get(): ParsedInput {
let toolchain: string = input.getInput("toolchain");

if (toolchain.startsWith("+")) {
Expand Down
51 changes: 29 additions & 22 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Reporter } from "./reporter";
import type { AnnotationWithMessageAndLevel, Context, Stats } from "./schema";

type Program = Cargo | Cross;

interface ClippyResult {
stats: Stats;
annotations: AnnotationWithMessageAndLevel[];
Expand Down Expand Up @@ -51,23 +52,8 @@ async function buildContext(program: Program): Promise<Context> {
return context;
}

async function runClippy(actionInput: input.Input, program: Program): Promise<ClippyResult> {
let args: string[] = [];

// Toolchain selection MUST go first in any condition
if (actionInput.toolchain) {
args.push(`+${actionInput.toolchain}`);
}

args.push("clippy");

// `--message-format=json` should just right after the `cargo clippy`
// because usually people are adding the `-- -D warnings` at the end
// of arguments and it will mess up the output.
args.push("--message-format=json");

args = args.concat(actionInput.args);

async function runClippy(actionInput: input.ParsedInput, program: Program): Promise<ClippyResult> {
const args = buildArgs(actionInput);
const outputParser = new OutputParser();

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

export async function run(actionInput: input.Input): Promise<void> {
let program: Cargo | Cross;
if (actionInput.useCross) {
program = await Cross.getOrInstall();
function getProgram(useCross: boolean): Promise<Program> {
if (useCross) {
return Cross.getOrInstall();
} else {
program = await Cargo.get();
return Cargo.get();
}
}

export async function run(actionInput: input.ParsedInput): Promise<void> {
const program: Program = await getProgram(actionInput.useCross);

const context = await buildContext(program);

Expand Down Expand Up @@ -128,4 +117,22 @@ async function main(): Promise<void> {
}
}

function buildArgs(actionInput: input.ParsedInput): string[] {
const args: string[] = [];

// Toolchain selection MUST go first in any condition
if (actionInput.toolchain) {
args.push(`+${actionInput.toolchain}`);
}

args.push("clippy");

// `--message-format=json` should just right after the `cargo clippy`
// because usually people are adding the `-- -D warnings` at the end
// of arguments and it will mess up the output.
args.push("--message-format=json");

return args.concat(actionInput.args);
}

void main();
11 changes: 7 additions & 4 deletions src/outputParser.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as core from "@actions/core";

import type { AnnotationWithMessageAndLevel, CargoMessage, Stats } from "./schema";
import type { AnnotationWithMessageAndLevel, CargoMessage, MaybeCargoMessage, Stats } from "./schema";
import { AnnotationLevel } from "./schema";

export class OutputParser {
Expand All @@ -27,7 +27,7 @@ export class OutputParser {
}

public tryParseClippyLine(line: string): void {
let contents: CargoMessage;
let contents: MaybeCargoMessage;
try {
contents = JSON.parse(line);
} catch (error) {
Expand All @@ -40,12 +40,15 @@ export class OutputParser {
return;
}

if (contents.message.code === null) {
if (!contents.message?.code) {
core.debug("Message code is missing, ignoring it");
return;
}

const parsedAnnotation = OutputParser.makeAnnotation(contents);
const cargoMessage = contents as CargoMessage;

const parsedAnnotation = OutputParser.makeAnnotation(cargoMessage);

const key = JSON.stringify(parsedAnnotation);

if (this._uniqueAnnotations.has(key)) {
Expand Down
11 changes: 11 additions & 0 deletions src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ export interface AnnotationWithMessageAndLevel {
properties: AnnotationProperties;
}

export interface MaybeCargoMessage {
reason: string;
message?: {
code?: string | null;
level: string;
message: string;
rendered: string;
spans: DiagnosticSpan[];
};
}

export interface CargoMessage {
reason: string;
message: {
Expand Down
186 changes: 186 additions & 0 deletions src/tests/outputParser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import { OutputParser } from "outputParser";
import { type CargoMessage, type MaybeCargoMessage, type Stats } from "schema";

jest.mock("@actions/core");

describe("outputParser", () => {
const emptyStats: Stats = {
error: 0,
warning: 0,
note: 0,
ice: 0,
help: 0,
};

const defaultMessage: CargoMessage = {
reason: "compiler-message",
message: {
code: "code",
message: "message",
rendered: "rendered",
level: "warning",
spans: [
{
is_primary: true,
column_start: 10,
column_end: 15,
line_start: 30,
line_end: 30,
file_name: "main.rs",
},
],
},
};

it("ignores invalid json", () => {
const outputParser = new OutputParser();

outputParser.tryParseClippyLine("I am not valid json");

expect(outputParser.stats).toEqual(emptyStats);
});

it("ignores non-compiler-messages", () => {
const outputParser = new OutputParser();

const output: MaybeCargoMessage = {
reason: "not-a-compiler-message",
};

outputParser.tryParseClippyLine(JSON.stringify(output));

expect(outputParser.stats).toEqual(emptyStats);
});

it("ignores when compiler-message doesn't have a code", () => {
const outputParser = new OutputParser();

const output: MaybeCargoMessage = {
reason: "compiler-message",
message: {
code: null,
message: "",
rendered: "",
level: "",
spans: [],
},
};

outputParser.tryParseClippyLine(JSON.stringify(output));

expect(outputParser.stats).toEqual(emptyStats);
});

test.each([
["help", undefined],
["note", undefined],
["warning", undefined],
["error", undefined],
["error: internal compiler error", "ice"],
])("bumps %s when message level is %s", (level, test) => {
const outputParser = new OutputParser();

const output: CargoMessage = {
reason: defaultMessage.reason,
message: {
...defaultMessage.message,
level,
},
};

outputParser.tryParseClippyLine(JSON.stringify(output));

expect(outputParser.stats).toEqual({ ...emptyStats, [test ?? level]: 1 });
});

it("ignores when level is not help, note, warning, error, ice", () => {
const outputParser = new OutputParser();

const output: CargoMessage = {
reason: defaultMessage.reason,
message: {
...defaultMessage.message,
level: "it's my birthday",
},
};

outputParser.tryParseClippyLine(JSON.stringify(output));

expect(outputParser.stats).toEqual({ ...emptyStats });
});

it("ignores duplicate", () => {
const outputParser = new OutputParser();

outputParser.tryParseClippyLine(JSON.stringify(defaultMessage));
outputParser.tryParseClippyLine(JSON.stringify(defaultMessage));

expect(outputParser.stats).toEqual({ ...emptyStats, [defaultMessage.message.level]: 1 });
});

it("fails when primary span cannot be found", () => {
const outputParser = new OutputParser();

const output: CargoMessage = {
reason: defaultMessage.reason,
message: {
...defaultMessage.message,
spans: [],
},
};

expect(() => {
outputParser.tryParseClippyLine(JSON.stringify(output));
}).toThrow(/Unable to find primary span for message/);
});

it("parses annotations into AnnotationWithMessageAndLevel", () => {
const outputParser = new OutputParser();

outputParser.tryParseClippyLine(
JSON.stringify({
reason: defaultMessage.reason,
message: {
...defaultMessage.message,
level: "error",
},
}),
);
outputParser.tryParseClippyLine(
JSON.stringify({
reason: defaultMessage.reason,
message: {
...defaultMessage.message,
level: "warning",
},
}),
);

expect(outputParser.annotations).toEqual([
{
level: 0,
message: "rendered",
properties: {
endColumn: 15,
endLine: 30,
file: "main.rs",
startColumn: 10,
startLine: 30,
title: "message",
},
},
{
level: 1,
message: "rendered",
properties: {
endColumn: 15,
endLine: 30,
file: "main.rs",
startColumn: 10,
startLine: 30,
title: "message",
},
},
]);
});
});
44 changes: 44 additions & 0 deletions src/tests/reporter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Reporter } from "reporter";
import { AnnotationLevel } from "schema";

jest.mock("@actions/core");

describe("reporter", () => {
it("works", async () => {
const r = new Reporter();

await expect(
r.report(
{
error: 0,
help: 0,
ice: 0,
note: 0,
warning: 0,
},
[
{
level: AnnotationLevel.Error,
message: "I'm an error",
properties: {},
},
{
level: AnnotationLevel.Warning,
message: "I'm a warning",
properties: {},
},
{
level: AnnotationLevel.Notice,
message: "I'm a notice",
properties: {},
},
],
{
cargo: "cargo",
clippy: "clippy",
rustc: "rustc",
},
),
).resolves.toBeUndefined();
});
});

0 comments on commit 5a19ca7

Please sign in to comment.