Files
arduino-ide/arduino-ide-extension/src/test/node/clang-formatter.test.ts
Akos Kitta 69ae38effa feat: simplify board and port handling (#2165)
Use Arduino CLI revision `38479dc`

Closes #43
Closes #82
Closes #1319
Closes #1366
Closes #2143
Closes #2158

Ref: 38479dc706

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2023-08-18 14:42:50 +02:00

163 lines
3.9 KiB
TypeScript

import {
Disposable,
DisposableCollection,
} from '@theia/core/lib/common/disposable';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { expect } from 'chai';
import { promises as fs } from 'node:fs';
import path from 'node:path';
import temp from 'temp';
import {
clangFormatFilename,
ClangFormatter,
} from '../../node/clang-formatter';
import { spawnCommand } from '../../node/exec-util';
import { createBaseContainer, startDaemon } from './node-test-bindings';
const unformattedContent = `void setup ( ) { pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite( LED_BUILTIN , HIGH );
delay( 1000 ) ;
digitalWrite( LED_BUILTIN , LOW);
delay ( 1000 ) ;
}
`;
const formattedContent = `void setup() {
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
}
`;
type ClangStyleValue =
| string
| number
| boolean
| ClangStyleValue[]
| { [key: string]: ClangStyleValue };
type ClangConfiguration = Record<string, ClangStyleValue>;
export interface ClangStyle {
readonly key: string;
readonly value: ClangStyleValue;
}
const singleClangStyles: ClangStyle[] = [
{
key: 'SpacesBeforeTrailingComments',
value: 0,
},
{
key: 'SortIncludes',
value: 'Never',
},
{
key: 'AlignTrailingComments',
value: true,
},
{
key: 'IfMacros',
value: ['KJ_IF_MAYBE'],
},
{
key: 'SpacesInLineCommentPrefix',
value: {
Minimum: 0,
Maximum: -1,
},
},
];
async function expectNoChanges(
formatter: ClangFormatter,
styleArg: string
): Promise<void> {
const minimalContent = `
void setup() {}
void loop() {}
`.trim();
const execPath = formatter['execPath']();
const actual = await spawnCommand(
execPath,
['-style', styleArg],
console.error,
minimalContent
);
expect(actual).to.be.equal(minimalContent);
}
describe('clang-formatter', () => {
let tracked: typeof temp;
let formatter: ClangFormatter;
let toDispose: DisposableCollection;
before(async () => {
tracked = temp.track();
toDispose = new DisposableCollection(
Disposable.create(() => tracked.cleanupSync())
);
const container = await createBaseContainer({
additionalBindings: (bind) =>
bind(ClangFormatter).toSelf().inSingletonScope(),
});
await startDaemon(container, toDispose);
formatter = container.get<ClangFormatter>(ClangFormatter);
});
after(() => toDispose.dispose());
singleClangStyles
.map((style) => ({
...style,
styleArg: JSON.stringify({ [style.key]: style.value }),
}))
.map(({ value, styleArg }) =>
it(`should execute the formatter with a single ${
Array.isArray(value) ? 'array' : typeof value
} type style configuration value: ${styleArg}`, async () => {
await expectNoChanges(formatter, styleArg);
})
);
it('should execute the formatter with a multiple clang formatter styles', async () => {
const styleArg = JSON.stringify(
singleClangStyles.reduce((config, curr) => {
config[curr.key] = curr.value;
return config;
}, {} as ClangConfiguration)
);
await expectNoChanges(formatter, styleArg);
});
it('should format with the default styles', async () => {
const actual = await formatter.format({
content: unformattedContent,
formatterConfigFolderUris: [],
});
expect(actual).to.be.equal(formattedContent);
});
it('should format with custom formatter configuration file', async () => {
const tempPath = tracked.mkdirSync();
await fs.writeFile(
path.join(tempPath, clangFormatFilename),
'SpaceInEmptyParentheses: true',
{
encoding: 'utf8',
}
);
const actual = await formatter.format({
content: 'void foo() {}',
formatterConfigFolderUris: [FileUri.create(tempPath).toString()],
});
expect(actual).to.be.equal('void foo( ) {}');
});
});