Code Examples

Compatibility
Runner Client 3.x exposes asynchronous APIs and is the recommended client for Runner 2.x servers.

  1. Working with Images and starting Sessions based on Images
  2. Simple session creation based on Runner installation
  3. Create and execute a test plan
  4. Load and execute a test plan
  5. Receiving results, logs and events from a Session
  6. How to work with component settings
  7. Configuring a Runner for default execution and using stored test plans on the Runner

Image creation

For this example we have a Runner started which has the ID: HKZ6QN3

Alt text

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using NATS.Client.Core;
using OpenTap.Runner.Client;

await using INatsConnection connection = new NatsConnection(new NatsOpts { Url = RunnerClient.DefaultUrl });
RunnerClient runner = new RunnerClient(connection);

Image image = new Image
{
    Packages = new List<PackageSpecifier>
    {
        new PackageSpecifier { Name = "Demonstration" },
        new PackageSpecifier { Name = "HTTP", Version = "1.0.0" },

        // We can even specify a different runner version for the session
        new PackageSpecifier { Name = "Runner", Version = "1.2.1" }
    },
    Repositories = new List<string> { "https://packages.opentap.io" }
};

Image? resolvedImage = await runner.ResolveImageAsync(new List<Image> { image });
if (resolvedImage is null)
    throw new InvalidOperationException("Image resolution failed.");

SessionClient newSession = await runner.StartImageSessionAsync(resolvedImage);

// Use the session for test automation

await runner.ShutdownSessionAsync(newSession.Id);

In this example, we successfully resolved an image, started and stopped a session based on the image. This can be verified in the Runner logs:

Alt text

Session creation

Although the Image approach offers flexibility, some use-cases can be kept simple by just using a Runner Session based on the Runner installation.

using NATS.Client.Core;
using OpenTap.Runner.Client;

await using INatsConnection connection = new NatsConnection(new NatsOpts { Url = RunnerClient.DefaultUrl });
RunnerClient runner = new RunnerClient(connection);

SessionClient newSession = await runner.StartSessionAsync();

// Use the session for test automation. Plugins available in this session
// come from the packages installed in the Runner.

await runner.ShutdownSessionAsync(newSession.Id);

Create and Run Test Plan

In this example, we use the simple session from the Session creation example above, but before shutting down the session, we setup and run a simple test plan.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NATS.Client.Core;
using OpenTap.Runner.Client;

await using INatsConnection connection = new NatsConnection(new NatsOpts { Url = RunnerClient.DefaultUrl });
RunnerClient runner = new RunnerClient(connection);
SessionClient newSession = await runner.StartSessionAsync();

List<TestStepType> types = await newSession.GetStepTypesAsync();
TestPlan plan = await newSession.GetTestPlanAsync(null);
plan.ChildTestSteps.Clear();
plan.ChildTestSteps.Add(types.First(step => step.TypeName.Contains("Delay")));
plan = await newSession.SetTestPlanAsync(plan);

Settings delayStepSettings = await newSession.GetSettingsAsync(plan.ChildTestSteps[0].Id);
if (delayStepSettings.FirstOrDefault(s => s.PropertyName == "DelaySecs") is TextBoxControl textBox)
    textBox.StringValue = "3 s";
delayStepSettings = await newSession.SetSettingsAsync(plan.ChildTestSteps[0].Id, delayStepSettings);

RunStatus status = await newSession.RunTestPlanAsync(new List<Parameter>());
while (status.SessionState != SessionState.Idle)
{
    await Task.Delay(1000);
    status = await newSession.GetStatusAsync();
}

Console.WriteLine(status.Verdict);

await runner.ShutdownSessionAsync(newSession.Id);

In this example, we retrieved the available test step types using GetStepTypes and chose a Delay step and inserted into the Test Plan using SetTestPlan.

Afterwards, we retrieved the Delay step settings and modified the Time Delay to be 3 s.

Lastly, we ran the Test Plan and contiunually asked for the status until the SessionState turned back to Idle. The Verdict of the Test Plan was logged and the output of this program would be NotSet.

Load and Run Test Plan

In this example, we use the simple session from the Session creation example above, but before shutting down the session, we load and run a Test Plan locally stored on the disk.

using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using NATS.Client.Core;
using OpenTap.Runner.Client;

await using INatsConnection connection = new NatsConnection(new NatsOpts { Url = RunnerClient.DefaultUrl });
RunnerClient runner = new RunnerClient(connection);
SessionClient newSession = await runner.StartSessionAsync();

