Skip to content

Commit 9f43f85

Browse files
authored
tests: smoke tests (#88)
1 parent 96f455f commit 9f43f85

5 files changed

Lines changed: 136 additions & 4 deletions

File tree

.github/workflows/test.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ env:
1111

1212
jobs:
1313
test:
14-
runs-on: ubuntu-latest
14+
name: Test (${{ matrix.os }})
15+
runs-on: ${{ matrix.os }}
16+
strategy:
17+
fail-fast: false
18+
matrix:
19+
os: [ubuntu-latest, macos-latest, windows-latest]
1520

1621
steps:
1722
- name: Checkout code
@@ -30,6 +35,12 @@ jobs:
3035
- name: Install dependencies
3136
run: npm ci
3237

38+
- name: Install CodeCarbon
39+
run: python -m pip install --upgrade pip codecarbon
40+
41+
- name: Integration smoke checks
42+
run: npm run test:smoke
43+
3344
- name: Run Python tests
3445
run: npm run test:python
3546

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ bundled/libs/
1111
**/.pytest_cache
1212
**/.vs
1313

14-
out-tests
14+
out-tests
15+
# Codecarbon emissions data
16+
emissions.csv

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,16 @@ If you want keyboard shortcuts, add these to your `keybindings.json`:
5555

5656
## Requirements
5757

58-
The extension uses `codecarbon` to measure the carbon emissions. This package connects to your hardware via specific APIs to get to know the power usage of your CPU/GPU/RAM. These APIs depend on the brand and OS. See https://mlco2.github.io/codecarbon/methodology.html#power-usage for the needed tools for your specific setup.
58+
The extension uses `codecarbon` to measure the carbon emissions. This package connects to your hardware via specific APIs to get to know the power usage of your CPU/GPU/RAM. These APIs depend on the brand and OS. See https://docs.codecarbon.io/latest/introduction/methodology/#power-usage for the needed tools for your specific setup.
5959

6060
> Note: if you do not install the requirements, codecarbon will track in fallback mode.
6161
62+
## Cross-platform caveats
63+
64+
- The extension behavior is designed to be consistent on Linux, macOS, and Windows (same start/stop commands, status bar state model, and logs).
65+
- Hardware-level power collection accuracy depends on OS-specific CodeCarbon dependencies and vendor tooling. Review the official CodeCarbon power usage requirements in the link above.
66+
- When those dependencies are unavailable, CodeCarbon may use fallback estimation mode; the extension still runs, but metrics fidelity can differ by platform.
67+
6268
## Extension Settings
6369

6470
This extension contributes the following settings:

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@
142142
"package": "vsce package",
143143
"test:python": "python3 -m unittest discover -s tests/python -p \"test_*.py\"",
144144
"test:ts": "tsc -p tsconfig.tests.json && node --test out-tests/tests/ts/*.test.js",
145-
"test": "npm run test:python && npm run test:ts"
145+
"test:smoke": "node --test tests/integration/*.test.mjs",
146+
"test": "npm run test:smoke && npm run test:python && npm run test:ts"
146147
},
147148
"devDependencies": {
148149
"@types/node": "^25.5.0",

tests/integration/smoke.test.mjs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { execFile, spawn } from 'node:child_process';
2+
import assert from 'node:assert/strict';
3+
import { fileURLToPath } from 'node:url';
4+
import path from 'node:path';
5+
import process from 'node:process';
6+
import test from 'node:test';
7+
8+
const __filename = fileURLToPath(import.meta.url);
9+
const __dirname = path.dirname(__filename);
10+
const workspaceRoot = path.resolve(__dirname, '../..');
11+
const trackerScript = path.resolve(workspaceRoot, 'src/scripts/tracker.py');
12+
const pythonCmd = process.env.PYTHON_CMD || 'python';
13+
14+
function execPython(args) {
15+
return new Promise((resolve) => {
16+
execFile(pythonCmd, args, (error, stdout, stderr) => {
17+
resolve({
18+
ok: !error,
19+
stdout: stdout?.toString() ?? '',
20+
stderr: stderr?.toString() ?? '',
21+
code: error?.code ?? 0,
22+
});
23+
});
24+
});
25+
}
26+
27+
async function checkInterpreterDiscovery() {
28+
const version = await execPython(['--version']);
29+
assert.equal(
30+
version.ok,
31+
true,
32+
`Interpreter discovery failed for "${pythonCmd}"\n${version.stdout}\n${version.stderr}`,
33+
);
34+
console.log(`Interpreter discovery OK: ${(version.stdout || version.stderr).trim()}`);
35+
36+
const pip = await execPython(['-m', 'pip', '--version']);
37+
assert.equal(pip.ok, true, `pip is unavailable for selected interpreter\n${pip.stdout}\n${pip.stderr}`);
38+
console.log(`pip check OK: ${pip.stdout.trim()}`);
39+
}
40+
41+
async function checkPackagePresenceProbe() {
42+
const show = await execPython(['-m', 'pip', 'show', 'codecarbon']);
43+
if (show.ok) {
44+
console.log('Package presence check OK: codecarbon is installed.');
45+
return;
46+
}
47+
48+
if (show.code === 1) {
49+
console.log('Package presence check OK: codecarbon not installed (probe behavior validated).');
50+
return;
51+
}
52+
53+
assert.fail(`Package presence probe failed unexpectedly\n${show.stdout}\n${show.stderr}`);
54+
}
55+
56+
async function checkTrackerLifecycle() {
57+
await new Promise((resolve, reject) => {
58+
const child = spawn(pythonCmd, ['-u', trackerScript, 'start'], {
59+
cwd: workspaceRoot,
60+
stdio: ['ignore', 'pipe', 'pipe'],
61+
});
62+
const timeoutMs = 20000;
63+
const timeout = setTimeout(() => {
64+
child.kill();
65+
reject(new Error(`Tracker did not start within ${timeoutMs}ms.`));
66+
}, timeoutMs);
67+
68+
let started = false;
69+
let output = '';
70+
71+
const onData = (chunk) => {
72+
const text = chunk.toString();
73+
output += text;
74+
if (!started && (text.includes('Starting the tracker...') || text.includes('Tracker started.'))) {
75+
started = true;
76+
setTimeout(() => child.kill(), 1500);
77+
}
78+
};
79+
80+
child.stdout?.on('data', onData);
81+
child.stderr?.on('data', onData);
82+
83+
child.on('close', (code) => {
84+
clearTimeout(timeout);
85+
if (!started) {
86+
reject(new Error(`Tracker lifecycle failed before start (exit code: ${code}).\n${output}`));
87+
return;
88+
}
89+
console.log(`Tracker lifecycle OK (exit code: ${code ?? 'unknown'}).`);
90+
resolve();
91+
});
92+
93+
child.on('error', (error) => {
94+
clearTimeout(timeout);
95+
reject(error);
96+
});
97+
});
98+
}
99+
100+
test('integration smoke: interpreter discovery', async () => {
101+
console.log(`Running integration smoke test with interpreter: ${pythonCmd}`);
102+
await checkInterpreterDiscovery();
103+
});
104+
105+
test('integration smoke: package presence probe', async () => {
106+
await checkPackagePresenceProbe();
107+
});
108+
109+
test('integration smoke: tracker lifecycle spawn/stop', async () => {
110+
await checkTrackerLifecycle();
111+
console.log('All integration smoke checks passed.');
112+
});

0 commit comments

Comments
 (0)