Tests on strings fails inside docker container - c#

I'm using dotnet core and running a couple os tests on customized string extensions inside a docker container and it fail always, but it works well on a windows machine.
i've tryed to enforce the portuguese culture but it does not work, what am i missing here?
As example, i'm trying to remove diacritics from a string using as follows:
public static string RemoveDiacritics(this string input)
{
var normalizedString = input.Normalize(NormalizationForm.FormD);
var stringBuilder = new StringBuilder();
foreach (var c in normalizedString)
{
var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
if (unicodeCategory != UnicodeCategory.NonSpacingMark)
{
stringBuilder.Append(c);
}
}
return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
}
Here is the test example (NUnit)
[Test]
public void StringExtensions_RemoveDiacritics_SUCCESS()
{
string originalStr = "amahã deverá ser çábado";
string cleanStr = originalStr.RemoveDiacritics();
Assert.AreEqual("amaha devera ser cabado", cleanStr);
}
Here is the dockerfile example:
FROM mcr.microsoft.com/dotnet/sdk:3.1-focal
WORKDIR /
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
ENV TZ Europe/Lisbon
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN dpkg-reconfigure --frontend noninteractive tzdata
For better undertanding how to reproduce, i've created the following repo:
https://github.com/FlavioCFOliveira/TestsOnStrings

Take a look at the behaviour described here: https://github.com/dotnet/dotnet-docker/issues/1360
Basically the base .NET images run in a globalization invariant mode by default. You have to manually disable it by doing the following in your Dockerfile:
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
RUN apk add --no-cache icu-libs
Ran into this issue myself when a .ToLower() of an "Ë" character didn't correctly transform it into "ë": it remained "Ë".
Doing these Dockerfile modifications fixed it for my case.

Related

How to get command line parameters in specific format?

For example, we have option "--date".
I use System.CommandLine library to get options from command line and it works with such format:
"--date 2023-02-06", but I want it to work with format kind of: "--date=2023-02-06". Is there a way to do this?
If you don't mind using a beta Microsoft library you could use System.CommandLine.
From Option-argument delimiters:
Option-argument delimiters
System.CommandLine lets you use a space, '=', or ':' as the delimiter between an option name and its argument. For example, the following commands are equivalent:
dotnet build -v quiet
dotnet build -v=quiet
dotnet build -v:quiet
For example (This is modified Tutorial: Get started with System.CommandLine):
// dotnet add package System.CommandLine --prerelease
using System.CommandLine;
internal class Program
{
static async Task<int> Main(string[] args)
{
var date = new Option<string?>(
name: "--date",
description: "TODO");
var rootCommand = new RootCommand("TODO");
rootCommand.AddOption(date);
rootCommand.SetHandler((date) =>
{
Run(date!);
},
date);
return await rootCommand.InvokeAsync(args);
}
static void Run(string date)
{
Console.WriteLine(date);
}
}
Then we can:
PS C:\git\games\bin\Release\net6.0\win10-x64\publish> .\games.exe --date 2023-02-06
2023-02-06
PS C:\git\games\bin\Release\net6.0\win10-x64\publish> .\games.exe --date:2023-02-06
2023-02-06
PS C:\git\games\bin\Release\net6.0\win10-x64\publish> .\games.exe --date=2023-02-06
2023-02-06

Codegeneration at runtime from a string to console exe is not working in C# .NET6