await newSession.SetTestPlanXMLAsync(File.ReadAllText("MyTestPlan.TapPlan"));

RunStatus status = await newSession.RunTestPlanAsync(new List<Parameter>());
while (status.SessionState != SessionState.Idle)
{
    await Task.Delay(1000);
    status = await newSession.GetStatusAsync();
}

Console.WriteLine(status.Verdict);

await runner.ShutdownSessionAsync(newSession.Id);

Results, Logs and Events

In this example, we use the simple session from the Create and Run Test Plan section above, but with results, logs and events logged.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NATS.Client.Core;
using OpenTap.Runner.Client;

await using INatsConnection connection = new NatsConnection(new NatsOpts { Url = RunnerClient.DefaultUrl });
RunnerClient runner = new RunnerClient(connection);
SessionClient newSession = await runner.StartSessionAsync();

newSession.ConnectSessionLogs(LogHandler);
Task LogHandler(LogList? list)
{
    if (list?.Logs != null)
        foreach (var log in list.Logs)
            Console.WriteLine($"{log.Source,-12}: {log.Message}");
    return Task.CompletedTask;
}

newSession.ConnectSessionResults(ResultHandler, RunHandler);
Task ResultHandler(Result? result)
{
    if (result != null)
        Console.WriteLine($"Result: {result.Status}");
    return Task.CompletedTask;
}
Task RunHandler(TestRun? run)
{
    if (run != null)
        Console.WriteLine($"Run: {run.Status}");
    return Task.CompletedTask;
}

newSession.ConnectSessionEvents(EventHandler);
Task EventHandler(SessionEvent? evt)
{
    if (evt != null)
        Console.WriteLine($"Event: {evt.GetType().Name}");
    return Task.CompletedTask;
}

List<TestStepType> types = await newSession.GetStepTypesAsync();
TestPlan plan = await newSession.GetTestPlanAsync(null);
plan.ChildTestSteps.Clear();
plan.ChildTestSteps.Add(types.First(step => step.TypeName.Contains("Delay")));
plan = await newSession.SetTestPlanAsync(plan);

Settings delayStepSettings = await newSession.GetSettingsAsync(plan.ChildTestSteps[0].Id);
if (delayStepSettings.FirstOrDefault(s => s.PropertyName == "DelaySecs") is TextBoxControl textBox)
    textBox.StringValue = "3 s";
delayStepSettings = await newSession.SetSettingsAsync(plan.ChildTestSteps[0].Id, delayStepSettings);

RunStatus status = await newSession.RunTestPlanAsync(new List<Parameter>());
while (status.SessionState != SessionState.Idle)
{
    await Task.Delay(1000);
    status = await newSession.GetStatusAsync();
}

Console.WriteLine(status.Verdict);

await runner.ShutdownSessionAsync(newSession.Id);

This code should produce the following output, where events, runs and results are prepended with “Event: “, “Run: “, and “Result: “.

Alt text

Working with ComponentSettings

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NATS.Client.Core;
using OpenTap.Runner.Client;

await using INatsConnection connection = new NatsConnection(new NatsOpts { Url = RunnerClient.DefaultUrl });
RunnerClient runner = new RunnerClient(connection);
SessionClient newSession = await runner.StartSessionAsync();

