Skip to content

Commit 21b743a

Browse files
committed
Initial unit test framework
1 parent 0455881 commit 21b743a

File tree

8 files changed

+385
-1
lines changed

8 files changed

+385
-1
lines changed

__mocks__/vscode.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,32 @@
1414
* limitations under the License.
1515
*/
1616

17+
require('domain');
1718
const { URI } = require('vscode-uri');
1819

1920
module.exports = {
21+
EventEmitter: jest.fn(() => {
22+
const callbacks = [];
23+
return {
24+
dispose: jest.fn(),
25+
event: (callback, thisArg) => {
26+
callbacks.push(thisArg ? callback.bind(thisArg) : callback);
27+
return { dispose: jest.fn() };
28+
},
29+
fire: event => callbacks.forEach(callback => callback(event))
30+
};
31+
}),
2032
Uri: URI,
33+
window: {
34+
createOutputChannel: jest.fn(() => ({
35+
appendLine: jest.fn(),
36+
trace: jest.fn(),
37+
debug: jest.fn(),
38+
info: jest.fn(),
39+
warn: jest.fn(),
40+
error: jest.fn(),
41+
})),
42+
},
2143
workspace: {
2244
getConfiguration: jest.fn(() => ({
2345
get: jest.fn(),
@@ -28,5 +50,8 @@ module.exports = {
2850
},
2951
commands: {
3052
executeCommand: jest.fn(),
31-
}
53+
},
54+
debug: {
55+
registerDebugConfigurationProvider: jest.fn(),
56+
},
3257
};
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/**
2+
* Copyright 2025 Arm Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { makeFactory, StubEvents } from './test-data-factory';
18+
import { Event, EventEmitter } from 'vscode';
19+
20+
describe('makeFactory', () => {
21+
22+
it('returns default values when used without options', () => {
23+
type Type = {
24+
value: number;
25+
text: string;
26+
}
27+
28+
const expected : Type = {
29+
value: 42,
30+
text: 'the answer'
31+
};
32+
33+
const factory = makeFactory<Type>({
34+
value: () => expected.value,
35+
text: () => expected.text,
36+
});
37+
38+
const value = factory();
39+
40+
expect(value).toEqual(expected);
41+
});
42+
43+
it('returns explicit values passed by options', () => {
44+
type Type = {
45+
value: number;
46+
text: string;
47+
}
48+
49+
const expected : Type = {
50+
value: 42,
51+
text: 'the answer'
52+
};
53+
54+
const factory = makeFactory<Type>({
55+
value: () => 43,
56+
text: () => expected.text,
57+
});
58+
59+
const value = factory({ value: expected.value });
60+
61+
expect(value).toEqual(expected);
62+
});
63+
64+
it('stubs vscode.Event|s by exposing vscode.EventEmitter', () => {
65+
type Type = {
66+
event: Event<number>,
67+
}
68+
69+
const factory = makeFactory<StubEvents<Type>>({
70+
eventEmitter: () => new EventEmitter(),
71+
event: (r) => jest.fn(r.eventEmitter?.event),
72+
});
73+
74+
const value = factory();
75+
76+
const listener = jest.fn();
77+
value.event(listener);
78+
value.eventEmitter.fire(42);
79+
80+
expect(listener).toHaveBeenCalledTimes(1);
81+
expect(listener).toHaveBeenCalledWith(42);
82+
});
83+
84+
});

src/__test__/test-data-factory.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* Copyright 2025 Arm Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { Event, EventEmitter } from 'vscode';
18+
19+
type Mutable<T> = {
20+
-readonly [P in keyof T]: T[P];
21+
}
22+
23+
type Factory<T> = (options?: Partial<T>) => Mutable<T>;
24+
25+
type Initializer<T> = {
26+
[P in keyof T]: (result: Partial<T>) => T[P]
27+
};
28+
29+
export type StubEvents<T, S extends string = 'Emitter'> = {
30+
[P in keyof T as T[P] extends Event<unknown> ? `${string & P}${S}` : never]: T[P] extends Event<infer U> ? EventEmitter<U> : never
31+
} & T;
32+
33+
export function makeFactory<T extends object>(initializer: Initializer<T>): Factory<T> {
34+
const factory = (options?: Partial<T>) => {
35+
const result = { ...options } as Mutable<T>;
36+
for (const key in initializer) {
37+
if (!(key in result)) {
38+
result[key] = initializer[key].call(result, result);
39+
}
40+
}
41+
return result;
42+
};
43+
return factory;
44+
}
45+
46+
export function makeGenerator<T>(factory: Factory<T>) {
47+
return (count: number = 1, options?: Partial<T>): T[] => {
48+
return [...Array(count)].map(_ => factory(options));
49+
};
50+
}

src/__test__/vscode.factory.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/**
2+
* Copyright 2025 Arm Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import * as vscode from 'vscode';
18+
19+
export function extensionContextFactory(): jest.Mocked<vscode.ExtensionContext> {
20+
return {
21+
subscriptions: [],
22+
workspaceState: {
23+
get: jest.fn(),
24+
update: jest.fn(),
25+
} as unknown as vscode.Memento,
26+
globalState: {
27+
get: jest.fn(),
28+
update: jest.fn(),
29+
setKeysForSync: jest.fn(),
30+
} as unknown as vscode.Memento & { setKeysForSync(keys: readonly string[]): void } ,
31+
secrets: {
32+
store: jest.fn(),
33+
get: jest.fn(),
34+
delete: jest.fn(),
35+
} as unknown as vscode.SecretStorage,
36+
extensionUri: vscode.Uri.file('/mock/uri'),
37+
extensionPath: '/mock/path',
38+
environmentVariableCollection: {
39+
persistent: true,
40+
replace: jest.fn(),
41+
append: jest.fn(),
42+
prepend: jest.fn(),
43+
get: jest.fn(),
44+
forEach: jest.fn(),
45+
getScoped: jest.fn(),
46+
} as unknown as vscode.GlobalEnvironmentVariableCollection,
47+
storageUri: vscode.Uri.file('/mock/storageUri'),
48+
globalStorageUri: vscode.Uri.file('/mock/globalStorageUri'),
49+
logUri: vscode.Uri.file('/mock/logUri'),
50+
storagePath: '/mock/storagePath',
51+
globalStoragePath: '/mock/globalStoragePath',
52+
logPath: '/mock/logPath',
53+
asAbsolutePath: jest.fn((relativePath: string) => `/mock/path/${relativePath}`),
54+
extensionMode: 3,
55+
extension: {
56+
id: 'mock.extension',
57+
extensionUri: vscode.Uri.file('/mock/uri'),
58+
extensionPath: '/mock/path',
59+
isActive: true,
60+
packageJSON: {},
61+
activate: jest.fn(),
62+
exports: {},
63+
extensionKind: 2,
64+
} as unknown as vscode.Extension<any>,
65+
languageModelAccessInformation: {
66+
getLanguageModel: jest.fn(),
67+
} as unknown as vscode.LanguageModelAccessInformation,
68+
};
69+
};
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* Copyright 2025 Arm Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import * as vscode from 'vscode';
18+
import { makeFactory } from '../__test__/test-data-factory';
19+
import { GDBTargetConfiguration } from './gdbtarget-configuration';
20+
21+
export const debugConfigurationFactory = makeFactory<vscode.DebugConfiguration>({
22+
type: () => 'cppdbg',
23+
name: () => 'Debug',
24+
request: () => 'launch',
25+
});
26+
27+
export const gdbTargetConfiguration = makeFactory<GDBTargetConfiguration>({
28+
type: () => 'gdb',
29+
name: () => 'Debug',
30+
request: () => 'launch',
31+
program: () => 'program',
32+
gdb: () => 'gdb',
33+
cwd: () => 'cwd',
34+
environment: () => ({ additionalProperties: 'additionalProperties' }),
35+
gdbAsync: () => false,
36+
gdbNonStop: () => false,
37+
verbose: () => false,
38+
logFile: () => 'logFile',
39+
openGdbConsole: () => false,
40+
initCommands: () => [],
41+
preRunCommands: () => [],
42+
imageAndSymbols: () => undefined,
43+
target: () => undefined,
44+
cmsis: () => undefined,
45+
});
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* Copyright 2025 Arm Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { GDBTargetConfigurationProvider } from './gdbtarget-configuration-provider';
18+
import { extensionContextFactory } from '../__test__/vscode.factory';
19+
20+
import * as vscode from 'vscode';
21+
import { debugConfigurationFactory } from './debug-configuration.factory';
22+
23+
describe('GDBTargetConfigurationProvider', () => {
24+
25+
it('should activate', async () => {
26+
const configProvider = new GDBTargetConfigurationProvider([]);
27+
const contextMock = extensionContextFactory();
28+
29+
configProvider.activate(contextMock);
30+
31+
expect(contextMock.subscriptions).toHaveLength(1);
32+
expect(vscode.debug.registerDebugConfigurationProvider as jest.Mock).toHaveBeenCalledWith('gdbtarget', configProvider);
33+
});
34+
35+
it('resolveDebugConfiguration', async () => {
36+
const configProvider = new GDBTargetConfigurationProvider([]);
37+
const debugConfig = debugConfigurationFactory();
38+
39+
const resolvedDebugConfig = await configProvider.resolveDebugConfiguration(undefined, debugConfig, undefined);
40+
41+
expect(resolvedDebugConfig).toBeDefined();
42+
});
43+
44+
it('resolveDebugConfigurationWithSubstitutedVariables', async () => {
45+
const configProvider = new GDBTargetConfigurationProvider([]);
46+
const debugConfig = debugConfigurationFactory();
47+
48+
const resolvedDebugConfig = await configProvider.resolveDebugConfigurationWithSubstitutedVariables(undefined, debugConfig, undefined);
49+
50+
expect(resolvedDebugConfig).toBeDefined();
51+
});
52+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Copyright 2025 Arm Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { debugConfigurationFactory } from '../debug-configuration.factory';
18+
import { JlinkConfigurationProvider } from './jlink-configuration-provider';
19+
20+
describe('JlinkConfigurationProvider', () => {
21+
22+
it('resolveDebugConfigurationWithSubstitutedVariables', async () => {
23+
const configProvider = new JlinkConfigurationProvider();
24+
const config = debugConfigurationFactory();
25+
const debugConfig = await configProvider.resolveDebugConfigurationWithSubstitutedVariables(undefined, config, undefined);
26+
expect(debugConfig).toBeDefined();
27+
});
28+
29+
});

0 commit comments

Comments
 (0)