Creating a command line in C# that includes file path - c#

I am trying to create a project that accepts a configuration file and 2 comparison files using a command line arguments with the paths to these files included. Would I construct this the same way you would pass any command line argument? Sorry I am new to this so I am not sure if there is an exception when trying to pass files.
Can I get an example of how this would be done? Here is a picture of the directions of what exactly I have been asked.
Accept the following command line arguments:
Configuration file (with path) (described below)
Comparison File 1 (with path)
Comparison File 2 (with path)

Take a look at the documentation of Main function arguments
Assuming this is your main function and you want to accept 3 parameters:
static int Main(string[] args)
{
// check the length of args for validation.
// args[0] -> Configuration file
// args[1] -> Comparison File 1
// args[2] -> Comparison File 2
..... DO SOMETHING...
return 0;
}
Usage (from command line or debugger):
SomeProgram.exe "ConfigFilePath" "ComparisonFile1" "ComparisonFile2".

Because I really like this nuget(No association just a fan). Here is an example of it using CommandLineUtils
First add an new project with dotnet new consol TestConsolUtils then add the nuget dotnet add package McMaster.Extensions.CommandLineUtils then copy this code to the program class.
using McMaster.Extensions.CommandLineUtils;
using System;
namespace ConsolUtilsTest
{
class Program
{
public static int Main(string[] args)
=> CommandLineApplication.Execute<Program>(args);
[Argument(0, Description = "Configuration file")]
[FileExists]
public string ConfigurationFile { get; }
[Argument(1, Description = "Comparison file 1")]
[FileExists]
public string ComparisonFile1 { get; }
[Argument(2, Description = "Comparison File 2")]
[FileExists]
public string ComparisonFile2 { get; }
private void OnExecute()
{
Console.WriteLine(ConfigurationFile);
Console.WriteLine(ComparisonFile1);
Console.WriteLine(ComparisonFile2);
}
}
}
do a dotnet build
Go to the dll folder that was just build most likely in Debug\netcoreapp2.2\
Create a fake file A.json this is required because the utility will check if the file exists.
Run it with dotnet command
dotnet TestConsolUtils.dll A.json A.json A.json
There are a lot more you can do with this utill just look at the documentation.

Related

How to append build version to end of the output exe file name in console appilication with using post build event?

I have a simple console application and it has auto increment build version. When I build project, I’d like to append build version to end of the output exe file name with using post-build event. There is no macro for build version.
You will need a small utility to rename the target:
using System;
using System.Diagnostics;
using System.IO;
namespace RenameWithVersion
{
public class Program
{
public static void Main(String[] args)
{
if (args.Length != 1)
throw new Exception("There should only be one command line parameter - the file to be renamed.");
var oldFilename = Path.GetFullPath(args[0]);
if (!File.Exists(oldFilename))
throw new Exception($"File '{oldFilename}' does not exist.");
var oldFilenameWithoutExt = Path.ChangeExtension(oldFilename, null);
var fileVersion = FileVersionInfo.GetVersionInfo(oldFilename).FileVersion;
var ext = Path.GetExtension(oldFilename);
var newFilename = $"{oldFilenameWithoutExt}{fileVersion}{ext}";
File.Delete(newFilename);
File.Move(oldFilename, newFilename);
}
}
}
Compile it and put the executable somewhere on your path.
In the post-build event, call the utility like this:
RenameWithVersion "$(TargetPath)"
(Remember to put $(TargetPath) in double quotes in case the path contains spaces.)

What is the replacement for TestContext.DataRow["MyColumnName"]