try
{
    List<ComponentSettingsIdentifier> componentSettings = await newSession.GetComponentSettingsOverviewAsync();
    foreach (var componentSetting in componentSettings)
        Console.WriteLine($"Component Setting: {componentSetting.Name} ({componentSetting.GroupName})");

    // Outputs:
    // Component Setting: DUTs(Bench)
    // Component Setting: Engine(-)
    // Component Setting: Instruments(Bench)
    // Component Setting: Connections(Bench)
    // Component Setting: Results(-)
    // Component Setting: Editor(-)
    // Component Setting: Cloud Drive(-)

    ComponentSettingsIdentifier instrumentIdentifier = componentSettings
        .First(s => s.Name == "Instruments" && s.GroupName == "Bench");

    List<ListItemType> instrumentTypes = await newSession.GetComponentSettingsListAvailableTypesAsync(
        instrumentIdentifier.GroupName,
        instrumentIdentifier.Name);
    foreach (var instrumentType in instrumentTypes)
        Console.WriteLine($"Instrument Type: {string.Join("/", instrumentType.TypeDisplay.Group)}/{instrumentType.TypeDisplay.Name}");

    // Outputs:
    // Instrument Type: / Generic SCPI Instrument
    // Instrument Type: Demo / Battery Test / Power Analyzer
    // Instrument Type: Demo / Battery Test / Temperature Chamber
    // Instrument Type: Demo / Results And Timing/ Timing Instrument

    ComponentSettingsBase instruments = await newSession.GetComponentSettingsAsync(
        instrumentIdentifier.GroupName,
        instrumentIdentifier.Name);
    if (instruments is ComponentSettingsList list)
        foreach (var instrument in list.Items)
            Console.WriteLine($"Instrument: {instrument.Name}");

    // Outputs:
    // Instrument: SCPI
    // Instrument: PSU
    // Instrument: TEMP

    ComponentSettingsBase updated = await newSession.AddComponentSettingsListItemAsync(
        instrumentIdentifier.GroupName,
        instrumentIdentifier.Name,
        instrumentTypes.First(type => type.TypeDisplay.Name == "Power Analyzer").TypeName);

    if (updated is ComponentSettingsList listWithNewItem)
    {
        foreach (var instrument in listWithNewItem.Items)
            Console.WriteLine($"Instrument: {instrument.Name}");

        // Outputs:
        // Instrument: SCPI
        // Instrument: PSU
        // Instrument: TEMP
        // Instrument: PSU (1)

        foreach (var setting in listWithNewItem.Items[^1].Settings)
        {
            Console.WriteLine($"Setting: {setting.Display.Name}");
            if (setting is TextBoxControl textBox)
                Console.WriteLine(textBox.StringValue);
        }
    }

    // Outputs:
    // Setting: Cell Size Factor
    // 0.005
}
finally
{
    await runner.ShutdownSessionAsync(newSession.Id);
}

Default Endpoints

Default endpoints are endpoints that affect each other. They are:

  • SetDefaultImage: Sets a default image in the Runner environment. The image includes essential packages.
  • GetDefaultImage: Retrieves the currently set default image.
  • SetDefaultSettings: Applies default settings for the Runner. Settings might have various package dependencies.
  • GetDefaultSettings: Retrieves the current settings from the Runner.
  • StartDefaultSession: Starts a session using the default settings and image.
  • StartDefaultSessionOverrideImage: Begins a session with an overridden image for specific use cases.
  • SetTestPlans: Sets a list of test plans to be used in the Runner.
  • AddTestPlan: Adds a single test plan to the current list in the Runner.
  • GetTestPlans: Retrieves the list of test plans stored in the Runner.

A Session’s plugin configuration depends on:

  • The dependencies of the TestPlan.
  • The dependencies of the Settings.
  • The packages defined in a “default image”.
  • Optionally, the packages defined in an “image override”.

The following example demonstrates how to use the RunnerClient for managing test plans in a Runner session.

public static async Task DefaultTestPlanShowcaseAsync(RunnerClient runnerClient)
{
    Image baseImage = new()
    {
        Packages = new List<PackageSpecifier>
        {
            new PackageSpecifier { Name = "Runner", Version = "1.7.0" }
        },
        Repositories = new List<string>
        {
            "https://packages.opentap.io"
        }
    };
    await runnerClient.Default.SetImageAsync(baseImage); // Apply the base image configuration

    RepositoryPackageReference settingsPackageReference = new()
    {
        Repository = "https://test-automation.pw.keysight.com/api/packages",
        Path = "/users/dennis.rasmussen@keysight.com/BatterySettings.TapPackage",
        Version = "0.2.0"
    };
    await runnerClient.Default.SetSettingsAsync(settingsPackageReference); // Apply settings package

    RepositoryPackageReference testPlanReference = new()
    {
        Repository = "https://test-automation.pw.keysight.com/api/packages",
        Path = "/users/dennis.rasmussen@keysight.com/BatteryPlan.TapPlan",
        Version = "0.1.0-Draft.5"
    };

    SessionClient session = await runnerClient.Default.StartSessionAsync(testPlanReference);

    try
    {
        RunStatus status = await session.RunTestPlanAsync(new List<Parameter>());
        while (status.SessionState != SessionState.Idle)
        {
            await Task.Delay(1000);
            status = await session.GetStatusAsync();
        }

        Console.WriteLine($"Verdict: {status.Verdict}");
    }
    finally
    {
        await session.ShutdownAsync();
        await runnerClient.ShutdownSessionAsync(session.Id);
    }
}

Future code examples:

  • Editing Step and TestPlan settings
  • Load from and save to Repository