diff --git a/docs/settings.md b/docs/settings.md
index a9a6a63550..c3b61a0a8c 100644
--- a/docs/settings.md
+++ b/docs/settings.md
@@ -345,12 +345,10 @@ Specifies Lint tool name.
Allowed Options: `staticcheck`, `golint`, `golangci-lint`, `revive`
Default: `"staticcheck"`
-### `go.logging.level`
+### `go.logging.level (deprecated)`
-The logging level the extension logs at, defaults to 'error'
-Allowed Options: `off`, `error`, `info`, `verbose`
+This setting is deprecated. Use 'Developer: Set Log Level...' command to control logging level instead.
-Default: `"error"`
### `go.playground`
The flags configured here will be passed through to command `goplay`
diff --git a/extension/package.json b/extension/package.json
index 0d50596b98..b07dd96f98 100644
--- a/extension/package.json
+++ b/extension/package.json
@@ -1514,14 +1514,7 @@
},
"go.logging.level": {
"type": "string",
- "default": "error",
- "enum": [
- "off",
- "error",
- "info",
- "verbose"
- ],
- "description": "The logging level the extension logs at, defaults to 'error'",
+ "deprecationMessage": "This setting is deprecated. Use 'Developer: Set Log Level...' command to control logging level instead.",
"scope": "machine-overridable"
},
"go.toolsManagement.go": {
diff --git a/extension/src/goDebugFactory.ts b/extension/src/goDebugFactory.ts
index e57169eeb5..85542b0c2d 100644
--- a/extension/src/goDebugFactory.ts
+++ b/extension/src/goDebugFactory.ts
@@ -12,7 +12,6 @@ import getPort = require('get-port');
import path = require('path');
import * as fs from 'fs';
import * as net from 'net';
-import { Logger, logVerbose, TimestampedLogger } from './goLogging';
import { DebugProtocol } from 'vscode-debugprotocol';
import { getWorkspaceFolderPath } from './util';
import { getEnvPath, getBinPathFromEnvVar } from './utils/pathUtils';
@@ -20,7 +19,7 @@ import { GoExtensionContext } from './context';
import { createRegisterCommand } from './commands';
export function activate(ctx: vscode.ExtensionContext, goCtx: GoExtensionContext) {
- const debugOutputChannel = vscode.window.createOutputChannel('Go Debug');
+ const debugOutputChannel = vscode.window.createOutputChannel('Go Debug', { log: true });
ctx.subscriptions.push(debugOutputChannel);
const factory = new GoDebugAdapterDescriptorFactory(debugOutputChannel);
@@ -40,7 +39,7 @@ export function activate(ctx: vscode.ExtensionContext, goCtx: GoExtensionContext
}
class GoDebugAdapterDescriptorFactory implements vscode.DebugAdapterDescriptorFactory {
- constructor(private outputChannel?: vscode.OutputChannel) {}
+ constructor(private outputChannel: vscode.LogOutputChannel) {}
public createDebugAdapterDescriptor(
session: vscode.DebugSession,
@@ -59,11 +58,11 @@ class GoDebugAdapterDescriptorFactory implements vscode.DebugAdapterDescriptorFa
private async createDebugAdapterDescriptorDlvDap(
configuration: vscode.DebugConfiguration
): Promise> {
- const logger = new TimestampedLogger(configuration.trace, this.outputChannel);
- logger.debug(`Config: ${JSON.stringify(configuration)}\n`);
+ const logger = this.outputChannel;
+ logger.debug(`Config: ${JSON.stringify(configuration)}`);
if (configuration.port) {
const host = configuration.host ?? '127.0.0.1';
- logger.info(`Connecting to DAP server at ${host}:${configuration.port}\n`);
+ logger.info(`Connecting to DAP server at ${host}:${configuration.port}`);
return new vscode.DebugAdapterServer(configuration.port, host);
}
const d = new DelveDAPOutputAdapter(configuration, logger);
@@ -72,28 +71,24 @@ class GoDebugAdapterDescriptorFactory implements vscode.DebugAdapterDescriptorFa
}
class GoDebugAdapterTrackerFactory implements vscode.DebugAdapterTrackerFactory {
- constructor(private outputChannel: vscode.OutputChannel) {}
+ constructor(private outputChannel: vscode.LogOutputChannel) {}
createDebugAdapterTracker(session: vscode.DebugSession) {
- const level = session.configuration?.trace;
- if (!level || level === 'off') {
- return null;
- }
- const logger = new TimestampedLogger(session.configuration?.trace || 'off', this.outputChannel);
+ const logger = this.outputChannel;
let requestsSent = 0;
let responsesReceived = 0;
return {
onWillStartSession: () =>
- logger.debug(`session ${session.id} will start with ${JSON.stringify(session.configuration)}\n`),
+ logger.debug(`session ${session.id} will start with ${JSON.stringify(session.configuration)}`),
onWillReceiveMessage: (message: any) => {
- logger.trace(`client -> ${JSON.stringify(message)}\n`);
+ logger.trace(`client -> ${JSON.stringify(message)}`);
requestsSent++;
},
onDidSendMessage: (message: any) => {
- logger.trace(`client <- ${JSON.stringify(message)}\n`);
+ logger.trace(`client <- ${JSON.stringify(message)}`);
responsesReceived++;
},
- onError: (error: Error) => logger.error(`error: ${error}\n`),
+ onError: (error: Error) => logger.error(`error: ${error}`),
onWillStopSession: () => {
if (
session.configuration.debugAdapter === 'dlv-dap' &&
@@ -109,7 +104,7 @@ class GoDebugAdapterTrackerFactory implements vscode.DebugAdapterTrackerFactory
logger.debug(`session ${session.id} will stop\n`);
},
onExit: (code: number | undefined, signal: string | undefined) =>
- logger.info(`debug adapter exited: (code: ${code}, signal: ${signal})\n`)
+ logger.info(`debug adapter exited: (code: ${code}, signal: ${signal})`)
};
}
@@ -118,6 +113,8 @@ class GoDebugAdapterTrackerFactory implements vscode.DebugAdapterTrackerFactory
const TWO_CRLF = '\r\n\r\n';
+type ILogger = Pick;
+
// Proxies DebugProtocolMessage exchanges between VSCode and a remote
// process or server connected through a duplex stream, after its
// start method is called.
@@ -126,10 +123,10 @@ export class ProxyDebugAdapter implements vscode.DebugAdapter {
// connection from/to server (= dlv dap)
private readable?: stream.Readable;
private writable?: stream.Writable;
- protected logger?: Logger;
+ protected logger: ILogger;
private terminated = false;
- constructor(logger: Logger) {
+ constructor(logger: ILogger) {
this.logger = logger;
this.onDidSendMessage = this.messageEmitter.event;
}
@@ -240,7 +237,7 @@ export class ProxyDebugAdapter implements vscode.DebugAdapter {
// VSCode and a dlv dap process spawned and managed by this adapter.
// It turns the process's stdout/stderrr into OutputEvent.
export class DelveDAPOutputAdapter extends ProxyDebugAdapter {
- constructor(private configuration: vscode.DebugConfiguration, logger: Logger) {
+ constructor(private configuration: vscode.DebugConfiguration, logger: ILogger) {
super(logger);
}
@@ -252,7 +249,7 @@ export class DelveDAPOutputAdapter extends ProxyDebugAdapter {
protected sendMessageToClient(message: vscode.DebugProtocolMessage) {
const m = message as any;
if (m.type === 'request') {
- logVerbose(`do not forward reverse request: dropping ${JSON.stringify(m)}`);
+ this.logger.debug(`do not forward reverse request: dropping ${JSON.stringify(m)}`);
return;
}
@@ -262,7 +259,7 @@ export class DelveDAPOutputAdapter extends ProxyDebugAdapter {
protected async sendMessageToServer(message: vscode.DebugProtocolMessage): Promise {
const m = message as any;
if (m.type === 'response') {
- logVerbose(`do not forward reverse request response: dropping ${JSON.stringify(m)}`);
+ this.logger.debug(`do not forward reverse request response: dropping ${JSON.stringify(m)}`);
return;
}
@@ -353,7 +350,7 @@ export class DelveDAPOutputAdapter extends ProxyDebugAdapter {
} catch (err) {
return { connected: false, reason: err };
}
- this.logger?.debug(`Running dlv dap server: pid=${this.dlvDapServer?.pid}\n`);
+ this.logger?.debug(`Running dlv dap server: pid=${this.dlvDapServer?.pid}`);
return { connected: true };
}
@@ -372,7 +369,7 @@ export class DelveDAPOutputAdapter extends ProxyDebugAdapter {
// may not appear in the DEBUG CONSOLE. For easier debugging, log
// the messages through the logger that prints to Go Debug output
// channel.
- this.logger?.info(msg);
+ this.logger?.trace(msg);
};
// If a port has been specified, assume there is an already
@@ -437,7 +434,7 @@ export class DelveDAPOutputAdapter extends ProxyDebugAdapter {
try {
const port = await getPort();
- const rendezvousServerPromise = waitForDAPServer(port, 30_000);
+ const rendezvousServerPromise = waitForDAPServer(port, 30_000, this.logger);
dlvArgs.push(`--client-addr=:${port}`);
@@ -470,7 +467,7 @@ function getSudo(): string | null {
return sudoPath;
}
-function waitForDAPServer(port: number, timeoutMs: number): Promise {
+function waitForDAPServer(port: number, timeoutMs: number, logger: ILogger): Promise {
return new Promise((resolve, reject) => {
// eslint-disable-next-line prefer-const
let s: net.Server | undefined;
@@ -482,7 +479,7 @@ function waitForDAPServer(port: number, timeoutMs: number): Promise
}, timeoutMs);
s = net.createServer({ pauseOnConnect: true }, (socket) => {
- logVerbose(
+ logger.debug(
`connected: ${port} (remote: ${socket.remoteAddress}:${socket.remotePort} local: ${socket.localAddress}:${socket.localPort})`
);
clearTimeout(timeoutToken);
@@ -491,7 +488,7 @@ function waitForDAPServer(port: number, timeoutMs: number): Promise
resolve(socket);
});
s.on('error', (err) => {
- logVerbose(`connection error ${err}`);
+ logger.error(`connection error ${err}`);
reject(err);
});
s.maxConnections = 1;
diff --git a/extension/src/goEnv.ts b/extension/src/goEnv.ts
index f416fe8b8f..138dcac760 100644
--- a/extension/src/goEnv.ts
+++ b/extension/src/goEnv.ts
@@ -8,9 +8,9 @@
import vscode = require('vscode');
import { getGoConfig } from './config';
import { getCurrentGoPath, getToolsGopath, resolvePath, substituteEnv } from './util';
-import { logVerbose } from './goLogging';
import { dirExists } from './utils/pathUtils';
import { getFromGlobalState, updateGlobalState } from './stateUtils';
+import { outputChannel } from './goStatus';
// toolInstallationEnvironment returns the environment in which tools should
// be installed. It always returns a new object.
@@ -67,7 +67,7 @@ export function toolExecutionEnvironment(uri?: vscode.Uri, addProcessEnv = true)
// Remove json flag (-json or --json=) from GOFLAGS because it will effect to result format of the execution
if (env['GOFLAGS'] && env['GOFLAGS'].includes('-json')) {
env['GOFLAGS'] = env['GOFLAGS'].replace(/(^|\s+)-?-json[^\s]*/g, '');
- logVerbose(`removed -json from GOFLAGS: ${env['GOFLAGS']}`);
+ outputChannel.debug(`removed -json from GOFLAGS: ${env['GOFLAGS']}`);
}
return env;
}
@@ -127,7 +127,9 @@ export async function setGOROOTEnvVar(configGOROOT: string) {
});
}
- logVerbose(`setting GOROOT = ${goroot} (old value: ${currentGOROOT}) because "go.goroot": "${configGOROOT}"`);
+ outputChannel.debug(
+ `setting GOROOT = ${goroot} (old value: ${currentGOROOT}) because "go.goroot": "${configGOROOT}"`
+ );
if (goroot) {
process.env['GOROOT'] = goroot;
} else {
diff --git a/extension/src/goEnvironmentStatus.ts b/extension/src/goEnvironmentStatus.ts
index 1db7758ece..e9d1778afb 100644
--- a/extension/src/goEnvironmentStatus.ts
+++ b/extension/src/goEnvironmentStatus.ts
@@ -15,7 +15,6 @@ import path = require('path');
import { promisify } from 'util';
import { getGoConfig, extensionInfo } from './config';
import { toolInstallationEnvironment } from './goEnv';
-import { logVerbose } from './goLogging';
import { addGoStatus, goEnvStatusbarItem, outputChannel, removeGoStatus } from './goStatus';
import { getFromGlobalState, getFromWorkspaceState, updateGlobalState, updateWorkspaceState } from './stateUtils';
import { getBinPath, getCheckForToolsUpdatesConfig, getGoVersion, GoVersion } from './util';
@@ -295,7 +294,7 @@ export function addGoRuntimeBaseToPATH(newGoRuntimeBase: string) {
}
const pathEnvVar = pathEnvVarName();
if (!pathEnvVar) {
- logVerbose("couldn't find PATH property in process.env");
+ outputChannel.debug("couldn't find PATH property in process.env");
return;
}
@@ -304,7 +303,7 @@ export function addGoRuntimeBaseToPATH(newGoRuntimeBase: string) {
defaultPathEnv = process.env[pathEnvVar];
}
- logVerbose(`addGoRuntimeBase(${newGoRuntimeBase}) when PATH=${defaultPathEnv}`);
+ outputChannel.debug(`addGoRuntimeBase(${newGoRuntimeBase}) when PATH=${defaultPathEnv}`);
// calling this multiple times will override the previous value.
// environmentVariableCollection.clear();
@@ -354,7 +353,7 @@ export function clearGoRuntimeBaseFromPATH() {
}
const pathEnvVar = pathEnvVarName();
if (!pathEnvVar) {
- logVerbose("couldn't find PATH property in process.env");
+ outputChannel.debug("couldn't find PATH property in process.env");
return;
}
environmentVariableCollection?.delete(pathEnvVar);
diff --git a/extension/src/goInstallTools.ts b/extension/src/goInstallTools.ts
index edbb526d81..356cb6c9a0 100644
--- a/extension/src/goInstallTools.ts
+++ b/extension/src/goInstallTools.ts
@@ -16,7 +16,6 @@ import { ConfigurationTarget } from 'vscode';
import { extensionInfo, getGoConfig, getGoplsConfig } from './config';
import { toolExecutionEnvironment, toolInstallationEnvironment } from './goEnv';
import { addGoRuntimeBaseToPATH, clearGoRuntimeBaseFromPATH } from './goEnvironmentStatus';
-import { logVerbose, logError } from './goLogging';
import { GoExtensionContext } from './context';
import { addGoStatus, initGoStatusBar, outputChannel, removeGoStatus } from './goStatus';
import { containsTool, getConfiguredTools, getImportPathWithVersion, getTool, Tool, ToolAtVersion } from './goTools';
@@ -109,22 +108,19 @@ export async function installAllTools(updateExistingToolsOnly = false) {
async function getGoForInstall(goVersion: GoVersion, silent?: boolean): Promise {
const configured = getGoConfig().get('toolsManagement.go');
if (!configured) {
- return goVersion;
+ return goVersion; // use the default.
}
try {
const go = await getGoVersion(configured);
if (go) return go;
} catch (e) {
- logError(`getGoForInstall failed to run 'go version' with the configured go for tool install: ${e}`);
- } finally {
if (!silent) {
outputChannel.error(
- `Ignoring misconfigured 'go.toolsManagement.go' (${configured}). Provide a valid path to the Go command.`
+ `failed to run "go version" with "${configured}". Provide a valid path to the Go binary`
);
}
}
-
return goVersion;
}
@@ -152,7 +148,10 @@ export async function installTools(
return [];
}
const { silent, skipRestartGopls } = options || {};
- outputChannel.appendLine('Installing tools...');
+ if (!silent) {
+ outputChannel.show();
+ }
+ outputChannel.clear();
const goForInstall = await getGoForInstall(goVersion);
@@ -219,9 +218,9 @@ export async function installTools(
if (silent) {
outputChannel.show();
}
- outputChannel.error(failures.length + ' tools failed to install.\n');
+ outputChannel.appendLine(failures.length + ' tools failed to install.\n');
for (const failure of failures) {
- outputChannel.error(`${failure.tool.name}: ${failure.reason} `);
+ outputChannel.appendLine(`${failure.tool.name}: ${failure.reason} `);
}
}
if (missing.some((tool) => tool.isImportant)) {
@@ -277,8 +276,8 @@ async function installToolWithGo(
const toolInstallPath = getBinPath(tool.name);
outputChannel.appendLine(`Installing ${importPath} (${toolInstallPath}) SUCCEEDED`);
} catch (e) {
- outputChannel.error(`Installing ${importPath} FAILED`);
- outputChannel.error(`${JSON.stringify(e, null, 1)}`);
+ outputChannel.appendLine(`Installing ${importPath} FAILED`);
+ outputChannel.appendLine(`${JSON.stringify(e, null, 1)}`);
return `failed to install ${tool.name}(${importPath}): ${e}`;
}
}
@@ -293,7 +292,7 @@ async function installToolWithGoInstall(goVersion: GoVersion, env: NodeJS.Dict
const { binPath, why } = getBinPathWithExplanation('go', false);
const goRuntimePath = binPath;
- logVerbose(`updateGoVarsFromConfig: found 'go' in ${goRuntimePath}`);
+ outputChannel.debug(`updateGoVarsFromConfig: found 'go' in ${goRuntimePath}`);
if (!goRuntimePath || !path.isAbsolute(goRuntimePath)) {
// getBinPath returns the absolute path to the tool if it exists.
// Otherwise, it may return the tool name (e.g. 'go').
@@ -481,12 +480,13 @@ export function updateGoVarsFromConfig(goCtx: GoExtensionContext): Promise
if (stderr) {
// 'go env' may output warnings about potential misconfiguration.
// Show the messages to users but keep processing the stdout.
- outputChannel.error(`'${goRuntimePath} env': ${stderr}`);
+ outputChannel.append(`'${goRuntimePath} env': ${stderr}`);
outputChannel.show();
}
- logVerbose(`${goRuntimePath} env ...:\n${stdout}`);
+ outputChannel.trace(`${goRuntimePath} env ...:\n${stdout}`);
const envOutput = JSON.parse(stdout);
if (envOutput.GOROOT && envOutput.GOROOT.trim()) {
+ outputChannel.debug('setCurrentGOROOT:', envOutput.GOROOT);
setCurrentGoRoot(envOutput.GOROOT.trim());
delete envOutput.GOROOT;
}
diff --git a/extension/src/goLogging.ts b/extension/src/goLogging.ts
deleted file mode 100644
index 9e6af42fb4..0000000000
--- a/extension/src/goLogging.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-/*---------------------------------------------------------
- * Copyright 2020 The Go Authors. All rights reserved.
- * Licensed under the MIT License. See LICENSE in the project root for license information.
- *--------------------------------------------------------*/
-
-'use strict';
-
-type LogLevel = 'off' | 'error' | 'warn' | 'info' | 'trace' | 'verbose';
-
-const levels: { [key in LogLevel]: number } = {
- off: -1,
- error: 0,
- warn: 1,
- info: 2,
- trace: 3,
- verbose: 4
-};
-
-function levelToString(level: number) {
- switch (level) {
- case levels.error:
- return 'Error';
- case levels.warn:
- return 'Warn';
- case levels.info:
- return 'Info';
- case levels.trace:
- return 'Trace';
- case levels.verbose:
- return 'Verbose';
- }
- return '';
-}
-
-interface outputChannelType {
- appendLine: (msg: string) => void;
-}
-// Logger outputs messages of the specified log levels to the vscode output channel or console.
-export class Logger {
- protected minLevel: number;
-
- constructor(levelName: LogLevel, private outputChannel?: outputChannelType, private logToConsole?: boolean) {
- this.minLevel = levels[levelName] || levels.error;
- }
-
- protected log(msgLevel: number, msg: string) {
- if (this.minLevel < 0) {
- return; // logging is off.
- }
- if (this.minLevel < msgLevel) {
- return;
- }
- this.outputChannel?.appendLine(msg);
- if (this.logToConsole) console.log(msg);
- }
-
- error(msg: string) {
- this.log(levels.error, msg);
- }
- warn(msg: string) {
- this.log(levels.warn, msg);
- }
- info(msg: string) {
- this.log(levels.info, msg);
- }
- trace(msg: string) {
- this.log(levels.trace, msg);
- }
- debug(msg: string) {
- this.log(levels.verbose, msg);
- }
-}
-
-// TimestampedLogger is a logger that prepends the timestamp to every log message.
-export class TimestampedLogger extends Logger {
- log(msgLevel: number, msg: string) {
- const ts = new Date();
- const hhmmss = ts.toLocaleTimeString([], {
- hour: '2-digit',
- minute: '2-digit',
- second: '2-digit',
- hour12: false
- });
- const msec = ts.getMilliseconds();
- super.log(msgLevel, `[${levelToString(msgLevel)} - ${hhmmss}.${msec}] ${msg}`);
- }
-}
-
-export interface LogConfig {
- level: LogLevel;
-}
-
-let defaultLogger: Logger;
-
-export function setLogConfig(cfg: LogConfig) {
- defaultLogger = new Logger(cfg.level, undefined, true);
-}
-
-export function logError(msg: string) {
- defaultLogger?.error(msg);
-}
-
-export function logWarn(msg: string) {
- defaultLogger?.warn(msg);
-}
-
-export function logInfo(msg: string) {
- defaultLogger?.info(msg);
-}
-
-export function logTrace(msg: string) {
- defaultLogger?.trace(msg);
-}
-
-export function logVerbose(msg: string) {
- defaultLogger?.debug(msg);
-}
diff --git a/extension/src/goMain.ts b/extension/src/goMain.ts
index 8048157dab..b1221e4bbb 100644
--- a/extension/src/goMain.ts
+++ b/extension/src/goMain.ts
@@ -41,12 +41,11 @@ import {
} from './goInstallTools';
import { RestartReason, showServerOutputChannel, watchLanguageServerConfiguration } from './language/goLanguageServer';
import { lintCode } from './goLint';
-import { setLogConfig } from './goLogging';
import { GO_MODE } from './goMode';
import { GO111MODULE, goModInit } from './goModules';
import { playgroundCommand } from './goPlayground';
import { GoRunTestCodeLensProvider } from './goRunTestCodelens';
-import { disposeGoStatusBar, expandGoStatusBar, updateGoStatusBar } from './goStatus';
+import { disposeGoStatusBar, expandGoStatusBar, outputChannel, updateGoStatusBar } from './goStatus';
import { vetCode } from './goVet';
import {
@@ -89,8 +88,6 @@ export async function activate(ctx: vscode.ExtensionContext): Promise {
+ console.log(msg);
+ },
+ debug: (msg: string) => {
+ console.log(msg);
+ },
+ info: (msg: string) => {
+ console.log(msg);
+ },
+ error: (msg: string) => {
+ console.error(msg);
+ }
+ };
+ super(config, logger);
}
private static TWO_CRLF = '\r\n\r\n';
diff --git a/extension/test/unit/logger.test.ts b/extension/test/unit/logger.test.ts
deleted file mode 100644
index 763bfaa55f..0000000000
--- a/extension/test/unit/logger.test.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/*---------------------------------------------------------
- * Copyright 2021 The Go Authors. All rights reserved.
- * Licensed under the MIT License. See LICENSE in the project root for license information.
- *--------------------------------------------------------*/
-
-import assert from 'assert';
-import sinon = require('sinon');
-import { Logger } from '../../src/goLogging';
-
-suite('Logger Tests', () => {
- let sandbox: sinon.SinonSandbox;
-
- setup(() => {
- sandbox = sinon.createSandbox();
- });
- teardown(() => {
- sandbox.restore();
- });
-
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- function runTest(level: any, want: number) {
- const appendLine = sandbox.fake();
- const logger = new Logger(level, { appendLine });
- logger.error('error');
- logger.warn('warn');
- logger.info('info');
- logger.debug('debug');
- logger.trace('trace');
- assert.strictEqual(appendLine.callCount, want, `called ${appendLine.callCount} times, want ${want}`);
- }
- test('logger level = off', () => runTest('off', 0));
- test('logger level = error', () => runTest('error', 1));
- test('logger level = warning', () => runTest('warn', 2));
- test('logger level = info', () => runTest('info', 3));
- test('logger level = trace', () => runTest('trace', 4));
- test('logger level = verbose', () => runTest('verbose', 5));
- test('logger level = undefined', () => runTest(undefined, 1));
- test('logger level = ""', () => runTest('', 1));
- test('logger level = object', () => runTest({}, 1));
- test('logger level = number', () => runTest(10, 1));
-});