Why isnt function executed? - c#

I am working on creating an app just like the Output Terminal (Unix) or the Command Line Prompt (Windows). I have created a dictionary that has some keywords for a function. But when I call those keywords, nothing is done. My program is called Command Line Control (or CLC). And I use the .NET Core which is for all running software(Linux, macOS, and Windows).
I do not know what to try exactly. A function is a function. It must be executed when I call it.
using System;
using System.Collections.Generic;
using System.Threading;
using System.IO;
namespace CLC
{
class Program
{
static DirectoryInfo maindirectory;
static Dictionary<string, string> keyaction;
static string value;
static void WritePathOfWorkingDirectory(DirectoryInfo directory)
{
if (directory != null)
{
Console.Write("{0}:", directory.FullName);
}
else
{
}
}
static void ProcessAnswer(string[] array)
{
string action = array.GetValue(0).ToString();
value = array.GetValue(1).ToString();
string c = keyaction[action];
Console.Write(c);
}
static string ListFiles()
{
foreach(var file in maindirectory.GetFiles())
{
Console.WriteLine(file.Name);
}
return "ok";
}
static string ListDirectories()
{
foreach(var directory in maindirectory.GetDirectories())
{
Console.WriteLine(directory);
}
return "ok";
}
static void MainProgramm()
{
WritePathOfWorkingDirectory(maindirectory);
string data = Console.ReadLine();
var arry = data.Split(' ');
ProcessAnswer(arry);
Thread repeat = new Thread(MainProgramm);
repeat.Start();
}
static void Main(string[] args)
{
maindirectory = new DirectoryInfo("C:/Users");
keyaction = new Dictionary<string, string>();
keyaction.Add("lf", ListFiles());
keyaction.Add("ld", ListDirectories());
Console.Clear();
maindirectory = new DirectoryInfo("C:/Users");
Thread thread = new Thread(new ThreadStart(MainProgramm));
thread.Start();
}
}
}
The expected result is to do what the key says: for example if I type ld (list directories) the list directory function should be executed. But I don't get anything! The program just repeats itself.

When you include the parentheses after a Method name, you invoke the method. So rather than passing the function as the Dictionary value, you're passing the result of calling that function. You need to get a reference to the function to be called then invoke it.
Your Main method should look like this:
static void Main(string[] args)
{
maindirectory = new DirectoryInfo("C:/Users");
keyaction = new Dictionary<string, Func<string>>(); // changed this to an Func instead of a string
keyaction.Add("lf", ListFiles); // notice I removed the parentheses here
keyaction.Add("ld", ListDirectories); // and here
Console.Clear();
maindirectory = new DirectoryInfo("C:/Users");
Thread thread = new Thread(new ThreadStart(MainProgramm));
thread.Start();
}
and keyaction should be declared like this:
static Dictionary<string, Func<string>> keyaction; // a Func<string> is a function that returns a string and takes no arguments
Then, in your ProcessAnswer method you need to call the function via the reference you have to it in the Dictionary:
static void ProcessAnswer(string[] array)
{
string action = array.GetValue(0).ToString();
value = array.GetValue(1).ToString();
string c = keyaction[action](); // calling the referenced funtion
Console.Write(c);
}
This should give you the expected result.
In the current state, your program's methods only get called once and then you clear the Console before you can see the output, so it probably looks like your program just says "ok" whenever you enter a command.

