Rules and board state live in Minesweeper.Core with no console calls, so the same board drives both the CLI and the tests.
Engine: seeded or random boards with classic presets; first click always safe (and its ring too when the board has room); cascading reveal that stops at numbers; flagging and chording; win by clearing every safe cell; FromMines() for deterministic layouts.
CLI: typed commands ("3 4", "f 2 5", "chord 6 7") plus optional arrow-key mode. Rows numbered, columns lettered, player input 1-based while engine coordinates stay 0-based, with CommandParser the only place that converts. ANSI colour when writing to a terminal, plain when redirected or NO_COLOR is set.
Tests: 186 covering flood-fill bounds, first-click safety, chording, win/loss, command parsing, session behaviour, and renderer alignment with colour on and off.
Co-authored-by: openhands <openhands@all-hands.dev>
150 lines
4.6 KiB
C#
150 lines
4.6 KiB
C#
using Minesweeper.Cli;
|
|
using Minesweeper.Core;
|
|
|
|
namespace Minesweeper.Core.Tests;
|
|
|
|
public class CommandLineTests
|
|
{
|
|
[Fact]
|
|
public void TryParse_WithoutArguments_UsesBeginnerDefaults()
|
|
{
|
|
Assert.True(CommandLine.TryParse([], out var options, out var error));
|
|
|
|
Assert.Empty(error);
|
|
Assert.Equal(GameDifficulty.Beginner, options.Difficulty);
|
|
Assert.Null(options.Seed);
|
|
Assert.False(options.UseKeys);
|
|
Assert.False(options.ShowHelp);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("-d", "expert", GameDifficulty.Expert)]
|
|
[InlineData("--difficulty", "EXPERT", GameDifficulty.Expert)]
|
|
[InlineData("-d", "hard", GameDifficulty.Expert)]
|
|
[InlineData("-d", "3", GameDifficulty.Expert)]
|
|
[InlineData("-d", "e", GameDifficulty.Expert)]
|
|
[InlineData("-d", "beginner", GameDifficulty.Beginner)]
|
|
[InlineData("-d", "easy", GameDifficulty.Beginner)]
|
|
[InlineData("-d", "1", GameDifficulty.Beginner)]
|
|
[InlineData("-d", "intermediate", GameDifficulty.Intermediate)]
|
|
[InlineData("-d", "medium", GameDifficulty.Intermediate)]
|
|
[InlineData("-d", "2", GameDifficulty.Intermediate)]
|
|
public void TryParse_DifficultySwitch(string flag, string value, GameDifficulty expected)
|
|
{
|
|
Assert.True(CommandLine.TryParse([flag, value], out var options, out _));
|
|
|
|
Assert.Equal(expected, options.Difficulty);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(new[] { "-b" }, GameDifficulty.Beginner)]
|
|
[InlineData(new[] { "-i" }, GameDifficulty.Intermediate)]
|
|
[InlineData(new[] { "-e" }, GameDifficulty.Expert)]
|
|
public void TryParse_DifficultyShorthands(string[] args, GameDifficulty expected)
|
|
{
|
|
Assert.True(CommandLine.TryParse(args, out var options, out _));
|
|
|
|
Assert.Equal(expected, options.Difficulty);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryParse_CombinesOptions()
|
|
{
|
|
Assert.True(CommandLine.TryParse(["-e", "--seed", "42", "-k"], out var options, out _));
|
|
|
|
Assert.Equal(GameDifficulty.Expert, options.Difficulty);
|
|
Assert.Equal(42, options.Seed);
|
|
Assert.True(options.UseKeys);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("0")]
|
|
[InlineData("-7")]
|
|
[InlineData("999999999")]
|
|
public void TryParse_AcceptsAnyIntegerSeed(string seed)
|
|
{
|
|
Assert.True(CommandLine.TryParse(["--seed", seed], out var options, out _));
|
|
|
|
Assert.Equal(int.Parse(seed), options.Seed);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryParse_Help()
|
|
{
|
|
Assert.True(CommandLine.TryParse(["--help"], out var options, out _));
|
|
Assert.True(options.ShowHelp);
|
|
Assert.True(CommandLine.TryParse(["-h"], out options, out _));
|
|
Assert.True(options.ShowHelp);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryParse_Keys()
|
|
{
|
|
Assert.True(CommandLine.TryParse(["--keys"], out var options, out _));
|
|
Assert.True(options.UseKeys);
|
|
Assert.True(CommandLine.TryParse(["-k"], out options, out _));
|
|
Assert.True(options.UseKeys);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("--difficulty")]
|
|
[InlineData("-d")]
|
|
public void TryParse_DifficultyWithoutValue_Fails(string flag)
|
|
{
|
|
Assert.False(CommandLine.TryParse([flag], out var options, out var error));
|
|
|
|
Assert.Null(options);
|
|
Assert.Contains("difficulty", error);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("impossible")]
|
|
[InlineData("")]
|
|
public void TryParse_UnknownDifficulty_Fails(string value)
|
|
{
|
|
Assert.False(CommandLine.TryParse(["-d", value], out var options, out var error));
|
|
|
|
Assert.Null(options);
|
|
Assert.Contains("difficulty", error);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("--seed")]
|
|
[InlineData("--seed", "abc")]
|
|
[InlineData("--seed", "1.5")]
|
|
public void TryParse_BadSeed_Fails(params string[] args)
|
|
{
|
|
Assert.False(CommandLine.TryParse(args, out var options, out var error));
|
|
|
|
Assert.Null(options);
|
|
Assert.Contains("seed", error);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryParse_UnknownOption_FailsAndNamesIt()
|
|
{
|
|
Assert.False(CommandLine.TryParse(["--turbo"], out var options, out var error));
|
|
|
|
Assert.Null(options);
|
|
Assert.Contains("--turbo", error);
|
|
}
|
|
|
|
[Fact]
|
|
public void Usage_MentionsEveryOption()
|
|
{
|
|
Assert.Contains("--difficulty", CommandLine.Usage);
|
|
Assert.Contains("--seed", CommandLine.Usage);
|
|
Assert.Contains("--keys", CommandLine.Usage);
|
|
Assert.Contains("--help", CommandLine.Usage);
|
|
}
|
|
|
|
[Fact]
|
|
public void HelpText_ListsEveryCommandVerb()
|
|
{
|
|
foreach (var verb in new[] { "reveal", "flag", "chord", "go", "difficulty", "new", "help", "quit" })
|
|
{
|
|
Assert.Contains(verb, GameSession.HelpText);
|
|
}
|
|
}
|
|
}
|