I have some code which must be able to generated a console application at runtime (Codegeneration with System.CodeDom). I did this already a lot, but in NET 6 now I am struggling with that and the new API. In the code below I try to compile simply from a string. See below the static class with method Start() which then should generates the application.
The compilations seems fine, no errors at the end. But when starting the generated AppCodegenerated.exe, it shows some reference exception with System.Runtime.
Please help, any Idea? Already researched a lot but could not find any useful solution..
//-
I used the Visual Studio 2022 / NET 6 and theses Nuget's:
using Basic.Reference.Assemblies;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using System.Text;
namespace CompilerSimplified
{
public static class Compiler
{
public static bool Start()
{
string FileName = "AppCodegenerated";
string ExePath = AppDomain.CurrentDomain.BaseDirectory + #"\" + FileName + ".exe";
string code = #"using System; Console.WriteLine(""Hello.""); Console.ReadLine(); ";
// ------- References -------------
// .net platform references
List<MetadataReference> References = new List<MetadataReference>();
foreach (var item in ReferenceAssemblies.Net60) // ReferenceAssemblies from Nuget: Basic.Reference.Assemblies;
References.Add(item);
// or tried this: loop manually through system platform
//string[] fileEntries = Directory.GetFiles(#"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.0\ref\net6.0\", "*.dll");
//foreach (string fileName in fileEntries)
// references.Add(MetadataReference.CreateFromFile(fileName));MetadataReference.CreateFromFile(fileName));
// ------- References END -------------
// delete existing file
if (File.Exists(ExePath))
File.Delete(ExePath);
// compiler options
CSharpCompilationOptions DefaultCompilationOptions =
new CSharpCompilationOptions(outputKind: OutputKind.ConsoleApplication, platform: Platform.AnyCpu)
.WithOverflowChecks(true).WithOptimizationLevel(OptimizationLevel.Release);
// encode soucre code
string sourceCode = SourceText.From(code, Encoding.UTF8).ToString();
// CSharp options
var parsedSyntaxTree = Parse(sourceCode, "", CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp10));
// compilation
var compilation = CSharpCompilation.Create(FileName, new SyntaxTree[] { parsedSyntaxTree }, references: References, DefaultCompilationOptions);
var result = compilation.Emit(ExePath);
// return
if (result.Success)
return true;
else
return false;
}
private static SyntaxTree Parse(string text, string filename = "", CSharpParseOptions options = null)
{
var stringText = SourceText.From(text, Encoding.UTF8);
return SyntaxFactory.ParseSyntaxTree(stringText, options, filename);
}
}
}
Above code runs fine without error and exports the AppCodegenerated.exe into the project /bin folder.
Execution of this generated AppCodegenerated.exe shows following on the output console:
Unhandled exception: System.IO.FileNotFoundException:
The file or assembly "System.Runtime, Version = 6.0.0.0, Culture = neutral,
PublicKeyToken = b03f5f7f11d50a3a" or a dependency on it was not found.
The system can not find the stated file.
It is not possible to codegenerate directly a console application like the initial approach above. One possible solution is to generate first a dll (what I mentioned above in the example code is working fine), and from there include that .dll into a .exe, from where the functionality can run.

Teamcity - writing tests that access a separate JSON file

