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) {