Skip to content

Commit 436afab

Browse files
authored
Merge pull request #1524 from Microsoft/billti/test-adapter-improvements
Test adapter improvements
2 parents 0c5de69 + 8ff4068 commit 436afab

File tree

7 files changed

+629
-195
lines changed

7 files changed

+629
-195
lines changed

Common/Product/SharedProject/ProcessOutput.cs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ public virtual void Show() {
5858
/// </summary>
5959
public virtual void ShowAndActivate() {
6060
}
61+
62+
/// <summary>
63+
/// Called to determine if stdin should be closed for a redirected process.
64+
/// The default is true.
65+
/// </summary>
66+
public virtual bool CloseStandardInput()
67+
{
68+
return true;
69+
}
6170
}
6271

6372
sealed class TeeRedirector : Redirector, IDisposable {
@@ -304,7 +313,7 @@ public static ProcessOutput RunElevated(
304313
return result;
305314
}
306315

307-
private static string GetArguments(IEnumerable<string> arguments, bool quoteArgs) {
316+
public static string GetArguments(IEnumerable<string> arguments, bool quoteArgs) {
308317
if (quoteArgs) {
309318
return string.Join(" ", arguments.Where(a => a != null).Select(QuoteSingleArgument));
310319
} else {
@@ -335,7 +344,7 @@ internal static IEnumerable<string> SplitLines(string source) {
335344
}
336345
}
337346

338-
internal static string QuoteSingleArgument(string arg) {
347+
public static string QuoteSingleArgument(string arg) {
339348
if (string.IsNullOrEmpty(arg)) {
340349
return "\"\"";
341350
}
@@ -423,10 +432,16 @@ private ProcessOutput(Process process, Redirector redirector) {
423432

424433
if (_process.StartInfo.RedirectStandardInput) {
425434
// Close standard input so that we don't get stuck trying to read input from the user.
426-
try {
427-
_process.StandardInput.Close();
428-
} catch (InvalidOperationException) {
429-
// StandardInput not available
435+
if (_redirector == null || (_redirector != null && _redirector.CloseStandardInput()))
436+
{
437+
try
438+
{
439+
_process.StandardInput.Close();
440+
}
441+
catch (InvalidOperationException)
442+
{
443+
// StandardInput not available
444+
}
430445
}
431446
}
432447
}
@@ -557,6 +572,20 @@ public Redirector Redirector {
557572
get { return _redirector; }
558573
}
559574

575+
/// <summary>
576+
/// Writes a line to stdin. A redirector must have been provided that indicates not
577+
/// to close the StandardInput stream.
578+
/// </summary>
579+
/// <param name="line"></param>
580+
public void WriteInputLine(string line)
581+
{
582+
if (IsStarted && _redirector != null && !_redirector.CloseStandardInput())
583+
{
584+
_process.StandardInput.WriteLine(line);
585+
_process.StandardInput.Flush();
586+
}
587+
}
588+
560589
private void FlushAndCloseOutput() {
561590
if (_process == null) {
562591
return;

Common/Product/TestAdapter/VisualStudioApp.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
using System;
1616
using System.Collections.Generic;
1717
using System.Diagnostics.CodeAnalysis;
18+
using System.IO;
1819
using System.Linq;
20+
using System.Reflection;
1921
using System.Runtime.InteropServices;
2022
using EnvDTE;
2123
using Microsoft.VisualStudio.OLE.Interop;
@@ -75,7 +77,35 @@ public DTE GetDTE() {
7577
return dte;
7678
}
7779

80+
#if DEV15
81+
private static bool DTELoaded = false;
82+
#endif
83+
7884
private static DTE GetDTE(int processId) {
85+
#if DEV15
86+
// VS 2017 doesn't install some assemblies to the GAC that are needed to work with the
87+
// debugger, and as the tests don't execute in the devenv.exe process, those assemblies
88+
// fail to load - so load them manually from PublicAssemblies.
89+
90+
// Use the executable name, as this is only needed for the out of proc test execution
91+
// that may interact with the debugger (vstest.executionengine.x86.exe).
92+
if (!DTELoaded)
93+
{
94+
string currentProc = Process.GetCurrentProcess().MainModule.FileName;
95+
if (StringComparer.OrdinalIgnoreCase.Equals(
96+
Path.GetFileName(currentProc), "vstest.executionengine.x86.exe"))
97+
{
98+
string baseDir = Path.GetDirectoryName(currentProc);
99+
string publicAssemblies = Path.Combine(baseDir, "..\\..\\..\\PublicAssemblies");
100+
101+
Assembly.LoadFrom(Path.Combine(publicAssemblies, "Microsoft.VisualStudio.OLE.Interop.dll"));
102+
Assembly.LoadFrom(Path.Combine(publicAssemblies, "envdte90.dll"));
103+
Assembly.LoadFrom(Path.Combine(publicAssemblies, "envdte80.dll"));
104+
Assembly.LoadFrom(Path.Combine(publicAssemblies, "envdte.dll"));
105+
}
106+
DTELoaded = true;
107+
}
108+
#endif
79109
MessageFilter.Register();
80110

81111
var prefix = Process.GetProcessById(processId).ProcessName;

Nodejs/Product/Nodejs/TestFrameworks/ExportRunner/exportrunner.js

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,26 @@
11
var fs = require('fs');
22
var path = require('path');
33
var vm = require('vm');
4+
var result = {
5+
'title': '',
6+
'passed': false,
7+
'stdOut': '',
8+
'stdErr': ''
9+
};
10+
11+
function append_stdout(string, encoding, fd) {
12+
result.stdOut += string;
13+
}
14+
function append_stderr(string, encoding, fd) {
15+
result.stdErr += string;
16+
}
17+
function hook_outputs() {
18+
process.stdout.write = append_stdout;
19+
process.stderr.write = append_stderr;
20+
}
21+
22+
23+
hook_outputs();
424

525
var find_tests = function (testFileList, discoverResultFile) {
626
var debug;
@@ -50,8 +70,43 @@ var find_tests = function (testFileList, discoverResultFile) {
5070
};
5171
module.exports.find_tests = find_tests;
5272

53-
var run_tests = function (testName, testFile) {
54-
var testCase = require(testFile);
55-
testCase[testName]();
73+
var run_tests = function (testCases, callback) {
74+
function post(event) {
75+
callback(event);
76+
hook_outputs();
77+
}
78+
79+
for (var test of testCases) {
80+
post({
81+
type: 'test start',
82+
title: test.testName
83+
});
84+
try {
85+
var testCase = require(test.testFile);
86+
result.title = test.testName;
87+
testCase[test.testName]();
88+
result.passed = true;
89+
} catch (err) {
90+
result.passed = false;
91+
console.error(err.name);
92+
console.error(err.message);
93+
}
94+
post({
95+
type: 'result',
96+
title: test.testName,
97+
result: result
98+
});
99+
result = {
100+
'title': '',
101+
'passed': false,
102+
'stdOut': '',
103+
'stdErr': ''
104+
};
105+
}
106+
callback({
107+
type: 'suite end',
108+
result: result
109+
});
110+
process.exit();
56111
};
57112
module.exports.run_tests = run_tests;

Nodejs/Product/Nodejs/TestFrameworks/Tape/tape.js

Lines changed: 83 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@
22
var EOL = require('os').EOL;
33
var fs = require('fs');
44
var path = require('path');
5+
var result = {
6+
'title': '',
7+
'passed': false,
8+
'stdOut': '',
9+
'stdErr': ''
10+
};
11+
12+
function append_stdout(string, encoding, fd) {
13+
result.stdOut += string;
14+
}
15+
16+
function append_stderr(string, encoding, fd) {
17+
result.stdErr += string;
18+
}
519

620
function find_tests(testFileList, discoverResultFile, projectFolder) {
721
var test = findTape(projectFolder);
@@ -10,7 +24,7 @@ function find_tests(testFileList, discoverResultFile, projectFolder) {
1024
}
1125

1226
var harness = test.getHarness({ exit: false });
13-
var tests = harness["_tests"];
27+
var tests = harness['_tests'];
1428

1529
var count = 0;
1630
var testList = [];
@@ -37,24 +51,78 @@ function find_tests(testFileList, discoverResultFile, projectFolder) {
3751
};
3852
module.exports.find_tests = find_tests;
3953

40-
function run_tests(testName, testFile, workingFolder, projectFolder) {
41-
var testCases = loadTestCases(testFile);
42-
if (testCases === null) {
54+
function run_tests(testInfo, callback) {
55+
var tape = findTape(testInfo[0].projectFolder);
56+
if (tape === null) {
4357
return;
4458
}
4559

46-
var test = findTape(projectFolder);
47-
if (test === null) {
48-
return;
49-
}
60+
var harness = tape.getHarness({objectMode: true});
61+
var capture = false; // Only capture between 'test' and 'end' events to avoid skipped test events.
62+
harness.createStream({ objectMode: true }).on('data', function (evt){
63+
switch (evt.type) {
64+
case 'test':
65+
capture = true;
66+
// Test is starting. Reset the result object. Send a "test start" event.
67+
result = {
68+
'title': evt.name,
69+
'passed': true,
70+
'stdOut': '',
71+
'stdErr': ''
72+
};
73+
callback({
74+
'type': 'test start',
75+
'title': result.title,
76+
'result': result
77+
});
78+
break;
79+
case 'assert':
80+
if (!capture) break;
81+
// Correlate the success/failure asserts for this test. There may be multiple per test
82+
var msg = "Operator: " + evt.operator + ". Expected: " + evt.expected + ". Actual: " + evt.actual + "\n";
83+
if (evt.ok) {
84+
result.stdOut += msg;
85+
} else {
86+
result.stdErr += msg + (evt.error.stack || evt.error.message) + "\n";
87+
result.passed = false;
88+
}
89+
break;
90+
case 'end':
91+
if (!capture) break;
92+
// Test is done. Send a "result" event.
93+
callback({
94+
'type': 'result',
95+
'title': result.title,
96+
'result': result
97+
});
98+
capture = false;
99+
break;
100+
default:
101+
break;
102+
}
103+
});
50104

51-
try {
52-
var harness = test.getHarness();
53-
harness.only(testName);
54-
} catch (e) {
55-
logError("Error running test:", testName, "in", testFile, e);
56-
return;
57-
}
105+
loadTestCases(testInfo[0].testFile);
106+
107+
// Skip those not selected to run. The rest will start running on the next tick.
108+
harness['_tests'].forEach(function(test){
109+
if( !testInfo.some( function(ti){ return ti.testName == test.name; }) ) {
110+
test._skip = true;
111+
}
112+
});
113+
114+
harness.onFinish(function () {
115+
if (capture) {
116+
// Something didn't finish. Finish it now.
117+
result.passed = false;
118+
callback({
119+
'type': 'result',
120+
'title': result.title,
121+
'result': result
122+
});
123+
}
124+
process.exit(0);
125+
});
58126
}
59127
module.exports.run_tests = run_tests;
60128

0 commit comments

Comments
 (0)