Skip to content

Commit ca0fb60

Browse files
committed
Added tests for SingleStepSolution and SudokuFilePuzzleProvider.
SudokuFilePuzzleProvider now ignores lines which start with '#' and can read files where there is no separator between values.
1 parent e7a5d64 commit ca0fb60

File tree

4 files changed

+154
-4
lines changed

4 files changed

+154
-4
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using NUnit.Framework;
2+
using SimpleSudokuSolver.Model;
3+
using SimpleSudokuSolver.PuzzleProviders;
4+
using System;
5+
using System.IO;
6+
7+
namespace SimpleSudokuSolver.Tests.PuzzleProviders
8+
{
9+
[TestFixture]
10+
public class SudokuFilePuzzleProviderTests
11+
{
12+
[Test]
13+
public void SudokuFilePuzzleProvider_ThrowsExceptionForEmptyPath_Test()
14+
{
15+
Assert.That(() => new SudokuFilePuzzleProvider(null), Throws.InstanceOf<ArgumentException>());
16+
Assert.That(() => new SudokuFilePuzzleProvider(string.Empty), Throws.InstanceOf<ArgumentException>());
17+
}
18+
19+
[Test]
20+
public void SudokuFilePuzzleProvider_WorksWithAllFormats_Test()
21+
{
22+
var filePath = Path.GetTempFileName();
23+
var separators = new[] { ",", " ", "\t", "" };
24+
var sudoku = new int[9, 9];
25+
sudoku[1, 2] = 5;
26+
27+
foreach(var separator in separators)
28+
{
29+
SaveSudokuUsingSeparator(sudoku, separator, filePath);
30+
var provider = new SudokuFilePuzzleProvider(filePath);
31+
var sudokuPuzzle = provider.GetPuzzle();
32+
Assert.That(sudokuPuzzle[1, 2], Is.EqualTo(5));
33+
}
34+
35+
File.Delete(filePath);
36+
}
37+
38+
private void SaveSudokuUsingSeparator(int[,] sudoku, string separator, string filePath)
39+
{
40+
var sudokuToString = new SudokuPuzzle(sudoku).ToString();
41+
var sudokuToStringWithSeparator = sudokuToString.Replace(" ", separator);
42+
File.WriteAllText(filePath, sudokuToStringWithSeparator);
43+
}
44+
}
45+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using NUnit.Framework;
2+
using System;
3+
4+
namespace SimpleSudokuSolver.Tests
5+
{
6+
[TestFixture]
7+
public class SingleStepSolutionTests
8+
{
9+
[Test]
10+
public void CandidateEqualityTest()
11+
{
12+
var candidate = new SingleStepSolution.Candidate(1, 2, 3);
13+
var otherCandidate = new SingleStepSolution.Candidate(1, 2, 3);
14+
15+
Assert.That(candidate != null);
16+
Assert.That(null != candidate);
17+
Assert.That(candidate == otherCandidate);
18+
Assert.That(candidate.Equals(otherCandidate));
19+
Assert.That(candidate.GetHashCode() == otherCandidate.GetHashCode());
20+
21+
var rowDiffers = new SingleStepSolution.Candidate(0, 2, 3);
22+
Assert.That(candidate != rowDiffers);
23+
24+
var columnDiffers = new SingleStepSolution.Candidate(1, 0, 3);
25+
Assert.That(candidate != columnDiffers);
26+
27+
var valueDiffers = new SingleStepSolution.Candidate(1, 2, 0);
28+
Assert.That(candidate != valueDiffers);
29+
30+
candidate = null;
31+
otherCandidate = null;
32+
Assert.That(candidate == otherCandidate);
33+
}
34+
35+
[Test]
36+
public void CandidateToStringTest()
37+
{
38+
var candidate = new SingleStepSolution.Candidate(1, 2, 3);
39+
Assert.That(candidate.ToString(), Is.EqualTo("[1,2] - 3"));
40+
}
41+
42+
[Test]
43+
public void SingleStepSolutionToStringTest()
44+
{
45+
var resultSolution = new SingleStepSolution(1, 2, 3, "test");
46+
var expectedResultText = "Row 2 Column 3 Value 3 [test]";
47+
Assert.That(resultSolution.ToString(), Is.EqualTo(expectedResultText));
48+
Assert.That(resultSolution.SolutionDescription, Is.EqualTo(expectedResultText));
49+
50+
var eliminationSolution = new SingleStepSolution(new[]
51+
{
52+
new SingleStepSolution.Candidate(1, 2, 3),
53+
new SingleStepSolution.Candidate(1, 2, 4)
54+
}, "test");
55+
var expectedEliminationText = $"Eliminated candidates [test]:{Environment.NewLine}- Row 2 Column 3 Values 3,4";
56+
Assert.That(eliminationSolution.ToString(), Is.EqualTo(expectedEliminationText));
57+
Assert.That(eliminationSolution.SolutionDescription, Is.EqualTo(expectedEliminationText));
58+
}
59+
}
60+
}

SimpleSudokuSolver/PuzzleProviders/SudokuFilePuzzleProvider.cs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
using System;
22
using System.IO;
33
using System.Linq;
4+
using System.Text.RegularExpressions;
45

56
namespace SimpleSudokuSolver.PuzzleProviders
67
{
78
/// <summary>
89
/// Reads sudoku puzzle from file.
910
/// Each row in the file represents one row of the puzzle.
10-
/// Values must be separated using either comma, space or tab.
11+
/// Values in the row are:
12+
/// - separated using either comma, space or tab
13+
/// - or not separated at all
14+
/// Lines starting with '#' are ignored.
1115
/// </summary>
1216
public class SudokuFilePuzzleProvider : ISudokuPuzzleProvider
1317
{
@@ -24,7 +28,14 @@ public SudokuFilePuzzleProvider(string filePath)
2428
/// <inheritdoc />
2529
public int[,] GetPuzzle()
2630
{
27-
var lines = File.ReadAllLines(_filePath).Where(x => !string.IsNullOrWhiteSpace(x.Trim())).ToArray();
31+
var lines = File.ReadAllLines(_filePath).Where(x =>
32+
{
33+
var trimmed = x.Trim();
34+
if (trimmed.StartsWith("#"))
35+
return false;
36+
37+
return !string.IsNullOrWhiteSpace(trimmed);
38+
}).ToArray();
2839
var firstRowElements = GetRowElements(lines[0]);
2940

3041
int rowCount = lines.Length;
@@ -46,8 +57,15 @@ public SudokuFilePuzzleProvider(string filePath)
4657

4758
private int[] GetRowElements(string row)
4859
{
49-
var splitCharacters = new char[] { ',', ' ', '\t' };
50-
return row.Split(splitCharacters)
60+
var separators = new[] { ",", " ", "\t" };
61+
62+
// if there are no separators, add them
63+
if (!separators.Any(x => row.Contains(x)))
64+
{
65+
row = Regex.Replace(row, ".{1}", "$0,");
66+
}
67+
68+
return row.Split(separators, StringSplitOptions.RemoveEmptyEntries)
5169
.Where(x => !string.IsNullOrWhiteSpace(x.Trim()))
5270
.Select(x => int.Parse(x.Trim()))
5371
.ToArray();

SimpleSudokuSolver/SingleStepSolution.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,33 @@ public Candidate(int indexOfRow, int indexOfColumn, int value)
3737
/// </summary>
3838
public int Value { get; }
3939

40+
/// <remarks>
41+
/// For implementation details see:
42+
/// https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/how-to-define-value-equality-for-a-type
43+
/// </remarks>
44+
public static bool operator ==(Candidate lhs, Candidate rhs)
45+
{
46+
// Check for null on left side.
47+
if (object.ReferenceEquals(lhs, null))
48+
{
49+
if (object.ReferenceEquals(rhs, null))
50+
{
51+
// null == null = true.
52+
return true;
53+
}
54+
55+
// Only the left side is null.
56+
return false;
57+
}
58+
// Equals handles case of null on right side.
59+
return lhs.Equals(rhs);
60+
}
61+
62+
public static bool operator !=(Candidate lhs, Candidate rhs)
63+
{
64+
return !(lhs == rhs);
65+
}
66+
4067
public override bool Equals(object obj)
4168
{
4269
return obj is Candidate candidate &&

0 commit comments

Comments
 (0)