diff --git a/Tasks/XamarinAndroid/xamarinandroid.ts b/Tasks/XamarinAndroid/xamarinandroid.ts index a9e9614bce50..b5bf84bd97cc 100644 --- a/Tasks/XamarinAndroid/xamarinandroid.ts +++ b/Tasks/XamarinAndroid/xamarinandroid.ts @@ -47,7 +47,11 @@ else { //find xbuild location to use var xbuildToolPath = tl.which('xbuild'); if(xbuildLocation) { - xbuildToolPath = xbuildLocation + '/xbuild'; + xbuildToolPath = path.join(xbuildLocation, 'xbuild'); + if (!tl.exist(xbuildToolPath)) { + xbuildToolPath = path.join(xbuildLocation, 'xbuild.exe'); + } + tl.checkPath(xbuildToolPath, 'xbuild'); } if(!xbuildToolPath) { tl.error('xbuild was not found in the path.'); diff --git a/Tasks/XamarinComponentRestore/xamarincomponent.ts b/Tasks/XamarinComponentRestore/xamarincomponent.ts index 4f1b1505f46d..d8cc4b7ec4c6 100644 --- a/Tasks/XamarinComponentRestore/xamarincomponent.ts +++ b/Tasks/XamarinComponentRestore/xamarincomponent.ts @@ -29,7 +29,8 @@ xamarinComponentTool.arg(tl.getInput('email', true)); xamarinComponentTool.arg('-p'); xamarinComponentTool.arg(tl.getInput('password', true)); -var solutionPath = tl.getPathInput('solution', true, false); +var solutionInput = tl.getPathInput('solution', true, false); +var solutionPath = solutionInput; if(tl.filePathSupplied('solution')) { var solutionMatches = tl.glob(solutionPath); @@ -41,9 +42,14 @@ if(tl.filePathSupplied('solution')) } solutionPath = solutionMatches[0]; + } else { + solutionPath = undefined; } -} else { - tl.debug('No solutions found'); +} + +if (!solutionPath) { + tl.error(tl.loc('XamarinComponentRestoreFailed', 'No solutions found matching the input: ' + solutionInput)); + tl.exit(1); } tl.debug("Restoring components for " + solutionPath); diff --git a/Tasks/XamarinLicense/xamarinlicense.ts b/Tasks/XamarinLicense/xamarinlicense.ts index b87bbeb2ca12..5f6bf6e2a99a 100644 --- a/Tasks/XamarinLicense/xamarinlicense.ts +++ b/Tasks/XamarinLicense/xamarinlicense.ts @@ -356,4 +356,7 @@ if (action == 'Activate') { } tl.exit(0); }); +} else { + tl.error('Unknown action: ' + action); + tl.exit(1); } \ No newline at end of file diff --git a/Tasks/XamarinTestCloud/xamarintestcloud.ts b/Tasks/XamarinTestCloud/xamarintestcloud.ts index 9e94b93951d1..8b08e89bcadd 100644 --- a/Tasks/XamarinTestCloud/xamarintestcloud.ts +++ b/Tasks/XamarinTestCloud/xamarintestcloud.ts @@ -3,7 +3,6 @@ import fs = require('fs'); import path = require('path'); -import shell = require('shelljs'); import tl = require('vsts-task-lib/task'); // Get inputs @@ -41,10 +40,14 @@ var onError = function (errorMsg) { tl.exit(1); } +// Find location of mono +var monoPath = tl.which('mono', true); +tl.debug("monoPath: " + monoPath); + // Resolve apps for the specified value or pattern if (app.indexOf('*') == -1 && app.indexOf('?') == -1) { // Check literal path to a single app file - if (!fs.existsSync(app)) { + if (!tl.exist(app)) { onError('The specified app file does not exist: ' + app); } @@ -65,19 +68,20 @@ else { } // Check and add parameter for test assembly directory -if (!shell.test('-d', testDir)) { +if (!tl.exist(testDir)) { onError('The test assembly directory does not exist: ' + testDir); } // Ensure that $testCloudLocation specifies test-cloud.exe (case-sensitive) if (path.basename(testCloudLocation) != 'test-cloud.exe') { - throw "test-cloud.exe location must end with '\\test-cloud.exe'." + tl.debug("testCloudLocation = " + testCloudLocation); + onError("test-cloud.exe location must end with '\\test-cloud.exe'."); } // Locate test-cloud.exe (part of the Xamarin.UITest NuGet package) if (testCloudLocation.indexOf('*') == -1 && testCloudLocation.indexOf('?') == -1) { // Check literal path to test-cloud.exe - if (!fs.existsSync(testCloudLocation)) { + if (!tl.exist(testCloudLocation)) { onError('test-cloud.exe does not exist at the specified location: ' + testCloudLocation); } @@ -100,12 +104,6 @@ else { var testCloud = testCloudExecutables[0]; } -// Find location of mono -var monoPath = tl.which('mono'); -if (!monoPath) { - onError('mono was not found in the path.'); -} - // Invoke test-cloud.exe for each app file var buildId = tl.getVariable('build.buildId'); var appFileIndex = 0; diff --git a/Tasks/XamariniOS/xamarinios.ts b/Tasks/XamariniOS/xamarinios.ts index c30b02ad81ac..a3141fab1248 100644 --- a/Tasks/XamariniOS/xamarinios.ts +++ b/Tasks/XamariniOS/xamarinios.ts @@ -14,30 +14,24 @@ var args = tl.getInput('args'); var packageApp = tl.getBoolInput('packageApp'); var buildForSimulator = tl.getBoolInput('forSimulator'); var device = (buildForSimulator) ? 'iPhoneSimulator' : 'iPhone'; -tl.debug('Device: ' + device); +tl.debug('device: ' + device); +var xbuildLocation = tl.getInput('mdtoolLocation', false); +var cwd = tl.getInput('cwd'); // Get path to xbuild -var xbuildToolPath = tl.which('xbuild'); -var xbuildLocation = tl.getInput('mdtoolLocation', false); +var xbuildToolPath = undefined; if (xbuildLocation) { - xbuildToolPath = xbuildLocation + '/xbuild'; + xbuildToolPath = path.join(xbuildLocation, 'xbuild'); tl.checkPath(xbuildToolPath, 'xbuild'); -} -if (!xbuildToolPath) { - tl.error('xbuild was not found in the path.'); - tl.exit(1); +} else { + xbuildToolPath = tl.which('xbuild', true); } // Find location of nuget -var nugetPath = tl.which('nuget'); -if (!nugetPath) { - tl.error('nuget was not found in the path.'); - tl.exit(1); -} +var nugetPath = tl.which('nuget', true); //Process working directory -var buildSourceDirectory = tl.getVariable('build.sourceDirectory') || tl.getVariable('build.sourcesDirectory'); -var cwd = tl.getInput('cwd') || buildSourceDirectory; +var cwd = cwd || tl.getVariable('build.sourceDirectory') || tl.getVariable('build.sourcesDirectory'); tl.cd(cwd); // Prepare function for tool execution failure @@ -147,9 +141,14 @@ nugetRunner.exec() .then(function (code) { tl.exit(code); }) + } else { + tl.setResult(tl.TaskResult.Succeeded, ''); } }) + } else { + tl.setResult(tl.TaskResult.Succeeded, ''); } + }) .fail(onFailedExecution) //xbuild }) diff --git a/Tests/L0/ANT/antFails.json b/Tests/L0/ANT/antFails.json index eca72e50366c..12a8addaceb7 100644 --- a/Tests/L0/ANT/antFails.json +++ b/Tests/L0/ANT/antFails.json @@ -14,6 +14,7 @@ } }, "checkPath" : { + "/usr/local/bin/ANT": true, "/build/build.xml": true }, "getVariable" : { diff --git a/Tests/L0/ANT/antGood.json b/Tests/L0/ANT/antGood.json index 3d381af59663..90807aa36f93 100644 --- a/Tests/L0/ANT/antGood.json +++ b/Tests/L0/ANT/antGood.json @@ -14,6 +14,7 @@ } }, "checkPath" : { + "/usr/local/bin/ANT": true, "/build/build.xml": true, "/usr/local/bin/ANT2": true }, diff --git a/Tests/L0/ANT/antVersionFails.json b/Tests/L0/ANT/antVersionFails.json index 8d5fad39ee17..79cbefc1e0b7 100644 --- a/Tests/L0/ANT/antVersionFails.json +++ b/Tests/L0/ANT/antVersionFails.json @@ -14,6 +14,7 @@ } }, "checkPath" : { + "/usr/local/bin/ANT": true, "/build/build.xml": true } } \ No newline at end of file diff --git a/Tests/L0/CMake/cmakeFails.json b/Tests/L0/CMake/cmakeFails.json index 7b6cc32fc006..450baae7f03d 100644 --- a/Tests/L0/CMake/cmakeFails.json +++ b/Tests/L0/CMake/cmakeFails.json @@ -3,6 +3,10 @@ "cmake": "/usr/local/bin/cmake", "node": "/usr/local/bin/node" }, + "checkPath": { + "/usr/local/bin/cmake": true, + "/usr/local/bin/node": true + }, "exec": { "/usr/local/bin/cmake ..": { "code": 1, diff --git a/Tests/L0/CMake/cmakeGood.json b/Tests/L0/CMake/cmakeGood.json index 6718975c8c15..2c8a4879c08f 100644 --- a/Tests/L0/CMake/cmakeGood.json +++ b/Tests/L0/CMake/cmakeGood.json @@ -3,6 +3,10 @@ "cmake": "/usr/local/bin/cmake", "node": "/usr/local/bin/node" }, + "checkPath": { + "/usr/local/bin/cmake": true, + "/usr/local/bin/node": true + }, "exec": { "/usr/local/bin/cmake ..": { "code": 0, diff --git a/Tests/L0/CMake/cmakeNoCwd.json b/Tests/L0/CMake/cmakeNoCwd.json index f6b9546b264d..6a128aa50dc0 100644 --- a/Tests/L0/CMake/cmakeNoCwd.json +++ b/Tests/L0/CMake/cmakeNoCwd.json @@ -1,5 +1,8 @@ { "which": { "cmake": "/usr/local/bin/cmake" + }, + "checkPath": { + "/usr/local/bin/cmake": true } } \ No newline at end of file diff --git a/Tests/L0/CmdLine/cmdlineFails.json b/Tests/L0/CmdLine/cmdlineFails.json index f4c3a51347ef..a1ae645525f4 100644 --- a/Tests/L0/CmdLine/cmdlineFails.json +++ b/Tests/L0/CmdLine/cmdlineFails.json @@ -3,6 +3,10 @@ "cmd": "/usr/local/bin/cmd", "node": "/usr/local/bin/node" }, + "checkPath": { + "/usr/local/bin/cmd": true, + "/usr/local/bin/node": true + }, "exec": { "/usr/local/bin/cmd /c cmd": { "code": 1, diff --git a/Tests/L0/CmdLine/cmdlineGood.json b/Tests/L0/CmdLine/cmdlineGood.json index 56c20e9e4595..8b7ebc9accbd 100644 --- a/Tests/L0/CmdLine/cmdlineGood.json +++ b/Tests/L0/CmdLine/cmdlineGood.json @@ -3,6 +3,10 @@ "cmd": "/usr/local/bin/cmd", "node": "/usr/local/bin/node" }, + "checkPath": { + "/usr/local/bin/cmd": true, + "/usr/local/bin/node": true + }, "exec": { "/usr/local/bin/cmd /c cmd": { "code": 0, diff --git a/Tests/L0/CmdLine/cmdlineNoCwd.json b/Tests/L0/CmdLine/cmdlineNoCwd.json index c6277b2e7f71..ef75abb0ec41 100644 --- a/Tests/L0/CmdLine/cmdlineNoCwd.json +++ b/Tests/L0/CmdLine/cmdlineNoCwd.json @@ -1,5 +1,8 @@ { "which": { "cmd": "/usr/local/bin/cmd" + }, + "checkPath": { + "/usr/local/bin/cmd": true } } \ No newline at end of file diff --git a/Tests/L0/CmdLine/cmdlineStderrFails.json b/Tests/L0/CmdLine/cmdlineStderrFails.json index c224636a4d07..5925ed37081e 100644 --- a/Tests/L0/CmdLine/cmdlineStderrFails.json +++ b/Tests/L0/CmdLine/cmdlineStderrFails.json @@ -3,6 +3,10 @@ "cmd": "/usr/local/bin/cmd", "node": "/usr/local/bin/node" }, + "checkPath": { + "/usr/local/bin/cmd": true, + "/usr/local/bin/node": true + }, "exec": { "/usr/local/bin/cmd /c cmd": { "code": 0, diff --git a/Tests/L0/Grunt/gruntFails.json b/Tests/L0/Grunt/gruntFails.json index 874fbb68b244..dcc1664a490e 100644 --- a/Tests/L0/Grunt/gruntFails.json +++ b/Tests/L0/Grunt/gruntFails.json @@ -11,6 +11,8 @@ } }, "checkPath": { + "/usr/local/bin/grunt": true, + "/usr/local/bin/node": true, "gruntfile.js": true }, "exist": { diff --git a/Tests/L0/Grunt/gruntGlobalGood.json b/Tests/L0/Grunt/gruntGlobalGood.json index 0fda0bac54a6..543b810aaafa 100644 --- a/Tests/L0/Grunt/gruntGlobalGood.json +++ b/Tests/L0/Grunt/gruntGlobalGood.json @@ -10,6 +10,8 @@ } }, "checkPath": { + "/usr/local/bin/grunt": true, + "/usr/local/bin/node": true, "gruntfile.js": true }, "exist": { diff --git a/Tests/L0/Grunt/gruntLocalGood.json b/Tests/L0/Grunt/gruntLocalGood.json index c60bc9828ac8..425da72a861c 100644 --- a/Tests/L0/Grunt/gruntLocalGood.json +++ b/Tests/L0/Grunt/gruntLocalGood.json @@ -13,6 +13,7 @@ } }, "checkPath": { + "/usr/local/bin/node": true, "gruntfile.js": true }, "exist": { diff --git a/Tests/L0/Grunt/gruntNoGruntCli.json b/Tests/L0/Grunt/gruntNoGruntCli.json index d0ec5c99cb02..91d7e4b76246 100644 --- a/Tests/L0/Grunt/gruntNoGruntCli.json +++ b/Tests/L0/Grunt/gruntNoGruntCli.json @@ -3,6 +3,7 @@ "node": "/usr/local/bin/node" }, "checkPath": { + "/usr/local/bin/node": true, "gruntfile.js": true } } \ No newline at end of file diff --git a/Tests/L0/Grunt/gruntNoGruntFile.json b/Tests/L0/Grunt/gruntNoGruntFile.json index c907bf3bf7b1..c1dbe765c3e7 100644 --- a/Tests/L0/Grunt/gruntNoGruntFile.json +++ b/Tests/L0/Grunt/gruntNoGruntFile.json @@ -3,6 +3,7 @@ "node": "/usr/local/bin/node" }, "checkPath": { + "/usr/local/bin/node": true, "gruntfile.js": false } } \ No newline at end of file diff --git a/Tests/L0/Gulp/gulpFails.json b/Tests/L0/Gulp/gulpFails.json index cfbf7fbd39bc..f08f9746c8b7 100644 --- a/Tests/L0/Gulp/gulpFails.json +++ b/Tests/L0/Gulp/gulpFails.json @@ -10,6 +10,7 @@ } }, "checkPath": { + "/usr/local/bin/gulp": true, "gulpfile.js": true }, "exist": { diff --git a/Tests/L0/Gulp/gulpGlobalGood.json b/Tests/L0/Gulp/gulpGlobalGood.json index cd1cc9e997fa..b7ef2c7fe970 100644 --- a/Tests/L0/Gulp/gulpGlobalGood.json +++ b/Tests/L0/Gulp/gulpGlobalGood.json @@ -9,6 +9,7 @@ } }, "checkPath": { + "/usr/local/bin/gulp": true, "gulpfile.js": true }, "exist": { diff --git a/Tests/L0/Gulp/gulpLocalGood.json b/Tests/L0/Gulp/gulpLocalGood.json index ee9fcfe7b360..0b4fff5d4c6b 100644 --- a/Tests/L0/Gulp/gulpLocalGood.json +++ b/Tests/L0/Gulp/gulpLocalGood.json @@ -13,6 +13,7 @@ } }, "checkPath": { + "/usr/local/bin/node": true, "gulpfile.js": true }, "exist": { diff --git a/Tests/L0/Gulp/gulpNoGulp.json b/Tests/L0/Gulp/gulpNoGulp.json index 445171d64b85..204bca01129f 100644 --- a/Tests/L0/Gulp/gulpNoGulp.json +++ b/Tests/L0/Gulp/gulpNoGulp.json @@ -3,6 +3,7 @@ "node": "/usr/local/bin/node" }, "checkPath": { + "/usr/local/bin/node": true, "gulpfile.js": true } } \ No newline at end of file diff --git a/Tests/L0/Npm/npmFails.json b/Tests/L0/Npm/npmFails.json index 64ce49d30b54..51318e6d93db 100644 --- a/Tests/L0/Npm/npmFails.json +++ b/Tests/L0/Npm/npmFails.json @@ -3,6 +3,10 @@ "npm": "/usr/local/bin/npm", "node": "/usr/local/bin/node" }, + "checkPath": { + "/usr/local/bin/npm": true, + "/usr/local/bin/node": true + }, "exec": { "/usr/local/bin/npm install npm -g": { "code": 1, diff --git a/Tests/L0/Npm/npmGood.json b/Tests/L0/Npm/npmGood.json index a80b90a7ede0..4a6d0dbf255c 100644 --- a/Tests/L0/Npm/npmGood.json +++ b/Tests/L0/Npm/npmGood.json @@ -3,6 +3,10 @@ "npm": "/usr/local/bin/npm", "node": "/usr/local/bin/node" }, + "checkPath": { + "/usr/local/bin/npm": true, + "/usr/local/bin/node": true + }, "exec": { "/usr/local/bin/npm install npm": { "code": 0, diff --git a/Tests/L0/ShellScript/shellscriptFails.json b/Tests/L0/ShellScript/shellscriptFails.json index 3b6698a9d454..f970c9581f24 100644 --- a/Tests/L0/ShellScript/shellscriptFails.json +++ b/Tests/L0/ShellScript/shellscriptFails.json @@ -11,6 +11,8 @@ } }, "checkPath" : { + "/usr/local/bin/bash": true, + "/usr/local/bin/node": true, "/script.sh" : true } } \ No newline at end of file diff --git a/Tests/L0/ShellScript/shellscriptGood.json b/Tests/L0/ShellScript/shellscriptGood.json index bc2632f0c7db..ce1caa6e0429 100644 --- a/Tests/L0/ShellScript/shellscriptGood.json +++ b/Tests/L0/ShellScript/shellscriptGood.json @@ -10,6 +10,8 @@ } }, "checkPath" : { + "/usr/local/bin/bash": true, + "/usr/local/bin/node": true, "/script.sh" : true } } \ No newline at end of file diff --git a/Tests/L0/ShellScript/shellscriptStderrFails.json b/Tests/L0/ShellScript/shellscriptStderrFails.json index 8dc1dbb798be..15ef7ff91400 100644 --- a/Tests/L0/ShellScript/shellscriptStderrFails.json +++ b/Tests/L0/ShellScript/shellscriptStderrFails.json @@ -11,6 +11,8 @@ } }, "checkPath" : { + "/usr/local/bin/bash": true, + "/usr/local/bin/node": true, "/script.sh" : true } } \ No newline at end of file diff --git a/Tests/L0/XamarinAndroid/_suite.ts b/Tests/L0/XamarinAndroid/_suite.ts new file mode 100644 index 000000000000..e1bfe021efa1 --- /dev/null +++ b/Tests/L0/XamarinAndroid/_suite.ts @@ -0,0 +1,385 @@ +/// +/// + +import assert = require('assert'); +import trm = require('../../lib/taskRunner'); +import path = require('path'); + +function setResponseFile(name: string) { + process.env['MOCK_RESPONSES'] = path.join(__dirname, name); +} + +describe('XamarinAndroid Suite', function() { + this.timeout(20000); + + before((done) => { + // init here + done(); + }); + + after(function() { + + }); + + it('run XamarinAndroid with all default inputs', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinAndroid', true); + tr.setInput('project', '**/Single*.csproj'); + tr.setInput('target', ''); + tr.setInput('outputDir', ''); + tr.setInput('configuration', ''); + tr.setInput('msbuildLocation', ''); + tr.setInput('msbuildArguments', ''); + tr.setInput('javaHomeSelection', 'JDKVersion'); + tr.setInput('jdkVersion', 'default'); + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/xbuild /user/build/fun/project.csproj /t:PackageForAndroid'), 'it should have run xamarin android'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinAndroid 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('run XamarinAndroid with project missing', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinAndroid', true); + //tr.setInput('project', '**/*.csproj'); + tr.setInput('target', ''); + tr.setInput('outputDir', ''); + tr.setInput('configuration', ''); + tr.setInput('msbuildLocation', ''); + tr.setInput('msbuildArguments', ''); + tr.setInput('javaHomeSelection', 'JDKVersion'); + tr.setInput('jdkVersion', 'default'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinAndroid'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: project') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('run XamarinAndroid where project does not match anything', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinAndroid', true); + tr.setInput('project', '**/home*.csproj'); + tr.setInput('target', ''); + tr.setInput('outputDir', ''); + tr.setInput('configuration', ''); + tr.setInput('msbuildLocation', ''); + tr.setInput('msbuildArguments', ''); + tr.setInput('javaHomeSelection', 'JDKVersion'); + tr.setInput('jdkVersion', 'default'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinAndroid'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stdout.indexOf('##vso[task.issue type=error;]No matching files were found with search pattern:') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('run XamarinAndroid where project is a single file', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinAndroid', true); + tr.setInput('project', '/user/build/fun/project.csproj'); + tr.setInput('target', ''); + tr.setInput('outputDir', ''); + tr.setInput('configuration', ''); + tr.setInput('msbuildLocation', ''); + tr.setInput('msbuildArguments', ''); + tr.setInput('javaHomeSelection', 'JDKVersion'); + tr.setInput('jdkVersion', 'default'); + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/xbuild /user/build/fun/project.csproj /t:PackageForAndroid'), 'it should have run xamarin android'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinAndroid 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('run XamarinAndroid where project is a single file that does not exist', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinAndroid', true); + tr.setInput('project', '/user/build/fun/project2.csproj'); + tr.setInput('target', ''); + tr.setInput('outputDir', ''); + tr.setInput('configuration', ''); + tr.setInput('msbuildLocation', ''); + tr.setInput('msbuildArguments', ''); + tr.setInput('javaHomeSelection', 'JDKVersion'); + tr.setInput('jdkVersion', 'default'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinAndroid'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('not found files: /user/build/fun/project2.csproj') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('run XamarinAndroid where project matches multiple files', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinAndroid', true); + tr.setInput('project', '**/Multiple*.csproj'); + tr.setInput('target', ''); + tr.setInput('outputDir', ''); + tr.setInput('configuration', ''); + tr.setInput('msbuildLocation', ''); + tr.setInput('msbuildArguments', ''); + tr.setInput('javaHomeSelection', 'JDKVersion'); + tr.setInput('jdkVersion', 'default'); + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/xbuild /user/build/fun/project1.csproj /t:PackageForAndroid'), 'it should have run xamarin android 1'); + assert(tr.ran('/home/bin/xbuild /user/build/fun/project2.csproj /t:PackageForAndroid'), 'it should have run xamarin android 2'); + assert(tr.ran('/home/bin/xbuild /user/build/fun/project3.csproj /t:PackageForAndroid'), 'it should have run xamarin android 3'); + assert(tr.invokedToolCount == 3, 'should have only run XamarinAndroid 3 times'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('run XamarinAndroid with jdkVersion set to 1.8', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinAndroid', true); + tr.setInput('project', '**/Single*.csproj'); + tr.setInput('target', ''); + tr.setInput('outputDir', ''); + tr.setInput('configuration', ''); + tr.setInput('msbuildLocation', ''); + tr.setInput('msbuildArguments', ''); + tr.setInput('javaHomeSelection', 'JDKVersion'); + tr.setInput('jdkVersion', '1.8'); + tr.setInput('jdkArchitecture', 'x86'); + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/xbuild /user/build/fun/project.csproj /t:PackageForAndroid /p:JavaSdkDirectory=/user/local/bin/Java8'), 'it should have run xamarin android'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinAndroid 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails with jdkVersion set to 1.5', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinAndroid', true); + tr.setInput('project', '**/Single*.csproj'); + tr.setInput('target', ''); + tr.setInput('outputDir', ''); + tr.setInput('configuration', ''); + tr.setInput('msbuildLocation', ''); + tr.setInput('msbuildArguments', ''); + tr.setInput('javaHomeSelection', 'JDKVersion'); + tr.setInput('jdkVersion', '1.5'); + tr.setInput('jdkArchitecture', 'x86'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinAndroid'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stdout.indexOf('Failed to find specified JDK version') >= 0, 'JAVA_HOME set?'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('run XamarinAndroid with all default inputs', (done) => { + // Not using a response file so that xbuild won't be found + setResponseFile('responseEmpty.json'); + + var tr = new trm.TaskRunner('XamarinAndroid', true); + tr.setInput('project', '**/Single*.csproj'); + tr.setInput('target', ''); + tr.setInput('outputDir', ''); + tr.setInput('configuration', ''); + tr.setInput('msbuildLocation', ''); + tr.setInput('msbuildArguments', ''); + tr.setInput('javaHomeSelection', 'JDKVersion'); + tr.setInput('jdkVersion', 'default'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinAndroid'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stdout.indexOf('##vso[task.issue type=error;]xbuild was not found in the path') >= 0, 'JAVA_HOME set?'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('run XamarinAndroid with msbuildlocation provided', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinAndroid', true); + tr.setInput('project', '**/Single*.csproj'); + tr.setInput('target', ''); + tr.setInput('outputDir', ''); + tr.setInput('configuration', ''); + tr.setInput('msbuildLocation', '/home/bin2/'); + tr.setInput('msbuildArguments', ''); + tr.setInput('javaHomeSelection', 'JDKVersion'); + tr.setInput('jdkVersion', 'default'); + + tr.run() + .then(() => { + // Note: in the response file we have checkPath returning true for /home/bin2/xbuild.exe + // This means that the tool path will contain the .exe extension and so we have to + // check for this below. + assert(tr.ran('/home/bin2/xbuild.exe /user/build/fun/project.csproj /t:PackageForAndroid'), 'it should have run xamarin android'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinAndroid 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('run XamarinAndroid with INVALID msbuildlocation', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinAndroid', true); + tr.setInput('project', '**/Single*.csproj'); + tr.setInput('target', ''); + tr.setInput('outputDir', ''); + tr.setInput('configuration', ''); + tr.setInput('msbuildLocation', '/home/bin/INVALID'); + tr.setInput('msbuildArguments', ''); + tr.setInput('javaHomeSelection', 'JDKVersion'); + tr.setInput('jdkVersion', 'default'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinAndroid'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('not found xbuild:') >= 0, 'xbuild tool path found?'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('run XamarinAndroid with ALL args provided', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinAndroid', true); + tr.setInput('project', '**/Single*.csproj'); + tr.setInput('target', '"My Target"'); + tr.setInput('outputDir', '"/home/o u t/dir"'); + tr.setInput('configuration', '"For Release"'); + tr.setInput('msbuildArguments', '/m:1 "/p:temp=/home/temp dir/" /f'); + tr.setInput('javaHomeSelection', 'JDKVersion'); + tr.setInput('jdkVersion', '1.8'); + tr.setInput('jdkArchitecture', 'x86'); + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/xbuild /user/build/fun/project.csproj /t:My Target /t:PackageForAndroid /m:1 /p:temp=/home/temp dir/ /f /p:OutputPath=/home/o u t/dir /p:Configuration=For Release /p:JavaSdkDirectory=/user/local/bin/Java8'), 'it should have run xamarin android'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinAndroid 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('run XamarinAndroid with multiple projects and ALL args provided', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinAndroid', true); + tr.setInput('project', '**/Multiple*.csproj'); + tr.setInput('target', '"My Target"'); + tr.setInput('outputDir', '"/home/o u t/dir"'); + tr.setInput('configuration', '"For Release"'); + tr.setInput('msbuildArguments', '/m:1 "/p:temp=/home/temp dir/" /f'); + tr.setInput('javaHomeSelection', 'JDKVersion'); + tr.setInput('jdkVersion', '1.8'); + tr.setInput('jdkArchitecture', 'x86'); + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/xbuild /user/build/fun/project1.csproj /t:My Target /t:PackageForAndroid /m:1 /p:temp=/home/temp dir/ /f /p:OutputPath=/home/o u t/dir /p:Configuration=For Release /p:JavaSdkDirectory=/user/local/bin/Java8'), 'it should have run xamarin android 1'); + assert(tr.ran('/home/bin/xbuild /user/build/fun/project2.csproj /t:My Target /t:PackageForAndroid /m:1 /p:temp=/home/temp dir/ /f /p:OutputPath=/home/o u t/dir /p:Configuration=For Release /p:JavaSdkDirectory=/user/local/bin/Java8'), 'it should have run xamarin android 2'); + assert(tr.ran('/home/bin/xbuild /user/build/fun/project3.csproj /t:My Target /t:PackageForAndroid /m:1 /p:temp=/home/temp dir/ /f /p:OutputPath=/home/o u t/dir /p:Configuration=For Release /p:JavaSdkDirectory=/user/local/bin/Java8'), 'it should have run xamarin android 3'); + assert(tr.invokedToolCount == 3, 'should have only run XamarinAndroid 3 times'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + +}); \ No newline at end of file diff --git a/Tests/L0/XamarinAndroid/response.json b/Tests/L0/XamarinAndroid/response.json new file mode 100644 index 000000000000..5324356d6f4c --- /dev/null +++ b/Tests/L0/XamarinAndroid/response.json @@ -0,0 +1,69 @@ +{ + "which": { + "xbuild": "/home/bin/xbuild" + }, + "exec": { + "/home/bin/xbuild /user/build/fun/project.csproj /t:PackageForAndroid": { + "code": 0, + "stdout": "Xamarin android" + }, + "/home/bin2/xbuild.exe /user/build/fun/project.csproj /t:PackageForAndroid": { + "code": 0, + "stdout": "Xamarin android" + }, + "/home/bin/xbuild /user/build/fun/project1.csproj /t:PackageForAndroid": { + "code": 0, + "stdout": "Xamarin android" + }, + "/home/bin/xbuild /user/build/fun/project2.csproj /t:PackageForAndroid": { + "code": 0, + "stdout": "Xamarin android" + }, + "/home/bin/xbuild /user/build/fun/project3.csproj /t:PackageForAndroid": { + "code": 0, + "stdout": "Xamarin android" + }, + "/home/bin/xbuild /user/build/fun/project.csproj /t:PackageForAndroid /p:JavaSdkDirectory=/user/local/bin/Java8": { + "code": 0, + "stdout": "Xamarin android" + }, + "/home/bin/xbuild /user/build/fun/project.csproj /t:My Target /t:PackageForAndroid /m:1 /p:temp=/home/temp dir/ /f /p:OutputPath=/home/o u t/dir /p:Configuration=For Release /p:JavaSdkDirectory=/user/local/bin/Java8": { + "code": 0, + "stdout": "Xamarin android" + }, + "/home/bin/xbuild /user/build/fun/project1.csproj /t:My Target /t:PackageForAndroid /m:1 /p:temp=/home/temp dir/ /f /p:OutputPath=/home/o u t/dir /p:Configuration=For Release /p:JavaSdkDirectory=/user/local/bin/Java8": { + "code": 0, + "stdout": "Xamarin android" + }, + "/home/bin/xbuild /user/build/fun/project2.csproj /t:My Target /t:PackageForAndroid /m:1 /p:temp=/home/temp dir/ /f /p:OutputPath=/home/o u t/dir /p:Configuration=For Release /p:JavaSdkDirectory=/user/local/bin/Java8": { + "code": 0, + "stdout": "Xamarin android" + }, + "/home/bin/xbuild /user/build/fun/project3.csproj /t:My Target /t:PackageForAndroid /m:1 /p:temp=/home/temp dir/ /f /p:OutputPath=/home/o u t/dir /p:Configuration=For Release /p:JavaSdkDirectory=/user/local/bin/Java8": { + "code": 0, + "stdout": "Xamarin android" + } + }, + "checkPath" : { + "/user/build/fun/project.csproj": true, + "/home/bin2/xbuild.exe": true + }, + "getVariable" : { + "JAVA_HOME_8_X86": "/user/local/bin/Java8" + }, + "find": { + "": [ + "/user/build" + ] + }, + "match": { + "**/Single*.csproj": [ + "/user/build/fun/project.csproj" + ], + "**/Multiple*.csproj": [ + "/user/build/fun/project1.csproj", + "/user/build/fun/project2.csproj", + "/user/build/fun/project3.csproj" + ] + } +} \ No newline at end of file diff --git a/Tests/L0/XamarinAndroid/responseEmpty.json b/Tests/L0/XamarinAndroid/responseEmpty.json new file mode 100644 index 000000000000..7a73a41bfdf7 --- /dev/null +++ b/Tests/L0/XamarinAndroid/responseEmpty.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Tests/L0/XamarinComponentRestore/_suite.ts b/Tests/L0/XamarinComponentRestore/_suite.ts new file mode 100644 index 000000000000..4f5777a10407 --- /dev/null +++ b/Tests/L0/XamarinComponentRestore/_suite.ts @@ -0,0 +1,158 @@ +/// +/// + +import assert = require('assert'); +import trm = require('../../lib/taskRunner'); +import path = require('path'); + +function setResponseFile(name: string) { + process.env['MOCK_RESPONSES'] = path.join(__dirname, name); +} + +describe('XamarinComponentRestore Suite', function() { + this.timeout(20000); + + before((done) => { + // init here + done(); + }); + + after(function() { + + }); + + it('run XamarinComponentRestore with all default inputs', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinComponentRestore', true, true); + tr.setInput('solution', '**/*.sln'); + tr.setInput('email', 'me@ms.com'); + tr.setInput('password', 'mypass'); + + tr.run() + .then(() => { + assert(tr.ran('/XamarinComponentRestore/xpkg/xamarin-component.exe restore -u me@ms.com -p mypass /user/build/fun/project.sln'), 'it should have run xamarin component restore'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinComponentRestore 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('run XamarinComponentRestore on multiple projects', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinComponentRestore', true, true); + tr.setInput('solution', '**/Multiple*.sln'); + tr.setInput('email', 'me@ms.com'); + tr.setInput('password', 'mypass'); + + tr.run() + .then(() => { + assert(tr.ran('/XamarinComponentRestore/xpkg/xamarin-component.exe restore -u me@ms.com -p mypass /user/build/fun/project.sln'), 'it should have run xamarin component restore'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinComponentRestore 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf('Found 3 solutions matching') >= 0, 'did not find all solutions'); + assert(tr.stdout.indexOf('##vso[task.issue type=warning;]multiple solution matches, using /user/build/fun/project.sln') >= 0, 'did not get correct warning'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('run XamarinComponentRestore matching NO projects', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinComponentRestore', true, true); + tr.setInput('solution', '**/NoneMatching*.sln'); + tr.setInput('email', 'me@ms.com'); + tr.setInput('password', 'mypass'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinComponentRestore'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stdout.indexOf('##vso[task.issue type=error;]xamarin-component.exe failed with error: No solutions found matching the input: **/NoneMatching*.sln') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('run XamarinComponentRestore missing solution', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinComponentRestore', true, true); + //tr.setInput('solution', '**/*.sln'); + tr.setInput('email', 'me@ms.com'); + tr.setInput('password', 'mypass'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinComponentRestore'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: solution') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('run XamarinComponentRestore missing email', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinComponentRestore', true, true); + tr.setInput('solution', '**/*.sln'); + //tr.setInput('email', 'me@ms.com'); + tr.setInput('password', 'mypass'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinComponentRestore'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: email') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('run XamarinComponentRestore missing password', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinComponentRestore', true, true); + tr.setInput('solution', '**/*.sln'); + tr.setInput('email', 'me@ms.com'); + //tr.setInput('password', 'mypass'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinComponentRestore'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: password') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + +}); \ No newline at end of file diff --git a/Tests/L0/XamarinComponentRestore/response.json b/Tests/L0/XamarinComponentRestore/response.json new file mode 100644 index 000000000000..3af3e06d4272 --- /dev/null +++ b/Tests/L0/XamarinComponentRestore/response.json @@ -0,0 +1,30 @@ +{ + "which": { + "mono": "/home/bin/mono" + }, + "exec": { + "/XamarinComponentRestore/xpkg/xamarin-component.exe restore -u me@ms.com -p mypass /user/build/fun/project.sln": { + "code": 0, + "stdout": "Xamarin component restore on Windows" + }, + "/home/bin/mono /XamarinComponentRestore/xpkg/xamarin-component.exe restore -u me@ms.com -p mypass /user/build/fun/project.sln": { + "code": 0, + "stdout": "Xamarin component restore on Linux/Mac" + } + }, + "checkPath" : { + "/user/build/fun/project.csproj": true + }, + "glob": { + "**/*.sln": [ + "/user/build/fun/project.sln" + ], + "**/Multiple*.sln": [ + "/user/build/fun/project.sln", + "/user/build/fun2/project.sln", + "/user/build/fun3/project.sln" + ], + "**/NoneMatching*.sln": [ + ] + } +} \ No newline at end of file diff --git a/Tests/L0/XamarinComponentRestore/responseEmpty.json b/Tests/L0/XamarinComponentRestore/responseEmpty.json new file mode 100644 index 000000000000..7a73a41bfdf7 --- /dev/null +++ b/Tests/L0/XamarinComponentRestore/responseEmpty.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Tests/L0/XamarinLicense/_suite.ts b/Tests/L0/XamarinLicense/_suite.ts new file mode 100644 index 000000000000..941312942198 --- /dev/null +++ b/Tests/L0/XamarinLicense/_suite.ts @@ -0,0 +1,196 @@ +/// +/// + +import assert = require('assert'); +import trm = require('../../lib/taskRunner'); +import path = require('path'); + +function setResponseFile(name: string) { + process.env['MOCK_RESPONSES'] = path.join(__dirname, name); +} + +describe('XamarinLicense Suite', function() { + this.timeout(20000); + + before((done) => { + // init here + done(); + }); + + after(function() { + + }); + + // Unfortunately, this task is not just a wrapper around an EXE + // For this reason, we can't really mock what it does to handle all the + // interesting test cases. Here we test to make sure the inputs are checked + // and that the we don't fail until the first step of work is attempted + + it('Activate XamarinLicense with all default inputs', (done) => { + setResponseFile('responseEmpty.json'); + + var tr = new trm.TaskRunner('XamarinLicense', true, true); + tr.setInput('action', 'Activate'); + tr.setInput('email', 'me@ms.com'); + tr.setInput('password', 'mypass'); + tr.setInput('product', 'MA'); + tr.setInput('timeout', '30'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run any tools'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stdout.indexOf('##vso[task.issue type=error;]Failed to login to Xamarin with specified email and password.') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('Deactivate XamarinLicense with all default inputs', (done) => { + setResponseFile('responseEmpty.json'); + + var tr = new trm.TaskRunner('XamarinLicense', true, true); + tr.setInput('action', 'Deactivate'); + tr.setInput('email', 'me@ms.com'); + tr.setInput('password', 'mypass'); + tr.setInput('product', 'MA'); + tr.setInput('timeout', '30'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run any tools'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stdout.indexOf('##vso[task.issue type=error;]Failed to login to Xamarin with specified email and password.') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('Fails for missing action', (done) => { + setResponseFile('responseEmpty.json'); + + var tr = new trm.TaskRunner('XamarinLicense', true, true); + //tr.setInput('action', ''); + tr.setInput('email', 'me@ms.com'); + tr.setInput('password', 'mypass'); + tr.setInput('product', 'MA'); + tr.setInput('timeout', '30'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run any tools'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: action') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('Fails for unknown action', (done) => { + setResponseFile('responseEmpty.json'); + + var tr = new trm.TaskRunner('XamarinLicense', true, true); + tr.setInput('action', 'unknown'); + tr.setInput('email', 'me@ms.com'); + tr.setInput('password', 'mypass'); + tr.setInput('product', 'MA'); + tr.setInput('timeout', '30'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run any tools'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stdout.indexOf('##vso[task.issue type=error;]Unknown action: unknown') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('Fails for missing email', (done) => { + setResponseFile('responseEmpty.json'); + + var tr = new trm.TaskRunner('XamarinLicense', true, true); + tr.setInput('action', 'Activate'); + //tr.setInput('email', 'me@ms.com'); + tr.setInput('password', 'mypass'); + tr.setInput('product', 'MA'); + tr.setInput('timeout', '30'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run any tools'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: email') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('Fails for missing password', (done) => { + setResponseFile('responseEmpty.json'); + + var tr = new trm.TaskRunner('XamarinLicense', true, true); + tr.setInput('action', 'Activate'); + tr.setInput('email', 'me@ms.com'); + //tr.setInput('password', 'mypass'); + tr.setInput('product', 'MA'); + tr.setInput('timeout', '30'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run any tools'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: password') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('Fails for missing product', (done) => { + setResponseFile('responseEmpty.json'); + + var tr = new trm.TaskRunner('XamarinLicense', true, true); + tr.setInput('action', 'Activate'); + tr.setInput('email', 'me@ms.com'); + tr.setInput('password', 'mypass'); + //tr.setInput('product', 'MA'); + tr.setInput('timeout', '30'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run any tools'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stdout.indexOf('##vso[task.issue type=error;]No product selected to activate.') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) +}); \ No newline at end of file diff --git a/Tests/L0/XamarinLicense/responseEmpty.json b/Tests/L0/XamarinLicense/responseEmpty.json new file mode 100644 index 000000000000..7a73a41bfdf7 --- /dev/null +++ b/Tests/L0/XamarinLicense/responseEmpty.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Tests/L0/XamarinTestCloud/_suite.ts b/Tests/L0/XamarinTestCloud/_suite.ts new file mode 100644 index 000000000000..c8d79739ae31 --- /dev/null +++ b/Tests/L0/XamarinTestCloud/_suite.ts @@ -0,0 +1,863 @@ +/// +/// + +import assert = require('assert'); +import trm = require('../../lib/taskRunner'); +import path = require('path'); + +function setResponseFile(name: string) { + process.env['MOCK_RESPONSES'] = path.join(__dirname, name); +} + +describe('XamarinTestCloud Suite', function() { + this.timeout(20000); + + before((done) => { + // init here + done(); + }); + + after(function() { + + }); + + it('run XamarinTestCloud with all default inputs', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + // optional inputs + //tr.setInput('dsym', ''); // iOS only + //tr.setInput('userDefinedLocale', ''); // shown when locale = user + //tr.setInput('optionalArgs', ''); + //tr.setInput('publishNUnitResults', ''); // boolean + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin/project.apk key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin'), 'it should have run xamarinTestCloud'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinTestCloud 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + // check all required inputs + it('fails when app is missing', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + //tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinTestCloud'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: app') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when teamApiKey is missing', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + //tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinTestCloud'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: teamApiKey') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when user is missing', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + //tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinTestCloud'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: user') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when devices is missing', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + //tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinTestCloud'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: devices') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when series is missing', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + //tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinTestCloud'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: series') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when testDir is missing', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + //tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinTestCloud'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: testDir') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when parallelization is missing', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + //tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinTestCloud'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: parallelization') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when locale is missing', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + //tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinTestCloud'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: locale') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when testCloudLocation is missing', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + //tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinTestCloud'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: testCloudLocation') >= 0, 'wrong error message: "' + tr.stderr + '"'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when app does not exist', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project2.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinTestCloud'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stdout.indexOf('##vso[task.issue type=error;]The specified app file does not exist: bin/project2.apk') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('runs when app pattern matches 1', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', '**/*Single.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin/project.apk key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin'), 'it should have run xamarinTestCloud'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinTestCloud 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('runs when app pattern matches 1', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', '**/*Multiple.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin/project.apk key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin'), 'it should have run xamarinTestCloud'); + assert(tr.ran('/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin2/project.apk key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin'), 'it should have run xamarinTestCloud'); + assert(tr.ran('/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin3/project.apk key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin'), 'it should have run xamarinTestCloud'); + assert(tr.invokedToolCount == 3, 'should have only run XamarinTestCloud 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when app pattern matches 0', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', '**/*None.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinTestCloud'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stdout.indexOf('##vso[task.issue type=error;]No matching app files were found with search pattern: **/*None.apk') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when testDir does not exist', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin2'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinTestCloud'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stdout.indexOf('##vso[task.issue type=error;]The test assembly directory does not exist: tests/bin2') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when testCloudLocation does not end in test-cloud.exe', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/test-fails-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinTestCloud'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stdout.indexOf('##vso[task.issue type=error;]test-cloud.exe location must end with \'/test-cloud.exe\'') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('runs when testcloudlocation does not have a pattern', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '/home/build/packages/project1/tools/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin/project.apk key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin'), 'it should have run xamarinTestCloud'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinTestCloud 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when testCloudLocation does not exist', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '/doesntexist/test-cloud.exe'); + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinTestCloud'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stdout.indexOf('##vso[task.issue type=error;]test-cloud.exe does not exist at the specified location: /doesntexist/test-cloud.exe') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('runs when testCloudLocation matches 2', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/matches/2/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin/project.apk key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin'), 'it should have run xamarinTestCloud'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinTestCloud 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when testCloudLocation matches 0', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/doesntexist/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinTestCloud'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stdout.indexOf('##vso[task.issue type=error;]test-cloud.exe could not be found with search pattern **/doesntexist/test-cloud.exe') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when mono does not exist', (done) => { + setResponseFile('responseEmpty.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamarinTestCloud'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('not found mono') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + //TODO build.buildId has a value + //TODO build.buildId has a space + + it('runs when dsym is set (but no .ipa files)', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + // optional inputs + tr.setInput('dsym', '**/bin/*.dsym'); // iOS only + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin/project.apk key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin'), 'it should have run xamarinTestCloud'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinTestCloud 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('runs when dsym matches 1', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.ipa'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + // optional inputs + tr.setInput('dsym', '**/bin/*Single.dsym'); // iOS only + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin/project.ipa key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin --dsym /bin/project1.dsym'), 'it should have run xamarinTestCloud'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinTestCloud 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('runs when dsym matches 2 (with warning)', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.ipa'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + // optional inputs + tr.setInput('dsym', '**/bin/*Multiple.dsym'); // iOS only + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin/project.ipa key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin'), 'it should have run xamarinTestCloud'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinTestCloud 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf('##vso[task.issue type=warning;]More than one matching dSYM file was found with pattern: **/bin/*Multiple.dsym') >= 0, 'wrong error message'); + + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('runs when dsym matches 0 (with warning)', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.ipa'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + // optional inputs + tr.setInput('dsym', '**/bin/*None.dsym'); // iOS only + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin/project.ipa key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin'), 'it should have run xamarinTestCloud'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinTestCloud 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf('##vso[task.issue type=warning;]No matching dSYM files were found with pattern: **/bin/*None.dsym') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('runs when dsym is not a pattern', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.ipa'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + // optional inputs + tr.setInput('dsym', '/bin/NoPattern.dsym'); // iOS only + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin/project.ipa key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin'), 'it should have run xamarinTestCloud'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinTestCloud 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf('##vso[task.issue type=warning;]No matching dSYM files were found with pattern: /bin/NoPattern.dsym') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('runs XamarinTestCloud when publishNUnitResults is true', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + // optional inputs + tr.setInput('publishNUnitResults', 'true'); // boolean + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin/project.apk key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin --nunit-xml tests/bin/xamarintest_undefined.0.xml'), + 'it should have run xamarinTestCloud'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinTestCloud 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when return code is non-zero', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamarinTestCloud', true, true); + // required inputs + tr.setInput('app', 'bin/FAIL/project.apk'); + tr.setInput('teamApiKey', 'key1'); + tr.setInput('user', 'me@ms.com'); + tr.setInput('devices', 'devices1'); + tr.setInput('series', 'master'); + tr.setInput('testDir', 'tests/bin'); + tr.setInput('parallelization', 'none'); + tr.setInput('locale', 'en_US'); + tr.setInput('testCloudLocation', '**/packages/**/tools/test-cloud.exe'); + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin/FAIL/project.apk key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin'), 'it should have run xamarinTestCloud'); + assert(tr.invokedToolCount == 1, 'should have only run XamarinTestCloud 1 time'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stdout.indexOf('FAILED') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) +}); \ No newline at end of file diff --git a/Tests/L0/XamarinTestCloud/response.json b/Tests/L0/XamarinTestCloud/response.json new file mode 100644 index 000000000000..72baaae8d71b --- /dev/null +++ b/Tests/L0/XamarinTestCloud/response.json @@ -0,0 +1,82 @@ +{ + "which": { + "mono": "/home/bin/mono" + }, + "exec": { + "/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin/project.apk key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin": { + "code": 0, + "stdout": "Xamarin test cloud" + }, + "/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin/FAIL/project.apk key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin": { + "code": 2, + "stdout": "FAILED" + }, + "/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin2/project.apk key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin": { + "code": 0, + "stdout": "Xamarin test cloud" + }, + "/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin3/project.apk key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin": { + "code": 0, + "stdout": "Xamarin test cloud" + }, + "/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin/project.ipa key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin --dsym /bin/project1.dsym": { + "code": 0, + "stdout": "Xamarin test cloud" + }, + "/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin/project.ipa key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin": { + "code": 0, + "stdout": "Xamarin test cloud" + }, + "/home/bin/mono /home/build/packages/project1/tools/test-cloud.exe submit bin/project.apk key1 --user me@ms.com --devices devices1 --series master --locale en_US --assembly-dir tests/bin --nunit-xml tests/bin/xamarintest_undefined.0.xml": { + "code": 0, + "stdout": "Xamarin test cloud" + } + }, + "checkPath" : { + "/home/bin/mono": true, + "bin/project.apk": true, + "bin/FAIL/project.apk": true, + "bin/project.ipa": true + }, + "exist" : { + "bin/project.apk": true, + "bin/FAIL/project.apk": true, + "bin/project.ipa": true, + "tests/bin": true, + "/home/build/packages/project1/tools/test-cloud.exe": true + }, + "find" : { + "/home/build": [ + "/home/build/packages/project1/tools/test-cloud.exe" + ] + }, + "getVariable" : { + "agent.buildDirectory": "/home/build" + }, + "match": { + "**/packages/**/tools/test-cloud.exe": [ + "/home/build/packages/project1/tools/test-cloud.exe" + ], + "**/matches/2/test-cloud.exe": [ + "/home/build/packages/project1/tools/test-cloud.exe", + "/home/build/packages/project2/tools/test-cloud.exe" + ], + "**/test-fails-cloud.exe": [ + ], + "**/*Single.apk": [ + "bin/project.apk" + ], + "**/*Multiple.apk": [ + "bin/project.apk", + "bin2/project.apk", + "bin3/project.apk" + ], + "**/bin/*Single.dsym": [ + "/bin/project1.dsym" + ], + "**/bin/*Multiple.dsym": [ + "/bin/project1.dsym", + "/bin/project2.dsym" + ] + } +} \ No newline at end of file diff --git a/Tests/L0/XamarinTestCloud/responseEmpty.json b/Tests/L0/XamarinTestCloud/responseEmpty.json new file mode 100644 index 000000000000..7a73a41bfdf7 --- /dev/null +++ b/Tests/L0/XamarinTestCloud/responseEmpty.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Tests/L0/XamariniOS/_suite.ts b/Tests/L0/XamariniOS/_suite.ts new file mode 100644 index 000000000000..788c3141a0ff --- /dev/null +++ b/Tests/L0/XamariniOS/_suite.ts @@ -0,0 +1,306 @@ +/// +/// + +import assert = require('assert'); +import trm = require('../../lib/taskRunner'); +import path = require('path'); + +function setResponseFile(name: string) { + process.env['MOCK_RESPONSES'] = path.join(__dirname, name); +} + +describe('XamariniOS Suite', function() { + this.timeout(20000); + + before((done) => { + // init here + done(); + }); + + after(function() { + + }); + + it('run XamariniOS with all default inputs', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamariniOS', true, true); + // Required inputs + tr.setInput('solution', 'src/project.sln'); //path + tr.setInput('configuration', 'Release'); + // Optional inputs + tr.setInput('args', ''); + tr.setInput('packageApp', ''); //boolean + tr.setInput('forSimulator', ''); //boolean + tr.setInput('mdtoolLocation', ''); + tr.setInput('unlockDefaultKeychain', ''); //boolean + tr.setInput('defaultKeychainPassword', ''); + tr.setInput('p12', ''); //path + tr.setInput('p12pwd', ''); + tr.setInput('iosSigningIdentity', ''); + tr.setInput('provProfileUuid', ''); + tr.setInput('provProfile', ''); //path + tr.setInput('removeProfile', ''); //boolean + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/nuget restore src/project.sln'), 'it should have run nuget restore'); + assert(tr.ran('/home/bin/xbuild src/project.sln /p:Configuration=Release /p:Platform=iPhone'), 'it should have run xbuild'); + assert(tr.invokedToolCount == 2, 'should have only run 2 commands'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('run XamariniOS with mdtoolLocation set', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamariniOS', true, true); + // Required inputs + tr.setInput('solution', 'src/project.sln'); //path + tr.setInput('configuration', 'Release'); + // Optional inputs + tr.setInput('args', ''); + tr.setInput('packageApp', ''); //boolean + tr.setInput('forSimulator', ''); //boolean + tr.setInput('mdtoolLocation', '/home/bin2/'); + tr.setInput('unlockDefaultKeychain', ''); //boolean + tr.setInput('defaultKeychainPassword', ''); + tr.setInput('p12', ''); //path + tr.setInput('p12pwd', ''); + tr.setInput('iosSigningIdentity', ''); + tr.setInput('provProfileUuid', ''); + tr.setInput('provProfile', ''); //path + tr.setInput('removeProfile', ''); //boolean + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/nuget restore src/project.sln'), 'it should have run nuget restore'); + assert(tr.ran('/home/bin2/xbuild src/project.sln /p:Configuration=Release /p:Platform=iPhone'), 'it should have run xbuild'); + assert(tr.invokedToolCount == 2, 'should have only run 2 commands'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when solution is a pattern', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamariniOS', true, true); + // Required inputs + tr.setInput('solution', '**/*.sln'); //path + tr.setInput('configuration', 'Release'); + // Optional inputs + tr.setInput('args', ''); + tr.setInput('packageApp', ''); //boolean + tr.setInput('forSimulator', ''); //boolean + tr.setInput('mdtoolLocation', ''); + tr.setInput('unlockDefaultKeychain', ''); //boolean + tr.setInput('defaultKeychainPassword', ''); + tr.setInput('p12', ''); //path + tr.setInput('p12pwd', ''); + tr.setInput('iosSigningIdentity', ''); + tr.setInput('provProfileUuid', ''); + tr.setInput('provProfile', ''); //path + tr.setInput('removeProfile', ''); //boolean + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamariniOS'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('not found solution: **/*.sln') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when solution is missing', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamariniOS', true, true); + // Required inputs + tr.setInput('solution', ''); //path + tr.setInput('configuration', 'Release'); + // Optional inputs + tr.setInput('args', ''); + tr.setInput('packageApp', ''); //boolean + tr.setInput('forSimulator', ''); //boolean + tr.setInput('mdtoolLocation', ''); + tr.setInput('unlockDefaultKeychain', ''); //boolean + tr.setInput('defaultKeychainPassword', ''); + tr.setInput('p12', ''); //path + tr.setInput('p12pwd', ''); + tr.setInput('iosSigningIdentity', ''); + tr.setInput('provProfileUuid', ''); + tr.setInput('provProfile', ''); //path + tr.setInput('removeProfile', ''); //boolean + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamariniOS'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: solution') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when solution is missing', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamariniOS', true, true); + // Required inputs + tr.setInput('solution', 'src/project.sln'); //path + tr.setInput('configuration', ''); + // Optional inputs + tr.setInput('args', ''); + tr.setInput('packageApp', ''); //boolean + tr.setInput('forSimulator', ''); //boolean + tr.setInput('mdtoolLocation', ''); + tr.setInput('unlockDefaultKeychain', ''); //boolean + tr.setInput('defaultKeychainPassword', ''); + tr.setInput('p12', ''); //path + tr.setInput('p12pwd', ''); + tr.setInput('iosSigningIdentity', ''); + tr.setInput('provProfileUuid', ''); + tr.setInput('provProfile', ''); //path + tr.setInput('removeProfile', ''); //boolean + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamariniOS'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('Input required: configuration') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when xbuildLocation not provided and xbuild is not found', (done) => { + setResponseFile('responseNoToolsFound.json'); + + var tr = new trm.TaskRunner('XamariniOS', true, true); + // Required inputs + tr.setInput('solution', 'src/project.sln'); //path + tr.setInput('configuration', 'Release'); + // Optional inputs + tr.setInput('args', ''); + tr.setInput('packageApp', ''); //boolean + tr.setInput('forSimulator', ''); //boolean + tr.setInput('mdtoolLocation', ''); + tr.setInput('unlockDefaultKeychain', ''); //boolean + tr.setInput('defaultKeychainPassword', ''); + tr.setInput('p12', ''); //path + tr.setInput('p12pwd', ''); + tr.setInput('iosSigningIdentity', ''); + tr.setInput('provProfileUuid', ''); + tr.setInput('provProfile', ''); //path + tr.setInput('removeProfile', ''); //boolean + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamariniOS'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('not found xbuild: null') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + it('fails when xbuildLocation is provided but is incorrect', (done) => { + setResponseFile('response.json'); + + var tr = new trm.TaskRunner('XamariniOS', true, true); + // Required inputs + tr.setInput('solution', 'src/project.sln'); //path + tr.setInput('configuration', 'Release'); + // Optional inputs + tr.setInput('args', ''); + tr.setInput('packageApp', ''); //boolean + tr.setInput('forSimulator', ''); //boolean + tr.setInput('mdtoolLocation', '/user/bin/'); + tr.setInput('unlockDefaultKeychain', ''); //boolean + tr.setInput('defaultKeychainPassword', ''); + tr.setInput('p12', ''); //path + tr.setInput('p12pwd', ''); + tr.setInput('iosSigningIdentity', ''); + tr.setInput('provProfileUuid', ''); + tr.setInput('provProfile', ''); //path + tr.setInput('removeProfile', ''); //boolean + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamariniOS'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('not found xbuild: /user/bin/xbuild') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) + + // fails when nuget not found + it('fails when nuget not found', (done) => { + setResponseFile('responseNoToolsFound.json'); + + var tr = new trm.TaskRunner('XamariniOS', true, true); + // Required inputs + tr.setInput('solution', 'src/project.sln'); //path + tr.setInput('configuration', 'Release'); + // Optional inputs + tr.setInput('args', ''); + tr.setInput('packageApp', ''); //boolean + tr.setInput('forSimulator', ''); //boolean + tr.setInput('mdtoolLocation', '/home/bin2'); + tr.setInput('unlockDefaultKeychain', ''); //boolean + tr.setInput('defaultKeychainPassword', ''); + tr.setInput('p12', ''); //path + tr.setInput('p12pwd', ''); + tr.setInput('iosSigningIdentity', ''); + tr.setInput('provProfileUuid', ''); + tr.setInput('provProfile', ''); //path + tr.setInput('removeProfile', ''); //boolean + + tr.run() + .then(() => { + assert(tr.invokedToolCount == 0, 'should not have run XamariniOS'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length > 0, 'should have written to stderr'); + assert(tr.failed, 'task should have failed'); + assert(tr.stderr.indexOf('not found nuget: null') >= 0, 'wrong error message'); + done(); + }) + .fail((err) => { + done(err); + }); + }) +}); \ No newline at end of file diff --git a/Tests/L0/XamariniOS/response.json b/Tests/L0/XamariniOS/response.json new file mode 100644 index 000000000000..83eb3f5c9abf --- /dev/null +++ b/Tests/L0/XamariniOS/response.json @@ -0,0 +1,26 @@ +{ + "which": { + "xbuild": "/home/bin/xbuild", + "nuget": "/home/bin/nuget" + }, + "exec": { + "/home/bin/nuget restore src/project.sln": { + "code": 0, + "stdout": "nuget restore" + }, + "/home/bin/xbuild src/project.sln /p:Configuration=Release /p:Platform=iPhone": { + "code": 0, + "stdout": "xbuild" + }, + "/home/bin2/xbuild src/project.sln /p:Configuration=Release /p:Platform=iPhone": { + "code": 0, + "stdout": "xbuild" + } + }, + "checkPath" : { + "/home/bin/xbuild": true, + "/home/bin2/xbuild": true, + "/home/bin/nuget": true, + "src/project.sln": true + } +} \ No newline at end of file diff --git a/Tests/L0/XamariniOS/responseNoToolsFound.json b/Tests/L0/XamariniOS/responseNoToolsFound.json new file mode 100644 index 000000000000..f051be4dbfb7 --- /dev/null +++ b/Tests/L0/XamariniOS/responseNoToolsFound.json @@ -0,0 +1,6 @@ +{ + "checkPath" : { + "/home/bin2/xbuild": true, + "src/project.sln": true + } +} \ No newline at end of file diff --git a/Tests/lib/taskRunner.ts b/Tests/lib/taskRunner.ts index bed8d4152af4..ed719bb684c0 100644 --- a/Tests/lib/taskRunner.ts +++ b/Tests/lib/taskRunner.ts @@ -18,7 +18,7 @@ function debug(message) { } export class TaskRunner extends events.EventEmitter { - constructor(name: string, ignoreSlashes?: boolean) { + constructor(name: string, normalizeSlashes?: boolean, ignoreTempPathsInResponse?: boolean) { super(); this._inputs = {}; this._name = name; @@ -32,7 +32,8 @@ export class TaskRunner extends events.EventEmitter { this.stderr = ''; this._tempPath = process.env['TASK_TEST_TEMP']; this._commands = []; - this._ignoreSlashes = ignoreSlashes; + this._normalizeSlashes = normalizeSlashes; + this._ignoreTempPathsInResponse = ignoreTempPathsInResponse; } public succeeded: boolean; @@ -50,15 +51,12 @@ export class TaskRunner extends events.EventEmitter { private _taskPath: string; private _tempPath: string; private _commands: string[]; - private _ignoreSlashes: boolean; + private _normalizeSlashes: boolean; + private _ignoreTempPathsInResponse: boolean; public ran(cmdLine: string): boolean { var executed: boolean = false; this._commands.forEach((cmd: string)=>{ - if (this._ignoreSlashes) { - cmdLine = cmdLine.replace(/\\/g, "/"); - cmd = cmd.replace(/\\/g, "/"); - } if(cmdLine.trim().localeCompare(cmd.trim()) === 0) { executed = true; } @@ -150,9 +148,6 @@ export class TaskRunner extends events.EventEmitter { if (line.indexOf('[command]') >= 0) { ++this.invokedToolCount; var command = line.substr(line.indexOf('[command]') + '[command]'.length).trim(); - if (this._ignoreSlashes) { - command = command.replace(/\\/g, "/"); - } this._commands.push(command); } @@ -187,6 +182,12 @@ export class TaskRunner extends events.EventEmitter { var envVarName = 'INPUT_' + key.replace(' ', '_').toUpperCase(); this._taskEnv[envVarName] = this._inputs[key]; } + + // Add additional environment variables based on test requirements + // These variables can be used by the mocked task-lib classes + this._taskEnv['MOCK_TEMP_PATH'] = this._tempPath; + this._taskEnv['MOCK_IGNORE_TEMP_PATH'] = this._ignoreTempPathsInResponse; + this._taskEnv['MOCK_NORMALIZE_SLASHES'] = this._normalizeSlashes; // // Run the task via node @@ -214,16 +215,24 @@ export class TaskRunner extends events.EventEmitter { return; } - this._processOutput(stdout.toString(), stderr.toString()); + var standardOut = stdout.toString(); + var standardErr = stderr.toString(); + + if (this._normalizeSlashes) { + standardOut = standardOut.replace(/\\/g, "/"); + standardErr = standardErr.replace(/\\/g, "/"); + } + + this._processOutput(standardOut, standardErr); if (stdout) { debug('stdout:'); - debug(stdout); + debug(standardOut); } if (stderr) { debug('stderr:'); - debug(stderr); + debug(standardErr); } defer.resolve(null); diff --git a/Tests/lib/vsts-task-lib/mock.ts b/Tests/lib/vsts-task-lib/mock.ts index 89d83a858bf5..281849cf151e 100644 --- a/Tests/lib/vsts-task-lib/mock.ts +++ b/Tests/lib/vsts-task-lib/mock.ts @@ -17,7 +17,7 @@ export function getResponse(cmd: string, key: string): any { return null; } - if (!answers[cmd][key] && key) { + if (!answers[cmd][key] && key && process.env['MOCK_NORMALIZE_SLASHES'] === 'true') { // try normalizing the slashes var key2 = key.replace(/\\/g, "/"); if (answers[cmd][key2]) { diff --git a/Tests/lib/vsts-task-lib/task.ts b/Tests/lib/vsts-task-lib/task.ts index 18f034d3a2aa..dab516dac3ec 100644 --- a/Tests/lib/vsts-task-lib/task.ts +++ b/Tests/lib/vsts-task-lib/task.ts @@ -476,7 +476,11 @@ export function mkdirP(p): void { } export function which(tool: string, check?: boolean): string { - return mock.getResponse('which', tool); + var response = mock.getResponse('which', tool); + if (check) { + checkPath(response, tool); + } + return response; } export function cp(options, source: string, dest: string): void { diff --git a/Tests/lib/vsts-task-lib/toolrunner.ts b/Tests/lib/vsts-task-lib/toolrunner.ts index e7cc11b77464..78ca4801c7a4 100644 --- a/Tests/lib/vsts-task-lib/toolrunner.ts +++ b/Tests/lib/vsts-task-lib/toolrunner.ts @@ -4,6 +4,7 @@ import Q = require('q'); import os = require('os'); +import path = require('path'); import events = require('events'); import mock = require('./mock'); @@ -182,12 +183,19 @@ export class ToolRunner extends events.EventEmitter { cmdString += (' ' + argString); } + this._debug('ignoreTempPath=' + process.env['MOCK_IGNORE_TEMP_PATH']); + this._debug('tempPath=' + process.env['MOCK_TEMP_PATH']); + if (process.env['MOCK_IGNORE_TEMP_PATH'] === 'true') { + // Using split/join to replace the temp path + cmdString = cmdString.split(process.env['MOCK_TEMP_PATH']).join(''); + } + if (!ops.silent) { ops.outStream.write('[command]' + cmdString + os.EOL); } // TODO: filter process.env - + var res = mock.getResponse('exec', cmdString); //console.log(JSON.stringify(res, null, 2)); if (res.stdout) {