Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add options to enable or disable costs and coverage reports in sdk #1441

Merged
merged 1 commit into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 44 additions & 2 deletions components/clarinet-sdk-wasm/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,25 @@ struct ProjectCache {
session: Session,
}

#[wasm_bindgen]
pub struct SDKOptions {
#[wasm_bindgen(js_name = trackCosts)]
pub track_costs: bool,
#[wasm_bindgen(js_name = trackCoverage)]
pub track_coverage: bool,
}

#[wasm_bindgen]
impl SDKOptions {
#[wasm_bindgen(constructor)]
pub fn new(track_costs: bool, track_coverage: bool) -> Self {
Self {
track_costs,
track_coverage,
}
}
}

#[wasm_bindgen]
pub struct SDK {
#[wasm_bindgen(getter_with_clone)]
Expand All @@ -264,16 +283,21 @@ pub struct SDK {
session: Option<Session>,

file_accessor: Box<dyn FileAccessor>,
options: SDKOptions,
current_test_name: String,
}

#[wasm_bindgen]
impl SDK {
#[wasm_bindgen(constructor)]
pub fn new(fs_request: JsFunction) -> Self {
pub fn new(fs_request: JsFunction, options: Option<SDKOptions>) -> Self {
panic::set_hook(Box::new(console_error_panic_hook::hook));

let fs = Box::new(WASMFileSystemAccessor::new(fs_request));

let track_coverage = options.as_ref().map_or(false, |o| o.track_coverage);
let track_costs = options.as_ref().map_or(false, |o| o.track_costs);

Self {
deployer: String::new(),
cache: HashMap::new(),
Expand All @@ -282,6 +306,10 @@ impl SDK {
contracts_locations: HashMap::new(),
session: None,
file_accessor: fs,
options: SDKOptions {
track_coverage,
track_costs,
},
current_test_name: String::new(),
}
}
Expand Down Expand Up @@ -642,9 +670,23 @@ impl SDK {
allow_private: bool,
) -> Result<TransactionRes, String> {
let test_name = self.current_test_name.clone();
let SDKOptions {
track_costs,
track_coverage,
} = self.options;

let session = self.get_session_mut();
let execution = session
.call_contract_fn(contract, method, args, sender, allow_private, test_name)
.call_contract_fn(
contract,
method,
args,
sender,
allow_private,
track_costs,
track_coverage,
test_name,
)
.map_err(|diagnostics| {
let mut message = format!(
"{}: {}::{}({})",
Expand Down
10 changes: 8 additions & 2 deletions components/clarinet-sdk/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Cl, ClarityValue } from "@stacks/transactions";
import {
SDK,
SDKOptions,
TransactionRes,
CallFnArgs,
DeployContractArgs,
Expand Down Expand Up @@ -279,10 +280,15 @@ const getSessionProxy = () => ({
function memoizedInit() {
let simnet: Simnet | null = null;

return async (manifestPath = "./Clarinet.toml", noCache = false) => {
return async (
manifestPath = "./Clarinet.toml",
noCache = false,
options?: { trackCosts: boolean; trackCoverage: boolean },
) => {
if (noCache || !simnet) {
const module = await wasmModule;
simnet = new Proxy(new module.SDK(vfs), getSessionProxy()) as unknown as Simnet;
let sdkOptions = new SDKOptions(!!options?.trackCosts, !!options?.trackCoverage);
simnet = new Proxy(new module.SDK(vfs, sdkOptions), getSessionProxy()) as unknown as Simnet;
}

// start a new simnet session
Expand Down
8 changes: 7 additions & 1 deletion components/clarinet-sdk/tests/pox-locking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,13 @@ describe("test pox-3", () => {
);
});

it("can get pox boot contract code coverage", () => {
it("can get pox boot contract code coverage", async () => {
const simnet = await initSimnet("tests/fixtures/Clarinet.toml", true, {
trackCoverage: true,
trackCosts: false,
});
simnet.setEpoch("2.4");

const stackStxArgs = [
Cl.uint(ustxAmount),
Cl.tuple({
Expand Down
109 changes: 109 additions & 0 deletions components/clarinet-sdk/tests/reports.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import fs from "node:fs";
import path from "node:path";
import { describe, expect, it, beforeEach, afterEach } from "vitest";

// test the built package and not the source code
// makes it simpler to handle wasm build
import { Simnet, initSimnet } from "../dist/esm";

const address1 = "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5";

const deploymentPlanPath = path.join(
process.cwd(),
"tests/fixtures/deployments/default.simnet-plan.yaml",
);

function deleteExistingDeploymentPlan() {
if (fs.existsSync(deploymentPlanPath)) {
fs.unlinkSync(deploymentPlanPath);
}
}

beforeEach(async () => {
deleteExistingDeploymentPlan();
});

afterEach(() => {
deleteExistingDeploymentPlan();
});
hugocaillard marked this conversation as resolved.
Show resolved Hide resolved

describe("simnet can get code coverage", () => {
it("does not report coverage by default", async () => {
const simnet = await initSimnet("tests/fixtures/Clarinet.toml", true);

simnet.callPublicFn("counter", "increment", [], address1);

const reports = simnet.collectReport(false, "");
expect(reports.coverage.length).toBe(0);
});

it("reports coverage if enabled", async () => {
const simnet = await initSimnet("tests/fixtures/Clarinet.toml", true, {
trackCoverage: true,
trackCosts: false,
});

simnet.callPublicFn("counter", "increment", [], address1);
simnet.callPublicFn("counter", "increment", [], address1);
simnet.callPrivateFn("counter", "inner-increment", [], address1);

const reports = simnet.collectReport(false, "");

// increment is called twice
expect(reports.coverage.includes("FNDA:2,increment")).toBe(true);
// inner-increment is called one time directly and twice by `increment`
expect(reports.coverage.includes("FNDA:3,inner-increment")).toBe(true);

expect(reports.coverage.startsWith("TN:")).toBe(true);
expect(reports.coverage.endsWith("end_of_record\n")).toBe(true);
});
});

describe("simnet can get costs reports", () => {
it("does not report costs by default", async () => {
const simnet = await initSimnet("tests/fixtures/Clarinet.toml", true);
simnet.callPublicFn("counter", "increment", [], address1);

const reports = simnet.collectReport(false, "");
expect(() => JSON.parse(reports.costs)).not.toThrow();

const parsedReports = JSON.parse(reports.costs);
expect(parsedReports).toHaveLength(0);
});

it("report costs if enabled", async () => {
const simnet = await initSimnet("tests/fixtures/Clarinet.toml", true, {
trackCoverage: false,
trackCosts: true,
});
simnet.callPublicFn("counter", "increment", [], address1);

const reports = simnet.collectReport(false, "");
expect(() => JSON.parse(reports.costs)).not.toThrow();

const parsedReports = JSON.parse(reports.costs);
expect(parsedReports).toHaveLength(1);

const report = parsedReports[0];
expect(report.contract_id).toBe(`${simnet.deployer}.counter`);
expect(report.method).toBe("increment");
expect(report.cost_result.total.write_count).toBe(3);
});
});

describe("simnet can report both costs and coverage", () => {
it("can report both costs and coverage", async () => {
const simnet = await initSimnet("tests/fixtures/Clarinet.toml", true, {
trackCoverage: true,
trackCosts: true,
});
simnet.callPublicFn("counter", "increment", [], address1);

const reports = simnet.collectReport(false, "");

const parsedReports = JSON.parse(reports.costs);
expect(parsedReports).toHaveLength(1);

expect(reports.coverage.length).greaterThan(0);
});
});
33 changes: 0 additions & 33 deletions components/clarinet-sdk/tests/simnet-usage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,39 +327,6 @@ describe("simnet can transfer stx", () => {
});
});

describe("simnet can get session reports", () => {
it("can get line coverage", () => {
simnet.callPublicFn("counter", "increment", [], address1);
simnet.callPublicFn("counter", "increment", [], address1);
simnet.callPrivateFn("counter", "inner-increment", [], address1);

const reports = simnet.collectReport(false, "");

// increment is called twice
expect(reports.coverage.includes("FNDA:2,increment")).toBe(true);
// inner-increment is called one time directly and twice by `increment`
expect(reports.coverage.includes("FNDA:3,inner-increment")).toBe(true);

expect(reports.coverage.startsWith("TN:")).toBe(true);
expect(reports.coverage.endsWith("end_of_record\n")).toBe(true);
});

it("can get costs", () => {
simnet.callPublicFn("counter", "increment", [], address1);

const reports = simnet.collectReport(false, "");
expect(() => JSON.parse(reports.costs)).not.toThrow();

const parsedReports = JSON.parse(reports.costs);
expect(parsedReports).toHaveLength(1);

const report = parsedReports[0];
expect(report.contract_id).toBe(`${simnet.deployer}.counter`);
expect(report.method).toBe("increment");
expect(report.cost_result.total.write_count).toBe(3);
});
});

describe("the sdk handles multiple manifests project", () => {
it("handle invalid project", () => {
const manifestPath = path.join(process.cwd(), "tests/fixtures/contracts/invalid.clar");
Expand Down
6 changes: 3 additions & 3 deletions components/clarity-repl/src/repl/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -941,7 +941,7 @@ impl ClarityInterpreter {
raw_args: &[Vec<u8>],
epoch: StacksEpochId,
clarity_version: ClarityVersion,
cost_track: bool,
track_costs: bool,
allow_private: bool,
eval_hooks: Option<Vec<&mut dyn EvalHook>>,
) -> Result<ExecutionResult, String> {
Expand All @@ -955,7 +955,7 @@ impl ClarityInterpreter {
conn.set_clarity_epoch_version(epoch)
.map_err(|e| e.to_string())?;
conn.commit().map_err(|e| e.to_string())?;
let cost_tracker = if cost_track {
let cost_tracker = if track_costs {
LimitedCostTracker::new(
false,
CHAIN_ID_TESTNET,
Expand Down Expand Up @@ -1020,7 +1020,7 @@ impl ClarityInterpreter {
})?;

let mut cost = None;
if cost_track {
if track_costs {
cost = Some(CostSynthesis::from_cost_tracker(&global_context.cost_track));
}

Expand Down
13 changes: 10 additions & 3 deletions components/clarity-repl/src/repl/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,8 @@ impl Session {
args: &[Vec<u8>],
sender: &str,
allow_private: bool,
track_costs: bool,
track_coverage: bool,
test_name: String,
) -> Result<ExecutionResult, Vec<Diagnostic>> {
let initial_tx_sender = self.get_tx_sender();
Expand All @@ -630,7 +632,9 @@ impl Session {

let mut hooks: Vec<&mut dyn EvalHook> = vec![];
let mut coverage = TestCoverageReport::new(test_name.clone());
hooks.push(&mut coverage);
if track_coverage {
hooks.push(&mut coverage);
}

let clarity_version = ClarityVersion::default_for_epoch(self.current_epoch);

Expand All @@ -641,7 +645,7 @@ impl Session {
args,
self.current_epoch,
clarity_version,
true,
track_costs,
allow_private,
Some(hooks),
) {
Expand All @@ -657,7 +661,10 @@ impl Session {
}
};
self.set_tx_sender(initial_tx_sender);
self.coverage_reports.push(coverage);

if track_coverage {
self.coverage_reports.push(coverage);
}

if let Some(ref cost) = execution.cost {
self.costs_reports.push(CostsReport {
Expand Down
Loading