-
Notifications
You must be signed in to change notification settings - Fork 357
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open Folder Unit Test #1959
Open Folder Unit Test #1959
Conversation
|
||
private async Task OnActiveWorkspaceChangedAsync(object sender, EventArgs e) | ||
{ | ||
this.currentWorkspace = this.workspaceService.CurrentWorkspace; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're not unsubscribing from the previous workspace's event handlers. Maybe that causes leaks, idk for certain. You will also want to subscribe to the new workspace's file watcher and index service. Each IWorkspace spins up a new instance of these services.
} | ||
} | ||
} | ||
this.TestContainersUpdated?.Invoke(this, EventArgs.Empty); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is being invoked outside of the lock (this.containerLock){ }
. Is that intentional? It's in the lock for FileSystemChangedAsync
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like it's outside the lock in both cases.
|
||
private static bool IsJavaScriptFile(string path) | ||
{ | ||
var ext = Path.GetExtension(path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're not checking if this is a directory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
var testRoot = packageJson.TestRoot; | ||
if (!string.IsNullOrEmpty(testRoot)) | ||
{ | ||
fileDataValues.Add(new FileDataValue(NodejsConstants.TestRootDataValueGuid, "TestRoot", testRoot)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"TestRoot" [](start = 96, length = 10)
Question, not suggestion: should this be a constant? #Closed
|
||
public string TestRoot { get; } | ||
|
||
public IEnumerable<Guid> DebugEngines { get { yield break; } } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yield break; [](start = 54, length = 12)
Unless there's an optimization I don't know about, this is way more expensive than Array.Empty
. #Closed
|
||
public int CompareTo(ITestContainer other) | ||
{ | ||
if (other == null || !(other is PackageJsonTestContainer testContainer)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is [](start = 41, length = 2)
is
includes a null check. #Closed
{ | ||
if (other == null || !(other is PackageJsonTestContainer testContainer)) | ||
{ | ||
return -1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-1 [](start = 23, length = 2)
This doesn't actually happen, does it? If it does, it seems unlikely that the comparison produces a consistent result when the operands are reversed. #Closed
|
||
var sourceCompare = StringComparer.OrdinalIgnoreCase.Compare(this.Source, testContainer.Source); | ||
|
||
return sourceCompare == 0 ? sourceCompare : StringComparer.OrdinalIgnoreCase.Compare(this.TestRoot, testContainer.TestRoot); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
== [](start = 33, length = 2)
!= 0
? #Closed
private readonly IVsFolderWorkspaceService workspaceService; | ||
|
||
private readonly List<PackageJsonTestContainer> containers = new List<PackageJsonTestContainer>(); | ||
private readonly object containerLock = new object(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
containerLock [](start = 32, length = 13)
I'm probably forgetting some framework guideline, but why not lock on containers
? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case locking on container would be fine, since it's not a public property. But I prefer to use the well-known pattern to avoid confusion in the future. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understood that pattern was for value types or public members of reference types, but I don't feel strongly about it.
In reply to: 192169424 [](ancestors = 192169424)
{ | ||
this.currentWorkspace = this.workspaceService.CurrentWorkspace; | ||
|
||
await AttemptUpdateAsync(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
await AttemptUpdateAsync(); [](start = 12, length = 27)
This seems to do a lot less work than the ctor. Maybe they should share a helper?
} | ||
} | ||
|
||
public Uri ExecutorUri { get; } = NodejsConstants.PackageJsonExecutorUri; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{ get; } = [](start = 31, length = 10)
=>
? #Closed
{ | ||
if (this.currentWorkspace != null) | ||
{ | ||
var indexServce = this.currentWorkspace.GetIndexWorkspaceService(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: indexService
#Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed #Closed
await AttemptUpdateAsync(); | ||
} | ||
|
||
private static bool IsJavaScriptFile(string path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IsJavaScriptFile [](start = 28, length = 16)
I'm skeptical that there isn't a helper for this or, at least, constants for known file extensions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There really isn't a helper for detecting JS files?
In reply to: 188796597 [](ancestors = 188796597)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope.
I'll refactor that in another PR
private readonly List<PackageJsonTestContainer> containers = new List<PackageJsonTestContainer>(); | ||
private readonly object containerLock = new object(); | ||
|
||
private IWorkspace currentWorkspace; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
currentWorkspace [](start = 27, length = 16)
If containers
is guarded by a lock, should this be as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to prevent the call to ToArray
in the TestContainers
property to now fail, because we would be changing the collection while converting it to an array.
foreach (var dataValue in filesDataValues) | ||
{ | ||
var rootFilePath = this.currentWorkspace.MakeRooted(dataValue.Key); | ||
var testRoot = dataValue.Value.Where(f => f.Name == "TestRoot").Select(f => f.Value).FirstOrDefault(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"TestRoot" [](start = 76, length = 10)
Constant for "TestRoot"
? #Closed
foreach (var dataValue in filesDataValues) | ||
{ | ||
var rootFilePath = this.currentWorkspace.MakeRooted(dataValue.Key); | ||
var testRoot = dataValue.Value.Where(f => f.Name == "TestRoot").Select(f => f.Value).FirstOrDefault(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.Select(f => f.Value).FirstOrDefault() [](start = 87, length = 38)
Alternatively, .FirstOrDefault()?.Value
. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed #Closed
|
||
namespace Microsoft.NodejsTools.TestAdapter | ||
{ | ||
[FileExtension(".json")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
".json" [](start = 19, length = 7)
Constant?
break; | ||
} | ||
} | ||
testFx = testFx ?? FrameworkDiscover.Intance.Get("ExportRunner"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intance [](start = 49, length = 7)
Typo: "Instance" #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed #Closed
break; | ||
} | ||
} | ||
testFx = testFx ?? FrameworkDiscover.Intance.Get("ExportRunner"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FrameworkDiscover.Intance.Get("ExportRunner") [](start = 31, length = 45)
Is there some reason to believe this isn't null? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is installed by us and doesn't require additional npm
packages. #Closed
} | ||
|
||
var fileList = Directory.GetFiles(testFolderPath, "*.js", SearchOption.AllDirectories); | ||
var files = string.Join(";", fileList); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var files = string.Join(";", fileList); [](start = 12, length = 39)
This seems likely to be expensive. Consider checking the logging level before evaluating it. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All log levels are always outputted, they only change the color of the output. #Closed
|
||
discoverySink.SendTestCase(testcase); | ||
} | ||
logger.SendMessage(TestMessageLevel.Informational, "Processing finished for framework 'mocha'."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mocha [](start = 99, length = 5)
What's the connection to mocha? #Closed
{ | ||
private List<TestCase> currentTests; | ||
private IFrameworkHandle frameworkHandle; | ||
private TestResult currentResult = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
= null [](start = 40, length = 7)
How come some of the fields are initialized with their default values? #Closed
{ | ||
var receiver = new JavaScriptTestExecutor.TestReceiver(); | ||
var discoverer = new PackageJsonTestDiscoverer(); | ||
discoverer.DiscoverTests(sources, null, frameworkHandle, receiver); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
null [](start = 46, length = 4)
A named argument might help. #Closed
foreach (var test in tests) | ||
{ | ||
var args = new List<string>(); | ||
args.AddRange(GetInterpreterArgs(test, workingDir, workingDir)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GetInterpreterArgs [](start = 30, length = 18)
Could you just call ToList
(or ToArray
) on GetInterpreterArgs
? #Closed
result.Messages.Add(new TestResultMessage(TestResultMessage.AdditionalInfoCategory, string.Join(Environment.NewLine, standardErrorLines))); | ||
frameworkHandle.RecordResult(result); | ||
frameworkHandle.RecordEnd(test, result.Outcome); | ||
this.currentTests.Remove(test); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove [](start = 30, length = 6)
It seems strange to remove from a random order list. Is this the correct collection type? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🕐
private IFrameworkHandle frameworkHandle; | ||
private TestResult currentResult = null; | ||
private ResultObject currentResultObject = null; | ||
private TestExecutorWorker worker; | ||
|
||
public void RunTests(IEnumerable<TestCase> tests, IRunContext runContext, IFrameworkHandle frameworkHandle) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RunTests [](start = 20, length = 8)
Unless there is expected to be exactly one RunTests
call, it seems like the in-progress worker should be cancelled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
{ | ||
internal sealed class TestReceiver : ITestCaseDiscoverySink | ||
{ | ||
public readonly List<TestCase> Tests = new List<TestCase>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arguably, this should be exposed as an IReadOnlyList
, but it's internal so I don't care very much.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made class private
|
||
private void KillNodeProcess() | ||
{ | ||
this.nodeProcess?.Kill(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this.nodeProcess?.Kill(); [](start = 12, length = 25)
Does this wait for it to exit? Should it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't, and it shouldn't since we only call this for cancel and on failure.
//get from NodeRemoteDebugPortSupplier::PortSupplierId | ||
private static readonly Guid NodejsRemoteDebugPortSupplierUnsecuredId = new Guid("{9E16F805-5EFC-4CE5-8B67-9AE9B643EF80}"); | ||
|
||
private readonly ManualResetEvent cancelRequested = new ManualResetEvent(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cancelRequested [](start = 42, length = 15)
I'm probably missing something, but this doesn't appear to be used for blocking. If it's just a flag, consider using a simpler type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, but don't really want to touch this at this point.
/// <param name="sources">Refers to the list of test sources passed to the test adapter from the client. (Client could be VS or command line)</param> | ||
/// <param name="runContext">Defines the settings related to the current run</param> | ||
/// <param name="frameworkHandle">Handle to framework. Used for recording results</param> | ||
public void RunTests(IEnumerable<string> sources, ITestDiscoverer discoverer) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there some reason to believe a run isn't already in progress?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added calls to cancel
} | ||
|
||
// where key is the file and value is a list of tests | ||
foreach (var entry in fileToTests) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the key isn't used, consider iterating over fileToTests.Values
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
var tests = this.currentTests.Where(n => n.DisplayName == testEvent.title); | ||
if (tests.Any()) | ||
{ | ||
switch (testEvent.type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type [](start = 38, length = 4)
Just to confirm, we're ignoring other event types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes
|
||
this.currentResult.Messages.Add(new TestResultMessage(TestResultMessage.StandardOutCategory, string.Join(Environment.NewLine, standardOutputLines))); | ||
this.currentResult.Messages.Add(new TestResultMessage(TestResultMessage.StandardErrorCategory, string.Join(Environment.NewLine, standardErrorLines))); | ||
this.currentResult.Messages.Add(new TestResultMessage(TestResultMessage.AdditionalInfoCategory, string.Join(Environment.NewLine, standardErrorLines))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If writing the same thing to two different streams is intentional, consider extracting a local.
private readonly List<PackageJsonTestContainer> containers = new List<PackageJsonTestContainer>(); | ||
private readonly object containerLock = new object(); | ||
|
||
private IWorkspace currentWorkspace; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why store this? Why not use this.workspaceService.CurrentWorkspace
? Does that value change more often than we want?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have some thread-safety concerns, but this looks generally correct.
Adds support for running unit tests in "Open Folder" scenarios.