File: Commands\Test\MTP\TestModulesFilterHandler.cs
Web Access
Project: src\src\sdk\src\Cli\dotnet\dotnet.csproj (dotnet)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.CommandLine;
using Microsoft.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Cli.Commands.Run;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli.Utils.Extensions;
using Microsoft.Extensions.FileSystemGlobbing;

namespace Microsoft.DotNet.Cli.Commands.Test;

internal sealed class TestModulesFilterHandler : ITestHandler
{
    private readonly string _testModules;
    private readonly string? _testModulesRoot;
    private readonly List<string> _testModulePaths;

    public TestModulesFilterHandler(string testModules, ParseResult parseResult)
    {
        _testModules = testModules;

        var definition = (TestCommandDefinition.MicrosoftTestingPlatform)parseResult.CommandResult.Command;

        // If the module path pattern(s) was provided, we will use that to filter the test modules
        // If the root directory was provided, we will use that to search for the test modules
        // Otherwise, we will use the current directory
        string? rootDirectory = Directory.GetCurrentDirectory();
        if (parseResult.HasOption(definition.TestModulesRootDirectoryOption))
        {
            rootDirectory = parseResult.GetValue(definition.TestModulesRootDirectoryOption);
        }

        _testModulesRoot = rootDirectory;
        _testModulePaths = GetMatchedModulePaths(_testModules, _testModulesRoot);
    }

    public bool Initialize()
    {
        // If the root directory is not valid, we simply return
        if (string.IsNullOrEmpty(_testModulesRoot) || !Directory.Exists(_testModulesRoot))
        {
            Reporter.Output.WriteLine(string.Format(CliCommandStrings.CmdNonExistentRootDirectoryErrorDescription, _testModulesRoot).Yellow());
            return false;
        }

        // If no matches were found, we simply return
        if (_testModulePaths.Count == 0)
        {
            Reporter.Output.WriteLine(string.Format(CliCommandStrings.CmdNoTestModulesErrorDescription, _testModules, _testModulesRoot).Yellow());
            return false;
        }

        return true;
    }

    public int RunTestApplications(TestApplicationActionQueue actionQueue)
    {
        var muxerPath = new Muxer().MuxerPath;
        foreach (string testModule in _testModulePaths)
        {
            // We want to produce the right RunCommand and RunArguments for TestApplication implementation to consume directly.
            // We don't want TestApplication class to be concerned about whether it's running dll via test module or not.
            // If we are given dll, we use dotnet exec. Otherwise, we run the executable directly.
            RunProperties runProperties = testModule.HasExtension(CliConstants.DLLExtension)
                ? new RunProperties(muxerPath, $@"exec ""{testModule}""", null)
                : new RunProperties(testModule, null, null);

            var testApp = new ParallelizableTestModuleGroupWithSequentialInnerModules(new TestModule(runProperties, null, null, true, null, testModule, DotnetRootArchVariableName: null));
            // Write the test application to the channel
            actionQueue.Enqueue(testApp);
        }

        return actionQueue.CompleteEnqueueAndWait();
    }

    private static List<string> GetMatchedModulePaths(string testModules, string? rootDirectory)
    {
        if (string.IsNullOrEmpty(rootDirectory))
        {
            return new List<string>();
        }

        var testModulePatterns = testModules.Split([';'], StringSplitOptions.RemoveEmptyEntries);

        Matcher matcher = new();
        matcher.AddIncludePatterns(testModulePatterns);

        // Make sure we have a non-lazy collection, so that if we enumerate multiple times we guarantee the same result.
        var results = matcher.GetResultsInFullPath(rootDirectory);
        if (results is List<string> resultsList)
        {
            return resultsList;
        }

        return results.ToList();
    }
}