The main problem is that you're storing a string value in the dictionary rather than the function itself, so when you add the functions, you're adding their immediate return value (before you ever prompt the user for input).
Instead, the dictionary value should be a Func<string>, which you can then .Invoke on demand.
Additionally, it seemed that you weren't validating the user entry. We can use TryGetValue for the user's entry to ensure that they entered a valid command (if it exists in the dictionary). If it doesn't, we can tell them their input was invalid.
Also, when accessing array values, you should first ensure that the index you're accessing is valid (if the user only enters one command, like "lf"with no value, then you can't access array index [1] because it doesn't exist.
Also, it seems like even when the user does input a path, you store it in value, but never use it. So I modified the ListFiles and ListDirectories methods to treat the value as a path, and if it exists, list the files or directories from the path specified.
Try out this code with the changes:
public class Program
{
private static DirectoryInfo _maindirectory = new DirectoryInfo("C:\\Users");
private static Dictionary<string, Func<string>> _keyaction =
new Dictionary<string, Func<string>>
{
{"lf", ListFiles},
{"ld", ListDirectories},
{"cd", ChangeDirectory},
{"exit", Exit}
};
private static string _value;
private static void DisplayPrompt(FileSystemInfo directory)
{
Console.Write($"{directory?.FullName ?? "[cmd]"}: ");
}
private static void ProcessAnswer(IReadOnlyList<string> array)
{
var action = array.Count > 0 ? array[0] : string.Empty;
_value = array.Count > 1 ? array[1] : null;
Func<string> method;
_keyaction.TryGetValue(action, out method);
if (method == null)
{
WriteError($"Unknown command: {action}");
}
else
{
Console.WriteLine(_keyaction[action].Invoke());
}
}
private static string ListFiles()
{
var dir = Directory.Exists(_value) ? new DirectoryInfo(_value) : _maindirectory;
foreach (var file in dir.GetFiles())
{
Console.WriteLine(file.Name);
}
return "ok";
}
private static string ListDirectories()
{
var dir = Directory.Exists(_value) ? new DirectoryInfo(_value) : _maindirectory;
foreach (var directory in dir.GetDirectories())
{
Console.WriteLine(directory);
}
return "ok";
}
private static string ChangeDirectory()
{
if (Directory.Exists(_value))
{
_maindirectory = new DirectoryInfo(_value);
}
else if (Directory.Exists(Path.Combine(_maindirectory.FullName, _value)))
{
_maindirectory = new DirectoryInfo(
Path.Combine(_maindirectory.FullName, _value));
}
else
{
WriteError("Directory not found.");
}
return "ok";
}
private static void WriteError(string message)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(message);
Console.ResetColor();
}
private static string Exit()
{
Environment.Exit(0);
return "ok";
}
private static void Main()
{
while (true)
{
DisplayPrompt(_maindirectory);
ProcessAnswer(Console.ReadLine()?.Split(' '));
}
}
}

Related

C# How to use lambda expression with dictionary's value which is a method

