corvus-dotnet / Corvus.Testing

Extensions and utilities for testing, primarily focused on SpecFlow. Sponsored by endjin.
Apache License 2.0
13 stars 10 forks source link

OSX/Linux Support #253

Open mumby0168 opened 3 years ago

mumby0168 commented 3 years ago

The current implementation as shown relies on cmd.exe which is not supported by any other operating system other than windows.

Screenshot 2021-05-17 at 19 05 10
mumby0168 commented 3 years ago

This may be a possible solution current have a fork so may try and PR this need check for any other references to windows only: https://stackoverflow.com/questions/23679283/c-sharp-execute-a-terminal-command-in-linux/23681651#23681651

mumby0168 commented 3 years ago

Seem to be more Windows only references as here:

Screenshot 2021-05-17 at 19 10 47
idg10 commented 3 years ago

This is something we'd like to do but haven't got to yet. The weird behaviour around PATH was unexpected. We'd need to look into what happens on both Mac and Linux to see whether a similar problem exists there, or whether on those platforms we can just execute npm directly.

As for the KillProcessAndChildren, IIRC the problem here is that when you run the functions emulator, you get two processes: the NodeJS one, and then a separate one for the .NET code, and on Windows, killing the parent process doesn't automatically kill the children. I'm not sure whether we'll have the same problem on Mac or Linux, but if we did, we'd need something equivalent to find the process hierarchy and clean up the whole thing.

That said .NET Core 3.0 added support for killing entire process trees, so it's possible that we could just switch to that.

idg10 commented 3 years ago

Based on a very quick experiment, this appears to work for launching npm in Linux:

var pi = new ProcessStartInfo("npm", "prefix -g");
var p = new Process { StartInfo = pi };
p.Start();

so on the face of it, it looks like the PATH issues we encountered on Windows are not an issue. (It's also possible, I suppose, that they're no longer a problem in Windows either—we haven't changed this for a while.)

andrewalderson commented 3 years ago

This isn't only need for local dev but also for CI/CD pipelines. I want to deploy my functions to a Linux plan and want to run my tests in Azure Pipelines on a Linux agent. I just spent a day trying to figure out why my tests wouldn't run and then I came across this issue. I didn't see anywhere in the documentation saying this was a Windows only library. Since it is built on netstandard2.0 I just assumed it would run on Linux.

idg10 commented 3 years ago

Since it is built on netstandard2.0 I just assumed it would run on Linux.

In general this is no guarantee. The System.Management NuGet package (which the Corvus.Testing.AzureFunctions package has a dependency on) supports netstandard2.0, but doesn't work on Linux. So one reason our netstandard2.0 library doesn't work on Linux is that it depends on a Microsoft-supplied netstandard2.0 library that also doesn't work on Linux.

The only thing netstandard2.0 really tells you is that a component can be loaded on both .NET FX and .NET Core. The extent to which it actually works across operating systems is determined by what the component actually does, and there are numerous examples in netstandard2.0 of APIs that appear to be available everywhere but which fail at runtime on some OSs.

And if you look at what System.Management actually does, it isn't entirely surprising it only works on Windows. There have been feature requests to make it "work" e.g. https://github.com/dotnet/runtime/issues/23918 but even then, there's more than one interpretation of "work". E.g., some people only want the ability to send a WMI query from a Linux machine to a Windows machine, and to be able to interpret the response on the Linux machine. But some people want to be able to run WMI queries against the local host.

That raises the question of what you'd actually expect a Linux box to do when asked to return a list of Win32_Process objects.

So I'd expect the only way for us to fix this is to find some way to achieve the same effect without using System.Management.

As I said earlier in the thread, we do want to fix this—we have several build pipelines currently using Windows where we'd prefer to use Linux (because if you're using single-use discardable build agents, Linux ones generally take less time to provision), but where we're stuck on Windows partly because of this limitation in this library. (Although there's also the fact that we've encountered a lot of problems deploying to Linux-based Azure Functions, meaning that in most cases we use Windows for the production deployments, at which point you tend to want to run your tests on Windows.)

So we do intend to fix this. It just hasn't reached the top of our priority list yet.

mwadams commented 3 years ago

PRs from people well versed in the Linux side of this would be very welcome, though.

idg10 commented 3 years ago

Since I'd already taken a look at the issues to be able to write a vaguely cogent reply, I thought I'd see if it's relatively straightforward. There's a draft PR, #271 which attempts to address the issues I know about. If you're feeling adventurous, I've produced a pre-release NuGet package https://www.nuget.org/packages/Corvus.Testing.AzureFunctions/1.5.0-253-unix-support.2 that you could try out to see if it works, but be aware that I've not tested it at all yet.

andrewalderson commented 3 years ago

I have tried this out and there is an issue with how you are checking for the azure functions core tools. Using npm prefix -g on linux reports the path to be /opt/hostedtoolcache/node/10.24.1/x64 but the core tools are installed in /opt/hostedtoolcache/node/10.24.1/x64/lib so the check for the core tools being installed is failing.

To fix this you should use npm root -g. This will return the path including the node_modules folder so in your GetToolPath method you would need to change

    string toolsFolder = Path.Combine(
                npmPrefix,
                @"node_modules\azure-functions-core-tools\bin");

to

    string toolsFolder = Path.Combine(
                npmPrefix,
                @"azure-functions-core-tools\bin");

I unfortunately do not currently have a Linux environment set up so I could only test this on Azure Pipelines with both a Windows and Linux agent and printing out the results of the npm root -g command. They both reported the node_modules folder correctly. I am in the process of setting up WSL with VS Code for local Linux dev. It is really interesting (and a little sad) to see how much faster Linux is not only on Azure Pipelines but in running dotnet code.

andrewalderson commented 3 years ago

I have gotten a local Linux environment set up to test this and I can confirm that the changes I outlined above do fix the npm issue with one small change - the path to the 'azure-functions-core-tools\bin' needs to use the OS specific directory separator. (@$"azure-functions-core-tools{Path.DirectorySeparatorChar}bin"). That is the good news. The bad news is that it is throwing an exception (System.ComponentModel.Win32Exception : No such file or directory) when it tries to start the process to run 'npm root -g'. From my search online this is supposed to work so not sure why it isn't. I will take another look at this on the weekend.

UPDATE I figured out where the exception was coming from. It wasn't the npm task. I missed the directory separators in the FunctionProject.ResolvePath method. They need to be change to OS specific separators

        char separator = Path.DirectorySeparatorChar;
        string directoryExtension = @$"bin{separator}release{separator}{runtime}";
        if (currentDirectory.Contains("debug"))
        {
            directoryExtension = @$"bin{separator}debug{separator}{runtime}";
        }

My tests are now working locally on Linux but every second test is failing with this exception

System.Net.Http.HttpRequestException : Cannot assign requested address (localhost:7075)

I am assuming it is because the port is still in use when the test runs. This could be related to the fact I am running everything in a Docker container. Also, they are still not running on Azure Pipelines. I am still getting the same Win32 exception and I don't know how to get the debug output from the logger to print out in the pipeline. Any help on this would be appreciated.