From 3f6d4bac194e9c240aea9dcbe2453144dc64fb63 Mon Sep 17 00:00:00 2001 From: Rudis Muiznieks Date: Fri, 24 Jul 2015 00:56:53 -0500 Subject: [PATCH] added blockhandler unit tests --- .gitignore | 2 + Ficdown.Console/Program.cs | 14 ++ Ficdown.Parser.Tests/BlockHandlerTests.cs | 114 ++++++++++++ .../Extensions/TestExtensions.cs | 13 ++ .../Ficdown.Parser.Tests.csproj | 162 +++++++++--------- .../Model/Parser/FicdownException.cs | 27 ++- Ficdown.Parser/Parser/BlockHandler.cs | 24 ++- scripts/build.sh | 4 + scripts/rebuild.sh | 5 + scripts/run.sh | 4 + scripts/test.sh | 7 + 11 files changed, 276 insertions(+), 100 deletions(-) create mode 100644 Ficdown.Parser.Tests/Extensions/TestExtensions.cs create mode 100755 scripts/build.sh create mode 100755 scripts/rebuild.sh create mode 100755 scripts/run.sh create mode 100755 scripts/test.sh diff --git a/.gitignore b/.gitignore index 3d16b6f..8a0fbaa 100644 --- a/.gitignore +++ b/.gitignore @@ -173,5 +173,7 @@ UpgradeLog*.htm # Microsoft Fakes FakesAssemblies/ +scripts/xunit.runner.console.2.0.0/ + # vim swap files .*.sw* diff --git a/Ficdown.Console/Program.cs b/Ficdown.Console/Program.cs index 516ad34..c5a94b9 100644 --- a/Ficdown.Console/Program.cs +++ b/Ficdown.Console/Program.cs @@ -6,11 +6,22 @@ using Microsoft.SqlServer.Server; using Parser; using Parser.Render; + using Parser.Model.Parser; internal class Program { private static int Main(string[] args) { + AppDomain.CurrentDomain.UnhandledException += (sender, e) => + { + if(e.ExceptionObject is FicdownException) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.Error.WriteLine(e.ExceptionObject.ToString()); + Environment.Exit(3); + } + }; + string infile = null; string output = null; string tempdir = null; @@ -118,7 +129,10 @@ story.Orphans.ToList().ForEach(o => { + var currentColor = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Yellow; Console.Error.WriteLine("Warning (line {0}): {1} {2} is unreachable", o.LineNumber, o.Type, o.Name); + Console.ForegroundColor = currentColor; }); IRenderer rend; diff --git a/Ficdown.Parser.Tests/BlockHandlerTests.cs b/Ficdown.Parser.Tests/BlockHandlerTests.cs index 40424c8..805ea59 100644 --- a/Ficdown.Parser.Tests/BlockHandlerTests.cs +++ b/Ficdown.Parser.Tests/BlockHandlerTests.cs @@ -1,6 +1,120 @@ namespace Ficdown.Parser.Tests { + using System; + using System.Linq; + using System.Collections.Generic; + using Xunit; + using Parser; + using Model.Parser; + using Extensions; + public class BlockHandlerTests { + [Fact] + public void NoStoryBlockThrowsException() + { + var bh = new BlockHandler(); + Assert.Throws(() => bh.ParseBlocks(bh.ExtractBlocks(@" +## this file has no story +just a lonely scene".ToLines()))); + } + + [Fact] + public void StoryWithNoAnchorThrowsException() + { + var bh = new BlockHandler(); + Assert.Throws(() => bh.ParseBlocks(bh.ExtractBlocks(@" +# my story +doesn't link to a scene +## a scene +nothing links here".ToLines()))); + } + + [Fact] + public void StoriesWithFancyAnchorsThrowExceptions() + { + var bh = new BlockHandler(); + Assert.Throws(() => bh.ParseBlocks(bh.ExtractBlocks(@" +# [my story](/a-scene?conditional) +story with a conditional +## a scene +this is a scene".ToLines()))); + Assert.Throws(() => bh.ParseBlocks(bh.ExtractBlocks(@" +# [my story](/a-scene#toggle) +story with a toggle +## a scene +this is a scene".ToLines()))); + Assert.Throws(() => bh.ParseBlocks(bh.ExtractBlocks(@" +# [my story](/a-scene#?conditional#toggle) +story with a conditional and a toggle +## a scene +this is a scene".ToLines()))); + } + + [Fact] + public void StoryLinkingToNonExistentSceneThrowsException() + { + var bh = new BlockHandler(); + Assert.Throws(() => bh.ParseBlocks(bh.ExtractBlocks(@" +# [a story](/non-existent) +this story links to a first scene that doesn't exist +## a scene +this scene is so cold and lonely".ToLines()))); + } + + [Fact] + public void StoryWithALegitAnchorParses() + { + var bh = new BlockHandler(); + bh.ParseBlocks(bh.ExtractBlocks(@" +# [my story](/a-scene) +story with a simple link +## a scene +this is a scene".ToLines())); + } + + [Fact] + public void StoryWithDuplicateActionsThrowsException() + { + var bh = new BlockHandler(); + Assert.Throws(() => bh.ParseBlocks(bh.ExtractBlocks(@" +# [a story](/a-scene) +this story is action-happy +## a scene +this is the first scene +### an action +this is an action +## another scene +this is another scene +### an action +oops, this is the same action!".ToLines()))); + } + + [Fact] + public void StoryWithScenesAndActionsParses() + { + var bh = new BlockHandler(); + var story = bh.ParseBlocks(bh.ExtractBlocks(@" +# [my story](/a-scene) +story with a simple link +## a scene +this is a scene +## [a scene](?something) +this is the same scene with a conditional +## another scene +this is another scene +### action1 +this is an action +### action 2 +this is another action +## yet another scene +yup here's some more +### another action +the last action (hero?)".ToLines())); + + Assert.Equal(3, story.Scenes.Count); + Assert.Equal(2, story.Scenes["a-scene"].Count); + Assert.Equal(3, story.Actions.Count); + } } } diff --git a/Ficdown.Parser.Tests/Extensions/TestExtensions.cs b/Ficdown.Parser.Tests/Extensions/TestExtensions.cs new file mode 100644 index 0000000..8aab81b --- /dev/null +++ b/Ficdown.Parser.Tests/Extensions/TestExtensions.cs @@ -0,0 +1,13 @@ +namespace Ficdown.Parser.Tests.Extensions +{ + using System; + using System.Collections.Generic; + + public static class TestExtensions + { + public static IEnumerable ToLines(this string content) + { + return content.Split(new[] {Environment.NewLine}, StringSplitOptions.None); + } + } +} diff --git a/Ficdown.Parser.Tests/Ficdown.Parser.Tests.csproj b/Ficdown.Parser.Tests/Ficdown.Parser.Tests.csproj index df79b6a..18cfbe6 100644 --- a/Ficdown.Parser.Tests/Ficdown.Parser.Tests.csproj +++ b/Ficdown.Parser.Tests/Ficdown.Parser.Tests.csproj @@ -1,88 +1,88 @@ - - - - - Debug - AnyCPU - {756192E2-BA47-4850-8096-289D44878A7E} - Library - Properties - Ficdown.Parser.Tests - Ficdown.Parser.Tests - v4.5 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\Moq.4.2.1402.2112\lib\net40\Moq.dll - - - ..\packages\ServiceStack.Text.4.0.22\lib\net40\ServiceStack.Text.dll - - - - - - - - - - ..\packages\xunit.1.9.2\lib\net20\xunit.dll - - - - - - - - - True - True - Resources.resx - - - - - - - - - - - - {780f652d-7541-4171-bb89-2d263d3961dc} - Ficdown.Parser - - - - - ResXFileCodeGenerator - Resources.Designer.cs - - - + + + + Debug + AnyCPU + {756192E2-BA47-4850-8096-289D44878A7E} + Library + Properties + Ficdown.Parser.Tests + Ficdown.Parser.Tests + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Moq.4.2.1402.2112\lib\net40\Moq.dll + + + ..\packages\ServiceStack.Text.4.0.22\lib\net40\ServiceStack.Text.dll + + + + + + + + + + ..\packages\xunit.1.9.2\lib\net20\xunit.dll + + + + + + + + + True + True + Resources.resx + + + + + + + + + + + + + {780f652d-7541-4171-bb89-2d263d3961dc} + Ficdown.Parser + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + --> \ No newline at end of file diff --git a/Ficdown.Parser/Model/Parser/FicdownException.cs b/Ficdown.Parser/Model/Parser/FicdownException.cs index 8e20729..c81054f 100644 --- a/Ficdown.Parser/Model/Parser/FicdownException.cs +++ b/Ficdown.Parser/Model/Parser/FicdownException.cs @@ -4,31 +4,26 @@ namespace Ficdown.Parser.Model.Parser public class FicdownException : Exception { - private string _blockName; - private int? _lineNumber; - private string _message; + public string BlockName { get; private set; } + public int? LineNumber { get; private set; } public FicdownException(string blockName, int? lineNumber, string message) : base(message) { - _blockName = blockName; - _lineNumber = lineNumber; - _message = message; + BlockName = blockName; + LineNumber = lineNumber; } - public FicdownException(string message) : base(message) - { - _message = message; - } + public FicdownException(string message) : base(message) { } public override string ToString() { - return !string.IsNullOrEmpty(_blockName) + return !string.IsNullOrEmpty(BlockName) ? string.Format("Error in block \"{0}\" (Line {1}): {2}", - _blockName, - _lineNumber.HasValue - ? _lineNumber.ToString() - : "unknown", _message) - : string.Format("Error: {0}", _message); + BlockName, + LineNumber.HasValue + ? LineNumber.ToString() + : "unknown", Message) + : string.Format("Error: {0}", Message); } } } diff --git a/Ficdown.Parser/Parser/BlockHandler.cs b/Ficdown.Parser/Parser/BlockHandler.cs index 9c14dee..71fc022 100644 --- a/Ficdown.Parser/Parser/BlockHandler.cs +++ b/Ficdown.Parser/Parser/BlockHandler.cs @@ -48,7 +48,15 @@ var storyBlock = blocks.SingleOrDefault(b => b.Type == BlockType.Story); if(storyBlock == null) throw new FicdownException("No story block found"); - var storyAnchor = Utilities.GetInstance(storyBlock.Name, storyBlock.LineNumber).ParseAnchor(storyBlock.Name); + Anchor storyAnchor; + try + { + storyAnchor = Utilities.GetInstance(storyBlock.Name, storyBlock.LineNumber).ParseAnchor(storyBlock.Name); + } + catch(FicdownException ex) + { + throw new FicdownException(ex.BlockName, ex.LineNumber, "Story block must be an anchor pointing to the first scene"); + } if (storyAnchor.Href.Target == null || storyAnchor.Href.Conditions != null || storyAnchor.Href.Toggles != null) @@ -70,8 +78,18 @@ story.Scenes[scene.Key].Add(scene); } var aid = 1; - story.Actions = - blocks.Where(b => b.Type == BlockType.Action).Select(b => BlockToAction(b, aid++)).ToDictionary(a => a.Toggle, a => a); + try + { + story.Actions = + blocks.Where(b => b.Type == BlockType.Action).Select(b => BlockToAction(b, aid++)).ToDictionary(a => a.Toggle, a => a); + } + catch(ArgumentException) + { + var a = blocks.First(b => b.Type == BlockType.Action && blocks.Any(d => b != d && BlockToAction(b, 0).Toggle == BlockToAction(d, 0).Toggle)); + var actionA = BlockToAction(a, a.LineNumber); + var dupe = blocks.First(b => b.Type == BlockType.Action && b != a && BlockToAction(b, 0).Toggle == actionA.Toggle); + throw new FicdownException(actionA.Toggle, actionA.LineNumber, string.Format("Action is defined again on line {0}", dupe.LineNumber)); + } if (!story.Scenes.ContainsKey(storyAnchor.Href.Target)) throw new FicdownException(storyBlock.Name, storyBlock.LineNumber, string.Format("Story targets non-existent scene: {0}", storyAnchor.Href.Target)); diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..c0a896b --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +DIR=`dirname $0` +xbuild $DIR/../Ficdown.sln diff --git a/scripts/rebuild.sh b/scripts/rebuild.sh new file mode 100755 index 0000000..7e6fcc9 --- /dev/null +++ b/scripts/rebuild.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +DIR=`dirname $0` +rm -rf $DIR/../*/bin $DIR/../*/obj +xbuild $DIR/../Ficdown.sln diff --git a/scripts/run.sh b/scripts/run.sh new file mode 100755 index 0000000..3b1a895 --- /dev/null +++ b/scripts/run.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +DIR=`dirname $0` +mono $DIR/../Ficdown.Console/bin/Debug/Ficdown.Console.exe "$@" diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..9e36b9f --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +DIR=`dirname $0` +if [ ! -d "$DIR/xunit.runner.console.2.0.0" ]; then + nuget install xunit.runner.console -Version 2.0.0 -OutputDirectory $DIR +fi +mono --debug $DIR/xunit.runner.console.2.0.0/tools/xunit.console.exe $DIR/../Ficdown.Parser.Tests/bin/Debug/Ficdown.Parser.Tests.dll