I have TeamCity running for a C# project. The Unit tests are written using MSTest and they include an external JSON file. They are loaded in because they're large and I don't want to have to escape them in C#.
I import them like this:
[TestInitialize]
public void Setup()
{
using (StreamReader r = new StreamReader(#".\currency2.json"))
{
_json = r.ReadToEnd();
}
...
They run fine locally. I have 'Copy always set' but when the tests are ran using Teamcity I get an error saying that it can't find them in a temp folder. They are copied over to the build server but they're not in this temp folder.
Could not find file 'E:\TeamCity\buildAgent\temp\buildTmp\SYSTEM_SERVER 2016-07-18 15_28_19\Out\currency2.json'
I have **\bin\release\*test*.dll setup as my Test File Names in the test build step.
Any help appreciated.
I had a similar problem.
I changed the properties of the test file to this
Build Action = Content
Copy to Output Directory = Copy always
Teamcity will copy the file to the build folder, but it does not seem to maintain the same structure you'd expect.
So I created a file lookup loop. That will step down the expected folder until it finds the text file in question.
var pathLookups = new string[]
{
"2ndFolder\\3rdFolder\\test.json", // folder that normally workes
"3rdFolder\\test.json",
"test.json"
};
foreach (var pathLookup in pathLookups)
{
try
{
jsonFileCollection = JsonFileLoader<TestJsonType>.LoadJson(pathLooksup);
if (jsonFileCollection!= null)
{
break;
}
}
catch (Exception)
{
Console.WriteLine("Attempted to load test json from path:" + pathLooksup);
}
}
It's not the cleanest solution, but it will get the job done. You could refactor this to look a little nicer.
You might pass the full pass by argument to your program (and value defined in TeamCity).
Something like this (this is a pseudo-code example only) :
string[] programArgs;
static void Main(string[] args)
{
programArgs = args
}
[TestInitialize]
public void Setup()
{
using (StreamReader r = new StreamReader(programArgs[1]))
{
_json = r.ReadToEnd();
}
...
}

Unity Cloud Build: post export method

Problem:
I can't seem to figure out the right signature for Unity cloud build's post export method. According to the documentation:
The fully-qualified name of a public static method you want us to call
after we finish the Unity build process (but before Xcode). For
example: ClassName.CoolMethod or NameSpace.ClassName.CoolMethod. No
trailing parenthesis, and it can't have the same name as your
Pre-Export method! This method must accept a string parameter, which
will receive the path to the exported Unity player (or Xcode project
in the case of iOS).
Here is my code:
public static void OnPostprocessDevBuildIOS(string ExportPath)
{
var projPath = ExportPath + "/Unity-iPhone.xcodeproj/project.pbxproj";
var proj = new PBXProject();
var nativeTarget =
proj.TargetGuidByName(PBXProject.GetUnityTargetName());
var testTarget =
proj.TargetGuidByName(PBXProject.GetUnityTestTargetName());
string[] buildTargets = {nativeTarget, testTarget};
proj.ReadFromString(File.ReadAllText(projPath));
proj.SetBuildProperty(buildTargets, "ENABLE_BITCODE", "NO");
File.WriteAllText(projPath, proj.WriteToString());
}
and here is the error:
I've tried multiple test method signatures and can't seem to get anything to work. I've even tried just a method that logs out the path.
Additional Information:
Unity Version: 5.3.1f
Unity Cloud Build: 5.3.1f
Target: iOS 8.0+
Also, my cloud build settings script is located in the editor folder as required.
Ok so I got the the bitCode disabling post process to work with the following code, but only when I build manually. When I build from cloud build, with no error the app freezes at the splash screen. When I build from my local machine, the app runs just fine.
[PostProcessBuild]
public static void OnPostprocessBuild(BuildTarget buildTarget, string path)
{
if (buildTarget == BuildTarget.iOS)
{
string projPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj";
PBXProject proj = new PBXProject();
proj.ReadFromString(File.ReadAllText(projPath));
string nativeTarget = proj.TargetGuidByName(PBXProject.GetUnityTargetName());
string testTarget = proj.TargetGuidByName(PBXProject.GetUnityTestTargetName());
string[] buildTargets = new string[]{nativeTarget, testTarget};
proj.SetBuildProperty(buildTargets, "ENABLE_BITCODE", "NO");
File.WriteAllText(projPath, proj.WriteToString());
}
}
I too had the same issue "splash screen stuck" right after launch....
I solved this issue. Please use the below code.
Tested in Unity 5.4.1p2 and Xcode 7.3.
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.IO;
using UnityEditor.Callbacks;
#if UNITY_IOS
using UnityEditor.iOS.Xcode;
#endif
public class Postprocessor : AssetPostprocessor
{
#if UNITY_IOS
[PostProcessBuild]
public static void OnPostprocessBuild(BuildTarget buildTarget, string path)
{
if (buildTarget == BuildTarget.iOS)
{
string projPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj";
PBXProject proj = new PBXProject();
proj.ReadFromString(File.ReadAllText(projPath));
string target = proj.TargetGuidByName("Unity-iPhone");
proj.SetBuildProperty(target, "ENABLE_BITCODE", "false");
File.WriteAllText(projPath, proj.WriteToString());
// Add url schema to plist file
string plistPath = path + "/Info.plist";
PlistDocument plist = new PlistDocument();
plist.ReadFromString(File.ReadAllText(plistPath));
// Get root
PlistElementDict rootDict = plist.root;
rootDict.SetBoolean("UIRequiresFullScreen",true);
plist.WriteToFile(plistPath);
}
}
#endif
}
In fact OnPostprocessBuild is always called, so you don't have to put anything in post export method field, which is designed for more specific methods.

nunit test working directory

I have the following code (sample1.evol - file attached to my unit test project):
[Test]
public void LexicalTest1()
{
var codePath = Path.GetFullPath(#"\EvolutionSamples\sample1.evol");
//.....
}
I found that the working directory of test execution is not the assembly directory: (in my case codepath variable assigned to d:\EvolutionSamples\sample1.evol).
So, how can I change the execution working directory (without hardcode)? What will be the best practice to load any files attached to test case?
You can use following to get the directory of assembly running the code something like
var AssemblyDirectory = TestContext.CurrentContext.TestDirectory
I use this for integration tests that need to access data files.
On any machine the test needs to run create a system environment variable named TestDataDirectory that points to the root of where your test data is.
Then have a static method that gets the file path for you..
public static class TestHelper
{
const string EnvironmentVariable = "TestDataDirectory";
static string testDataDir = Environment.GetEnvironmentVariable(EnvironmentVariable);
public static string GetTestFile(string partialPath)
{
return Path.Combine(testDataDir, partialPath);
}
}
...
[Test]
public void LexicalTest1()
{
var codePath = TestHelper.GetTestFile(#"\EvolutionSamples\sample1.evol");
//.....
}
I am using this code:
var str = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
if (str.StartsWith(#"file:\")){
str = str.Substring(6);
}
Getting in str variable the assembly directory.
We were having a problem where tests run using ReSharper and NCrunch would work, but the native VS Test Runner would not be able to find the files, when given just a relative file path for the test to use. I solved it by creating a function that you pass the relative test file path into, and it will give you the absolute file path.
private static string _basePath = Path.GetDirectoryName(typeof(NameOfYourTestClassGoesHere).Assembly.Location);
private string GetAbsoluteTestFilePath(string relativePath) => Path.Combine(_basePath, relativePath);
You would then use the function like so:
var input = File.ReadAllLines(GetAbsoluteTestFilePath(#"TestData/YourTestDataFile.txt"));

Categories