Skip to content

Commit

Permalink
Merge pull request #6235 from NomicFoundation/solidity-test-stack-traces
Browse files Browse the repository at this point in the history
feat: integrate stack traces into solidity test
  • Loading branch information
galargh authored Feb 7, 2025
2 parents 1ec485f + 285d3a6 commit 14ebbdb
Show file tree
Hide file tree
Showing 10 changed files with 207 additions and 77 deletions.
66 changes: 33 additions & 33 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions v-next/example-project/contracts/Counter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity ^0.8.0;

import "./Counter.sol";
import "hardhat/console.sol";

contract CounterTest {
Counter counter;
Expand Down Expand Up @@ -41,14 +42,18 @@ contract FailingCounterTest {
Counter counter;

function setUp() public {
console.log("Setting up");
counter = new Counter();
console.log("Counter set up");
}

function testInitialValue() public view {
console.log("Testing initial value");
require(counter.x() == 1, "Initial value should be 1");
}

function testFuzzInc(uint8 x) public {
console.log("Fuzz testing inc");
for (uint8 i = 0; i < x; i++) {
counter.inc();
}
Expand All @@ -59,6 +64,7 @@ contract FailingCounterTest {
}

function testFailFuzzInc(uint8 x) public {
console.log("Fuzz testing inc fail");
for (uint8 i = 0; i < x; i++) {
counter.inc();
}
Expand Down
2 changes: 1 addition & 1 deletion v-next/hardhat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
"typescript-eslint": "7.7.1"
},
"dependencies": {
"@ignored/edr": "0.6.4-alpha.2",
"@ignored/edr": "0.8.0-alpha.1",
"@ignored/edr-optimism": "0.6.5-alpha.2",
"@ignored/hardhat-vnext-errors": "workspace:^3.0.0-next.15",
"@ignored/hardhat-vnext-utils": "workspace:^3.0.0-next.15",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export function createSolidityErrorWithStackTrace(
}
}

function encodeStackTraceEntry(
export function encodeStackTraceEntry(
stackTraceEntry: SolidityStackTraceEntry,
): SolidityCallSite {
switch (stackTraceEntry.type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ const solidityTestUserConfigType = z.object({
write: z.array(z.string()).optional(),
})
.optional(),
trace: z.boolean().optional(),
testFail: z.boolean().optional(),
labels: z
.array(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import type { TestResult } from "@ignored/edr";
import { bytesToHexString } from "@ignored/hardhat-vnext-utils/hex";
import chalk from "chalk";

import { encodeStackTraceEntry } from "../network-manager/edr/stack-traces/stack-trace-solidity-errors.js";

import { formatArtifactId } from "./formatters.js";

/**
Expand Down Expand Up @@ -117,27 +119,55 @@ export async function* testReporter(
for (const [index, failure] of failures.entries()) {
yield `\n${chalk.bold(chalk.red(`Failure (${index + 1})`))}: ${failure.name}\n`;

if (failure.decodedLogs !== undefined && failure.decodedLogs.length > 0) {
yield `Decoded Logs${chalk.grey(`: ${failure.decodedLogs.join("\n")}\n`)}`;
if (
failure.reason !== undefined &&
failure.reason !== null &&
failure.reason !== ""
) {
yield `Reason: ${chalk.grey(failure.reason)}\n`;
}

const stackTrace = failure.stackTrace();
if (
stackTrace !== undefined &&
stackTrace !== null &&
stackTrace.length > 0
) {
const stackTraceStack: string[] = [];
for (const entry of stackTrace.reverse()) {
const callsite = encodeStackTraceEntry(entry);
if (callsite !== undefined) {
stackTraceStack.push(` at ${callsite.toString()}`);
}
}

if (stackTraceStack.length > 0) {
yield `${chalk.grey(stackTraceStack.join("\n"))}\n`;
}
}

if (failure.reason !== undefined && failure.reason !== "") {
yield `Reason${chalk.grey(`: ${failure.reason}\n`)}`;
if (
failure.decodedLogs !== undefined &&
failure.decodedLogs !== null &&
failure.decodedLogs.length > 0
) {
yield `Decoded Logs:\n${chalk.grey(failure.decodedLogs.map((log) => ` ${log}`).join("\n"))}\n`;
}

if (failure.counterexample !== undefined) {
if (
failure.counterexample !== undefined &&
failure.counterexample !== null
) {
const counterexamples = Array.isArray(failure.counterexample)
? failure.counterexample
: [failure.counterexample];

for (const counterexample of counterexamples) {
const details = `{\n${Object.entries(counterexample)
.map(
([key, value]) =>
` ${key}: ${Buffer.isBuffer(value) ? bytesToHexString(value) : value}`,
)
.join(",\n")}\n}`;
yield `Counterexample${chalk.grey(`: ${details}\n`)}`;
const details = Object.entries(counterexample).map(
([key, value]) =>
` ${key}: ${Buffer.isBuffer(value) ? bytesToHexString(value) : value}`,
);
yield `Counterexample:\n${chalk.grey(details.join("\n"))}\n`;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {
ArtifactId,
Artifact,
SolidityTestRunnerConfigArgs,
TracingConfigWithBuffers,
} from "@ignored/edr";

import { Readable } from "node:stream";
Expand Down Expand Up @@ -49,6 +50,7 @@ export function run(
artifacts: Artifact[],
testSuiteIds: ArtifactId[],
configArgs: SolidityTestRunnerConfigArgs,
tracingConfig: TracingConfigWithBuffers,
options?: RunOptions,
): TestsStream {
const stream = new ReadableStream<TestEvent>({
Expand Down Expand Up @@ -83,6 +85,7 @@ export function run(
artifacts,
testSuiteIds,
configArgs,
tracingConfig,
(suiteResult) => {
controller.enqueue({
type: "suite:result",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import type { RunOptions } from "./runner.js";
import type { TestEvent } from "./types.js";
import type { BuildOptions } from "../../../types/solidity/build-system.js";
import type { NewTaskActionFunction } from "../../../types/tasks.js";
import type { SolidityTestRunnerConfigArgs } from "@ignored/edr";
import type {
SolidityTestRunnerConfigArgs,
TracingConfigWithBuffers,
} from "@ignored/edr";

import { finished } from "node:stream/promises";

Expand All @@ -13,6 +16,7 @@ import { createNonClosingWriter } from "@ignored/hardhat-vnext-utils/stream";
import { shouldMergeCompilationJobs } from "../solidity/build-profiles.js";
import {
getArtifacts,
getBuildInfos,
throwIfSolidityBuildFailed,
} from "../solidity/build-results.js";

Expand Down Expand Up @@ -65,7 +69,8 @@ const runSolidityTests: NewTaskActionFunction<TestActionArguments> = async (

throwIfSolidityBuildFailed(results);

const artifacts = await getArtifacts(results, hre.config.paths.artifacts);
const buildInfos = await getBuildInfos(results, hre.artifacts);
const artifacts = await getArtifacts(results, buildInfos);
const testSuiteIds = artifacts.map((artifact) => artifact.id);

console.log("Running Solidity tests");
Expand All @@ -81,10 +86,20 @@ const runSolidityTests: NewTaskActionFunction<TestActionArguments> = async (
hre.config.paths.root,
solidityTestConfig,
);
const tracingConfig: TracingConfigWithBuffers = {
buildInfos,
ignoreContracts: false,
};
const options: RunOptions =
solidityTestConfigToRunOptions(solidityTestConfig);

const runStream = run(artifacts, testSuiteIds, config, options);
const runStream = run(
artifacts,
testSuiteIds,
config,
tracingConfig,
options,
);

const testReporterStream = runStream
.on("data", (event: TestEvent) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ declare module "../../../types/config.js" {
read?: string[];
write?: string[];
};
trace?: boolean;
testFail?: boolean;
labels?: Array<{
address: string; // 0x-prefixed hex string
Expand Down
Loading

0 comments on commit 14ebbdb

Please sign in to comment.