I'm creating a program which will execute a command after user input.
Some commands I want to implement are: creating, reading a file, getting current working directory etc.
I created a dictionary which will store user input and corresponding command:
public static Dictionary<string, Action<string[]>> Commands { get; set; } = new Dictionary<string, Action<string[]>>()
{
{"pwd", PrintWorkingDirectory },
{"create", CreateFile },
{"print", ReadFile },
};
Unfortunately I have issues with triggering the method:
public void Run()
{
Console.WriteLine("Welcome, type in command.");
string input = null;
do
{
Console.Write("> ");
input = Console.ReadLine();
Execute(input);
} while (input != "exit");
}
public int Execute(string input)
{
if(Commands.Keys.Contains(input))
{
var action = Commands.Values.FirstOrDefault(); //doesn't work, gives '{command} not found'
}
Console.WriteLine($"{input} not found");
return 1;
}
Also I noticed that this solution would not work with method which is not void, but returns something, as for example CreateFile.
public static string CreateFile(string path)
{
Console.WriteLine("Create a file");
string userInput = Console.ReadLine();
try
{
string[] file = userInput.Split(new char[] { ' ' }).Skip(1).ToArray();
string newPath = Path.GetFullPath(Path.Combine(file));
using (FileStream stream = new FileStream(newPath, FileMode.Create, FileAccess.ReadWrite))
{
stream.Close();
}
using (StreamWriter sw = new StreamWriter(newPath))
{
Console.WriteLine("Please type the content.Press Enter to save.");
sw.WriteLine(Console.ReadLine());
sw.Close();
Console.WriteLine("File {0} has been created", newPath);
}
}
catch (Exception)
{
throw;
}
return path;
}
public static void ReadFile(string[] args)
{
Console.WriteLine("Reading file");
string userInput = Console.ReadLine();
string[] file = userInput.Split(new char[] { ' ' }).Skip(1).ToArray();
string newPath = Path.GetFullPath(Path.Combine(file));
string[] lines = File.ReadAllLines(newPath);
foreach (string line in lines)
Console.WriteLine(line);
}
public static void PrintWorkingDirectory(string[] args)
{
var currentDirectory = Directory.GetCurrentDirectory();
Console.WriteLine(currentDirectory);
}
Could somebody advise me how to deal with these issues?
Is it that this dictionary I created does not make much sense at all?
First problem: You're always fetching the first element of the dictionary and are not using the index operator to retrieve the correct value. Therefore change:
if(Commands.Keys.Contains(input))
{
var action = Commands.Values.FirstOrDefault(); //doesn't work, gives '{command} not found'
}
to:
public int Execute(string input)
{
if (Commands.Keys.Contains(input))
{
var action = Commands[input]; //doesn't work, gives '{command} not found'
action?.Invoke(new string[] { });
}
else
{
Console.WriteLine($"{input} not found");
}
return 1;
}
Regarding to your second question about dictionary usage. I think it is ok to use a dictionary to map different commands based on a given key. The alternative would be switch or if constructs, which can be prevented in Object Oriented Programming.
Regarding to your question about string CreateFile(string path). Since C# is strongly typed language your dictionary can only contain objects of type Action<string[]>, so you can't use methods with another signature than that. One solution is to add another dictionary in the form of Dictionary<string,Func<string[], string>. As a result you'll get more and more dictionaries depending on your method signatures. From here on you should think to build to encapsulate your commands in an e.g. CommandInterpreter class, that could offer an API like that:
void Request(string cmdName, string[] cmdParameters);
string GetLastResult();
int GetLastCode();
Update:
Below code shows a possible object oriented solution (I've left out interfaces to make the code more compact):
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
public class Command<T>
{
public string Name { get; }
public T TheCommand { get; }
public Command(string name, T theCommand)
{
Name = name;
TheCommand = theCommand;
}
}
public interface ICommandResult
{
void Ok(Action<ICommandResult> yes, Action<ICommandResult> no);
int Code { get; }
string Description { get; }
}
public abstract class CommandResult : ICommandResult
{
public int Code { get; }
public string Description { get; }
protected CommandResult(int code, string description)
{
Code = code;
Description = description;
}
public abstract void Ok(Action<ICommandResult> yes, Action<ICommandResult> no);
}
public class NullCommandResult : CommandResult
{
public NullCommandResult() : base(-1, "null")
{
}
public override void Ok(Action<ICommandResult> yes, Action<ICommandResult> no) => no?.Invoke(this);
}
public class SuccessCommandResult : CommandResult
{
public SuccessCommandResult(string description) : base(0, description)
{
}
public override void Ok(Action<ICommandResult> yes, Action<ICommandResult> no) => yes?.Invoke(this);
}
public class CommandInterpreter
{
private Dictionary<string, Func<IEnumerable<string>, ICommandResult>> Commands = new Dictionary<string, Func<IEnumerable<string>, ICommandResult>>();
public void RegisterCommand(Command<Func<IEnumerable<string>, ICommandResult>> cmd)
=> Commands.Add(cmd.Name, cmd.TheCommand);
public ICommandResult RunCommand(string name, IEnumerable<string> parameters)
=> Commands.Where(kvp => kvp.Key.Equals(name))
.Select(kvp => kvp.Value)
.DefaultIfEmpty(strArr => new NullCommandResult())
.Single()
.Invoke(parameters);
}
class Program
{
private CommandInterpreter _cmdInterpreter;
private Program()
{
_cmdInterpreter = new CommandInterpreter();
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("pwd", PrintWorkingDirectory));
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("create", CreateFile));
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("print", ReadFile));
}
private static CommandResult ReadFile(IEnumerable<string> arg) => new SuccessCommandResult("File read");
private static CommandResult CreateFile(IEnumerable<string> arg) => new SuccessCommandResult("File xyz created");
private static CommandResult PrintWorkingDirectory(IEnumerable<string> arg) => new SuccessCommandResult("Printed something");
static void Main() => new Program().Run();
private void Run()
{
Console.WriteLine("Welcome, type in command.");
string input;
do
{
Console.Write("> ");
input = Console.ReadLine();
var cmdResult = _cmdInterpreter.RunCommand(input, Enumerable.Empty<string>());
cmdResult.Ok(
r => Console.WriteLine($"Success: {cmdResult.Code}, {cmdResult.Description}"),
r => Console.WriteLine($"FAILED: {cmdResult.Code}, {cmdResult.Description}"));
} while (input != "exit");
}
}
}
Output:
Welcome, type in command.
> pwd
Success: 0, Printed something
> create
Success: 0, File xyz created
> abc
FAILED: -1, null
>
You can just copy the code and play around with it.

How to save output redirect to a file?

