зеркало из https://github.com/microsoft/TACO.git
Merge pull request #91 from Microsoft/add_additional_tests_for_install_req_telemetry
Added full coverage tests for the telemetry code in AndroidSdkInstaller
This commit is contained in:
Коммит
c3c1e28ed4
|
@ -35,7 +35,8 @@
|
|||
"should": "4.3.0",
|
||||
"taco-tests-utils": "file:../taco-tests-utils",
|
||||
"typescript": "~1.6.2",
|
||||
"mockery": "~1.4.0"
|
||||
"mockery": "~1.4.0",
|
||||
"mock-fs": "^3.4.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
/// <reference path="../../typings/mockery.d.ts"/>
|
||||
/// <reference path="../../typings/should.d.ts"/>
|
||||
/// <reference path="../../typings/telemetryFakes.d.ts"/>
|
||||
/// <reference path="../../typings/mock-fs.d.ts"/>
|
||||
/// <reference path="../../typings/nodeFakes.d.ts"/>
|
||||
/// <reference path="../../typings/lodash.d.ts"/>
|
||||
/// <reference path="../../typings/tacoTestsUtils.d.ts"/>
|
||||
|
||||
"use strict";
|
||||
|
||||
|
@ -23,6 +27,8 @@ var shouldModule = require("should");
|
|||
import installerProtocol = require("../elevatedInstallerProtocol");
|
||||
import ILogger = installerProtocol.ILogger;
|
||||
import mockery = require("mockery");
|
||||
import mockFs = require("mock-fs");
|
||||
import path = require("path");
|
||||
import Q = require("q");
|
||||
import tacoTestsUtils = require("taco-tests-utils");
|
||||
import _ = require("lodash");
|
||||
|
@ -50,64 +56,95 @@ class FakeLogger implements ILogger {
|
|||
}
|
||||
|
||||
describe("AndroidSdkInstaller telemetry", () => {
|
||||
// Parameters for AndroidSdkInstaller
|
||||
var steps: DependencyInstallerInterfaces.IStepsDeclaration;
|
||||
var installerInfo: DependencyInstallerInterfaces.IInstallerData = {
|
||||
installSource: "",
|
||||
sha1: "",
|
||||
bytes: 0,
|
||||
installDestination: "",
|
||||
steps: steps
|
||||
};
|
||||
var softwareVersion: string = "";
|
||||
var installTo: string = "C:\\Program Files (x86)\\Android"; // Default installation directory in windows
|
||||
|
||||
// Mocks used by the tests
|
||||
var mockPath: typeof path;
|
||||
var fakeTelemetryHelper: TacoTestsUtils.TelemetryFakes.Helper;
|
||||
var fakeProcess: nodeFakes.Process;
|
||||
var androidSdkInstallerClass: any;
|
||||
var childProcessModule: nodeFakes.ChildProcessModule;
|
||||
|
||||
before(() => {
|
||||
// We tell mockery to replace "require()" with our own custom mock objects
|
||||
mockery.enable({ useCleanCache: true, warnOnUnregistered: false });
|
||||
|
||||
fakeProcess = new nodeFakes.Process()
|
||||
.fakeDeterministicHrtime();
|
||||
|
||||
var fakeProcessUtilsModule = { ProcessUtils: fakeProcess.buildProcessUtils() };
|
||||
|
||||
mockery.registerMock("./processUtils", fakeProcessUtilsModule); // TelemetryHelper loads ./processUtils
|
||||
var tacoUtils: typeof TacoUtility = require("taco-utils");
|
||||
tacoUtils.Telemetry.init("TACO/dependencyInstaller", "1.2.3", false);
|
||||
|
||||
// Register mocks. child_process and taco-utils mocks needs to be registered before
|
||||
// AndroidSdkInstaller is required for the mocking to work
|
||||
childProcessModule = new nodeFakes.ChildProcessModule().fakeAllExecCallsEndingWithErrors();
|
||||
mockery.registerMock("child_process", childProcessModule);
|
||||
|
||||
// Reload taco-tests-utils but now with the fake processUtils loaded, so the fake telemetry will use the fake process
|
||||
var tacoTestsUtilsWithMocks: typeof tacoTestsUtils = require("taco-tests-utils");
|
||||
|
||||
fakeTelemetryHelper = new tacoTestsUtilsWithMocks.TelemetryFakes.Helper();
|
||||
var tacoUtilsWithFakes = _.extend({}, tacoUtils, { TelemetryHelper: fakeTelemetryHelper, HasFakes: true },
|
||||
fakeProcessUtilsModule);
|
||||
mockery.registerMock("taco-utils", tacoUtilsWithFakes); // AndroidSdkInstaller loads taco-utils
|
||||
|
||||
// We need to mock path if we want to run windows tests on a mac, so it'll use ; as path delimiter
|
||||
mockPath = <typeof path> _.extend({}, path);
|
||||
mockery.registerMock("path", mockPath); // installerUtils uses path.delimiter, and it breaks the Windows tests on mac if not
|
||||
|
||||
// We require the AndroidSdkInstaller file, which will use all the mocked dependencies
|
||||
androidSdkInstallerClass = require("../installers/androidSdkInstaller");
|
||||
});
|
||||
|
||||
after(() => {
|
||||
// Clean up and revert everything back to normal
|
||||
mockery.deregisterAll();
|
||||
mockery.disable();
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fakeTelemetryHelper.clear(); // So we'll only get the new events in each scenario
|
||||
steps = { download: false, install: false, updateVariables: false, postInstall: false }; // We reset all the steps to false
|
||||
fakeProcess.clearEnv(); // Reset environment variables, given that we modify some of them in the tests
|
||||
});
|
||||
|
||||
function telemetryGeneratedShouldBe(expectedTelemetry: TacoUtility.ICommandTelemetryProperties[],
|
||||
expectedMessagePattern: RegExp, done: MochaDone): Q.Promise<any> {
|
||||
var androidSdkInstaller = new androidSdkInstallerClass(installerInfo, softwareVersion, installTo,
|
||||
new FakeLogger(), steps);
|
||||
|
||||
return androidSdkInstaller.run()
|
||||
.then(() => Q.reject(new Error("Should have gotten a rejection in this test")), (error: Error) => {
|
||||
return fakeTelemetryHelper.getAllSentEvents().then((allSentEvents: TelemetryEvent[]) => {
|
||||
// We check the message first, because some coding defects can make the tests end in unexpected states
|
||||
error.message.should.match(expectedMessagePattern);
|
||||
|
||||
// Then we validate the telemetry
|
||||
allSentEvents.should.eql(expectedTelemetry);
|
||||
});
|
||||
}).done(done, done);
|
||||
}
|
||||
|
||||
describe("updateVariablesDarwin", () => {
|
||||
it("generates telemetry if there is an error on the update command", (done: MochaDone) => {
|
||||
var fakeProcessUtilsModule = {
|
||||
ProcessUtils: new nodeFakes.Process().fakeMacOS()
|
||||
.fakeDeterministicHrtime().buildProcessUtils()
|
||||
};
|
||||
mockery.registerMock("./processUtils", fakeProcessUtilsModule); // TelemetryHelper loads ./processUtils
|
||||
var tacoUtils: typeof TacoUtility = require("taco-utils");
|
||||
tacoUtils.Telemetry.init("TACO/dependencyInstaller", "1.2.3", false);
|
||||
fakeProcess.fakeMacOS();
|
||||
steps.updateVariables = true; // We only test this step on this test
|
||||
|
||||
// Register mocks. child_process and taco-utils mocks needs to be register before
|
||||
// AndroidSdkInstaller is required for the mock to work
|
||||
mockery.registerMock("child_process", new nodeFakes.ChildProcessModule().fakeAllExecCallsEndingWithErrors());
|
||||
|
||||
// Reload taco-tests-utils but now with the fake processUtils loaded, so the fake telemetry will use the fake process
|
||||
var tacoTestsUtilsWithMocks: typeof tacoTestsUtils = require("taco-tests-utils");
|
||||
|
||||
var fakeTelemetryHelper: TacoTestsUtils.TelemetryFakes.Helper = new tacoTestsUtilsWithMocks.TelemetryFakes.Helper();
|
||||
var tacoUtilsWithFakes = _.extend({}, tacoUtils, { TelemetryHelper: fakeTelemetryHelper, HasFakes: true },
|
||||
fakeProcessUtilsModule);
|
||||
mockery.registerMock("taco-utils", tacoUtilsWithFakes); // AndroidSdkInstaller loads taco-utils
|
||||
|
||||
// We require the AndroidSdkInstaller file, which will use all the mocked dependencies
|
||||
var androidSdkInstallerClass = require("../installers/androidSdkInstaller");
|
||||
|
||||
var steps: DependencyInstallerInterfaces.IStepsDeclaration = {
|
||||
download: false,
|
||||
install: false,
|
||||
updateVariables: true, // We only test this step on this test
|
||||
postInstall: false
|
||||
};
|
||||
|
||||
var installerInfo: DependencyInstallerInterfaces.IInstallerData = {
|
||||
installSource: "",
|
||||
sha1: "",
|
||||
bytes: 0,
|
||||
installDestination: "",
|
||||
steps: steps
|
||||
};
|
||||
|
||||
var softwareVersion: string = "";
|
||||
var installTo: string = "";
|
||||
|
||||
var androidSdkInstaller = new androidSdkInstallerClass(installerInfo, softwareVersion, installTo,
|
||||
new FakeLogger(), steps);
|
||||
|
||||
var expectedTelemetry = [
|
||||
var expectedTelemetry: TacoUtility.ICommandTelemetryProperties[] = [
|
||||
{
|
||||
"initialStep.time": { isPii: false, value: "2000" },
|
||||
step: { isPii: false, value: "initialStep" }
|
||||
|
@ -121,12 +158,193 @@ describe("AndroidSdkInstaller telemetry", () => {
|
|||
}
|
||||
];
|
||||
|
||||
return androidSdkInstaller.run()
|
||||
.then(() => Q.reject(new Error("Should have gotten a rejection in this test")), () => {
|
||||
return fakeTelemetryHelper.getAllSentEvents().then((allSentEvents: TelemetryEvent[]) => {
|
||||
allSentEvents.should.eql(expectedTelemetry);
|
||||
});
|
||||
}).done(done, done);
|
||||
return telemetryGeneratedShouldBe(expectedTelemetry, /Error while executing/, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe("installation", () => {
|
||||
it("generates telemetry error if there is no install location", (done: MochaDone) => {
|
||||
fakeProcess.fakeMacOS();
|
||||
steps.install = true; // We only test this step on this test
|
||||
installTo = ""; // We don't have an install location
|
||||
|
||||
var expectedTelemetry: TacoUtility.ICommandTelemetryProperties[] = [
|
||||
{
|
||||
"initialStep.time": { isPii: false, value: "2000" },
|
||||
step: { isPii: false, value: "initialStep" }
|
||||
},
|
||||
{
|
||||
"error.description": { isPii: false, value: "NeedInstallDestination on installDefault" },
|
||||
"install.time": { isPii: false, value: "2000" },
|
||||
lastStepExecuted: { isPii: false, value: "install" },
|
||||
step: { isPii: false, value: "install" },
|
||||
time: { isPii: false, value: "3000" }
|
||||
}];
|
||||
|
||||
return telemetryGeneratedShouldBe(expectedTelemetry, /NeedInstallDestination/, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe("post-installation in mac", () => {
|
||||
it("generates telemetry error if we can't give executable permissions to the android executable", (done: MochaDone) => {
|
||||
fakeProcess.fakeMacOS();
|
||||
|
||||
steps.postInstall = true; // We only test this step on this test
|
||||
steps.updateVariables = true; // We need this step because post-install uses this.androidHomeValue populated in this step
|
||||
|
||||
// child_process.exec will succeed while setting the path, and fail while giving permissions
|
||||
childProcessModule.fakeUsingCommandToDetermineResult((command: string) => /export PATH=/.test(command),
|
||||
(command: string) => /chmod a\+x/.test(command));
|
||||
|
||||
var filePath = path.join(fakeProcess.env.HOME, ".bash_profile");
|
||||
var files: mockFs.Config = {};
|
||||
files[filePath] = "";
|
||||
mockFs(files);
|
||||
|
||||
var expectedTelemetry: TacoUtility.ICommandTelemetryProperties[] = [
|
||||
{
|
||||
"initialStep.time": { isPii: false, value: "2000" },
|
||||
step: { isPii: false, value: "initialStep" }
|
||||
},
|
||||
{
|
||||
step: { isPii: false, value: "updateVariables" },
|
||||
"updateVariables.time": { isPii: false, value: "1000" }
|
||||
},
|
||||
{
|
||||
"error.description": {
|
||||
isPii: false,
|
||||
value: "ErrorOnChildProcess on addExecutePermission"
|
||||
},
|
||||
lastStepExecuted: { isPii: false, value: "postInstall" },
|
||||
"postInstall.time": { isPii: false, value: "2000" },
|
||||
step: { isPii: false, value: "postInstall" },
|
||||
time: { isPii: false, value: "5000" }
|
||||
}
|
||||
];
|
||||
|
||||
return telemetryGeneratedShouldBe(expectedTelemetry, /Error while executing/g, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe("post-installation in windows", () => {
|
||||
beforeEach(() => {
|
||||
fakeProcess.fakeWindows();
|
||||
mockPath.delimiter = ";"; // Installer utils uses this, and the path logic breaks in Mac if we don't mock it
|
||||
|
||||
steps.postInstall = true; // We only test this step on this test
|
||||
steps.updateVariables = true; // We need this step because post-install uses this.androidHomeValue populated in this step
|
||||
|
||||
// Set ANDROID_HOME so updateVariables won't try to re-set it
|
||||
installTo = "C:\\Program Files (x86)\\Android"; // We don't have an install location
|
||||
var androidHomePath = fakeProcess.env.ANDROID_HOME = mockPath.join(installTo, "android-sdk-windows");
|
||||
|
||||
// Set android paths in the PATH so we won't have to add them
|
||||
var platformToolsPath = mockPath.join(androidHomePath, "platform-tools");
|
||||
var androidToolsPath = mockPath.join(androidHomePath, "tools");
|
||||
fakeProcess.env.PATH = platformToolsPath + mockPath.delimiter + androidToolsPath;
|
||||
});
|
||||
|
||||
it("installAndroidPackages generates telemetry error if the command used generates errors in stderr", (done: MochaDone) => {
|
||||
var spawnErrorMessage = "Couldn't find the Android update command executable";
|
||||
childProcessModule.mockSpawn.setDefault(
|
||||
childProcessModule.mockSpawn.simple(/*exitCode*/ 0, /*stdout*/ "", /*stderr*/ spawnErrorMessage));
|
||||
|
||||
var expectedTelemetry: TacoUtility.ICommandTelemetryProperties[] = [
|
||||
{
|
||||
"initialStep.time": { isPii: false, value: "2000" },
|
||||
step: { isPii: false, value: "initialStep" }
|
||||
},
|
||||
{
|
||||
step: { isPii: false, value: "updateVariables" },
|
||||
"updateVariables.time": { isPii: false, value: "1000" }
|
||||
},
|
||||
{
|
||||
"error.code": { isPii: false, value: "0" },
|
||||
"error.description": {
|
||||
isPii: false,
|
||||
value: "ErrorOnExitOfChildProcess on postInstallDefault"
|
||||
},
|
||||
"error.message": {
|
||||
isPii: true,
|
||||
value: "Couldn't find the Android update command executable"
|
||||
},
|
||||
lastStepExecuted: { isPii: false, value: "postInstall" },
|
||||
"postInstall.time": { isPii: false, value: "2000" },
|
||||
step: { isPii: false, value: "postInstall" },
|
||||
time: { isPii: false, value: "5000" }
|
||||
}
|
||||
];
|
||||
|
||||
return telemetryGeneratedShouldBe(expectedTelemetry, new RegExp(spawnErrorMessage), done);
|
||||
});
|
||||
|
||||
it("installAndroidPackages generates telemetry error if the command used is invalid", (done: MochaDone) => {
|
||||
var spawnErrorMessage = "The command is not recognized by the system";
|
||||
childProcessModule.mockSpawn.setDefault(function (callback: Function): void {
|
||||
/* Warning: The "this" in the next line is the one passed by mockSpawn library. For that
|
||||
to work this context needs to be a JavaScript lambda function. Do not convert this to
|
||||
an arrow function, or this will break because of the change in semantics. */
|
||||
this.emit("error", new Error(spawnErrorMessage)); // invokes childProcess.on('error')
|
||||
setTimeout(() => callback(8), 0); // Then the child process ends with an arbitrary error code of 8
|
||||
});
|
||||
|
||||
var expectedTelemetry: TacoUtility.ICommandTelemetryProperties[] = [
|
||||
{
|
||||
"initialStep.time": { isPii: false, value: "2000" },
|
||||
step: { isPii: false, value: "initialStep" }
|
||||
},
|
||||
{
|
||||
step: { isPii: false, value: "updateVariables" },
|
||||
"updateVariables.time": { isPii: false, value: "1000" }
|
||||
},
|
||||
{
|
||||
"error.description": {
|
||||
isPii: false,
|
||||
value: "ErrorOnChildProcess on postInstallDefault"
|
||||
},
|
||||
lastStepExecuted: { isPii: false, value: "postInstall" },
|
||||
"postInstall.time": { isPii: false, value: "2000" },
|
||||
step: { isPii: false, value: "postInstall" },
|
||||
time: { isPii: false, value: "5000" }
|
||||
}
|
||||
];
|
||||
|
||||
return telemetryGeneratedShouldBe(expectedTelemetry, new RegExp(spawnErrorMessage), done);
|
||||
});
|
||||
|
||||
it("killAdb generates telemetry error if the command used is invalid", (done: MochaDone) => {
|
||||
// First call to install the android packages will succeed
|
||||
childProcessModule.mockSpawn.sequence.add(childProcessModule.mockSpawn.simple(/*exitCode*/ 0, /*stdout*/ "", /*stderr*/ ""));
|
||||
|
||||
// Second call to kill adb will fail
|
||||
var spawnErrorMessage = "The kill adb command is not recognized by the system";
|
||||
childProcessModule.mockSpawn.sequence.add(function (callback: Function): void {
|
||||
/* Warning: The "this" in the next line is the one passed by mockSpawn library. For that
|
||||
to work this context needs to be a JavaScript lambda function. Do not convert this to
|
||||
an arrow function, or this will break because of the change in semantics. */
|
||||
this.emit("error", new Error(spawnErrorMessage)); // invokes childProcess.on('error')
|
||||
setTimeout(() => callback(8), 0); // Then the child process ends with an arbitrary error code of 8
|
||||
});
|
||||
|
||||
var expectedTelemetry: TacoUtility.ICommandTelemetryProperties[] = [
|
||||
{
|
||||
"initialStep.time": { isPii: false, value: "2000" },
|
||||
step: { isPii: false, value: "initialStep" }
|
||||
},
|
||||
{
|
||||
step: { isPii: false, value: "updateVariables" },
|
||||
"updateVariables.time": { isPii: false, value: "1000" }
|
||||
},
|
||||
{
|
||||
"error.description": { isPii: false, value: "ErrorOnKillingAdb in killAdb" },
|
||||
lastStepExecuted: { isPii: false, value: "postInstall" },
|
||||
"postInstall.time": { isPii: false, value: "2000" },
|
||||
step: { isPii: false, value: "postInstall" },
|
||||
time: { isPii: false, value: "5000" }
|
||||
}
|
||||
];
|
||||
|
||||
return telemetryGeneratedShouldBe(expectedTelemetry, new RegExp(spawnErrorMessage), done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -41,7 +41,7 @@ module InstallerUtils {
|
|||
}
|
||||
|
||||
class InstallerUtils {
|
||||
private static pathName: string = os.platform() === "win32" ? "Path" : "PATH";
|
||||
private static PATH_NAME: string = "PATH"; // *nix uses uppercase and it's case sensitive. Windows is case insensitive
|
||||
/**
|
||||
* Verifies if the specified file is valid by comparing its sha1 signature and its size in bytes with the provided expectedSha1 and expectedBytes.
|
||||
*
|
||||
|
@ -119,8 +119,8 @@ class InstallerUtils {
|
|||
public static promptUser(message: string): Q.Promise<string> {
|
||||
var deferred: Q.Deferred<any> = Q.defer<any>();
|
||||
var rl: readline.ReadLine = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
input: tacoUtils.ProcessUtils.getProcess().stdin,
|
||||
output: tacoUtils.ProcessUtils.getProcess().stdout
|
||||
});
|
||||
|
||||
rl.question(message, function (answer: string): void {
|
||||
|
@ -143,9 +143,9 @@ class InstallerUtils {
|
|||
* @return {Q.Promise<boolean>} A promise resolved with a boolean indicating whether the specified environment variable must be set
|
||||
*/
|
||||
public static mustSetSystemVariable(name: string, value: string, logger: ILogger): Q.Promise<boolean> {
|
||||
if (!process.env[name]) {
|
||||
if (!tacoUtils.ProcessUtils.getProcess().env[name]) {
|
||||
return Q.resolve(true);
|
||||
} else if (path.resolve(utils.expandEnvironmentVariables(process.env[name])) === path.resolve(utils.expandEnvironmentVariables(value))) {
|
||||
} else if (path.resolve(utils.expandEnvironmentVariables(tacoUtils.ProcessUtils.getProcess().env[name])) === path.resolve(utils.expandEnvironmentVariables(value))) {
|
||||
// If this environment variable is already defined, but it is already set to what we need, we don't need to set it again
|
||||
return Q.resolve(false);
|
||||
}
|
||||
|
@ -197,7 +197,7 @@ class InstallerUtils {
|
|||
*
|
||||
* @return {boolean} A boolean set to true if the Path system variable already contains the specified value in one of its segments
|
||||
*/
|
||||
public static pathContains(valueToCheck: string, pathValue: string = process.env[InstallerUtils.pathName]): boolean {
|
||||
public static pathContains(valueToCheck: string, pathValue: string = tacoUtils.ProcessUtils.getProcess().env[InstallerUtils.PATH_NAME]): boolean {
|
||||
if (!pathValue) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ class InstallerUtilsWin32 {
|
|||
*
|
||||
* @param {string} name The name of the environment variable to set
|
||||
* @param {string} value The desired value for the specified environment variable
|
||||
* @param {InstallerProtocol.ILogger} logger The logger for the current process
|
||||
* @param {InstallerProtocol.ILogger} logger The logger for the process
|
||||
*
|
||||
* @return {Q.Promise<any>} A promise resolved with an empty object if the operation succeeds, or rejected with the appropriate error if not
|
||||
*/
|
||||
|
@ -52,8 +52,8 @@ class InstallerUtilsWin32 {
|
|||
* @return {Q.Promise<any>} A promise resolved with an empty object if the operation succeeds, or rejected with the appropriate error if not
|
||||
*/
|
||||
public static addToPathIfNeededWin32(addToPath: string[]): Q.Promise<any> {
|
||||
var pathName: string = "Path";
|
||||
var pathValue: string = process.env[pathName];
|
||||
var pathName: string = "PATH"; // Windows is case-insensitive. We use uppercase to be more compatible with *nix systems
|
||||
var pathValue: string = tacoUtils.ProcessUtils.getProcess().env[pathName];
|
||||
|
||||
addToPath.forEach(function (value: string): void {
|
||||
if (!installerUtils.pathContains(value)) {
|
||||
|
@ -61,7 +61,7 @@ class InstallerUtilsWin32 {
|
|||
}
|
||||
});
|
||||
|
||||
if (pathValue === process.env[pathName]) {
|
||||
if (pathValue === tacoUtils.ProcessUtils.getProcess().env[pathName]) {
|
||||
return Q.resolve({});
|
||||
}
|
||||
|
||||
|
@ -78,13 +78,13 @@ class InstallerUtilsWin32 {
|
|||
* @return {Q.Promise<any>} A promise resolved with an empty object if the operation succeeds, or rejected with the appropriate error if not
|
||||
*/
|
||||
public static setEnvironmentVariableWin32(name: string, value: string): Q.Promise<any> {
|
||||
if (process.platform !== "win32") {
|
||||
if (tacoUtils.ProcessUtils.getProcess().platform !== "win32") {
|
||||
// No-op for platforms other than win32
|
||||
return Q.resolve({});
|
||||
}
|
||||
|
||||
// Set variable for this running process
|
||||
process.env[name] = value;
|
||||
tacoUtils.ProcessUtils.getProcess().env[name] = value;
|
||||
|
||||
// Set variable for the system
|
||||
var scriptPath: string = path.resolve(__dirname, "setSystemVariable.ps1");
|
||||
|
|
|
@ -17,9 +17,18 @@
|
|||
|
||||
import _ = require("lodash");
|
||||
|
||||
/* tslint:disable:no-var-requires */
|
||||
/* We don't have a .d.ts file for mock-spawn */
|
||||
var mockSpawnModule = require("mock-spawn");
|
||||
/* tslint:enable:no-var-requires */
|
||||
|
||||
export module NodeFakes {
|
||||
export interface IEnvironmentVariables {
|
||||
// We should add here the environment variables that we use in TACO. Remember to also add them in the .d.ts file
|
||||
HOME?: string;
|
||||
ANDROID_HOME?: string;
|
||||
PATH?: string;
|
||||
TACO_UNIT_TEST?: string;
|
||||
}
|
||||
|
||||
export type IChildProcess = NodeJSChildProcess.ChildProcess;
|
||||
|
@ -30,6 +39,8 @@ export module NodeFakes {
|
|||
|
||||
export type ExecSecondArgument = IExecOptions | Callback;
|
||||
|
||||
export type CommandTester = (command: string) => boolean;
|
||||
|
||||
export type ExecFileOptions = {
|
||||
cwd?: string; stdio?: any; customFds?: any; env?: any;
|
||||
encoding?: string; timeout?: number; maxBuffer?: string; killSignal?: string;
|
||||
|
@ -68,7 +79,7 @@ export module NodeFakes {
|
|||
public env: IEnvironmentVariables;
|
||||
|
||||
constructor() {
|
||||
this.env = _.extend({}, process.env);
|
||||
this.clearEnv();
|
||||
}
|
||||
|
||||
public asProcess(): NodeJS.Process {
|
||||
|
@ -109,7 +120,12 @@ export module NodeFakes {
|
|||
public fakeWindows(): Process {
|
||||
var username = "my_username";
|
||||
this.asProcess().platform = "win32";
|
||||
// this.asProcess().env.HOME = "C:\\Users\\" + username;
|
||||
this.asProcess().env.HOME = "C:\\Users\\" + username;
|
||||
return this;
|
||||
}
|
||||
|
||||
public clearEnv(): Process {
|
||||
this.env = { TACO_UNIT_TEST: "true" };
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -175,27 +191,42 @@ export module NodeFakes {
|
|||
}
|
||||
|
||||
export class ChildProcessModule /* implements typeof NodeJSChildProcess*/ {
|
||||
/** Methods to configure the fake process **/
|
||||
/** mock spawn variable. Docs at https://www.npmjs.com/package/mock-spawn **/
|
||||
public mockSpawn: any = mockSpawnModule();
|
||||
|
||||
// We simulate that all calls to exect are succesfull
|
||||
public fakeAllExecCallsSucceed(): ChildProcessModule {
|
||||
this.exec = (command: string, optionsOrCallback: ExecSecondArgument, callback: Callback = null): IChildProcess => {
|
||||
return this.callCallback(command, optionsOrCallback, callback, true);
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
// We simulate that all calls to exect end with an error
|
||||
public fakeAllExecCallsEndingWithErrors(): ChildProcessModule {
|
||||
this.exec = (command: string, optionsOrCallback: ExecSecondArgument, callback: Callback = null): IChildProcess => {
|
||||
return this.callCallback(command, optionsOrCallback, callback, false);
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
public fakeUsingCommandToDetermineResult(successFilter: CommandTester, failureFilter: CommandTester): ChildProcessModule {
|
||||
this.exec = (command: string, optionsOrCallback: ExecSecondArgument, callback?: Callback): IChildProcess => {
|
||||
var realCallback = <Callback> (callback || optionsOrCallback);
|
||||
var isSuccess: boolean = successFilter(command);
|
||||
var isFailure: boolean = failureFilter(command);
|
||||
|
||||
// We call the callback in an async way
|
||||
setTimeout(() => {
|
||||
realCallback(new Error("Error while executing " + command), /*stdout*/ new Buffer(""), /*stderr*/ new Buffer(""));
|
||||
}, 0);
|
||||
|
||||
return new ChildProcess();
|
||||
if (isSuccess !== isFailure) {
|
||||
return this.callCallback(command, optionsOrCallback, callback, isSuccess);
|
||||
} else {
|
||||
throw new Error("A command should be exactly a success, or a failure. It can't be both nor either.\n"
|
||||
+ "Command: " + command + " isSuccess: " + isSuccess + " isFailure: " + isFailure);
|
||||
}
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
public spawn(command: string, args?: string[], options?: SpawnOptions): IChildProcess {
|
||||
/* TODO: We should consider integrating this method with this library https://www.npmjs.com/package/mock-spawn
|
||||
if we need to mock spawn */
|
||||
throw this.notImplementedError();
|
||||
return this.mockSpawn(command, args, options);
|
||||
}
|
||||
|
||||
public exec(command: string, options: IExecOptions, callback: Callback): IChildProcess;
|
||||
|
@ -215,6 +246,17 @@ export module NodeFakes {
|
|||
throw this.notImplementedError();
|
||||
}
|
||||
|
||||
private callCallback(command: string, optionsOrCallback: ExecSecondArgument,
|
||||
callback: Callback, wasSuccessful: boolean): ChildProcess {
|
||||
var realCallback = <Callback> (callback || optionsOrCallback);
|
||||
// We call the callback in an async way
|
||||
var error = wasSuccessful ? null : new Error("Error while executing " + command);
|
||||
setTimeout(() => {
|
||||
realCallback(error, /*stdout*/ new Buffer(""), /*stderr*/ new Buffer(""));
|
||||
}, 0);
|
||||
return new ChildProcess();
|
||||
}
|
||||
|
||||
private notImplementedError(): Error {
|
||||
return Error("This method hasn't been implemented for this test");
|
||||
}
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
"main": "tacoTestsUtils.js",
|
||||
"dependencies": {
|
||||
"lodash": "^3.10.1",
|
||||
"q": "~1.4.1"
|
||||
"q": "~1.4.1",
|
||||
"mock-spawn": "~0.2.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "~1.6.2"
|
||||
|
|
|
@ -66,5 +66,9 @@ export module TelemetryFakes {
|
|||
() => generator.time(null, () => codeGeneratingTelemetry(generator)),
|
||||
() => generator.sendAndNotify()); // After
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this.telemetryGenerators = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,9 @@ import fs = require ("fs");
|
|||
import path = require ("path");
|
||||
|
||||
import argsHelper = require ("./argsHelper");
|
||||
import processUtils = require("./processUtils");
|
||||
import resourceSet = require("./resourceSet");
|
||||
import tacoGlobalConfig = require ("./tacoGlobalConfig");
|
||||
import resourceSet = require ("./resourceSet");
|
||||
|
||||
import ArgsHelper = argsHelper.ArgsHelper;
|
||||
import TacoGlobalConfig = tacoGlobalConfig.TacoGlobalConfig;
|
||||
|
@ -95,7 +96,7 @@ module TacoUtility {
|
|||
var args: string[] = ArgsHelper.getOptionalArgsArrayFromFunctionCall(arguments, 1);
|
||||
var result: string = this.getStringForLocale(this.bestLanguageMatch(this.getCurrentLocale()), id, args);
|
||||
|
||||
if (result && process.env["TACO_UNIT_TEST"]) {
|
||||
if (result && processUtils.ProcessUtils.getProcess().env["TACO_UNIT_TEST"]) {
|
||||
// Mock out resources for consistency in unit tests, but only if they exist
|
||||
return id;
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
// Type definitions for mock-fs 2.5.0
|
||||
// Project: https://github.com/tschaub/mock-fs
|
||||
// Definitions by: Wim Looman <https://github.com/Nemo157>
|
||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||
|
||||
/// <reference path="./node.d.ts" />
|
||||
|
||||
declare module "mock-fs" {
|
||||
import fs = require("fs");
|
||||
|
||||
function mock(config?: mock.Config): void;
|
||||
|
||||
module mock {
|
||||
function file(config: FileConfig): File;
|
||||
function directory(config: DirectoryConfig): Directory;
|
||||
function symlink(config: SymlinkConfig): Symlink;
|
||||
|
||||
function restore(): void;
|
||||
|
||||
function fs(config?: Config): typeof fs;
|
||||
|
||||
interface Config {
|
||||
[path: string]: string | Buffer | File | Directory | Symlink | Config;
|
||||
}
|
||||
|
||||
interface CommonConfig {
|
||||
mode?: number;
|
||||
uid?: number;
|
||||
git?: number;
|
||||
atime?: Date;
|
||||
ctime?: Date;
|
||||
mtime?: Date;
|
||||
}
|
||||
|
||||
interface FileConfig extends CommonConfig {
|
||||
content: string | Buffer;
|
||||
}
|
||||
interface DirectoryConfig extends CommonConfig {
|
||||
items: Config;
|
||||
}
|
||||
interface SymlinkConfig extends CommonConfig {
|
||||
path: string;
|
||||
}
|
||||
|
||||
class File { private _file: any; }
|
||||
class Directory { private _directory: any; }
|
||||
class Symlink { private _symlink: any; }
|
||||
}
|
||||
|
||||
export = mock;
|
||||
}
|
|
@ -11,11 +11,16 @@
|
|||
declare module TacoTestsUtils {
|
||||
export module NodeFakes {
|
||||
interface IEnvironmentVariables {
|
||||
// We should add here the environment variables that we use in TACO. Remember to also add them in the .ts file
|
||||
HOME?: string;
|
||||
ANDROID_HOME?: string;
|
||||
PATH?: string;
|
||||
TACO_UNIT_TEST?: string;
|
||||
}
|
||||
type IChildProcess = NodeJSChildProcess.ChildProcess;
|
||||
type IExecOptions = NodeJSChildProcess.IExecOptions;
|
||||
type Callback = (error: Error, stdout: Buffer, stderr: Buffer) => void;
|
||||
type CommandTester = (command: string) => boolean;
|
||||
type ExecSecondArgument = IExecOptions | Callback;
|
||||
type ExecFileOptions = {
|
||||
cwd?: string;
|
||||
|
@ -83,14 +88,16 @@ declare module TacoTestsUtils {
|
|||
getProcess(): NodeJS.Process;
|
||||
}
|
||||
class Process {
|
||||
env: IEnvironmentVariables;
|
||||
constructor();
|
||||
asProcess(): NodeJS.Process;
|
||||
buildProcessUtils(): IProcessUtils;
|
||||
public env: IEnvironmentVariables;
|
||||
public constructor();
|
||||
public asProcess(): NodeJS.Process;
|
||||
public buildProcessUtils(): IProcessUtils;
|
||||
|
||||
/** Methods to configure the fake process **/
|
||||
fakeDeterministicHrtime(): Process;
|
||||
fakeMacOS(): Process;
|
||||
fakeWindows(): Process;
|
||||
public fakeDeterministicHrtime(): Process;
|
||||
public fakeMacOS(): Process;
|
||||
public fakeWindows(): Process;
|
||||
public clearEnv(): Process;
|
||||
}
|
||||
abstract class EventEmitter implements NodeJS.EventEmitter {
|
||||
protected abstract notImplementedError(): Error;
|
||||
|
@ -115,7 +122,17 @@ declare module TacoTestsUtils {
|
|||
}
|
||||
class ChildProcessModule {
|
||||
/** Methods to configure the fake process **/
|
||||
// This method will make all child_process.exec call succeed
|
||||
fakeAllExecCallsSucceed(): ChildProcessModule;
|
||||
// This method will make all child_process.exec call fail
|
||||
fakeAllExecCallsEndingWithErrors(): ChildProcessModule;
|
||||
// This method will make some child_process.exec call fail, and some succeed based on the lambda tests passed
|
||||
fakeUsingCommandToDetermineResult(successFilter: CommandTester, failureFilter: CommandTester): ChildProcessModule;
|
||||
|
||||
/** mock spawn variable. Docs at https://www.npmjs.com/package/mock-spawn **/
|
||||
public mockSpawn: any; // This is an instance of mockSpawn();
|
||||
|
||||
/** child_process methods **/
|
||||
spawn(command: string, args?: string[], options?: SpawnOptions): IChildProcess;
|
||||
exec(command: string, options: IExecOptions, callback: Callback): IChildProcess;
|
||||
exec(command: string, callback: Callback): IChildProcess;
|
||||
|
|
|
@ -22,6 +22,7 @@ declare module TacoTestsUtils {
|
|||
generate<T>(componentName: string, codeGeneratingTelemetry: { (telemetry: TacoUtility.TelemetryGenerator): T; }): T;
|
||||
// Warning: We have no way of rejecting this promise, so we might get a test timeout if the events are never sent
|
||||
getAllSentEvents(): Q.Promise<TacoUtility.ICommandTelemetryProperties[]>;
|
||||
clear(): void;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче