Skip to content

Commit

Permalink
feat(validation): Validate that sector borders are contiguous (#169)
Browse files Browse the repository at this point in the history
  • Loading branch information
AndyTWF authored Jan 8, 2022
1 parent badda79 commit eff1607
Show file tree
Hide file tree
Showing 9 changed files with 936 additions and 28 deletions.
10 changes: 10 additions & 0 deletions src/Compiler/Model/Sectorline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,15 @@ public override string GetCompileData(SectorElementCollection elements)
{
return $"SECTORLINE:{this.Name}";
}

public Coordinate Start()
{
return Coordinates.First().Coordinate;
}

public Coordinate End()
{
return Coordinates.Last().Coordinate;
}
}
}
80 changes: 80 additions & 0 deletions src/Compiler/Validate/AllSectorsBordersMustBeContiguous.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System.Collections.Generic;
using Compiler.Event;
using Compiler.Model;
using Compiler.Error;
using Compiler.Argument;
using System.Linq;

namespace Compiler.Validate
{
public class AllSectorBordersMustBeContiguous : IValidationRule
{
public void Validate(SectorElementCollection sectorElements, CompilerArguments args, IEventLogger events)
{
Dictionary<string, (Coordinate, Coordinate)> sectorlines = sectorElements.SectorLines.ToDictionary(
sectorline => sectorline.Name,
sectorline => (sectorline.Start(), sectorline.End())
);

foreach (Sector sector in sectorElements.Sectors)
{
foreach (SectorBorder border in sector.Borders)
{
// Get the start and end coordinates of all the SECTORLINE elements used in the border.
List<KeyValuePair<string, (Coordinate, Coordinate)>> borderLines = border.BorderLines
.Where(borderLine => sectorlines.ContainsKey(borderLine))
.Select(
borderLine =>
new KeyValuePair<string, (Coordinate, Coordinate)>(borderLine, sectorlines[borderLine])
).ToList();

if (borderLines.Count == 0)
{
continue;
}

/*
* First up, lets work out whether it's the "first" or the "last" coordinate of the first BORDER
* segment that's joining up to the last BORDER segment, so we know how to proceed.
*
* If the END coordinate of the first border line matches up with any of the coordinates
* of the last border line, then it's going to be the START coordinate of that line that
* matches up with something on the second border line as we work our way around.
*/
bool startOfLine = borderLines.First().Value.Item2.Equals(borderLines.Last().Value.Item1) ||
borderLines.First().Value.Item2.Equals(borderLines.Last().Value.Item2);

// Loop around the lines
for (int i = 0; i < borderLines.Count; i++)
{
var firstCoordinate = startOfLine
? borderLines[i].Value.Item1
: borderLines[i].Value.Item2;

if (firstCoordinate.Equals(borderLines[(i + 1) % borderLines.Count].Value.Item1))
{
startOfLine = false;
}
else if (firstCoordinate.Equals(borderLines[(i + 1) % borderLines.Count].Value.Item2))
{
startOfLine = true;
}
else
{
events.AddEvent(new ValidationRuleFailure(
ErrorMessage(sector, borderLines[i].Key, borderLines[(i + 1) % borderLines.Count].Key),
border));
break;
}
}
}
}
}

private string ErrorMessage(Sector sector, string firstBorderline, string secondBorderline)
{
return
$"Non-contiguous BORDER for SECTOR {sector.Name}, line {firstBorderline} does not join with {secondBorderline}";
}
}
}
42 changes: 42 additions & 0 deletions src/Compiler/Validate/AllSectorsBordersMustBeSingleIfCircle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Collections.Generic;
using Compiler.Event;
using Compiler.Model;
using Compiler.Error;
using Compiler.Argument;
using System.Linq;

