added blockhandler unit tests

This commit is contained in:
Rudis Muiznieks 2015-07-24 00:56:53 -05:00
parent f6021557fc
commit 3f6d4bac19
11 changed files with 276 additions and 100 deletions

2
.gitignore vendored
View File

@ -173,5 +173,7 @@ UpgradeLog*.htm
# Microsoft Fakes # Microsoft Fakes
FakesAssemblies/ FakesAssemblies/
scripts/xunit.runner.console.2.0.0/
# vim swap files # vim swap files
.*.sw* .*.sw*

View File

@ -6,11 +6,22 @@
using Microsoft.SqlServer.Server; using Microsoft.SqlServer.Server;
using Parser; using Parser;
using Parser.Render; using Parser.Render;
using Parser.Model.Parser;
internal class Program internal class Program
{ {
private static int Main(string[] args) 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 infile = null;
string output = null; string output = null;
string tempdir = null; string tempdir = null;
@ -118,7 +129,10 @@
story.Orphans.ToList().ForEach(o => 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.Error.WriteLine("Warning (line {0}): {1} {2} is unreachable", o.LineNumber, o.Type, o.Name);
Console.ForegroundColor = currentColor;
}); });
IRenderer rend; IRenderer rend;

View File

@ -1,6 +1,120 @@
namespace Ficdown.Parser.Tests 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 public class BlockHandlerTests
{ {
[Fact]
public void NoStoryBlockThrowsException()
{
var bh = new BlockHandler();
Assert.Throws<FicdownException>(() => bh.ParseBlocks(bh.ExtractBlocks(@"
## this file has no story
just a lonely scene".ToLines())));
}
[Fact]
public void StoryWithNoAnchorThrowsException()
{
var bh = new BlockHandler();
Assert.Throws<FicdownException>(() => 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<FicdownException>(() => bh.ParseBlocks(bh.ExtractBlocks(@"
# [my story](/a-scene?conditional)
story with a conditional
## a scene
this is a scene".ToLines())));
Assert.Throws<FicdownException>(() => bh.ParseBlocks(bh.ExtractBlocks(@"
# [my story](/a-scene#toggle)
story with a toggle
## a scene
this is a scene".ToLines())));
Assert.Throws<FicdownException>(() => 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<FicdownException>(() => 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<FicdownException>(() => 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);
}
} }
} }

View File

@ -0,0 +1,13 @@
namespace Ficdown.Parser.Tests.Extensions
{
using System;
using System.Collections.Generic;
public static class TestExtensions
{
public static IEnumerable<string> ToLines(this string content)
{
return content.Split(new[] {Environment.NewLine}, StringSplitOptions.None);
}
}
}

View File

@ -1,88 +1,88 @@
<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <PropertyGroup>
<PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <ProjectGuid>{756192E2-BA47-4850-8096-289D44878A7E}</ProjectGuid>
<ProjectGuid>{756192E2-BA47-4850-8096-289D44878A7E}</ProjectGuid> <OutputType>Library</OutputType>
<OutputType>Library</OutputType> <AppDesignerFolder>Properties</AppDesignerFolder>
<AppDesignerFolder>Properties</AppDesignerFolder> <RootNamespace>Ficdown.Parser.Tests</RootNamespace>
<RootNamespace>Ficdown.Parser.Tests</RootNamespace> <AssemblyName>Ficdown.Parser.Tests</AssemblyName>
<AssemblyName>Ficdown.Parser.Tests</AssemblyName> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <FileAlignment>512</FileAlignment>
<FileAlignment>512</FileAlignment> </PropertyGroup>
</PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols>
<DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType>
<DebugType>full</DebugType> <Optimize>false</Optimize>
<Optimize>false</Optimize> <OutputPath>bin\Debug\</OutputPath>
<OutputPath>bin\Debug\</OutputPath> <DefineConstants>DEBUG;TRACE</DefineConstants>
<DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport>
<ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel>
<WarningLevel>4</WarningLevel> </PropertyGroup>
</PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>pdbonly</DebugType>
<DebugType>pdbonly</DebugType> <Optimize>true</Optimize>
<Optimize>true</Optimize> <OutputPath>bin\Release\</OutputPath>
<OutputPath>bin\Release\</OutputPath> <DefineConstants>TRACE</DefineConstants>
<DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport>
<ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel>
<WarningLevel>4</WarningLevel> </PropertyGroup>
</PropertyGroup> <ItemGroup>
<ItemGroup> <Reference Include="Moq">
<Reference Include="Moq"> <HintPath>..\packages\Moq.4.2.1402.2112\lib\net40\Moq.dll</HintPath>
<HintPath>..\packages\Moq.4.2.1402.2112\lib\net40\Moq.dll</HintPath> </Reference>
</Reference> <Reference Include="ServiceStack.Text">
<Reference Include="ServiceStack.Text"> <HintPath>..\packages\ServiceStack.Text.4.0.22\lib\net40\ServiceStack.Text.dll</HintPath>
<HintPath>..\packages\ServiceStack.Text.4.0.22\lib\net40\ServiceStack.Text.dll</HintPath> </Reference>
</Reference> <Reference Include="System" />
<Reference Include="System" /> <Reference Include="System.Core" />
<Reference Include="System.Core" /> <Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data.DataSetExtensions" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="System.Data" />
<Reference Include="System.Data" /> <Reference Include="System.Xml" />
<Reference Include="System.Xml" /> <Reference Include="xunit">
<Reference Include="xunit"> <HintPath>..\packages\xunit.1.9.2\lib\net20\xunit.dll</HintPath>
<HintPath>..\packages\xunit.1.9.2\lib\net20\xunit.dll</HintPath> </Reference>
</Reference> </ItemGroup>
</ItemGroup> <ItemGroup>
<ItemGroup> <Compile Include="BlockHandlerTests.cs" />
<Compile Include="BlockHandlerTests.cs" /> <Compile Include="IntegrationTests.cs" />
<Compile Include="IntegrationTests.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="StateResolverTests.cs" />
<Compile Include="StateResolverTests.cs" /> <Compile Include="TestStories\Resources.Designer.cs">
<Compile Include="TestStories\Resources.Designer.cs"> <AutoGen>True</AutoGen>
<AutoGen>True</AutoGen> <DesignTime>True</DesignTime>
<DesignTime>True</DesignTime> <DependentUpon>Resources.resx</DependentUpon>
<DependentUpon>Resources.resx</DependentUpon> </Compile>
</Compile> <Compile Include="UtilityTests.cs" />
<Compile Include="UtilityTests.cs" /> <Compile Include="Extensions\TestExtensions.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />
<None Include="TestStories\CloakOfDarkness.md" /> <None Include="TestStories\CloakOfDarkness.md" />
<None Include="TestStories\TheRobotKing.md" /> <None Include="TestStories\TheRobotKing.md" />
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Ficdown.Parser\Ficdown.Parser.csproj"> <ProjectReference Include="..\Ficdown.Parser\Ficdown.Parser.csproj">
<Project>{780f652d-7541-4171-bb89-2d263d3961dc}</Project> <Project>{780f652d-7541-4171-bb89-2d263d3961dc}</Project>
<Name>Ficdown.Parser</Name> <Name>Ficdown.Parser</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="TestStories\Resources.resx"> <EmbeddedResource Include="TestStories\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator> <Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput> <LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
</Target> </Target>
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
</Project> </Project>

View File

@ -4,31 +4,26 @@ namespace Ficdown.Parser.Model.Parser
public class FicdownException : Exception public class FicdownException : Exception
{ {
private string _blockName; public string BlockName { get; private set; }
private int? _lineNumber; public int? LineNumber { get; private set; }
private string _message;
public FicdownException(string blockName, int? lineNumber, string message) : base(message) public FicdownException(string blockName, int? lineNumber, string message) : base(message)
{ {
_blockName = blockName; BlockName = blockName;
_lineNumber = lineNumber; LineNumber = lineNumber;
_message = message;
} }
public FicdownException(string message) : base(message) public FicdownException(string message) : base(message) { }
{
_message = message;
}
public override string ToString() public override string ToString()
{ {
return !string.IsNullOrEmpty(_blockName) return !string.IsNullOrEmpty(BlockName)
? string.Format("Error in block \"{0}\" (Line {1}): {2}", ? string.Format("Error in block \"{0}\" (Line {1}): {2}",
_blockName, BlockName,
_lineNumber.HasValue LineNumber.HasValue
? _lineNumber.ToString() ? LineNumber.ToString()
: "unknown", _message) : "unknown", Message)
: string.Format("Error: {0}", _message); : string.Format("Error: {0}", Message);
} }
} }
} }

View File

@ -48,7 +48,15 @@
var storyBlock = blocks.SingleOrDefault(b => b.Type == BlockType.Story); var storyBlock = blocks.SingleOrDefault(b => b.Type == BlockType.Story);
if(storyBlock == null) throw new FicdownException("No story block found"); 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 || if (storyAnchor.Href.Target == null || storyAnchor.Href.Conditions != null ||
storyAnchor.Href.Toggles != null) storyAnchor.Href.Toggles != null)
@ -70,8 +78,18 @@
story.Scenes[scene.Key].Add(scene); story.Scenes[scene.Key].Add(scene);
} }
var aid = 1; var aid = 1;
story.Actions = try
blocks.Where(b => b.Type == BlockType.Action).Select(b => BlockToAction(b, aid++)).ToDictionary(a => a.Toggle, a => a); {
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)) 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)); throw new FicdownException(storyBlock.Name, storyBlock.LineNumber, string.Format("Story targets non-existent scene: {0}", storyAnchor.Href.Target));

4
scripts/build.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
DIR=`dirname $0`
xbuild $DIR/../Ficdown.sln

5
scripts/rebuild.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
DIR=`dirname $0`
rm -rf $DIR/../*/bin $DIR/../*/obj
xbuild $DIR/../Ficdown.sln

4
scripts/run.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
DIR=`dirname $0`
mono $DIR/../Ficdown.Console/bin/Debug/Ficdown.Console.exe "$@"

7
scripts/test.sh Executable file
View File

@ -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