Using MSTest in a .Net Core Unit test project. I am attempting to use a csv datasource to provide the data for a test method.
Previously, I would use something like below in a .Net Framework test project:
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", #"data.csv", "data#csv", DataAccessMethod.Sequential),
DeploymentItem("data.csv"),
TestMethod]
public void ValuesController_Post()
{
_controller.Post(TestContext.DataRow["body"]);
_valuesRepository.Verify(_ => _.Post(It.IsAny<string>()), Times.Once);
}
The key here being the DataRow property found in TestContext. This doesn't appear to exist in the .Net Core version of the TestContext.
How would I go about doing this in .Net Core?
Since moving to aspnet core, I've never been able to use the same [Datasource(...)] attribute to iterate through test data, my data-driven tests are always skipped.
Have you considered switching to another approach with [DataTestMethod] and [DynamicData] with a custom source that reads you file ?
Here's a good article on this :
https://www.meziantou.net/2018/02/05/mstest-v2-data-tests
Maybe another way would be to read the whole file at the begining of the test and then iterate through the dataset as One single unit test?
Hope this helps.
It took me an afternoon to fiddle with things, but I finally found a solution. Since you don't specify your test or CSV file, here is a quick example I could get working.
Long story short, I installed the CsvHelper NuGet package, because parsing CSV is dead easy right up to the point it is not. As Carl Verret pointed out, you need to use the [DynamicData(...)] attribute above your test method, and then parse the CSV using CsvHelper.
The CSV File (Example.csv)
A,B,IsLessThanZero
1,2,FALSE
3,-5,TRUE
Important: Make sure this CSV file is included in your test project and "Copy To Output Directory" is set to "Always" in the properties for the CSV file in Solution Explorer.
Data Transfer Object Used By CsvHelper
public class AdditionData
{
public int A { get; set; }
public int B { get; set; }
public bool IsLessThanZero { get; set; }
}
The Test Class
[TestClass]
public class ExampleTests
{
// HINT: Look in {Your Test Project Folder}\bin\{Configuration}\netcore3.1\FolderYourCsvFileIsIn for the CSV file.
// Change this path to work with your test project folder structure.
private static readonly string DataFilePath = Path.GetDirectoryName(typeof(ExampleTests).Assembly.Location) + #"\FolderYourCsvFileIsIn\Example.csv";
[TestMethod]
[DynamicData(nameof(GetData), DynamicDataSourceType.Method)]
public void AddingTwoNumbers(AdditionData data)
{
bool isLessThanZero = data.A + data.B < 0;
Assert.AreEqual(data.IsLessThanZero, isLessThanZero);
}
private static IEnumerable<object[]> GetData()
{
using var stream = new StreamReader(DataFilePath);
using var reader = new CsvReader(stream, new CsvConfiguration(CultureInfo.CurrentCulture));
var rows = reader.GetRecords<AdditionData>();
foreach (var row in rows)
{
yield return new object[] { row };
}
}
}
After building your solution, you will see a single test in Test Explorer. Running this single test runs all variants defined in your CSV file:

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();
}
...
}

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"));

Print the source filename and linenumber in C#

Is there any way to retrieve the current source filename and linenumber in C# code and print that value in the console output? Like LINE and FILE in C?
Please advise.
Many thanks
Anders Hejlsberg presented new API for that in BUILD keynote:
Print current file name, method name and line number
private static void Log(string text,
[CallerFilePath] string file = "",
[CallerMemberName] string member = "",
[CallerLineNumber] int line = 0)
{
Console.WriteLine("{0}_{1}({2}): {3}", Path.GetFileName(file), member, line, text);
}
Test:
Log(".NET rocks!");
Output:
Program.cs_Main(11): .NET rocks!
What's going on here?
You define a method with optional parameters and decorate them with special attributes. If you call method without passing actual arguments (leave defaults) - the Framework populates them for you.
This answer is outdated! See #taras' answer for more recent information.
No constant :(
What you can do is a lot uglier :
string currentFile = new System.Diagnostics.StackTrace(true).GetFrame(0).GetFileName();
int currentLine = new System.Diagnostics.StackTrace(true).GetFrame(0).GetFileLineNumber();
Works only when PDB files are available.
You can use the StackTrace object from the System.Diagnostics namespace but the information will only be available if the PDB files are there.
PDB files are generated by default for both the Debug and Release builds the only difference is that Debug is setup to generate a full debug info where as the Release build is setup to only generate a pdb (full/pdb-only).
Console.WriteLine(new StackTrace(true).GetFrame(0).GetFileName());
Console.WriteLine(new StackTrace(true).GetFrame(0).GetFileLineNumber());
There are no constants defined for that as of now.
The .NET way of doing it is using StackTrace class.
It however works only for Debug builds. So in case you use it, you can have the code using StackTrace between
#if DEBUG
//your StackTrace code here
#endif
You can read about using #if preprocessors for your DEBUG vs. RELEASE builds in the following Stackoverflow thread.
C# if/then directives for debug vs release
EDIT: Just in case you still need this debugging information in release builds, read the following answer on Stackoverflow:
Display lines number in Stack Trace for .NET assembly in Release mode
If you want some more internal detail, but you don't specifically need filename and line number, you can do something like this:
System.Diagnostics.Debug.Print(this.GetType().ToString() + " My Message");
This has an advantage over printing out the filename in that if you put this in a parent class, it will print out the child class name that is actually running the code.
If you wanted to write your own version of Debug.Assert, then here's a more complete answer:
// CC0, Public Domain
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System;
public static class Logger {
[Conditional("DEBUG")]
public static void Assert(bool condition, string msg,
[CallerFilePath] string file = "",
[CallerMemberName] string member = "",
[CallerLineNumber] int line = 0
)
{
// Debug.Assert opens a msg box and Trace only appears in
// a debugger, so implement our own.
if (!condition)
{
// Roughly follow style of C# error messages:
// > ideone.cs(14,11): error CS1585: Member modifier 'static' must precede the member type and name
Console.WriteLine($"{file}({line}): assert: in {member}: {msg}");
// Or more precisely match style with a fake error so error-parsing tools will detect it:
// Console.WriteLine($"{file}({line}): warning CS0: {msg}");
}
}
}
class Program {
static void Main(string[] args) {
Logger.Assert(1+1 == 4, "Why not!");
}
}
Try it online.

Categories