mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-11-10 02:48:33 +00:00
feat: introduced cloud state in sketchbook view
Closes #1879 Closes #1876 Closes #1899 Closes #1878 Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Disposable } from '@theia/core/lib/common/disposable';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { Container } from '@theia/core/shared/inversify';
|
||||
import { expect } from 'chai';
|
||||
import { BoardSearch, BoardsService } from '../../common/protocol';
|
||||
@@ -10,26 +10,18 @@ import {
|
||||
|
||||
describe('boards-service-impl', () => {
|
||||
let boardService: BoardsService;
|
||||
let toDispose: Disposable[] = [];
|
||||
let toDispose: DisposableCollection;
|
||||
|
||||
before(async function () {
|
||||
configureBackendApplicationConfigProvider();
|
||||
this.timeout(20_000);
|
||||
toDispose = [];
|
||||
toDispose = new DisposableCollection();
|
||||
const container = createContainer();
|
||||
await start(container, toDispose);
|
||||
boardService = container.get<BoardsService>(BoardsService);
|
||||
});
|
||||
|
||||
after(() => {
|
||||
let disposable = toDispose.pop();
|
||||
while (disposable) {
|
||||
try {
|
||||
disposable?.dispose();
|
||||
} catch {}
|
||||
disposable = toDispose.pop();
|
||||
}
|
||||
});
|
||||
after(() => toDispose.dispose());
|
||||
|
||||
describe('search', () => {
|
||||
it('should run search', async function () {
|
||||
@@ -37,7 +29,7 @@ describe('boards-service-impl', () => {
|
||||
expect(result).is.not.empty;
|
||||
});
|
||||
|
||||
it("should boost a result when 'types' includes 'arduino', and lower the score if deprecated", async function () {
|
||||
it("should boost a result when 'types' includes 'arduino', and lower the score if deprecated", async () => {
|
||||
const result = await boardService.search({});
|
||||
const arduinoIndexes: number[] = [];
|
||||
const otherIndexes: number[] = [];
|
||||
@@ -108,7 +100,7 @@ function createContainer(): Container {
|
||||
|
||||
async function start(
|
||||
container: Container,
|
||||
toDispose: Disposable[]
|
||||
toDispose: DisposableCollection
|
||||
): Promise<void> {
|
||||
return startDaemon(container, toDispose);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CancellationTokenSource } from '@theia/core/lib/common/cancellation';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import { Disposable } from '@theia/core/lib/common/disposable';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { isWindows } from '@theia/core/lib/common/os';
|
||||
import { FileUri } from '@theia/core/lib/node/file-uri';
|
||||
import { Container, injectable } from '@theia/core/shared/inversify';
|
||||
@@ -23,7 +23,7 @@ const uno = 'arduino:avr:uno';
|
||||
|
||||
describe('core-service-impl', () => {
|
||||
let container: Container;
|
||||
let toDispose: Disposable[];
|
||||
let toDispose: DisposableCollection;
|
||||
|
||||
before(() => {
|
||||
configureBackendApplicationConfigProvider();
|
||||
@@ -31,20 +31,12 @@ describe('core-service-impl', () => {
|
||||
|
||||
beforeEach(async function () {
|
||||
this.timeout(setupTimeout);
|
||||
toDispose = [];
|
||||
toDispose = new DisposableCollection();
|
||||
container = createContainer();
|
||||
await start(container, toDispose);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
let disposable = toDispose.pop();
|
||||
while (disposable) {
|
||||
try {
|
||||
disposable?.dispose();
|
||||
} catch {}
|
||||
disposable = toDispose.pop();
|
||||
}
|
||||
});
|
||||
afterEach(() => toDispose.dispose());
|
||||
|
||||
describe('compile', () => {
|
||||
it('should execute a command with the build path', async function () {
|
||||
@@ -92,7 +84,7 @@ describe('core-service-impl', () => {
|
||||
|
||||
async function start(
|
||||
container: Container,
|
||||
toDispose: Disposable[]
|
||||
toDispose: DisposableCollection
|
||||
): Promise<void> {
|
||||
await startDaemon(container, toDispose, async (container) => {
|
||||
const boardService = container.get<BoardsService>(BoardsService);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Disposable } from '@theia/core/lib/common/disposable';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { Container } from '@theia/core/shared/inversify';
|
||||
import { expect } from 'chai';
|
||||
import { LibrarySearch, LibraryService } from '../../common/protocol';
|
||||
@@ -11,26 +11,18 @@ import {
|
||||
|
||||
describe('library-service-impl', () => {
|
||||
let libraryService: LibraryService;
|
||||
let toDispose: Disposable[] = [];
|
||||
let toDispose: DisposableCollection;
|
||||
|
||||
before(async function () {
|
||||
configureBackendApplicationConfigProvider();
|
||||
this.timeout(20_000);
|
||||
toDispose = [];
|
||||
toDispose = new DisposableCollection();
|
||||
const container = createContainer();
|
||||
await start(container, toDispose);
|
||||
libraryService = container.get<LibraryService>(LibraryService);
|
||||
});
|
||||
|
||||
after(() => {
|
||||
let disposable = toDispose.pop();
|
||||
while (disposable) {
|
||||
try {
|
||||
disposable?.dispose();
|
||||
} catch {}
|
||||
disposable = toDispose.pop();
|
||||
}
|
||||
});
|
||||
after(() => toDispose.dispose());
|
||||
|
||||
describe('search', () => {
|
||||
it('should run search', async function () {
|
||||
@@ -89,7 +81,7 @@ function createContainer(): Container {
|
||||
|
||||
async function start(
|
||||
container: Container,
|
||||
toDispose: Disposable[]
|
||||
toDispose: DisposableCollection
|
||||
): Promise<void> {
|
||||
return startDaemon(container, toDispose);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import { FileUri } from '@theia/core/lib/node/file-uri';
|
||||
import { Container } from '@theia/core/shared/inversify';
|
||||
import { expect } from 'chai';
|
||||
import { promises as fs } from 'fs';
|
||||
import { basename, join } from 'path';
|
||||
import { sync as rimrafSync } from 'rimraf';
|
||||
import { Sketch, SketchesService } from '../../common/protocol';
|
||||
import { SketchesServiceImpl } from '../../node/sketches-service-impl';
|
||||
import { ErrnoException } from '../../node/utils/errors';
|
||||
import {
|
||||
configureBackendApplicationConfigProvider,
|
||||
createBaseContainer,
|
||||
startDaemon,
|
||||
} from './test-bindings';
|
||||
|
||||
const testTimeout = 10_000;
|
||||
|
||||
describe('sketches-service-impl', () => {
|
||||
let container: Container;
|
||||
let toDispose: DisposableCollection;
|
||||
|
||||
before(async () => {
|
||||
configureBackendApplicationConfigProvider();
|
||||
toDispose = new DisposableCollection();
|
||||
container = createContainer();
|
||||
await start(container, toDispose);
|
||||
});
|
||||
|
||||
after(() => toDispose.dispose());
|
||||
|
||||
describe('copy', () => {
|
||||
it('should copy a sketch when the destination does not exist', async function () {
|
||||
this.timeout(testTimeout);
|
||||
const sketchesService =
|
||||
container.get<SketchesServiceImpl>(SketchesService);
|
||||
const destinationPath = await sketchesService['createTempFolder']();
|
||||
let sketch = await sketchesService.createNewSketch();
|
||||
toDispose.push(disposeSketch(sketch));
|
||||
const sourcePath = FileUri.fsPath(sketch.uri);
|
||||
const libBasename = 'lib.cpp';
|
||||
const libContent = 'lib content';
|
||||
const libPath = join(sourcePath, libBasename);
|
||||
await fs.writeFile(libPath, libContent, { encoding: 'utf8' });
|
||||
const headerBasename = 'header.h';
|
||||
const headerContent = 'header content';
|
||||
const headerPath = join(sourcePath, headerBasename);
|
||||
await fs.writeFile(headerPath, headerContent, { encoding: 'utf8' });
|
||||
|
||||
sketch = await sketchesService.loadSketch(sketch.uri);
|
||||
expect(Sketch.isInSketch(FileUri.create(libPath), sketch)).to.be.true;
|
||||
expect(Sketch.isInSketch(FileUri.create(headerPath), sketch)).to.be.true;
|
||||
|
||||
const copied = await sketchesService.copy(sketch, {
|
||||
destinationUri: FileUri.create(destinationPath).toString(),
|
||||
});
|
||||
toDispose.push(disposeSketch(copied));
|
||||
expect(copied.name).to.be.equal(basename(destinationPath));
|
||||
expect(
|
||||
Sketch.isInSketch(
|
||||
FileUri.create(
|
||||
join(destinationPath, `${basename(destinationPath)}.ino`)
|
||||
),
|
||||
copied
|
||||
)
|
||||
).to.be.true;
|
||||
expect(
|
||||
Sketch.isInSketch(
|
||||
FileUri.create(join(destinationPath, libBasename)),
|
||||
copied
|
||||
)
|
||||
).to.be.true;
|
||||
expect(
|
||||
Sketch.isInSketch(
|
||||
FileUri.create(join(destinationPath, headerBasename)),
|
||||
copied
|
||||
)
|
||||
).to.be.true;
|
||||
});
|
||||
|
||||
it("should copy only sketch files if 'onlySketchFiles' is true", async function () {
|
||||
this.timeout(testTimeout);
|
||||
const sketchesService =
|
||||
container.get<SketchesServiceImpl>(SketchesService);
|
||||
const destinationPath = await sketchesService['createTempFolder']();
|
||||
let sketch = await sketchesService.createNewSketch();
|
||||
toDispose.push(disposeSketch(sketch));
|
||||
const sourcePath = FileUri.fsPath(sketch.uri);
|
||||
const libBasename = 'lib.cpp';
|
||||
const libContent = 'lib content';
|
||||
const libPath = join(sourcePath, libBasename);
|
||||
await fs.writeFile(libPath, libContent, { encoding: 'utf8' });
|
||||
const headerBasename = 'header.h';
|
||||
const headerContent = 'header content';
|
||||
const headerPath = join(sourcePath, headerBasename);
|
||||
await fs.writeFile(headerPath, headerContent, { encoding: 'utf8' });
|
||||
const logBasename = 'inols-clangd-err.log';
|
||||
const logContent = 'log file content';
|
||||
const logPath = join(sourcePath, logBasename);
|
||||
await fs.writeFile(logPath, logContent, { encoding: 'utf8' });
|
||||
|
||||
sketch = await sketchesService.loadSketch(sketch.uri);
|
||||
expect(Sketch.isInSketch(FileUri.create(libPath), sketch)).to.be.true;
|
||||
expect(Sketch.isInSketch(FileUri.create(headerPath), sketch)).to.be.true;
|
||||
expect(Sketch.isInSketch(FileUri.create(logPath), sketch)).to.be.false;
|
||||
const reloadedLogContent = await fs.readFile(logPath, {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
expect(reloadedLogContent).to.be.equal(logContent);
|
||||
|
||||
const copied = await sketchesService.copy(sketch, {
|
||||
destinationUri: FileUri.create(destinationPath).toString(),
|
||||
onlySketchFiles: true,
|
||||
});
|
||||
toDispose.push(disposeSketch(copied));
|
||||
expect(copied.name).to.be.equal(basename(destinationPath));
|
||||
expect(
|
||||
Sketch.isInSketch(
|
||||
FileUri.create(
|
||||
join(destinationPath, `${basename(destinationPath)}.ino`)
|
||||
),
|
||||
copied
|
||||
)
|
||||
).to.be.true;
|
||||
expect(
|
||||
Sketch.isInSketch(
|
||||
FileUri.create(join(destinationPath, libBasename)),
|
||||
copied
|
||||
)
|
||||
).to.be.true;
|
||||
expect(
|
||||
Sketch.isInSketch(
|
||||
FileUri.create(join(destinationPath, headerBasename)),
|
||||
copied
|
||||
)
|
||||
).to.be.true;
|
||||
expect(
|
||||
Sketch.isInSketch(
|
||||
FileUri.create(join(destinationPath, logBasename)),
|
||||
copied
|
||||
)
|
||||
).to.be.false;
|
||||
try {
|
||||
await fs.readFile(join(destinationPath, logBasename), {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
expect.fail(
|
||||
'Log file must not exist in the destination. Expected ENOENT when loading the log file.'
|
||||
);
|
||||
} catch (err) {
|
||||
expect(ErrnoException.isENOENT(err)).to.be.true;
|
||||
}
|
||||
});
|
||||
|
||||
it('should copy sketch inside the sketch folder', async function () {
|
||||
this.timeout(testTimeout);
|
||||
const sketchesService =
|
||||
container.get<SketchesServiceImpl>(SketchesService);
|
||||
let sketch = await sketchesService.createNewSketch();
|
||||
const destinationPath = join(FileUri.fsPath(sketch.uri), 'nested_copy');
|
||||
toDispose.push(disposeSketch(sketch));
|
||||
const sourcePath = FileUri.fsPath(sketch.uri);
|
||||
const libBasename = 'lib.cpp';
|
||||
const libContent = 'lib content';
|
||||
const libPath = join(sourcePath, libBasename);
|
||||
await fs.writeFile(libPath, libContent, { encoding: 'utf8' });
|
||||
const headerBasename = 'header.h';
|
||||
const headerContent = 'header content';
|
||||
const headerPath = join(sourcePath, headerBasename);
|
||||
await fs.writeFile(headerPath, headerContent, { encoding: 'utf8' });
|
||||
|
||||
sketch = await sketchesService.loadSketch(sketch.uri);
|
||||
expect(Sketch.isInSketch(FileUri.create(libPath), sketch)).to.be.true;
|
||||
expect(Sketch.isInSketch(FileUri.create(headerPath), sketch)).to.be.true;
|
||||
|
||||
const copied = await sketchesService.copy(sketch, {
|
||||
destinationUri: FileUri.create(destinationPath).toString(),
|
||||
});
|
||||
toDispose.push(disposeSketch(copied));
|
||||
expect(copied.name).to.be.equal(basename(destinationPath));
|
||||
expect(
|
||||
Sketch.isInSketch(
|
||||
FileUri.create(
|
||||
join(destinationPath, `${basename(destinationPath)}.ino`)
|
||||
),
|
||||
copied
|
||||
)
|
||||
).to.be.true;
|
||||
expect(
|
||||
Sketch.isInSketch(
|
||||
FileUri.create(join(destinationPath, libBasename)),
|
||||
copied
|
||||
)
|
||||
).to.be.true;
|
||||
expect(
|
||||
Sketch.isInSketch(
|
||||
FileUri.create(join(destinationPath, headerBasename)),
|
||||
copied
|
||||
)
|
||||
).to.be.true;
|
||||
});
|
||||
|
||||
it('should copy sketch with overwrite when source and destination sketch folder names are the same', async function () {
|
||||
this.timeout(testTimeout);
|
||||
const sketchesService =
|
||||
container.get<SketchesServiceImpl>(SketchesService);
|
||||
const sketchFolderName = 'alma';
|
||||
const contentOne = 'korte';
|
||||
const contentTwo = 'szilva';
|
||||
const [sketchOne, sketchTwo] = await Promise.all([
|
||||
sketchesService.createNewSketch(sketchFolderName, contentOne),
|
||||
sketchesService.createNewSketch(sketchFolderName, contentTwo),
|
||||
]);
|
||||
toDispose.push(disposeSketch(sketchOne, sketchTwo));
|
||||
const [mainFileContentOne, mainFileContentTwo] = await Promise.all([
|
||||
mainFileContentOf(sketchOne),
|
||||
mainFileContentOf(sketchTwo),
|
||||
]);
|
||||
expect(mainFileContentOne).to.be.equal(contentOne);
|
||||
expect(mainFileContentTwo).to.be.equal(contentTwo);
|
||||
|
||||
await sketchesService.copy(sketchOne, { destinationUri: sketchTwo.uri });
|
||||
const [mainFileContentOneAfterCopy, mainFileContentTwoAfterCopy] =
|
||||
await Promise.all([
|
||||
mainFileContentOf(sketchOne),
|
||||
mainFileContentOf(sketchTwo),
|
||||
]);
|
||||
expect(mainFileContentOneAfterCopy).to.be.equal(contentOne);
|
||||
expect(mainFileContentTwoAfterCopy).to.be.equal(contentOne);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function disposeSketch(...sketch: Sketch[]): Disposable {
|
||||
return new DisposableCollection(
|
||||
...sketch
|
||||
.map(({ uri }) => FileUri.fsPath(uri))
|
||||
.map((path) =>
|
||||
Disposable.create(() => rimrafSync(path, { maxBusyTries: 5 }))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
async function mainFileContentOf(sketch: Sketch): Promise<string> {
|
||||
return fs.readFile(FileUri.fsPath(sketch.mainFileUri), {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
}
|
||||
|
||||
async function start(
|
||||
container: Container,
|
||||
toDispose: DisposableCollection
|
||||
): Promise<void> {
|
||||
await startDaemon(container, toDispose);
|
||||
}
|
||||
|
||||
function createContainer(): Container {
|
||||
return createBaseContainer();
|
||||
}
|
||||
@@ -4,7 +4,10 @@ import {
|
||||
CommandService,
|
||||
} from '@theia/core/lib/common/command';
|
||||
import { bindContributionProvider } from '@theia/core/lib/common/contribution-provider';
|
||||
import { Disposable } from '@theia/core/lib/common/disposable';
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import { EnvVariablesServer as TheiaEnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
import { ILogger, Loggable } from '@theia/core/lib/common/logger';
|
||||
import { LogLevel } from '@theia/core/lib/common/logger-protocol';
|
||||
@@ -289,18 +292,23 @@ export function createBaseContainer(
|
||||
|
||||
export async function startDaemon(
|
||||
container: Container,
|
||||
toDispose: Disposable[],
|
||||
toDispose: DisposableCollection,
|
||||
startCustomizations?: (
|
||||
container: Container,
|
||||
toDispose: Disposable[]
|
||||
toDispose: DisposableCollection
|
||||
) => Promise<void>
|
||||
): Promise<void> {
|
||||
const daemon = container.get<ArduinoDaemonImpl>(ArduinoDaemonImpl);
|
||||
const configService = container.get<ConfigServiceImpl>(ConfigServiceImpl);
|
||||
const coreClientProvider =
|
||||
container.get<CoreClientProvider>(CoreClientProvider);
|
||||
toDispose.push(Disposable.create(() => daemon.stop()));
|
||||
configService.onStart();
|
||||
daemon.onStart();
|
||||
await waitForEvent(daemon.onDaemonStarted, 10_000);
|
||||
await Promise.all([
|
||||
waitForEvent(daemon.onDaemonStarted, 10_000),
|
||||
coreClientProvider.client,
|
||||
]);
|
||||
if (startCustomizations) {
|
||||
await startCustomizations(container, toDispose);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user