private async Task RunEnvironmentTestAsync(string expression, Action <IReadOnlyList <REnvironment> > assert) { var sessionProvider = VsAppShell.Current.ExportProvider.GetExportedValue <IRSessionProvider>(); var environmentChanged = new CountdownEvent(3); IReadOnlyList <REnvironment> environments = null; using (var rHostScript = new RHostScript(sessionProvider, null)) { using (var environmentProvider = new REnvironmentProvider(rHostScript.Session)) { environmentProvider.EnvironmentChanged += (sender, e) => { environments = e.Environments; environmentChanged.Signal(); }; using (var interaction = await rHostScript.Session.BeginInteractionAsync(false)) { await interaction.RespondAsync(expression + Environment.NewLine); } bool timedout = true; if (System.Diagnostics.Debugger.IsAttached) { environmentChanged.Wait(); } else { timedout = environmentChanged.Wait(TimeSpan.FromSeconds(10)); } timedout.Should().BeTrue(); assert(environments); } } }
private async Task Environments(string script, params IREnvironment[] expectedEnvs) { // Detach all packages that can be detached before doing anything else. The only two that // cannot be detached are .GlobalEnv, which is the first in the list, and package:base, // which is the last. So, just keep detaching the 2nd item until the list only has 2 left. await _session.ExecuteAsync("while (length(search()) > 2) detach(2)"); // Wait for prompt to appear. using (var eval = await _session.BeginInteractionAsync()) { } var envProvider = new REnvironmentProvider(_session); var envTcs = new TaskCompletionSource <IREnvironment[]>(); envProvider.Environments.CollectionChanged += (sender, args) => { envTcs.TrySetResult(envProvider.Environments.ToArray()); }; using (var inter = await _session.BeginInteractionAsync()) { inter.RespondAsync(script + "\n").DoNotWait(); } // Wait until we hit the Browse> prompt. using (var inter = await _session.BeginInteractionAsync()) { inter.Contexts.IsBrowser().Should().BeTrue(); } var actualEnvs = await envTcs.Task; actualEnvs.ShouldAllBeEquivalentTo(expectedEnvs, options => options .Including(env => env.Name) .Including(env => env.Kind) .WithStrictOrdering()); // Validating EnvironmentExpression: // For environments that are on the search list, we can validate it by retrieving environment by name, // and verifying that it is indeed the same. format() generates comparable string values - it returns strings // that are unique for any given environment (using its name if it has one, and its memory address otherwise). // For all other environments, we validate by looking at a variable named 'tag' in that environment, and // comparing its value to the name of the function extracted from the call (i.e. environment name). var expectedObjects = new List <string>(); var actualObjects = new List <string>(); foreach (var env in actualEnvs) { if (env == null) { expectedObjects.Add(null); actualObjects.Add(null); } else if (env.Kind == REnvironmentKind.Function) { int tagEnd = env.Name.IndexOf("("); tagEnd.Should().BePositive(); string tag = env.Name.Substring(0, tagEnd); expectedObjects.Add(tag); actualObjects.Add(await _session.EvaluateAsync <string>( $"({env.EnvironmentExpression})$tag", REvaluationKind.Normal)); } else { expectedObjects.Add(await _session.EvaluateAsync <string>( $"format(as.environment({env.Name.ToRStringLiteral()}))", REvaluationKind.Normal)); actualObjects.Add(await _session.EvaluateAsync <string>( $"format({env.EnvironmentExpression})", REvaluationKind.Normal)); } } actualObjects.Should().Equal(expectedObjects); }