@tact-lang/coverage
Advanced tools
Comparing version 0.0.1 to 0.0.2
/// <reference types="node" /> | ||
import { Maybe } from "../utils/maybe"; | ||
import { LogEntry } from "./parseVMLogs"; | ||
@@ -18,4 +19,4 @@ type CollectedCell = { | ||
logs: LogEntry[]; | ||
gasLimit: bigint; | ||
gasLimit?: Maybe<bigint>; | ||
}): void; | ||
export {}; |
@@ -52,5 +52,25 @@ "use strict"; | ||
function collectCoverage(args) { | ||
// Determine gas limit | ||
let gasLimit = 1000000000n; | ||
if (typeof args.gasLimit === 'bigint') { | ||
gasLimit = args.gasLimit; | ||
} | ||
else { | ||
// Try to determine gas limit from logs, ignoring the first opcode that usually | ||
// is the SETCP 0 and means nothing | ||
if (args.logs.length > 4) { | ||
if (args.logs[0].kind === 'stack' | ||
&& args.logs[1].kind === 'cell' | ||
&& args.logs[2].kind === 'execute' | ||
&& args.logs[3].kind === 'gas') { | ||
if (args.logs[2].command === 'SETCP 0') { | ||
gasLimit = args.logs[3].remaining; | ||
} | ||
} | ||
} | ||
} | ||
// Collect coverage | ||
let collector = args.collector; | ||
let logs = args.logs; | ||
let gasRemaining = args.gasLimit; | ||
let gasRemaining = gasLimit; | ||
let offset = 0; | ||
@@ -67,6 +87,4 @@ while (offset < logs.length) { | ||
if (cell.kind === 'execute' && cell.command.startsWith('implicit ')) { | ||
while (offset < logs.length) { | ||
if (logs[offset].kind === 'gas') { | ||
break; | ||
} | ||
// Skip non-gas | ||
while (logs[offset].kind !== 'gas') { | ||
offset++; | ||
@@ -90,7 +108,16 @@ } | ||
} | ||
// Load gas | ||
// Collect all intermediate messages | ||
let messages = []; | ||
while (logs[offset].kind !== 'gas') { | ||
messages.push(logs[offset++]); | ||
} | ||
// Check for gas limit change | ||
let gasLimitChange = messages.find((v) => v.kind === 'gas-limit-change'); | ||
if (gasLimitChange) { | ||
let delta = gasLimitChange.limit - gasLimit; | ||
gasLimit = gasLimitChange.limit; | ||
gasRemaining += delta; | ||
} | ||
// Look for end of execution | ||
let gas = logs[offset++]; | ||
if (gas.kind === 'output_action') { // Skip output action | ||
gas = logs[offset++]; | ||
} | ||
if (gas.kind !== 'gas') { | ||
@@ -97,0 +124,0 @@ throw new Error('Expected gas entry, got: ' + cell.kind + " at " + offset); |
@@ -37,5 +37,25 @@ "use strict"; | ||
}); | ||
it('should collect coverage', () => { | ||
it('should parse logs with empty lines', () => { | ||
let logs = fs.readFileSync(path.resolve(__dirname, '__testdata__', 'log2.txt'), 'utf8'); | ||
(0, parseVMLogs_1.parseVMLogs)(logs); | ||
}); | ||
// it('should collect coverage', () => { | ||
// // Collect coverage | ||
// let rawLogs = fs.readFileSync(path.resolve(__dirname, '__testdata__', 'log1.txt'), 'utf8'); | ||
// let logs = parseVMLogs(rawLogs); | ||
// let collector = new CoverageCollector(); | ||
// collectCoverage({ | ||
// collector, | ||
// logs, | ||
// gasLimit: 1000000000n | ||
// }); | ||
// expect(collector.export()).toMatchSnapshot(); | ||
// // Print coverage | ||
// let rawCode = Cell.fromBoc(fs.readFileSync(path.resolve(__dirname, '__testdata__', 'log1.boc')))[0]; | ||
// let res = printCoverage(rawCode, collector); | ||
// fs.writeFileSync(path.resolve(__dirname, '__testdata__', 'log1.html'), res); | ||
// }); | ||
it('should collect coverage 2', () => { | ||
// Collect coverage | ||
let rawLogs = fs.readFileSync(path.resolve(__dirname, '__testdata__', 'log1.txt'), 'utf8'); | ||
let rawLogs = fs.readFileSync(path.resolve(__dirname, '__testdata__', 'log2.txt'), 'utf8'); | ||
let logs = (0, parseVMLogs_1.parseVMLogs)(rawLogs); | ||
@@ -45,11 +65,10 @@ let collector = new coverage_1.CoverageCollector(); | ||
collector, | ||
logs, | ||
gasLimit: 1000000000n | ||
logs | ||
}); | ||
expect(collector.export()).toMatchSnapshot(); | ||
// Print coverage | ||
let rawCode = ton_core_1.Cell.fromBoc(fs.readFileSync(path.resolve(__dirname, '__testdata__', 'log1.boc')))[0]; | ||
let rawCode = ton_core_1.Cell.fromBase64('te6ccgEBCAEAlwABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQC48oMI1xgg0x/TH9MfAvgju/Jj7UTQ0x/TH9P/0VEyuvKhUUS68qIE+QFUEFX5EPKj9ATR+AB/jhYhgBD0eG+lIJgC0wfUMAH7AJEy4gGz5lsBpMjLH8sfy//J7VQABNAwAgFIBgcAF7s5ztRNDTPzHXC/+AARuMl+1E0NcLH4'); | ||
let res = (0, printCoverage_1.printCoverage)(rawCode, collector); | ||
fs.writeFileSync(path.resolve(__dirname, '__testdata__', 'log1.html'), res); | ||
fs.writeFileSync(path.resolve(__dirname, '__testdata__', 'log2.html'), res); | ||
}); | ||
}); |
@@ -15,4 +15,8 @@ export type LogEntry = { | ||
} | { | ||
kind: 'output_action'; | ||
kind: 'gas-limit-change'; | ||
limit: bigint; | ||
} | { | ||
kind: 'info'; | ||
message: string; | ||
}; | ||
export declare function parseVMLogs(logs: string): LogEntry[]; |
@@ -5,3 +5,3 @@ "use strict"; | ||
function parseVMLogs(logs) { | ||
let lines = logs.split('\n'); | ||
let lines = logs.split('\n').map((v) => v.trim()).filter((v) => v.length > 0); | ||
let res = []; | ||
@@ -28,7 +28,8 @@ for (let l of lines) { | ||
} | ||
else if (l.startsWith('installing an output action')) { | ||
res.push({ kind: 'output_action' }); | ||
else if (l.startsWith('changing gas limit to ')) { | ||
let limit = BigInt(l.substring('changing gas limit to '.length)); | ||
res.push({ kind: 'gas-limit-change', limit }); | ||
} | ||
else { | ||
throw new Error('Unknown log line: ' + l); | ||
res.push({ kind: 'info', message: l }); | ||
} | ||
@@ -35,0 +36,0 @@ } |
@@ -57,3 +57,5 @@ "use strict"; | ||
let line = span('.'.repeat(indent * 2), 'padding') + span(src.op, 'opcode'); | ||
let cov = collector.coverageForCell(src.hash); | ||
// console.log(src.hash.toLowerCase()); | ||
// console.log(src); | ||
let cov = collector.coverageForCell(src.hash.toLowerCase()); | ||
let lineClass; | ||
@@ -60,0 +62,0 @@ if (cov && cov.offsets.has(src.offset)) { |
@@ -52,4 +52,3 @@ "use strict"; | ||
logs: parsed, | ||
collector, | ||
gasLimit: 1000000000n | ||
collector | ||
}); | ||
@@ -56,0 +55,0 @@ } |
{ | ||
"name": "@tact-lang/coverage", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"main": "dist/index.js", | ||
@@ -18,3 +18,3 @@ "repository": "https://github.com/tact-lang/ton-coverage.git", | ||
"dependencies": { | ||
"@tact-lang/opcode": "^0.0.5", | ||
"@tact-lang/opcode": "^0.0.9", | ||
"prando": "^6.0.1", | ||
@@ -26,2 +26,3 @@ "teslabot": "^1.5.0", | ||
"@release-it/keep-a-changelog": "^3.1.0", | ||
"@tact-lang/emulator": "^3.4.1", | ||
"@types/jest": "^29.2.5", | ||
@@ -28,0 +29,0 @@ "@types/node": "^18.11.18", |
@@ -13,6 +13,29 @@ import * as fs from 'fs'; | ||
}); | ||
it('should collect coverage', () => { | ||
it('should parse logs with empty lines', () => { | ||
let logs = fs.readFileSync(path.resolve(__dirname, '__testdata__', 'log2.txt'), 'utf8'); | ||
parseVMLogs(logs); | ||
}); | ||
// it('should collect coverage', () => { | ||
// // Collect coverage | ||
// let rawLogs = fs.readFileSync(path.resolve(__dirname, '__testdata__', 'log1.txt'), 'utf8'); | ||
// let logs = parseVMLogs(rawLogs); | ||
// let collector = new CoverageCollector(); | ||
// collectCoverage({ | ||
// collector, | ||
// logs, | ||
// gasLimit: 1000000000n | ||
// }); | ||
// expect(collector.export()).toMatchSnapshot(); | ||
// // Print coverage | ||
// let rawCode = Cell.fromBoc(fs.readFileSync(path.resolve(__dirname, '__testdata__', 'log1.boc')))[0]; | ||
// let res = printCoverage(rawCode, collector); | ||
// fs.writeFileSync(path.resolve(__dirname, '__testdata__', 'log1.html'), res); | ||
// }); | ||
it('should collect coverage 2', () => { | ||
// Collect coverage | ||
let rawLogs = fs.readFileSync(path.resolve(__dirname, '__testdata__', 'log1.txt'), 'utf8'); | ||
let rawLogs = fs.readFileSync(path.resolve(__dirname, '__testdata__', 'log2.txt'), 'utf8'); | ||
let logs = parseVMLogs(rawLogs); | ||
@@ -22,4 +45,3 @@ let collector = new CoverageCollector(); | ||
collector, | ||
logs, | ||
gasLimit: 1000000000n | ||
logs | ||
}); | ||
@@ -29,6 +51,6 @@ expect(collector.export()).toMatchSnapshot(); | ||
// Print coverage | ||
let rawCode = Cell.fromBoc(fs.readFileSync(path.resolve(__dirname, '__testdata__', 'log1.boc')))[0]; | ||
let rawCode = Cell.fromBase64('te6ccgEBCAEAlwABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQC48oMI1xgg0x/TH9MfAvgju/Jj7UTQ0x/TH9P/0VEyuvKhUUS68qIE+QFUEFX5EPKj9ATR+AB/jhYhgBD0eG+lIJgC0wfUMAH7AJEy4gGz5lsBpMjLH8sfy//J7VQABNAwAgFIBgcAF7s5ztRNDTPzHXC/+AARuMl+1E0NcLH4'); | ||
let res = printCoverage(rawCode, collector); | ||
fs.writeFileSync(path.resolve(__dirname, '__testdata__', 'log1.html'), res); | ||
fs.writeFileSync(path.resolve(__dirname, '__testdata__', 'log2.html'), res); | ||
}); | ||
}); |
@@ -0,1 +1,2 @@ | ||
import { Maybe } from "../utils/maybe"; | ||
import { LogEntry } from "./parseVMLogs"; | ||
@@ -51,7 +52,28 @@ | ||
logs: LogEntry[], | ||
gasLimit: bigint | ||
gasLimit?: Maybe<bigint> | ||
}) { | ||
// Determine gas limit | ||
let gasLimit = 1000000000n; | ||
if (typeof args.gasLimit === 'bigint') { | ||
gasLimit = args.gasLimit; | ||
} else { | ||
// Try to determine gas limit from logs, ignoring the first opcode that usually | ||
// is the SETCP 0 and means nothing | ||
if (args.logs.length > 4) { | ||
if (args.logs[0].kind === 'stack' | ||
&& args.logs[1].kind === 'cell' | ||
&& args.logs[2].kind === 'execute' | ||
&& args.logs[3].kind === 'gas') { | ||
if (args.logs[2].command === 'SETCP 0') { | ||
gasLimit = args.logs[3].remaining; | ||
} | ||
} | ||
} | ||
} | ||
// Collect coverage | ||
let collector = args.collector; | ||
let logs = args.logs; | ||
let gasRemaining = args.gasLimit; | ||
let gasRemaining = gasLimit; | ||
let offset = 0; | ||
@@ -71,6 +93,4 @@ while (offset < logs.length) { | ||
if (cell.kind === 'execute' && cell.command.startsWith('implicit ')) { | ||
while (offset < logs.length) { | ||
if (logs[offset].kind === 'gas') { | ||
break; | ||
} | ||
// Skip non-gas | ||
while (logs[offset].kind !== 'gas') { | ||
offset++; | ||
@@ -97,7 +117,18 @@ } | ||
// Load gas | ||
// Collect all intermediate messages | ||
let messages: LogEntry[] = []; | ||
while (logs[offset].kind !== 'gas') { | ||
messages.push(logs[offset++]); | ||
} | ||
// Check for gas limit change | ||
let gasLimitChange = messages.find((v) => v.kind === 'gas-limit-change'); | ||
if (gasLimitChange) { | ||
let delta = (gasLimitChange as { limit: bigint }).limit - gasLimit; | ||
gasLimit = (gasLimitChange as { limit: bigint }).limit; | ||
gasRemaining += delta; | ||
} | ||
// Look for end of execution | ||
let gas = logs[offset++]; | ||
if (gas.kind === 'output_action') { // Skip output action | ||
gas = logs[offset++] | ||
} | ||
if (gas.kind !== 'gas') { | ||
@@ -104,0 +135,0 @@ throw new Error('Expected gas entry, got: ' + cell.kind + " at " + offset); |
@@ -15,7 +15,11 @@ export type LogEntry = { | ||
} | { | ||
kind: 'output_action' | ||
kind: 'gas-limit-change', | ||
limit: bigint | ||
} | { | ||
kind: 'info', | ||
message: string | ||
} | ||
export function parseVMLogs(logs: string) { | ||
let lines = logs.split('\n'); | ||
let lines = logs.split('\n').map((v) => v.trim()).filter((v) => v.length > 0); | ||
let res: LogEntry[] = []; | ||
@@ -38,6 +42,7 @@ for (let l of lines) { | ||
res.push({ kind: 'stack', stack }); | ||
} else if (l.startsWith('installing an output action')) { | ||
res.push({ kind: 'output_action' }); | ||
} else if (l.startsWith('changing gas limit to ')) { | ||
let limit = BigInt(l.substring('changing gas limit to '.length)); | ||
res.push({ kind: 'gas-limit-change', limit }); | ||
} else { | ||
throw new Error('Unknown log line: ' + l); | ||
res.push({ kind: 'info', message: l }); | ||
} | ||
@@ -44,0 +49,0 @@ } |
@@ -60,3 +60,5 @@ import { decompileAll, Printer } from "@tact-lang/opcode"; | ||
let line = span('.'.repeat(indent * 2), 'padding') + span(src.op, 'opcode'); | ||
let cov = collector.coverageForCell(src.hash); | ||
// console.log(src.hash.toLowerCase()); | ||
// console.log(src); | ||
let cov = collector.coverageForCell(src.hash.toLowerCase()); | ||
let lineClass: string; | ||
@@ -63,0 +65,0 @@ if (cov && cov.offsets.has(src.offset)) { |
@@ -30,4 +30,3 @@ import { collectCoverage, CoverageCollector } from "../collector/coverage"; | ||
logs: parsed, | ||
collector, | ||
gasLimit: 1000000000n | ||
collector | ||
}); | ||
@@ -34,0 +33,0 @@ } catch (e) { |
Sorry, the diff of this file is not supported yet
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
425096
41
990
0
29
11
+ Added@tact-lang/opcode@0.0.9(transitive)
- Removed@tact-lang/opcode@0.0.5(transitive)
Updated@tact-lang/opcode@^0.0.9