New to C# and want to implement below reading from input.txt and write result to output.txt
myprogram.exe < input.txt > output.txt
my code as below
class Program
{
static void Main(string[] args)
{
String line = Console.ReadLine();
while(line != null)
{
Console.Out.WriteLine(line);
line = Console.ReadLine();
}
}
}
The result is reading from input.txt is fine, and output.txt can be created but nothing in it.
UPDATE: the rootcause is output.txt been locked by other process which is not C# language related.
using System;
using System.IO;
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
var parameters = GetParameters(args);
var inputContent = GetInputContent(parameters);
CreateOutputFile(parameters, inputContent);
Console.WriteLine("it's ok. Press key to exit");
Console.ReadKey();
}
private static void CreateOutputFile(MyProgramParameters parameters, string inputContent)
{
File.WriteAllText(parameters.OutputFileName, inputContent);
}
private static string GetInputContent(MyProgramParameters parameters)
{
var result = File.ReadAllText(parameters.InputFileName);
return result;
}
private static MyProgramParameters GetParameters(string[] args)
{
CheckInputParameters(args);
return new MyProgramParameters
{
InputFileName = args[0],
OutputFileName = args[1]
};
}
private static void CheckInputParameters(string[] args)
{
if (args.Length != 2)
throw new ArgumentException("invalid parameters. For example, myProgram.exe input1.txt output.txt");
var inputFileName = args[0];
if (!File.Exists(inputFileName))
throw new IOException(string.Format("File {0} not found", inputFileName));
}
}
public class MyProgramParameters
{
public string InputFileName { get; set; }
public string OutputFileName { get; set; }
}
}
You haven't read documentation at all. Here you have the link: click
You should just do it in 2 steps:
Read from file to string[]
Write to file from string[]
To read use
string[] lines = System.IO.File.ReadAllLines(#"C:\Users\Public\TestFolder\WriteLines2.txt");
To write use
System.IO.File.WriteAllLines(#"C:\Users\Public\TestFolder\WriteLines.txt", lines);
Whole function would look something like this:
private void CopyFromFileToFile(string inputFileDest, string outputFileDest)
{
string[] lines;
lines = System.IO.File.ReadAllLines(inputFileDest);
System.IO.File.WriteAllLines(outputFileDest, lines);
}

universal Thread method

Hello everyone I want to ask , is there any methods that using thread that can run every methods[B] with argument and return any kind of those methods[B] refer :
private static Image queeImageFromString (string str)
{
// bla bla
return myImage;
} ///methods B return image
private static byte[] queeImagetoBtye (string str, string b)
{
//bla bla
return myBytesArray;
} //methods B return image
//**this is the methode I want to ask**
private static dynamic RunMethods(Action action)
{
var file;
var thread = new Thread(() => { file = action; });
thread.Start();
thread.Join();
return file; //will return anything like string, image, bytearray depending on method[B] returne
/// note : this code still wrong
}
/// I want to do 'RunMethods' run like this
public static string getResultByte (string str, string b)
{
// if methods[B] return string it will return string
StringBuilder strb = new StringBuilder (RunMethods(queeImagetoBtye (str,b));
return strb.ToString();
}
public static Image getImageResult (string str)
{
//if methods[B] return Image it will return Image
Image imgs = RunMethods( queeImageFromString (str));
return imgs;
}
Thank you.
Your code isn't working cause of many reasons.
Action can't return anything, it's an action, you have to use Func
You do not call the action, just assign it, so you have to write this code: file = action();
You're creating the action from lambda wrong way, you need to do like this: () => queeImageFromString(str)
Not a major problem, but you don't have to create a StringBuilder to create a string, you can return the method result itself.
What you need is a generic method, as dynamic is very slow structure, and dealing with it always relates with a reflection, which is slow by default. You can provide the type for function to return, like this:
private static T RunMethods<T>(Func<T> f)
{
var file;
var thread = new Thread(() => { file = f(); });
thread.Start();
thread.Join();
return file; //will return anything like string, image, bytearray depending on method[B] return value
}
So the call will looks like this:
//if methods[B] return Image it will return Image
Image imgs = RunMethods<Image>(() => return queeImageFromString(str));
Maybe the call could be even more simplified, but right now I can't check that. Also you really should consider the TPL usage instead of threads.
Check if doing it another way would be suitable for you.
I would create a class containing returned values, together with some indicator saying what type of object I'm returning.
public class ReturnValues
{
public object ReturnValue;
public ReturnValueType RetType;
}
enum ReturnValueType
{
typeOfString,
typeOfImage,
typeofInt
}
public static ReturnValues queeString (object[] args)
{
//.....
return new ReturnValues{ ReturnValue="Test", RetType=ReturnValueType.typeOfString };
}
Now, instead of Action, you have to use Func.
If you have to take parameters, you have to take them in some object too, like object array object[].
public static ReturnValues RunMethods(Func<object[],ReturnValues> action)
{
var file=action;
//.....
return file;
}
public static string getResultByte (object[] args)
{
string str=(string)args[0];
string b=(string)args[1];
var initValue=RunMethods(queeString(args));
string returnedString;
if (initValue.RetType==ReturnValueType.typeOfString)
{
returnedString=(string)initValue.ReturnValue;
}
StringBuilder strb = new StringBuilder (returnedString);
return strb.ToString();
}

Map commands to methods

I have a command line app where I map commands to methods, using a dictionary from the one-letter command to the name of the method (as a string). I can use this method both to invoke it and also to tell the user the list of available commands, like so:
private static readonly Dictionary<string, string> commands =
new Dictionary<string, string>
{
{"u", "DestroyUniverse"},
{"g", "DestroyGalaxy"},
{"p", "DestroyPlanet"},
{"t", "TimeTravel"}
};
public void DestroyUniverse(Stack<string> argStack)
{
// destroy the universe according to the options in argStack
// ...
}
public void DestroyGalaxy(Stack<string> argStack)
{
// destroy the galaxy according to the options in argStack
// ...
}
// ... other command methods
public void Run(Stack<string> argStack)
{
var cmd = argStack.Next();
string methodName;
// if no command given, or command is not found, tell
// user the list of available commands
if (cmd == null || !commands.TryGetValue(cmd, out methodName))
{
Console.WriteLine("Available Commands:{0}{1}",
Environment.NewLine,
string.Join(Environment.NewLine,
commands.OrderBy(kv => kv.Key)
.Select(kv => string.Format("{0} - {1}", kv.Key, kv.Value))));
Environment.ExitCode = 1;
return;
}
// command is valid, call the method
GetType().GetMethod(methodName).Invoke(this, new object[] {argStack});
}
This is working fine, except I don't like that I'm using strings as the value in the dictionary. There is thus no compiler support for making sure there is a method for every string. I'd rather use "methods" somehow, but still have access to the name of the method, for the part where I list the commands. Is there anything like that available?
Why doesn't this work?
class Program
{
static void Main(string[] args)
{
var p = new Program();
p.Run(new Stack<string>(args.Reverse()));
Console.ReadKey();
}
private readonly Dictionary<string, Action<Stack<string>>> commands;
public Program() {
commands =
new Dictionary<string, Action<Stack<string>>>
{
{"u", DestroyUniverse },
{"g", DestroyGalaxy },
{"p", DestroyPlanet },
{"t", TimeTravel }
};
}
public void DestroyUniverse(Stack<string> argStack)
{
// destroy the universe according to the options in argStack
// ...
}
public void DestroyGalaxy(Stack<string> argStack)
{
// destroy the galaxy according to the options in argStack
// ...
}
private string Next(Stack<string argStack)
{
// wish this was a method of Stack<T>
return argStack.Any() ? argStack.Pop() : null;
}
public void Run(Stack<string> argStack)
{
var cmd = Next(argStack);
Action<Stack<string>> action = null;
// if no command given, or command is not found, tell
// user the list of available commands
if (cmd == null || !commands.TryGetValue(cmd, out action))
{
Console.WriteLine("Available Commands:{0}{1}",
Environment.NewLine,
string.Join(Environment.NewLine,
commands.OrderBy(kv => kv.Key)
.Select(kv => string.Format("{0} - {1}",
kv.Key, kv.Value.Method.Name))));
Environment.ExitCode = 1;
return;
}
// command is valid, call the method
action(argStack);
}
}
You can use Reflection. Get a MethodInfo of all the methods you're intrested in and put them in the dictionary. Later then you can invoke one of them. If you need the name of the method as string, you can get it also from the MethodInfo.

Approvaltests and PDF

Can I use ApprovalTests with PDF's? I tried using the FileLauncher but it seems the identical PDF's are slightly different at file (bit) level. Or did I use it wrongly?
[TestMethod]
[UseReporter(typeof(FileLauncherReporter))]
public void TestPdf()
{
var createSomePdf = PdfCreate();
ApprovalTests.Approvals.Verify(new FileInfo(createSomePdf.FileName));
}
The Pdf is most likely being created with a timestamp. Depending on the method used to create the pdf, you might be able to mock out the created time. but I had to scrub it.
Here's the code I used to do that.
public static void VerifyPdf(string coverFile)
{
ScrubPdf(coverFile);
Approvals.Verify(new ExistingFileWriter(coverFile));
}
private static void ScrubPdf(string coverFile)
{
long location;
using (var pdf = File.OpenRead(coverFile))
{
location = Find("/CreationDate (", pdf);
}
using (var pdf = File.OpenWrite(coverFile))
{
pdf.Seek(location, SeekOrigin.Begin);
var original = "/CreationDate (D:20110426104115-07'00')";
var desired = new System.Text.ASCIIEncoding().GetBytes(original);
pdf.Write(desired, 0, desired.Length);
pdf.Flush();
}
}
I found a command-line tool, diff-pdf. Compares 2 PDFs and returns exit code 0 if they're the same, 1 if they differ. Download + extract + add it to your PATH.
Downside - it must render both PDFs to perform the diff. If they're big, perf hit.
Approver (based heavily on ApprovalTests.Approvers.FileApprover):
public class DiffPdfApprover : IApprovalApprover
{
public static void Verify(byte[] bytes)
{
var writer = new ApprovalTests.Writers.BinaryWriter(bytes, "pdf");
var namer = ApprovalTests.Approvals.GetDefaultNamer();
var reporter = ApprovalTests.Approvals.GetReporter();
ApprovalTests.Core.Approvals.Verify(new DiffPdfApprover(writer, namer), reporter);
}
private DiffPdfApprover(IApprovalWriter writer, IApprovalNamer namer)
{
this.writer = writer;
this.namer = namer;
}
private readonly IApprovalNamer namer;
private readonly IApprovalWriter writer;
private string approved;
private ApprovalException failure;
private string received;
public virtual bool Approve()
{
string basename = string.Format(#"{0}\{1}", namer.SourcePath, namer.Name);
approved = Path.GetFullPath(writer.GetApprovalFilename(basename));
received = Path.GetFullPath(writer.GetReceivedFilename(basename));
received = writer.WriteReceivedFile(received);
failure = Approve(approved, received);
return failure == null;
}
public static ApprovalException Approve(string approved, string received)
{
if (!File.Exists(approved))
{
return new ApprovalMissingException(received, approved);
}
var process = new Process();
//settings up parameters for the install process
process.StartInfo.FileName = "diff-pdf";
process.StartInfo.Arguments = String.Format("\"{0}\" \"{1}\"", received, approved);
process.Start();
process.WaitForExit();
if (process.ExitCode != 0)
{
return new ApprovalMismatchException(received, approved);
}
return null;
}
public void Fail()
{
throw failure;
}
public void ReportFailure(IApprovalFailureReporter reporter)
{
reporter.Report(approved, received);
}
public void CleanUpAfterSucess(IApprovalFailureReporter reporter)
{
File.Delete(received);
if (reporter is IApprovalReporterWithCleanUp)
{
((IApprovalReporterWithCleanUp)reporter).CleanUp(approved, received);
}
}
}
To Verify:
DiffPdfApprover.Verify(pdfBytes);
diff-pdf can visually show diffs as well. I rolled a Reporter for this, but don't use it much. I think it'll come in handy if there are regressions after initial report dev (which is where I'm at right now).
public class DiffPdfReporter : GenericDiffReporter
{
private static readonly string Path = FindFullPath("diff-pdf.exe");
public DiffPdfReporter() : base(Path,
GetArgs(),
"Please put diff-pdf.exe in your %PATH%. https://github.com/vslavik/diff-pdf. And restart whatever's running the tests. Everything seems to cache the %PATH%.") { }
private static string GetArgs()
{
return "--view \"{0}\" \"{1}\"";
}
private static string FindFullPath(string programInPath)
{
foreach (var path in from path in Environment.GetEnvironmentVariable("path").Split(';')
select path)
{
var fullPath = System.IO.Path.Combine(path, programInPath);
if (File.Exists(fullPath))
return fullPath;
}
return null;
}
}
Looks like this is built in to ApprovalTests now.
usage:
Approvals.VerifyPdfFile(pdfFileLocation);
See the source:
public static void VerifyPdfFile(string pdfFilePath)
{
PdfScrubber.ScrubPdf(pdfFilePath);
Verify(new ExistingFileWriter(pdfFilePath));
}

Categories

Resources