using System; using System.Collections.Generic; using UnitTestSharp; using static Clave.UnitTests.ChordArbiterTests; namespace Clave.UnitTests { public class ChordSetBuilderTests : TestFixture { public static readonly Chord Delete = Chord.Press(KeyCode.Delete); public static readonly Chord ShiftDelete = Delete & Chord.HoldAny(KeyState.Shift); public static KeyboardSnapshot PressingShiftDelete() { return ChordArbiterTests.Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)); } public class CtorTests : TestFixture { public void FromASet_HoldsItsChordsPrioritiesAndEnabledStates() { var original = new ChordSetBuilder(); original.Add(Delete, priority: 3); original.Add(ShiftDelete); original.SetEnabled(ShiftDelete, false); var builder = new ChordSetBuilder(original.Build()); CheckEqual(2, builder.Count); CheckEqual(3, builder.PriorityOf(Delete)); Check(builder.IsEnabled(Delete)); CheckFalse(builder.IsEnabled(ShiftDelete)); } public void FromASet_LeavesTheSetAlone() { ChordSet set = ChordSet.Of(Delete); new ChordSetBuilder(set).Add(ShiftDelete); CheckEqual(1, set.Count); } public void FromANullSet_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSetBuilder(null); } } public class BuildTests : TestFixture { public void HoldsTheChordsPrioritiesAndEnabledStates() { var builder = new ChordSetBuilder(); Chord enabled = builder.Add(Delete, priority: 3); Chord disabled = builder.Add(ShiftDelete); builder.SetEnabled(disabled, false); ChordSet built = builder.Build(); CheckEqual(2, built.Count); CheckEqual(3, built.PriorityOf(enabled)); CheckTrue(built.IsEnabled(enabled)); CheckFalse(built.IsEnabled(disabled)); } public void LaterChangesToTheBuilder_DoNotReachTheSet() { var builder = new ChordSetBuilder(); builder.Add(Delete); ChordSet built = builder.Build(); builder.Add(ShiftDelete); CheckEqual(1, built.Count); } } public class AddTests : TestFixture { public void ReturnsTheChord() { CheckEqual(Delete, new ChordSetBuilder().Add(Delete)); } public void TheSameChordTwice_Throws() { var builder = new ChordSetBuilder(); builder.Add(Delete); CheckThrow(typeof(ArgumentException)); builder.Add(Delete); } public void ANullChord_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSetBuilder().Add(null); } public void Null_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSetBuilder().Add(null); } public void ChordsOnDifferentKeys_BothShouldFire() { var set = new ChordSetBuilder(); Chord a = set.Add(Chord.Hold(KeyCode.A)); Chord b = set.Add(Chord.Hold(KeyCode.B)); KeyboardSnapshot both = Holding(KeyCode.A, KeyCode.B); Check(set.ShouldFire(a, both)); Check(set.ShouldFire(b, both)); } public void ChordsSharingOnlyTheirSecondKey_StillCompete() { var set = new ChordSetBuilder(); Chord b = set.Add(Chord.Hold(KeyCode.B)); Chord ab = set.Add(Chord.Hold(KeyCode.A) & Chord.Hold(KeyCode.B)); KeyboardSnapshot both = Holding(KeyCode.A, KeyCode.B); CheckFalse(set.ShouldFire(b, both)); Check(set.ShouldFire(ab, both)); } public void AChordMatchingEverythingOnOneKey_IsHiddenByAChordOnAnother() { var set = new ChordSetBuilder(); set.Add(MatchesEverythingOnA); Chord b = set.Add(Chord.Hold(KeyCode.B)); KeyboardSnapshot heldB = Holding(KeyCode.B); CheckFalse(set.ShouldFire(MatchesEverythingOnA, heldB)); Check(set.ShouldFire(b, heldB)); } public void AChordMatchingEverythingOnOneKeyAddedLast_IsHiddenByAChordOnAnother() { var set = new ChordSetBuilder(); set.Add(Chord.Hold(KeyCode.B)); set.Add(MatchesEverythingOnA); CheckFalse(set.ShouldFire(MatchesEverythingOnA, Holding(KeyCode.B))); } public void AChordMatchingEverythingOnNoKey_IsHiddenByAChordOnAKey() { var set = new ChordSetBuilder(); set.Add(MatchesEverythingOnNoKey); set.Add(Chord.Hold(KeyCode.B)); CheckFalse(set.ShouldFire(MatchesEverythingOnNoKey, Holding(KeyCode.B))); } public void AChordMatchingEverythingOnNoKeyAddedLast_IsHiddenByAChordOnAKey() { var set = new ChordSetBuilder(); set.Add(Chord.Hold(KeyCode.B)); set.Add(MatchesEverythingOnNoKey); CheckFalse(set.ShouldFire(MatchesEverythingOnNoKey, Holding(KeyCode.B))); } public void EquivalentChordsMatchingEverythingOnDifferentKeys_OnlyTheHigherPriorityShouldFire() { var set = new ChordSetBuilder(); set.Add(MatchesEverythingOnA, priority: 1); Chord high = set.Add(MatchesEverythingOnNoKey, priority: 2); CheckFalse(set.ShouldFire(MatchesEverythingOnA, Holding(KeyCode.B))); Check(set.ShouldFire(high, Holding(KeyCode.B))); } public void AChordMatchingAnyOfSeveralKeys_IsHiddenByAChordOnOneOfThem() { var set = new ChordSetBuilder(); Chord either = set.Add(Chord.PressAny(new KeyState(KeyCode.A, KeyCode.B))); Chord a = set.Add(Chord.Press(KeyCode.A)); CheckFalse(set.ShouldFire(either, Pressing(KeyCode.A))); Check(set.ShouldFire(a, Pressing(KeyCode.A))); Check(set.ShouldFire(either, Pressing(KeyCode.B))); } public void AChordMatchingAnyOfSeveralKeysAddedLast_IsHiddenByAChordOnOneOfThem() { var set = new ChordSetBuilder(); Chord a = set.Add(Chord.Press(KeyCode.A)); Chord either = set.Add(Chord.PressAny(new KeyState(KeyCode.A, KeyCode.B))); CheckFalse(set.ShouldFire(either, Pressing(KeyCode.A))); Check(set.ShouldFire(a, Pressing(KeyCode.A))); } public void ChordsPressedOrRepeated_StillCompeteOnTheirKey() { var set = new ChordSetBuilder(); Chord z = set.Add(Chord.PressOrRepeat(KeyCode.Z)); Chord controlZ = set.Add(Chord.PressOrRepeat(KeyCode.Z).WithAnyHeld(KeyState.Control)); KeyboardSnapshot controlledZ = Pressing(KeyCode.Z, alsoHeld: new KeyState(KeyCode.ControlLeft)); CheckFalse(set.ShouldFire(z, controlledZ)); Check(set.ShouldFire(controlZ, controlledZ)); Check(set.ShouldFire(z, Pressing(KeyCode.Z))); } public void AKeyboardsWorthOfShortcuts_FitsInTheTestBudget() { var set = new ChordSetBuilder(); for (KeyCode key = KeyCode.A; key <= KeyCode.Z; ++key) { Chord press = set.Add(Chord.Press(key)); Chord control = set.Add(press & Chord.HoldAny(KeyState.Control)); set.Add(control & Chord.HoldAny(KeyState.Shift)); } } } public class AddAllTests : TestFixture { public void FromASet_AddsEveryChordAtThePriority() { var builder = new ChordSetBuilder(); builder.AddAll(ChordSet.Of(Delete, ShiftDelete), priority: 2); CheckEqual(2, builder.PriorityOf(Delete)); CheckEqual(2, builder.PriorityOf(ShiftDelete)); } public void FromABuilder_AddsEveryChordAtThePriority() { var other = new ChordSetBuilder(); other.Add(Delete); other.Add(ShiftDelete); var builder = new ChordSetBuilder(); builder.AddAll(other, priority: 2); CheckEqual(2, builder.Count); CheckEqual(2, builder.PriorityOf(Delete)); CheckEqual(0, other.PriorityOf(Delete)); } public void FromABuilder_RanksTheChordsAgainstEachOther() { var other = new ChordSetBuilder(); other.Add(Delete); other.Add(ShiftDelete); var builder = new ChordSetBuilder(); builder.AddAll(other); CheckFalse(builder.ShouldFire(Delete, PressingShiftDelete())); Check(builder.ShouldFire(ShiftDelete, PressingShiftDelete())); } public void ANullSet_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSetBuilder().AddAll((ChordSet)null); } public void ANullBuilder_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSetBuilder().AddAll((ChordSetBuilder)null); } public void FromAFixedSet_AddsEveryChordAndCompetesLikeAnyOther() { var set = new ChordSetBuilder(); set.AddAll(ChordSet.Of(Delete, ShiftDelete)); CheckFalse(set.ShouldFire(Delete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); Check(set.ShouldFire(ShiftDelete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void FromAFixedSet_AtAPriority_GivesEveryChordThatPriority() { var set = new ChordSetBuilder(); set.AddAll(ChordSet.Of(Delete), priority: 4); CheckEqual(4, set.PriorityOf(Delete)); } public void FromANullFixedSet_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSetBuilder().AddAll((ChordSet)null); } public void AddsEveryChordOfTheOtherSet() { var other = new ChordSetBuilder(); other.Add(Delete); other.Add(ShiftDelete); var set = new ChordSetBuilder(); set.AddAll(other); Check(set.Contains(Delete)); Check(set.Contains(ShiftDelete)); } public void AddedChords_CompeteLikeAnyOther() { var other = new ChordSetBuilder(); other.Add(Delete); other.Add(ShiftDelete); var set = new ChordSetBuilder(); set.AddAll(other); CheckFalse(set.ShouldFire(Delete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void MergedChords_CompeteAcrossSpacesWithTheOnesAlreadyThere() { var other = new ChordSetBuilder(); other.Add(LayoutUndo); other.Add(Chord.Press(KeyCode.Q, KeySpace.Layout)); var set = new ChordSetBuilder(); Chord game = set.Add(Chord.Press(KeyCode.W)); set.AddAll(other, priority: 1); CheckFalse(set.ShouldFire(game, KeyEvent(KeyCode.W, KeyCode.Z, KeyCode.ControlLeft))); } public void AddedChords_CompeteWithTheOnesAlreadyThere() { var other = new ChordSetBuilder(); other.Add(ShiftDelete); var set = new ChordSetBuilder(); set.Add(Delete); set.AddAll(other); CheckFalse(set.ShouldFire(Delete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void AChordAddedAfterAMerge_CompetesWithTheMergedChords() { var other = new ChordSetBuilder(); other.Add(ShiftDelete); other.Add(Chord.Press(KeyCode.A)); var set = new ChordSetBuilder(); set.AddAll(other); set.Add(Delete); CheckFalse(set.ShouldFire(Delete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void TakesThePriorityGiven() { var other = new ChordSetBuilder(); Chord incoming = other.Add(Chord.Press(KeyCode.Delete)); var set = new ChordSetBuilder(); Chord existing = set.Add(Chord.Press(KeyCode.Delete), priority: 1); set.AddAll(other, priority: 2); CheckFalse(set.ShouldFire(existing, Pressing(KeyCode.Delete))); Check(set.ShouldFire(incoming, Pressing(KeyCode.Delete))); } public void LeavesTheOtherSetAsItWas() { var other = new ChordSetBuilder(); other.Add(Delete); var set = new ChordSetBuilder(); set.AddAll(other); set.Remove(Delete); Check(other.Contains(Delete)); Check(other.ShouldFire(Delete, Pressing(KeyCode.Delete))); } public void AChordAlreadyHere_ThrowsAndAddsNone() { var other = new ChordSetBuilder(); other.Add(ShiftDelete); other.Add(Delete); var set = new ChordSetBuilder(); set.Add(Delete); bool threw = false; try { set.AddAll(other); } catch (ArgumentException) { threw = true; } Check(threw); CheckFalse(set.Contains(ShiftDelete)); } public void AMergedSetKeepsTheRankingsAmongItsOwnChords() { var other = new ChordSetBuilder(); other.Add(ShiftDelete); other.Add(Delete); other.Add(Chord.Press(KeyCode.A)); var set = new ChordSetBuilder(); set.Add(Chord.Press(KeyCode.B)); set.AddAll(other); CheckFalse(set.ShouldFire(Delete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); Check(set.ShouldFire(ShiftDelete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void AMergedSetCompetesWithEachChordAlreadyThere() { var other = new ChordSetBuilder(); other.Add(ShiftDelete); other.Add(Chord.Press(KeyCode.A)); var set = new ChordSetBuilder(); set.Add(Chord.Press(KeyCode.B)); set.Add(Delete); var withShift = Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)); set.AddAll(other); CheckFalse(set.ShouldFire(Delete, withShift)); } public void AMergedChordThatIsStrongerThanOneAlreadyThere_HidesIt() { var other = new ChordSetBuilder(); other.Add(Chord.Press(KeyCode.A)); Chord incoming = other.Add(Chord.Press(KeyCode.Delete)); var set = new ChordSetBuilder(); Chord existing = set.Add(Chord.Press(KeyCode.Delete), priority: 1); set.AddAll(other, priority: 2); CheckFalse(set.ShouldFire(existing, Pressing(KeyCode.Delete))); Check(set.ShouldFire(incoming, Pressing(KeyCode.Delete))); } public void ASetWithMixedPriorities_TakesTheOnePriorityGiven() { var other = new ChordSetBuilder(); Chord low = other.Add(Chord.Press(KeyCode.Delete), priority: 1); Chord high = other.Add(Chord.Press(KeyCode.Delete), priority: 2); other.Add(Chord.Press(KeyCode.A)); var set = new ChordSetBuilder(); set.AddAll(other, priority: 3); Check(set.ShouldFire(low, Pressing(KeyCode.Delete))); Check(set.ShouldFire(high, Pressing(KeyCode.Delete))); } public void ADisabledChordOfTheOtherSet_IsDisabledHere() { var other = new ChordSetBuilder(); Chord disabled = other.Add(Chord.Press(KeyCode.A)); other.Add(Chord.Press(KeyCode.B)); other.SetEnabled(disabled, false); var set = new ChordSetBuilder(); set.AddAll(other); CheckFalse(set.IsEnabled(disabled)); } public void AnEmptySet_AddsNothing() { var set = new ChordSetBuilder(); set.AddAll(new ChordSetBuilder()); CheckFalse(set.Contains(Delete)); } public void Null_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSetBuilder().AddAll((ChordSetBuilder)null); } } public class RemoveTests : TestFixture { public void TakesTheChordOut() { var builder = new ChordSetBuilder(); builder.Add(Delete); builder.Remove(Delete); CheckFalse(builder.Contains(Delete)); } public void RemovedChord_IsNoLongerRegistered() { var set = new ChordSetBuilder(); set.Add(Delete); set.Remove(Delete); CheckThrow(typeof(ArgumentException)); set.IsEnabled(Delete); } public void RemovedMoreSpecificChord_NoLongerHidesTheLessSpecific() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); set.Remove(ShiftDelete); Check(set.ShouldFire(Delete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void RemovedHigherPriorityChord_NoLongerHidesTheEquivalentOne() { var set = new ChordSetBuilder(); Chord low = set.Add(Chord.Press(KeyCode.Delete), priority: 1); Chord high = set.Add(Chord.Press(KeyCode.Delete), priority: 2); set.Remove(high); Check(set.ShouldFire(low, Pressing(KeyCode.Delete))); } public void RemovedLessSpecificChord_LeavesTheMoreSpecificOneFiring() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); set.Remove(Delete); Check(set.ShouldFire(ShiftDelete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void RemovedChordSharingOnlyTheSecondKey_NoLongerHidesTheOther() { var set = new ChordSetBuilder(); Chord b = set.Add(Chord.Hold(KeyCode.B)); Chord ab = set.Add(Chord.Hold(KeyCode.A) & Chord.Hold(KeyCode.B)); set.Remove(ab); Check(set.ShouldFire(b, Holding(KeyCode.A, KeyCode.B))); } public void RemovedChordMatchingEverythingOnNoKey_NoLongerHidesTheEquivalentOne() { var set = new ChordSetBuilder(); set.Add(MatchesEverythingOnA, priority: 1); Chord high = set.Add(MatchesEverythingOnNoKey, priority: 2); set.Remove(high); Check(set.ShouldFire(MatchesEverythingOnA, Holding(KeyCode.B))); } public void RemovedChordMatchingEverythingOnOneKey_NoLongerHidesTheEquivalentOne() { var set = new ChordSetBuilder(); set.Add(MatchesEverythingOnNoKey, priority: 1); Chord high = set.Add(MatchesEverythingOnA, priority: 2); set.Remove(high); Check(set.ShouldFire(MatchesEverythingOnNoKey, Holding(KeyCode.B))); } public void RemovedAndAddedAgain_CompetesAgain() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); set.Remove(ShiftDelete); set.Add(ShiftDelete); CheckFalse(set.ShouldFire(Delete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void AChordAddedAfterward_IgnoresTheRemovedOne() { var set = new ChordSetBuilder(); set.Add(ShiftDelete); set.Remove(ShiftDelete); set.Add(Delete); Check(set.ShouldFire(Delete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void AChordItsFoldCannotSatisfy_CanBeRemovedAndAddedAgain() { var set = new ChordSetBuilder(); Chord unfoldable = set.Add(Chord.Hold(KeyCode.A) & Chord.HoldNone(new KeyState(KeyCode.A), KeySpace.Layout)); Chord a = set.Add(Chord.Hold(KeyCode.A)); set.Remove(unfoldable); set.Add(unfoldable); CheckEqual(2, set.Count); Check(set.ShouldFire(a, Holding(KeyCode.A))); Check(set.ShouldFire(unfoldable, Holding(KeyCode.A))); } public void RemovingOneOfSeveralHiders_KeepsTheRestHiding() { var set = new ChordSetBuilder(); Chord z = set.Add(Chord.Press(KeyCode.Z)); Chord controlZ = set.Add(z & Chord.HoldAny(KeyState.Control)); Chord shiftZ = set.Add(z & Chord.HoldAny(KeyState.Shift)); set.Remove(controlZ); KeyboardSnapshot shiftedZ = Pressing(KeyCode.Z, alsoHeld: new KeyState(KeyCode.ShiftLeft)); CheckFalse(set.ShouldFire(z, shiftedZ)); Check(set.ShouldFire(shiftZ, shiftedZ)); } public void UnregisteredChord_Throws() { CheckThrow(typeof(ArgumentException)); new ChordSetBuilder().Remove(Delete); } public void Null_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSetBuilder().Remove(null); } } public class RemoveAllTests : TestFixture { public void FromASet_TakesEveryChordOut() { var builder = new ChordSetBuilder(); builder.AddAll(ChordSet.Of(Delete, ShiftDelete)); builder.RemoveAll(ChordSet.Of(Delete, ShiftDelete)); CheckEqual(0, builder.Count); } public void FromABuilder_TakesEveryChordOut() { var other = new ChordSetBuilder(); other.Add(Delete); var builder = new ChordSetBuilder(); builder.Add(Delete); builder.Add(ShiftDelete); builder.RemoveAll(other); CheckFalse(builder.Contains(Delete)); Check(builder.Contains(ShiftDelete)); } public void ANullSet_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSetBuilder().RemoveAll((ChordSet)null); } public void ANullBuilder_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSetBuilder().RemoveAll((ChordSetBuilder)null); } public void FixedSet_RemovesEveryChordOfIt() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); set.RemoveAll(ChordSet.Of(Delete, ShiftDelete)); CheckEqual(0, set.Count); } public void NullFixedSet_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSetBuilder().RemoveAll((ChordSet)null); } public void RemovesEveryChordOfTheOtherSet() { var other = new ChordSetBuilder(); other.Add(Delete); other.Add(ShiftDelete); var set = new ChordSetBuilder(); set.AddAll(other); set.RemoveAll(other); CheckFalse(set.Contains(Delete)); CheckFalse(set.Contains(ShiftDelete)); } public void LeavesOtherChordsAlone() { var other = new ChordSetBuilder(); other.Add(ShiftDelete); var set = new ChordSetBuilder(); set.Add(Delete); set.AddAll(other); set.RemoveAll(other); Check(set.ShouldFire(Delete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void ChordsThatWereHiddenByTheRemovedOnes_FireAgain() { var other = new ChordSetBuilder(); other.Add(ShiftDelete); other.Add(Chord.Press(KeyCode.A)); var set = new ChordSetBuilder(); set.Add(Delete); set.AddAll(other); var withShift = Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)); CheckFalse(set.ShouldFire(Delete, withShift)); set.RemoveAll(other); Check(set.ShouldFire(Delete, withShift)); } public void TheRemovedChordsCanBeAddedAgain() { var other = new ChordSetBuilder(); other.Add(ShiftDelete); other.Add(Chord.Press(KeyCode.A)); var set = new ChordSetBuilder(); set.Add(Delete); set.AddAll(other); set.RemoveAll(other); set.AddAll(other); CheckFalse(set.ShouldFire(Delete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void AChordAddedAfterward_IgnoresTheRemovedOnes() { var other = new ChordSetBuilder(); other.Add(ShiftDelete); other.Add(Chord.Press(KeyCode.A)); var set = new ChordSetBuilder(); set.AddAll(other); set.RemoveAll(other); set.Add(Delete); Check(set.ShouldFire(Delete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void AChordNotHere_ThrowsAndRemovesNone() { var other = new ChordSetBuilder(); other.Add(Delete); other.Add(ShiftDelete); var set = new ChordSetBuilder(); set.Add(Delete); bool threw = false; try { set.RemoveAll(other); } catch (ArgumentException) { threw = true; } Check(threw); Check(set.Contains(Delete)); } public void Null_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSetBuilder().RemoveAll((ChordSetBuilder)null); } } public class CloneTests : TestFixture { public void IsIndependentOfTheOriginal() { var builder = new ChordSetBuilder(); builder.Add(Delete); ChordSetBuilder clone = builder.Clone(); clone.Add(ShiftDelete); CheckEqual(1, builder.Count); CheckEqual(2, clone.Count); } public void KeepsPrioritiesEnabledStatesAndRankings() { var builder = new ChordSetBuilder(); builder.Add(Delete, priority: 4); builder.Add(ShiftDelete); builder.SetEnabled(ShiftDelete, false); ChordSetBuilder clone = builder.Clone(); CheckEqual(4, clone.PriorityOf(Delete)); CheckFalse(clone.IsEnabled(ShiftDelete)); Check(clone.ShouldFire(Delete, PressingShiftDelete())); } public static KeyboardSnapshot PressingShiftDelete() { return Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)); } public void HoldsTheSameChords() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); ChordSetBuilder clone = set.Clone(); CheckEqual(2, clone.Count); Check(clone.Contains(Delete)); Check(clone.Contains(ShiftDelete)); } public void ChordsInDifferentSpaces_StillHideEachOther() { var set = new ChordSetBuilder(); Chord game = set.Add(Chord.Press(KeyCode.W)); set.Add(LayoutUndo, priority: 1); ChordSetBuilder clone = set.Clone(); CheckFalse(clone.ShouldFire(game, KeyEvent(KeyCode.W, KeyCode.Z, KeyCode.ControlLeft))); } public void AChordAddedToTheClone_RanksAgainstTheCopiedChordsInOtherSpaces() { var set = new ChordSetBuilder(); Chord game = set.Add(Chord.Press(KeyCode.W)); Chord layout = set.Add(Chord.Press(KeyCode.Q, KeySpace.Layout)); ChordSetBuilder clone = set.Clone(); clone.Add(LayoutUndo, priority: 1); Chord physical = clone.Add(Chord.Press(KeyCode.Q), priority: 2); CheckFalse(clone.ShouldFire(game, KeyEvent(KeyCode.W, KeyCode.Z, KeyCode.ControlLeft))); CheckFalse(clone.ShouldFire(layout, KeyEvent(KeyCode.Q, KeyCode.Q))); Check(clone.ShouldFire(physical, KeyEvent(KeyCode.Q, KeyCode.Q))); } public void CompetesLikeTheOriginal() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); ChordSetBuilder clone = set.Clone(); CheckFalse(clone.ShouldFire(Delete, PressingShiftDelete())); Check(clone.ShouldFire(ShiftDelete, PressingShiftDelete())); Check(clone.ShouldFire(Delete, Pressing(KeyCode.Delete))); } public void KeepsThePriorities() { var set = new ChordSetBuilder(); Chord low = set.Add(Chord.Press(KeyCode.Delete), priority: 1); Chord high = set.Add(Chord.Press(KeyCode.Delete), priority: 2); ChordSetBuilder clone = set.Clone(); CheckFalse(clone.ShouldFire(low, Pressing(KeyCode.Delete))); Check(clone.ShouldFire(high, Pressing(KeyCode.Delete))); } public void KeepsTheChordsThatAreDisabled() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); set.SetEnabled(ShiftDelete, false); ChordSetBuilder clone = set.Clone(); CheckFalse(clone.IsEnabled(ShiftDelete)); Check(clone.IsEnabled(Delete)); Check(clone.ShouldFire(Delete, PressingShiftDelete())); } public void KeepsTheHeldChordsAChordThatRepeatsHides() { var set = new ChordSetBuilder(); Chord held = set.Add(Chord.HoldAll(new KeyState(KeyCode.A))); set.Add(Chord.PressOrRepeat(KeyCode.A)); KeyboardSnapshot repeating = Keyboard(new KeyState(KeyCode.A), default, default, new KeyState(KeyCode.A)); ChordSetBuilder clone = set.Clone(); CheckFalse(clone.ShouldFire(held, repeating)); } public void ChangesToTheCloneLeaveTheOriginalAlone() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); ChordSetBuilder clone = set.Clone(); clone.Remove(ShiftDelete); clone.SetEnabled(Delete, false); CheckEqual(2, set.Count); Check(set.IsEnabled(Delete)); CheckFalse(set.ShouldFire(Delete, PressingShiftDelete())); } public void ChangesToTheOriginalLeaveTheCloneAlone() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); ChordSetBuilder clone = set.Clone(); set.Remove(ShiftDelete); CheckEqual(2, clone.Count); CheckFalse(clone.ShouldFire(Delete, PressingShiftDelete())); } public void ChordsAddedToTheCloneCompeteWithTheCopiedOnes() { var set = new ChordSetBuilder(); set.Add(Delete); ChordSetBuilder clone = set.Clone(); clone.Add(ShiftDelete); CheckFalse(clone.ShouldFire(Delete, PressingShiftDelete())); } public void AnEmptySet_ClonesToAnEmptySet() { ChordSetBuilder clone = new ChordSetBuilder().Clone(); CheckEqual(0, clone.Count); } } public class AddToTests : TestFixture { public void PutsEveryChordInPlayOnTheMountAtThePriority() { var builder = new ChordSetBuilder(); builder.Add(Delete); builder.Add(ShiftDelete); var arbiter = new ChordArbiter(); ChordMount mount = arbiter.Mount(); builder.AddTo(arbiter, mount, priority: 2); CheckEqual(2, arbiter.CountOn(mount)); CheckEqual(2, arbiter.PriorityOf(Delete, mount)); CheckEqual(2, arbiter.PriorityOf(ShiftDelete, mount)); } public void CarriesTheRankingsAndEnabledStates() { var builder = new ChordSetBuilder(); builder.Add(Delete); builder.Add(ShiftDelete); builder.SetEnabled(ShiftDelete, false); var arbiter = new ChordArbiter(); ChordMount mount = arbiter.Mount(); builder.AddTo(arbiter, mount); CheckFalse(arbiter.IsEnabled(ShiftDelete, mount)); Check(arbiter.ShouldFire(Delete, mount, PressingShiftDelete())); } public void LeavesTheBuilderAlone() { var builder = new ChordSetBuilder(); builder.Add(Delete); var arbiter = new ChordArbiter(); builder.AddTo(arbiter, arbiter.Mount()); arbiter.Mount(ShiftDelete); CheckEqual(1, builder.Count); } public void ANullTarget_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSetBuilder().AddTo(null, new ChordArbiter().Mount()); } } public class GetEnumeratorTests : TestFixture { public void WalksEveryChordOnce() { var builder = new ChordSetBuilder(); builder.Add(Delete); builder.Add(ShiftDelete); var walked = new List(); foreach (Chord chord in builder) { walked.Add(chord); } CheckEqual(2, walked.Count); Check(walked.Contains(Delete)); Check(walked.Contains(ShiftDelete)); } public static List Walk(ChordSetBuilder set) { var returnMe = new List(); foreach (Chord chord in set) { returnMe.Add(chord); } return returnMe; } public void EmptySet_YieldsNothing() { CheckEqual(0, Walk(new ChordSetBuilder()).Count); } public void YieldsEveryChordOnce() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); Chord other = set.Add(Chord.Press(KeyCode.Z)); List walked = Walk(set); CheckEqual(3, walked.Count); Check(walked.Contains(Delete)); Check(walked.Contains(ShiftDelete)); Check(walked.Contains(other)); } public void DoesNotYieldRemovedChords() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); set.Remove(Delete); List walked = Walk(set); CheckEqual(1, walked.Count); CheckEqual(ShiftDelete, walked[0]); } public void YieldsDisabledChordsToo() { var set = new ChordSetBuilder(); set.Add(Delete); set.SetEnabled(Delete, enabled: false); CheckEqual(1, Walk(set).Count); } public void AddingWhileWalking_Throws() { var set = new ChordSetBuilder(); set.Add(Delete); CheckThrow(typeof(InvalidOperationException)); foreach (Chord chord in set) { set.Add(Chord.Press(KeyCode.Z)); } } public void RemovingWhileWalking_Throws() { var set = new ChordSetBuilder(); set.Add(Delete); CheckThrow(typeof(InvalidOperationException)); foreach (Chord chord in set) { set.Remove(chord); } } public void EnablingAndDisablingWhileWalking_IsFine() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); foreach (Chord chord in set) { set.SetEnabled(chord, enabled: false); } CheckFalse(set.IsEnabled(Delete)); CheckFalse(set.IsEnabled(ShiftDelete)); } } public class QueryTests : TestFixture { public void ContainsPriorityOfAndIsEnabled_ReportWhatWasAdded() { var builder = new ChordSetBuilder(); builder.Add(Delete, priority: 5); Check(builder.Contains(Delete)); CheckFalse(builder.Contains(ShiftDelete)); CheckEqual(5, builder.PriorityOf(Delete)); Check(builder.IsEnabled(Delete)); } public void SetEnabled_StopsTheChordFiring() { var builder = new ChordSetBuilder(); builder.Add(Delete); builder.SetEnabled(Delete, false); CheckFalse(builder.IsEnabled(Delete)); CheckFalse(builder.ShouldFire(Delete, ChordArbiterTests.Pressing(KeyCode.Delete))); } public void AChordNotInTheBuilder_Throws() { CheckThrow(typeof(ArgumentException)); new ChordSetBuilder().ShouldFire(Delete, ChordArbiterTests.Pressing(KeyCode.Delete)); } public void ShouldFireAny_RanksTheChordsOfTheBuilder() { var builder = new ChordSetBuilder(); builder.Add(Delete); builder.Add(ShiftDelete); CheckFalse(builder.ShouldFireAny(ChordSet.Of(Delete), PressingShiftDelete())); Check(builder.ShouldFireAny(ChordSet.Of(Delete, ShiftDelete), PressingShiftDelete())); } } public class ShouldFireTests : TestFixture { public void MatchingWithNothingMoreSpecific_ShouldFire() { var set = new ChordSetBuilder(); set.Add(Delete); Check(set.ShouldFire(Delete, Pressing(KeyCode.Delete))); } public void NotMatching_ShouldNotFire() { var set = new ChordSetBuilder(); set.Add(Delete); CheckFalse(set.ShouldFire(Delete, Pressing(KeyCode.Backspace))); } public void AMoreSpecificChordMatching_HidesIt() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); KeyboardSnapshot shiftDelete = Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)); CheckFalse(set.ShouldFire(Delete, shiftDelete)); Check(set.ShouldFire(ShiftDelete, shiftDelete)); } public void AMoreSpecificChordNotMatching_LeavesItShouldFire() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); Check(set.ShouldFire(Delete, Pressing(KeyCode.Delete))); } public void TheMoreSpecificChordAddedFirst_StillHidesIt() { var set = new ChordSetBuilder(); set.Add(ShiftDelete); set.Add(Delete); CheckFalse(set.ShouldFire(Delete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void OnlyTheMostSpecificOfAChain_ShouldFire() { var set = new ChordSetBuilder(); Chord z = set.Add(Chord.Press(KeyCode.Z)); Chord controlZ = set.Add(z & Chord.HoldAny(KeyState.Control)); Chord controlShiftZ = set.Add(controlZ & Chord.HoldAny(KeyState.Shift)); KeyboardSnapshot all = Pressing(KeyCode.Z, alsoHeld: new KeyState(KeyCode.ControlLeft, KeyCode.ShiftLeft)); CheckFalse(set.ShouldFire(z, all)); CheckFalse(set.ShouldFire(controlZ, all)); Check(set.ShouldFire(controlShiftZ, all)); } public void EquivalentChords_BothShouldFire() { var set = new ChordSetBuilder(); Chord first = set.Add(Chord.Press(KeyCode.Delete)); Chord second = set.Add(Chord.Press(KeyCode.Delete)); Check(set.ShouldFire(first, Pressing(KeyCode.Delete))); Check(set.ShouldFire(second, Pressing(KeyCode.Delete))); } public void EquivalentChordsOfDifferentPriority_OnlyTheHigherShouldFire() { var set = new ChordSetBuilder(); Chord low = set.Add(Chord.Press(KeyCode.Delete), priority: 1); Chord high = set.Add(Chord.Press(KeyCode.Delete), priority: 2); CheckFalse(set.ShouldFire(low, Pressing(KeyCode.Delete))); Check(set.ShouldFire(high, Pressing(KeyCode.Delete))); } public void TheHigherPriorityAddedFirst_StillHidesTheLower() { var set = new ChordSetBuilder(); Chord high = set.Add(Chord.Press(KeyCode.Delete), priority: 2); Chord low = set.Add(Chord.Press(KeyCode.Delete), priority: 1); CheckFalse(set.ShouldFire(low, Pressing(KeyCode.Delete))); Check(set.ShouldFire(high, Pressing(KeyCode.Delete))); } public void ADefaultPriority_IsBeatenByAPositiveOneAndBeatsANegativeOne() { var set = new ChordSetBuilder(); Chord negative = set.Add(Chord.Press(KeyCode.Delete), priority: -1); Chord standard = set.Add(Chord.Press(KeyCode.Delete)); Chord positive = set.Add(Chord.Press(KeyCode.Delete), priority: 1); CheckFalse(set.ShouldFire(negative, Pressing(KeyCode.Delete))); CheckFalse(set.ShouldFire(standard, Pressing(KeyCode.Delete))); Check(set.ShouldFire(positive, Pressing(KeyCode.Delete))); } public void TheHigherPriorityDisabled_LeavesTheLowerShouldFire() { var set = new ChordSetBuilder(); Chord low = set.Add(Chord.Press(KeyCode.Delete), priority: 1); Chord high = set.Add(Chord.Press(KeyCode.Delete), priority: 2); set.SetEnabled(high, false); Check(set.ShouldFire(low, Pressing(KeyCode.Delete))); } public void AMoreSpecificChordOfLowerPriority_StillHidesALessSpecificOneOfHigherPriority() { var set = new ChordSetBuilder(); Chord delete = set.Add(Delete, priority: 5); Chord shiftDelete = set.Add(ShiftDelete, priority: 0); KeyboardSnapshot shiftDeleteSnapshot = Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)); CheckFalse(set.ShouldFire(delete, shiftDeleteSnapshot)); Check(set.ShouldFire(shiftDelete, shiftDeleteSnapshot)); } public void EquivalentHeldPatternsOfDifferentPriority_OnlyTheHigherShouldFire() { var set = new ChordSetBuilder(); Chord low = set.Add(Chord.Hold(KeyCode.W), priority: 0); Chord high = set.Add(Chord.Hold(KeyCode.W), priority: 1); var holdingW = Keyboard(new KeyState(KeyCode.W), default, default); CheckFalse(set.ShouldFire(low, holdingW)); Check(set.ShouldFire(high, holdingW)); } public void OverlappingChordsNeitherSuperseding_BothShouldFire() { var set = new ChordSetBuilder(); Chord controlZ = set.Add(Chord.Press(KeyCode.Z) & Chord.HoldAny(KeyState.Control)); Chord shiftZ = set.Add(Chord.Press(KeyCode.Z) & Chord.HoldAny(KeyState.Shift)); KeyboardSnapshot both = Pressing(KeyCode.Z, alsoHeld: new KeyState(KeyCode.ControlLeft, KeyCode.ShiftLeft)); Check(set.ShouldFire(controlZ, both)); Check(set.ShouldFire(shiftZ, both)); } public void AnotherWayToMatchThatTheMoreSpecificChordDidNotUse_StillFires() { var set = new ChordSetBuilder(); Chord zOrX = set.Add(Chord.Press(KeyCode.Z) | Chord.Press(KeyCode.X)); set.Add(Chord.Press(KeyCode.Z) & Chord.HoldAny(KeyState.Control)); var both = Keyboard(new KeyState(KeyCode.ControlLeft, KeyCode.Z, KeyCode.X), new KeyState(KeyCode.Z, KeyCode.X), default); Check(set.ShouldFire(zOrX, both)); } public void OnlyTheWayTheMoreSpecificChordUsed_IsHidden() { var set = new ChordSetBuilder(); Chord zOrX = set.Add(Chord.Press(KeyCode.Z) | Chord.Press(KeyCode.X)); set.Add(Chord.Press(KeyCode.Z) & Chord.HoldAny(KeyState.Control)); CheckFalse(set.ShouldFire(zOrX, Pressing(KeyCode.Z, alsoHeld: new KeyState(KeyCode.ControlLeft)))); } public void EveryWayUsedUpBySeveralMoreSpecificChords_IsHidden() { var set = new ChordSetBuilder(); Chord zOrX = set.Add(Chord.Press(KeyCode.Z) | Chord.Press(KeyCode.X)); set.Add(Chord.Press(KeyCode.Z) & Chord.HoldAny(KeyState.Control)); set.Add(Chord.Press(KeyCode.X) & Chord.HoldAny(KeyState.Control)); var both = Keyboard(new KeyState(KeyCode.ControlLeft, KeyCode.Z, KeyCode.X), new KeyState(KeyCode.Z, KeyCode.X), default); CheckFalse(set.ShouldFire(zOrX, both)); } public void HeldKeysOfTheMoreSpecificChord_AreNotUsedUp() { var set = new ChordSetBuilder(); Chord chord = set.Add(Chord.Press(KeyCode.Z) | (Chord.Press(KeyCode.X) & Chord.HoldNone(new KeyState(KeyCode.ShiftLeft)))); set.Add(Chord.Press(KeyCode.Z) & Chord.Hold(KeyCode.ShiftLeft)); var shiftZAndX = Keyboard(new KeyState(KeyCode.ShiftLeft, KeyCode.Z, KeyCode.X), new KeyState(KeyCode.Z, KeyCode.X), default); CheckFalse(set.ShouldFire(chord, shiftZAndX)); } public void AMoreSpecificHeldPatternMatching_HidesIt() { var set = new ChordSetBuilder(); Chord walk = set.Add(Chord.Hold(KeyCode.W)); Chord sprint = set.Add(walk & Chord.Hold(KeyCode.ShiftLeft)); var sprinting = Keyboard(new KeyState(KeyCode.W, KeyCode.ShiftLeft), default, default); CheckFalse(set.ShouldFire(walk, sprinting)); Check(set.ShouldFire(sprint, sprinting)); } public void AnEdgeChordBuiltOnAHeldPattern_LeavesBothFiring() { var set = new ChordSetBuilder(); Chord walk = set.Add(Chord.Hold(KeyCode.W)); Chord longJump = set.Add(walk & Chord.Press(KeyCode.Space)); KeyboardSnapshot jumping = Pressing(KeyCode.Space, alsoHeld: new KeyState(KeyCode.W)); Check(set.ShouldFire(walk, jumping)); Check(set.ShouldFire(longJump, jumping)); } public void ARepeatingChord_HidesAHeldPatternBetweenRepeats() { var set = new ChordSetBuilder(); Chord turn = set.Add(Chord.Hold(KeyCode.ArrowLeft)); set.Add(Chord.PressOrRepeat(KeyCode.ArrowLeft)); var holding = Keyboard(new KeyState(KeyCode.ArrowLeft), default, default); CheckFalse(set.ShouldFire(turn, holding)); } public void ARepeatingChordAddedFirst_HidesAHeldPatternBetweenRepeats() { var set = new ChordSetBuilder(); set.Add(Chord.PressOrRepeat(KeyCode.ArrowLeft)); Chord turn = set.Add(Chord.Hold(KeyCode.ArrowLeft)); var holding = Keyboard(new KeyState(KeyCode.ArrowLeft), default, default); CheckFalse(set.ShouldFire(turn, holding)); } public void ARepeatingChordThatIsDisabled_LeavesAHeldPatternFiring() { var set = new ChordSetBuilder(); Chord turn = set.Add(Chord.Hold(KeyCode.ArrowLeft)); Chord field = set.Add(Chord.PressOrRepeat(KeyCode.ArrowLeft)); set.SetEnabled(field, false); var holding = Keyboard(new KeyState(KeyCode.ArrowLeft), default, default); Check(set.ShouldFire(turn, holding)); } public void ARepeatingChordNeedingAnUnheldKey_LeavesAHeldPatternFiring() { var set = new ChordSetBuilder(); Chord turn = set.Add(Chord.Hold(KeyCode.ArrowLeft)); set.Add(Chord.PressOrRepeat(KeyCode.ArrowLeft).WithAnyHeld(KeyState.Control)); var holding = Keyboard(new KeyState(KeyCode.ArrowLeft), default, default); Check(set.ShouldFire(turn, holding)); } public void APressOnlyChord_LeavesAHeldPatternFiringBetweenPresses() { var set = new ChordSetBuilder(); Chord turn = set.Add(Chord.Hold(KeyCode.ArrowLeft)); set.Add(Chord.Press(KeyCode.ArrowLeft)); var holding = Keyboard(new KeyState(KeyCode.ArrowLeft), default, default); Check(set.ShouldFire(turn, holding)); } public void ALayoutChordMatching_HidesAMoreGeneralLayoutChord() { var set = new ChordSetBuilder(); Chord plain = set.Add(Chord.Press(KeyCode.Delete, KeySpace.Layout)); Chord shifted = set.Add(plain & Chord.HoldAny(KeyState.Shift, KeySpace.Layout)); var shiftDelete = LayoutKeyboard(new KeyState(KeyCode.Delete, KeyCode.ShiftLeft), new KeyState(KeyCode.Delete), default); CheckFalse(set.ShouldFire(plain, shiftDelete)); Check(set.ShouldFire(shifted, shiftDelete)); } public void ALayoutChordMatching_HidesAPhysicalChordOnTheSameKey() { var set = new ChordSetBuilder(); Chord physical = set.Add(Chord.Press(KeyCode.Delete)); Chord layout = set.Add(Chord.Press(KeyCode.Delete, KeySpace.Layout) .WithAnyHeld(KeyState.Shift, KeySpace.Layout)); var both = new KeyboardSnapshot( new KeySpaceSnapshot(new KeyState(KeyCode.Delete), new KeyState(KeyCode.Delete), default), new KeySpaceSnapshot(new KeyState(KeyCode.Delete, KeyCode.ShiftLeft), new KeyState(KeyCode.Delete), default)); CheckFalse(set.ShouldFire(physical, both)); Check(set.ShouldFire(layout, both)); } public void AStrongerLayoutChord_UsesUpOnlyTheEdgesItDependsOnInTheLayoutSpace() { var set = new ChordSetBuilder(); Chord zOrX = set.Add(Chord.PressAny(new KeyState(KeyCode.Z, KeyCode.X), KeySpace.Layout)); set.Add(Chord.Press(KeyCode.Z, KeySpace.Layout).WithAnyHeld(KeyState.Control, KeySpace.Layout)); var controlZAndX = LayoutKeyboard(new KeyState(KeyCode.ControlLeft, KeyCode.Z, KeyCode.X), new KeyState(KeyCode.Z, KeyCode.X), default); var controlZ = LayoutKeyboard(new KeyState(KeyCode.ControlLeft, KeyCode.Z), new KeyState(KeyCode.Z), default); Check(set.ShouldFire(zOrX, controlZAndX)); CheckFalse(set.ShouldFire(zOrX, controlZ)); } public void ALayoutRepeatingChord_HidesALayoutHeldPatternBetweenRepeats() { var set = new ChordSetBuilder(); Chord turn = set.Add(Chord.Hold(KeyCode.ArrowLeft, KeySpace.Layout)); set.Add(Chord.PressOrRepeat(KeyCode.ArrowLeft, KeySpace.Layout)); var holding = LayoutKeyboard(new KeyState(KeyCode.ArrowLeft), default, default); CheckFalse(set.ShouldFire(turn, holding)); } public void ALayoutRepeatingChord_HidesAPhysicalHeldPatternOnTheSameKey() { var set = new ChordSetBuilder(); Chord turn = set.Add(Chord.Hold(KeyCode.ArrowLeft)); set.Add(Chord.PressOrRepeat(KeyCode.ArrowLeft, KeySpace.Layout)); var holding = new KeyboardSnapshot( new KeySpaceSnapshot(new KeyState(KeyCode.ArrowLeft), default, default), new KeySpaceSnapshot(new KeyState(KeyCode.ArrowLeft), default, default)); CheckFalse(set.ShouldFire(turn, holding)); } public void AHeldPatternInTheLayoutSpace_IsHiddenByAStrongerHeldPatternThere() { var set = new ChordSetBuilder(); Chord plain = set.Add(Chord.Hold(KeyCode.W, KeySpace.Layout)); set.Add(Chord.Hold(KeyCode.W, KeySpace.Layout).WithAnyHeld(KeyState.Shift, KeySpace.Layout)); var sprinting = LayoutKeyboard(new KeyState(KeyCode.W, KeyCode.ShiftLeft), default, default); CheckFalse(set.ShouldFire(plain, sprinting)); } public void ALayoutChordWithMoreKeys_HidesThePhysicalChordOfTheSameKeyOnAnAlignedKeyboard() { var set = new ChordSetBuilder(); Chord game = set.Add(Chord.Press(KeyCode.Z)); set.Add(LayoutUndo, priority: 1); Check(set.ShouldFire(LayoutUndo, KeyEvent(KeyCode.Z, KeyCode.Z, KeyCode.ControlLeft))); CheckFalse(set.ShouldFire(game, KeyEvent(KeyCode.Z, KeyCode.Z, KeyCode.ControlLeft))); } public void ALayoutChordWithMoreKeys_HidesAPhysicalChordAddedAfterIt() { var set = new ChordSetBuilder(); set.Add(LayoutUndo, priority: 1); Chord game = set.Add(Chord.Press(KeyCode.Z)); CheckFalse(set.ShouldFire(game, KeyEvent(KeyCode.Z, KeyCode.Z, KeyCode.ControlLeft))); } public void AcrossSpaces_AnEdgeChordThatNeverRepeats_LeavesAHeldChordFiring() { var set = new ChordSetBuilder(); Chord walk = set.Add(Chord.Hold(KeyCode.W)); set.Add(Chord.Press(KeyCode.W, KeySpace.Layout), priority: 1); var holding = new KeyboardSnapshot( new KeySpaceSnapshot(new KeyState(KeyCode.W), default, default, default), new KeySpaceSnapshot(new KeyState(KeyCode.W), default, default, default)); Check(set.ShouldFire(walk, holding)); } public void APhysicalChordWithMoreKeys_HidesTheLayoutChordOfTheSameKeyDespitePriority() { var set = new ChordSetBuilder(); Chord layout = set.Add(Chord.Press(KeyCode.Z, KeySpace.Layout), priority: 1); Chord physical = set.Add(Chord.Press(KeyCode.Z).WithAnyHeld(KeyState.Control)); KeyboardSnapshot keyboard = KeyEvent(KeyCode.Z, KeyCode.Z, KeyCode.ControlLeft); Check(set.ShouldFire(physical, keyboard)); CheckFalse(set.ShouldFire(layout, keyboard)); } public void ALayoutChordOfOutrankingPriority_HidesAPhysicalChordAtTheKeyItWasTypedOn() { var set = new ChordSetBuilder(); Chord game = set.Add(Chord.Press(KeyCode.W)); set.Add(LayoutUndo, priority: 1); KeyboardSnapshot keyboard = KeyEvent(KeyCode.W, KeyCode.Z, KeyCode.ControlLeft); Check(set.ShouldFire(LayoutUndo, keyboard)); CheckFalse(set.ShouldFire(game, keyboard)); } public void APhysicalChordOfOutrankingPriority_HidesALayoutChordTypedOnItsKey() { var set = new ChordSetBuilder(); Chord game = set.Add(Chord.Press(KeyCode.W), priority: 1); Chord layout = set.Add(Chord.Press(KeyCode.Z, KeySpace.Layout)); KeyboardSnapshot keyboard = KeyEvent(KeyCode.W, KeyCode.Z); Check(set.ShouldFire(game, keyboard)); CheckFalse(set.ShouldFire(layout, keyboard)); } public void AcrossSpacesAtEqualPriority_BothFireForOneKeyEvent() { var set = new ChordSetBuilder(); Chord game = set.Add(Chord.Press(KeyCode.W)); Chord layout = set.Add(Chord.Press(KeyCode.Z, KeySpace.Layout)); KeyboardSnapshot keyboard = KeyEvent(KeyCode.W, KeyCode.Z); Check(set.ShouldFire(game, keyboard)); Check(set.ShouldFire(layout, keyboard)); } public void AChordAtAnotherKeyThanTheEvent_IsLeftAlone() { var set = new ChordSetBuilder(); Chord game = set.Add(Chord.Press(KeyCode.Q)); set.Add(LayoutUndo, priority: 1); Check(set.ShouldFire(game, KeyEvent(KeyCode.Q, KeyCode.Q))); } public void AcrossSpaces_AHeldPatternIsNotHiddenByAnOutrankingHeldPattern() { var set = new ChordSetBuilder(); Chord sprint = set.Add(Chord.Hold(KeyCode.W)); set.Add(Chord.Hold(KeyCode.Z, KeySpace.Layout), priority: 1); var holding = new KeyboardSnapshot( new KeySpaceSnapshot(new KeyState(KeyCode.W), default, default, default), new KeySpaceSnapshot(new KeyState(KeyCode.Z), default, default, default)); Check(set.ShouldFire(sprint, holding)); } public void AChordMixingSpaces_DoesNotHideWhatItsFoldCannotSatisfy() { var set = new ChordSetBuilder(); Chord nonsense = set.Add(Chord.Press(KeyCode.Z) & ~Chord.Press(KeyCode.Z, KeySpace.Layout)); Chord other = set.Add(Chord.Press(KeyCode.Q, KeySpace.Layout)); KeyboardSnapshot keyboard = KeyEvent(KeyCode.Z, KeyCode.Q); Check(set.ShouldFire(nonsense, keyboard)); Check(set.ShouldFire(other, keyboard)); } public void UnregisteredChord_Throws() { CheckThrow(typeof(ArgumentException)); new ChordSetBuilder().ShouldFire(Delete,Pressing(KeyCode.Delete)); } public void Null_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSetBuilder().ShouldFire(null, Pressing(KeyCode.Delete)); } } public class IsEnabledTests : TestFixture { public void NewlyAdded_IsEnabled() { var set = new ChordSetBuilder(); set.Add(Delete); Check(set.IsEnabled(Delete)); } public void UnregisteredChord_Throws() { CheckThrow(typeof(ArgumentException)); new ChordSetBuilder().IsEnabled(Delete); } } public class SetEnabledTests : TestFixture { public void Disabling_IsReportedByIsEnabled() { var set = new ChordSetBuilder(); set.Add(Delete); set.SetEnabled(Delete, enabled: false); CheckFalse(set.IsEnabled(Delete)); } public void Disabled_NeverShouldFire() { var set = new ChordSetBuilder(); set.Add(Delete); set.SetEnabled(Delete, enabled: false); CheckFalse(set.ShouldFire(Delete, Pressing(KeyCode.Delete))); } public void DisabledMoreSpecificChord_NoLongerHidesTheLessSpecific() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); set.SetEnabled(ShiftDelete, enabled: false); Check(set.ShouldFire(Delete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void ReEnabled_ShouldFireAgain() { var set = new ChordSetBuilder(); set.Add(Delete); set.SetEnabled(Delete, enabled: false); set.SetEnabled(Delete, enabled: true); Check(set.ShouldFire(Delete, Pressing(KeyCode.Delete))); } public void UnregisteredChord_Throws() { CheckThrow(typeof(ArgumentException)); new ChordSetBuilder().SetEnabled(Delete, enabled: false); } } public class ClearTests : TestFixture { public void TakesOutEveryChord() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); set.Clear(); CheckEqual(0, set.Count); CheckFalse(set.Contains(Delete)); CheckFalse(set.Contains(ShiftDelete)); } public void OnAnEmptyBuilder_ChangesNothing() { var set = new ChordSetBuilder(); set.Clear(); CheckEqual(0, set.Count); } public void AClearedBuilder_TakesTheSameChordsAgain() { var set = new ChordSetBuilder(); set.Add(Delete); set.Clear(); set.Add(Delete); Check(set.ShouldFire(Delete, Pressing(KeyCode.Delete))); } public void LeavesASetBuiltEarlierAlone() { var set = new ChordSetBuilder(); set.Add(Delete); ChordSet built = set.Build(); set.Clear(); Check(built.Contains(Delete)); } } public class SetPriorityTests : TestFixture { public void AChord_TakesTheNewPriority() { var set = new ChordSetBuilder(); set.Add(Delete, priority: 1); set.SetPriority(Delete, 5); CheckEqual(5, set.PriorityOf(Delete)); } public void AChordRaisedAboveItsEquivalent_NowHidesIt() { var set = new ChordSetBuilder(); Chord rival = Chord.Press(KeyCode.Delete); set.Add(Delete, priority: 1); set.Add(rival, priority: 2); set.SetPriority(Delete, 3); Check(set.ShouldFire(Delete, Pressing(KeyCode.Delete))); CheckFalse(set.ShouldFire(rival, Pressing(KeyCode.Delete))); } public void AChordLoweredBelowItsEquivalent_IsNowHiddenByIt() { var set = new ChordSetBuilder(); Chord rival = Chord.Press(KeyCode.Delete); set.Add(Delete, priority: 2); set.Add(rival, priority: 1); set.SetPriority(Delete, 0); CheckFalse(set.ShouldFire(Delete, Pressing(KeyCode.Delete))); Check(set.ShouldFire(rival, Pressing(KeyCode.Delete))); } public void AChord_KeepsItsEnabledState() { var set = new ChordSetBuilder(); set.Add(Delete); set.SetEnabled(Delete, enabled: false); set.SetPriority(Delete, 2); CheckFalse(set.IsEnabled(Delete)); } public void AChord_LeavesTheOthersAlone() { var set = new ChordSetBuilder(); set.Add(Delete, priority: 1); set.Add(ShiftDelete, priority: 1); set.SetPriority(Delete, 3); CheckEqual(1, set.PriorityOf(ShiftDelete)); } public void AChordNotHere_Throws() { CheckThrow(typeof(ArgumentException)); new ChordSetBuilder().SetPriority(Delete, 1); } public void ANullChord_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSetBuilder().SetPriority(null, 1); } } public class CountTests : TestFixture { public void EmptySet_IsZero() { CheckEqual(0, new ChordSetBuilder().Count); } public void CountsAddedChords() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); CheckEqual(2, set.Count); } public void DropsRemovedChords() { var set = new ChordSetBuilder(); set.Add(Delete); set.Add(ShiftDelete); set.Remove(Delete); CheckEqual(1, set.Count); } } public class ContainsTests : TestFixture { public void AddedChord_IsContained() { var set = new ChordSetBuilder(); set.Add(Delete); Check(set.Contains(Delete)); } public void ChordNeverAdded_IsNotContained() { CheckFalse(new ChordSetBuilder().Contains(Delete)); } public void RemovedChord_IsNotContained() { var set = new ChordSetBuilder(); set.Add(Delete); set.Remove(Delete); CheckFalse(set.Contains(Delete)); } public void AnEquivalentChordThatWasAdded_IsNotTheSameChord() { var set = new ChordSetBuilder(); set.Add(Chord.Press(KeyCode.Delete)); CheckFalse(set.Contains(Delete)); } public void Null_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSetBuilder().Contains(null); } } public class PriorityOfTests : TestFixture { public void UnownedChord_ReturnsThePriorityItWasAddedWith() { var set = new ChordSetBuilder(); Chord chord = set.Add(Chord.Press(KeyCode.A), priority: 4); CheckEqual(4, set.PriorityOf(chord)); } public void ChordNotInTheSet_Throws() { CheckThrow(typeof(ArgumentException)); new ChordSetBuilder().PriorityOf(Chord.Press(KeyCode.A)); } } } }