Skip to content

Commit fb79593

Browse files
Austin O'NeilAustin O'Neil
Austin O'Neil
authored and
Austin O'Neil
committed
refactored host platforms to use a factory-type pattern for easier testing
1 parent 68335af commit fb79593

File tree

11 files changed

+155
-134
lines changed

11 files changed

+155
-134
lines changed

src/arduino/arduinoSettings.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
import * as os from "os";
55
import * as path from "path";
66
import * as WinReg from "winreg";
7+
import { IHostPlatform } from "../common/i-host-platform";
78
import * as util from "../common/util";
89

9-
import { resolveArduinoPath } from "../common/platform";
10-
1110
import { VscodeSettings } from "./vscodeSettings";
1211

1312
export interface IArduinoSettings {
@@ -43,7 +42,7 @@ export class ArduinoSettings implements IArduinoSettings {
4342

4443
private _defaultTimestampFormat: string;
4544

46-
public constructor() {
45+
public constructor(private _platform: IHostPlatform) {
4746
}
4847

4948
public async initialize() {
@@ -231,7 +230,7 @@ export class ArduinoSettings implements IArduinoSettings {
231230
const configValue = VscodeSettings.getInstance().arduinoPath;
232231
if (!configValue || !configValue.trim()) {
233232
// 2 & 3. Resolve arduino path from system environment variables and usual software installation directory.
234-
this._arduinoPath = await Promise.resolve(resolveArduinoPath(useArduinoCli));
233+
this._arduinoPath = await Promise.resolve(this._platform.resolveArduinoPath(useArduinoCli));
235234
} else {
236235
this._arduinoPath = configValue;
237236
}

src/arduinoActivator.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ExampleProvider } from "./arduino/exampleProvider";
1111
import { LibraryManager } from "./arduino/libraryManager";
1212
import { ProgrammerManager } from "./arduino/programmerManager";
1313
import ArduinoContext from "./arduinoContext";
14+
import { hostPlatform } from "./common/platform";
1415
import { DeviceContext } from "./deviceContext";
1516

1617
class ArduinoActivator {
@@ -22,7 +23,7 @@ class ArduinoActivator {
2223
}
2324

2425
this._initializePromise = (async () => {
25-
const arduinoSettings = new ArduinoSettings();
26+
const arduinoSettings = new ArduinoSettings(hostPlatform());
2627
await arduinoSettings.initialize();
2728
const arduinoApp = new ArduinoApp(arduinoSettings);
2829
await arduinoApp.initialize();

src/arduinoContext.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import { ArduinoApp } from "./arduino/arduino";
55
import { BoardManager } from "./arduino/boardManager";
6+
import { hostPlatform } from "./common/platform";
67
import { DebuggerManager } from "./debug/debuggerManager";
78
import { DeviceContext } from "./deviceContext";
89

@@ -34,7 +35,8 @@ class ArduinoContext {
3435
this._debuggerManager = new DebuggerManager(
3536
DeviceContext.getInstance().extensionPath,
3637
this.arduinoApp.settings,
37-
this.boardManager);
38+
this.boardManager,
39+
hostPlatform());
3840
this._debuggerManager.initialize();
3941
}
4042
return this._debuggerManager;

src/common/i-host-platform.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export interface IHostPlatform {
2+
resolveArduinoPath(useArduinoCli?: boolean): string | Promise<string> | undefined;
3+
validateArduinoPath(arduinoPath: string, useArduinoCli?: boolean);
4+
findFile(fileName: string, cwd: string): string;
5+
getExecutableFileName(fileName: string): string;
6+
}

src/common/platform.ts

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,19 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
3-
import * as path from "path";
3+
import { IHostPlatform } from "./i-host-platform";
4+
import { DarwinPlatform } from "./sys/darwin";
5+
import { LinuxPlatform } from "./sys/linux";
6+
import { WindowsPlatform } from "./sys/win32";
47

5-
export const isWindows = (process.platform === "win32");
6-
export const isMacintosh = (process.platform === "darwin");
7-
export const isLinux = (process.platform === "linux");
8-
9-
/*tslint:disable:no-var-requires*/
10-
const internalSysLib = require(path.join(__dirname, `sys/${process.platform}`));
11-
12-
export function resolveArduinoPath(useArduinoCli = false): string {
13-
return internalSysLib.resolveArduinoPath(useArduinoCli);
14-
}
15-
16-
export function validateArduinoPath(arduinoPath: string, useArduinoCli = false): boolean {
17-
return internalSysLib.validateArduinoPath(arduinoPath, useArduinoCli);
18-
}
19-
20-
export function findFile(fileName: string, cwd: string): string {
21-
return internalSysLib.findFile(fileName, cwd);
22-
}
23-
24-
export function getExecutableFileName(fileName: string): string {
25-
if (isWindows) {
26-
return `${fileName}.exe`;
8+
export function hostPlatform(): IHostPlatform {
9+
switch(process.platform) {
10+
case 'win32':
11+
return new WindowsPlatform();
12+
case 'darwin':
13+
return new DarwinPlatform();
14+
case 'linux':
15+
return new LinuxPlatform();
16+
default:
17+
return new LinuxPlatform();
2718
}
28-
return fileName;
2919
}

src/common/sys/darwin.ts

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,52 +3,59 @@
33

44
import * as childProcess from "child_process";
55
import * as path from "path";
6+
import { IHostPlatform } from "../i-host-platform";
67
import { directoryExistsSync, fileExistsSync, resolveMacArduinoAppPath } from "../util";
78

8-
export function resolveArduinoPath(useArduinoCli = false): string {
9-
let result;
9+
export class DarwinPlatform implements IHostPlatform {
10+
public resolveArduinoPath(useArduinoCli?: boolean) {
11+
let result;
1012

11-
const appName = useArduinoCli ? 'arduino-cli' : 'Arduino.app'
12-
const defaultCommonPaths = [path.join(process.env.HOME, "Applications"), "/Applications", '/opt/homebrew/bin'];
13-
for (const scanPath of defaultCommonPaths) {
14-
if (directoryExistsSync(path.join(scanPath, appName))) {
15-
result = scanPath;
16-
break;
13+
const appName = useArduinoCli ? 'arduino-cli' : 'Arduino.app'
14+
const defaultCommonPaths = [path.join(process.env.HOME, "Applications"), "/Applications", '/opt/homebrew/bin'];
15+
for (const scanPath of defaultCommonPaths) {
16+
if (directoryExistsSync(path.join(scanPath, appName))) {
17+
result = scanPath;
18+
break;
19+
}
1720
}
21+
if(!result) {
22+
result = this.which(appName);
23+
}
24+
return result || "";
1825
}
19-
if(!result) {
20-
result = which(appName);
26+
27+
public validateArduinoPath(arduinoPath: string, useArduinoCli?: boolean) {
28+
let fileExists = fileExistsSync(path.join(resolveMacArduinoAppPath(arduinoPath, useArduinoCli), useArduinoCli
29+
? "arduino-cli" :
30+
"/Contents/MacOS/Arduino"));
31+
return fileExists;
2132
}
22-
return result || "";
23-
}
2433

25-
export function validateArduinoPath(arduinoPath: string, useArduinoCli = false): boolean {
26-
let fileExists = fileExistsSync(path.join(resolveMacArduinoAppPath(arduinoPath, useArduinoCli), useArduinoCli
27-
? "arduino-cli" :
28-
"/Contents/MacOS/Arduino"));
29-
return fileExists;
30-
}
34+
public findFile(fileName: string, cwd: string): string {
35+
let pathString;
36+
try {
37+
pathString = childProcess.execSync(`find ${cwd} -name ${fileName} -type f`, { encoding: "utf8" }).split("\n");
3138

32-
function which(programName: string) {
33-
let location = childProcess.execSync(`which ${programName}`, { encoding: "utf8" }).trim();
34-
if(location === `${programName} not found`) {
35-
return undefined;
39+
if (pathString && pathString[0] && fileExistsSync(pathString[0].trim())) {
40+
pathString = path.normalize(pathString[0].trim());
41+
} else {
42+
pathString = null;
43+
}
44+
} catch (ex) {
45+
// Ignore the errors.
46+
}
47+
return pathString;
3648
}
37-
return location.substring(0, location.length - programName.length);
38-
}
3949

40-
export function findFile(fileName: string, cwd: string): string {
41-
let pathString;
42-
try {
43-
pathString = childProcess.execSync(`find ${cwd} -name ${fileName} -type f`, { encoding: "utf8" }).split("\n");
50+
public getExecutableFileName(fileName: string): string {
51+
return fileName;
52+
}
4453

45-
if (pathString && pathString[0] && fileExistsSync(pathString[0].trim())) {
46-
pathString = path.normalize(pathString[0].trim());
47-
} else {
48-
pathString = null;
54+
private which(programName: string) {
55+
let location = childProcess.execSync(`which ${programName}`, { encoding: "utf8" }).trim();
56+
if(location === `${programName} not found`) {
57+
return undefined;
4958
}
50-
} catch (ex) {
51-
// Ignore the errors.
59+
return location.substring(0, location.length - programName.length);
5260
}
53-
return pathString;
5461
}

src/common/sys/linux.ts

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,46 @@
33

44
import * as childProcess from "child_process";
55
import * as path from "path";
6+
import { IHostPlatform } from "../i-host-platform";
67
import { fileExistsSync } from "../util";
78

8-
export function resolveArduinoPath(): string {
9-
let pathString;
10-
try {
11-
pathString = childProcess.execSync("readlink -f $(which arduino)", { encoding: "utf8" });
12-
pathString = path.resolve(pathString).trim();
13-
if (fileExistsSync(pathString)) {
14-
pathString = path.dirname(path.resolve(pathString));
9+
export class LinuxPlatform implements IHostPlatform {
10+
public resolveArduinoPath(useArduinoCli?: boolean): string {
11+
let pathString;
12+
try {
13+
pathString = childProcess.execSync("readlink -f $(which arduino)", { encoding: "utf8" });
14+
pathString = path.resolve(pathString).trim();
15+
if (fileExistsSync(pathString)) {
16+
pathString = path.dirname(path.resolve(pathString));
17+
}
18+
} catch (ex) {
19+
// Ignore the errors.
1520
}
16-
} catch (ex) {
17-
// Ignore the errors.
21+
22+
return pathString || "";
1823
}
1924

20-
return pathString || "";
21-
}
22-
23-
export function validateArduinoPath(arduinoPath: string, useArduinoCli = false): boolean {
24-
return fileExistsSync(path.join(arduinoPath, useArduinoCli ? "arduino-cli" : "arduino"));
25-
}
25+
public validateArduinoPath(arduinoPath: string, useArduinoCli?: boolean) {
26+
return fileExistsSync(path.join(arduinoPath, useArduinoCli ? "arduino-cli" : "arduino"));
27+
}
2628

27-
export function findFile(fileName: string, cwd: string): string {
28-
let pathString;
29-
try {
30-
pathString = childProcess.execSync(`find ${cwd} -name ${fileName} -type f`, { encoding: "utf8" }).split("\n");
29+
public findFile(fileName: string, cwd: string): string {
30+
let pathString;
31+
try {
32+
pathString = childProcess.execSync(`find ${cwd} -name ${fileName} -type f`, { encoding: "utf8" }).split("\n");
3133

32-
if (pathString && pathString[0] && fileExistsSync(pathString[0].trim())) {
33-
pathString = path.normalize(pathString[0].trim());
34-
} else {
35-
pathString = null;
34+
if (pathString && pathString[0] && fileExistsSync(pathString[0].trim())) {
35+
pathString = path.normalize(pathString[0].trim());
36+
} else {
37+
pathString = null;
38+
}
39+
} catch (ex) {
40+
// Ignore the errors.
3641
}
37-
} catch (ex) {
38-
// Ignore the errors.
42+
return pathString;
43+
}
44+
45+
public getExecutableFileName(fileName: string): string {
46+
return fileName;
3947
}
40-
return pathString;
4148
}

src/common/sys/win32.ts

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,50 @@
44
import * as childProcess from "child_process";
55
import * as path from "path";
66
import * as WinReg from "winreg";
7+
import { IHostPlatform } from "../i-host-platform";
78
import { directoryExistsSync, fileExistsSync, getRegistryValues } from "../util";
89

9-
export async function resolveArduinoPath() {
10-
// eslint-disable-next-line no-prototype-builtins
11-
const isWin64 = process.arch === "x64" || process.env.hasOwnProperty("PROCESSOR_ARCHITEW6432");
12-
let pathString = await getRegistryValues(WinReg.HKLM,
13-
isWin64 ? "\\SOFTWARE\\WOW6432Node\\Arduino" : "\\SOFTWARE\\Arduino",
14-
"Install_Dir");
15-
if (directoryExistsSync(pathString)) {
16-
return pathString;
17-
}
18-
try {
19-
pathString = childProcess.execSync("where arduino", { encoding: "utf8" });
20-
pathString = path.resolve(pathString).trim();
21-
if (fileExistsSync(pathString)) {
22-
pathString = path.dirname(path.resolve(pathString));
10+
export class WindowsPlatform implements IHostPlatform {
11+
public async resolveArduinoPath(useArduinoCli?: boolean): Promise<string> {
12+
// eslint-disable-next-line no-prototype-builtins
13+
const isWin64 = process.arch === "x64" || process.env.hasOwnProperty("PROCESSOR_ARCHITEW6432");
14+
let pathString = await getRegistryValues(WinReg.HKLM,
15+
isWin64 ? "\\SOFTWARE\\WOW6432Node\\Arduino" : "\\SOFTWARE\\Arduino",
16+
"Install_Dir");
17+
if (directoryExistsSync(pathString)) {
18+
return pathString;
19+
}
20+
try {
21+
pathString = childProcess.execSync("where arduino", { encoding: "utf8" });
22+
pathString = path.resolve(pathString).trim();
23+
if (fileExistsSync(pathString)) {
24+
pathString = path.dirname(path.resolve(pathString));
25+
}
26+
} catch (error) {
27+
// when "where arduino"" execution fails, the childProcess.execSync will throw error, just ignore it
2328
}
24-
} catch (error) {
25-
// when "where arduino"" execution fails, the childProcess.execSync will throw error, just ignore it
26-
}
27-
28-
return pathString;
29-
}
3029

31-
export function validateArduinoPath(arduinoPath: string, useArduinoCli = false): boolean {
32-
return fileExistsSync(path.join(arduinoPath, useArduinoCli ? "arduino-cli.exe" : "arduino_debug.exe"));
30+
return pathString;
31+
}
3332

34-
}
33+
public validateArduinoPath(arduinoPath: string, useArduinoCli?: boolean) {
34+
return fileExistsSync(path.join(arduinoPath, useArduinoCli ? "arduino-cli.exe" : "arduino_debug.exe"));
35+
}
3536

36-
export function findFile(fileName: string, cwd: string): string {
37-
let result;
38-
try {
39-
const pathString = childProcess.execSync(`dir ${fileName} /S /B`, { encoding: "utf8", cwd }).split("\n");
40-
if (pathString && pathString[0] && fileExistsSync(pathString[0].trim())) {
41-
result = path.normalize(pathString[0].trim());
37+
public findFile(fileName: string, cwd: string): string {
38+
let result;
39+
try {
40+
const pathString = childProcess.execSync(`dir ${fileName} /S /B`, { encoding: "utf8", cwd }).split("\n");
41+
if (pathString && pathString[0] && fileExistsSync(pathString[0].trim())) {
42+
result = path.normalize(pathString[0].trim());
43+
}
44+
} catch (ex) {
45+
// Ignore the errors.
4246
}
43-
} catch (ex) {
44-
// Ignore the errors.
47+
return result;
48+
}
49+
50+
public getExecutableFileName(fileName: string): string {
51+
return `${fileName}.exe`;
4552
}
46-
return result;
4753
}

0 commit comments

Comments
 (0)