namespace Compiler.Validate
{
public class AllSectorsBordersMustBeSingleIfCircle : IValidationRule
{
public void Validate(SectorElementCollection sectorElements, CompilerArguments args, IEventLogger events)
{
Dictionary<string, CircleSectorline> sectorlines = sectorElements.CircleSectorLines.ToDictionary(
sectorline => sectorline.Name,
sectorline => sectorline
);

foreach (Sector sector in sectorElements.Sectors)
{
foreach (SectorBorder border in sector.Borders)
{
var circleBorders = border.BorderLines.Where(
borderLine => sectorlines.ContainsKey(borderLine)
).ToList();

if (circleBorders.Count > 0 && border.BorderLines.Count > 1)
{
events.AddEvent(new ValidationRuleFailure(ErrorMessage(sector), border));
break;
}
}
}
}

private string ErrorMessage(Sector sector)
{
return
$"SECTOR {sector.Name} has a BORDER with a CIRCLELINE, but contains more than one element";
}
}
}
2 changes: 2 additions & 0 deletions src/Compiler/Validate/OutputValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public class OutputValidator
new AllSectorsMustHaveValidOwner(),
new AllSectorsMustHaveValidAltOwner(),
new AllSectorsMustHaveValidBorder(),
new AllSectorsBordersMustBeSingleIfCircle(),
new AllSectorBordersMustBeContiguous(),
new AllSectorsMustHaveValidActiveAirport(),
new AllSectorsMustHaveValidActiveRunway(),
new AllSectorsMustHaveValidGuestAirports(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,22 @@ namespace CompilerTest.Bogus.Factory
{
static class SectorlineCoordinateFactory
{
public static SectorlineCoordinate Make()
public static SectorlineCoordinate Make(Coordinate? coordinate = null)
{
return GetGenerator().Generate();
return GetGenerator(coordinate).Generate();
}

public static Faker<SectorlineCoordinate> GetGenerator()
public static Faker<SectorlineCoordinate> GetGenerator(Coordinate? coordinate = null)
{
return new Faker<SectorlineCoordinate>()
.CustomInstantiator(
_ => new SectorlineCoordinate(
CoordinateFactory.Make(),
coordinate ?? CoordinateFactory.Make(),
DefinitionFactory.Make(),
DocblockFactory.Make(),
CommentFactory.Make()
)
);

}

public static List<SectorlineCoordinate> MakeList(int count = 1)
Expand Down
15 changes: 9 additions & 6 deletions tests/CompilerTest/Bogus/Factory/SectorlineFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,31 @@ namespace CompilerTest.Bogus.Factory
{
static class SectorlineFactory
{
public static Sectorline Make(string name = null, List<SectorlineDisplayRule> displayRules = null, Definition definition = null)
public static Sectorline Make(string name = null, List<SectorlineDisplayRule> displayRules = null,
Definition definition = null, List<SectorlineCoordinate> coordinates = null)
{
return GetGenerator(name, displayRules, definition).Generate();
return GetGenerator(name, displayRules, definition, coordinates).Generate();
}

private static Faker<Sectorline> GetGenerator(string name = null, List<SectorlineDisplayRule> displayRules = null, Definition definition = null)
private static Faker<Sectorline> GetGenerator(string name = null,
List<SectorlineDisplayRule> displayRules = null, Definition definition = null,
List<SectorlineCoordinate> coordinates = null)
{
return new Faker<Sectorline>()
.CustomInstantiator(
f => new Sectorline(
name ?? f.Random.String2(5),
displayRules ?? SectorLineDisplayRuleFactory.MakeList(2),
SectorlineCoordinateFactory.MakeList(4),
coordinates ?? SectorlineCoordinateFactory.MakeList(4),
definition ?? DefinitionFactory.Make(),
DocblockFactory.Make(),
CommentFactory.Make()
)
);

}

public static List<Sectorline> MakeList(int count = 1, string name = null, List<SectorlineDisplayRule> displayRules = null)
public static List<Sectorline> MakeList(int count = 1, string name = null,
List<SectorlineDisplayRule> displayRules = null)
{
return GetGenerator(name, displayRules).Generate(count).ToList();
}
Expand Down
46 changes: 29 additions & 17 deletions tests/CompilerTest/Model/SectorlineTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ public class SectorlineTest

public SectorlineTest()
{
this.displayRules = SectorLineDisplayRuleFactory.MakeList();
this.coordinates = SectorlineCoordinateFactory.MakeList();
this.model = new Sectorline(
displayRules = SectorLineDisplayRuleFactory.MakeList();
coordinates = SectorlineCoordinateFactory.MakeList(3);
model = new Sectorline(
"Test Sectorline",
this.displayRules,
this.coordinates,
displayRules,
coordinates,
DefinitionFactory.Make(),
DocblockFactory.Make(),
CommentFactory.Make()
Expand All @@ -29,41 +29,53 @@ public SectorlineTest()
[Fact]
public void TestItSetsName()
{
Assert.Equal("Test Sectorline", this.model.Name);
Assert.Equal("Test Sectorline", model.Name);
}

[Fact]
public void TestItSetsDisplayRules()
{
Assert.Equal(this.displayRules, this.model.DisplayRules);
Assert.Equal(displayRules, model.DisplayRules);
}

[Fact]
public void TestItSetsCoordinates()
{
Assert.Equal(this.coordinates, this.model.Coordinates);
Assert.Equal(coordinates, model.Coordinates);
}

[Fact]
public void TestItReturnsStartOfLine()
{
Assert.Equal(coordinates.First().Coordinate, model.Start());
}

[Fact]
public void TestItReturnsEndOfLine()
{
Assert.Equal(coordinates.Last().Coordinate, model.End());
}

[Fact]
public void TestItReturnsCompilableElements()
{
IEnumerable<ICompilableElement> expected = new List<ICompilableElement>
{
this.model
}
.Concat(this.displayRules)
.Concat(this.coordinates);
Assert.Equal(expected, this.model.GetCompilableElements());
{
model
}
.Concat(displayRules)
.Concat(coordinates);

Assert.Equal(expected, model.GetCompilableElements());
}

[Fact]
public void TestItCompiles()
{
Assert.Equal(
"SECTORLINE:Test Sectorline",
this.model.GetCompileData(new SectorElementCollection())
model.GetCompileData(new SectorElementCollection())
);
}
}
}
}
Loading

0 comments on commit eff1607

Please sign in to comment.