using System; using System.Collections.Generic; using UnitTestSharp; namespace Clave.UnitTests { public class ChordSetTests : TestFixture { public static readonly Chord Delete = Chord.Press(KeyCode.Delete); public static readonly Chord ShiftDelete = Delete & Chord.HoldAny(KeyState.Shift); public static KeySnapshot Pressing(KeyCode key, KeyState alsoHeld = default) { return new KeySnapshot(alsoHeld | new KeyState(key), new KeyState(key), default); } public class AddTests : TestFixture { public void ReturnsTheChord() { CheckEqual(Delete, new ChordSet().Add(Delete)); } public void TheSameChordTwice_Throws() { var set = new ChordSet(); set.Add(Delete); CheckThrow(typeof(ArgumentException)); set.Add(Delete); } public void Null_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSet().Add(null); } public void AKeyboardsWorthOfShortcuts_FitsInTheTestBudget() { var set = new ChordSet(); 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 ShouldFireTests : TestFixture { public void MatchingWithNothingMoreSpecific_ShouldFire() { var set = new ChordSet(); set.Add(Delete); Check(set.ShouldFire(Delete, Pressing(KeyCode.Delete))); } public void NotMatching_ShouldNotFire() { var set = new ChordSet(); set.Add(Delete); CheckFalse(set.ShouldFire(Delete, Pressing(KeyCode.Backspace))); } public void AMoreSpecificChordMatching_HidesIt() { var set = new ChordSet(); set.Add(Delete); set.Add(ShiftDelete); KeySnapshot 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 ChordSet(); set.Add(Delete); set.Add(ShiftDelete); Check(set.ShouldFire(Delete, Pressing(KeyCode.Delete))); } public void TheMoreSpecificChordAddedFirst_StillHidesIt() { var set = new ChordSet(); 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 ChordSet(); 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)); KeySnapshot 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 ChordSet(); 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 ChordSet(); 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 ChordSet(); 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 ChordSet(); 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 ChordSet(); 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 ChordSet(); Chord delete = set.Add(Delete, priority: 5); Chord shiftDelete = set.Add(ShiftDelete, priority: 0); KeySnapshot 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 ChordSet(); Chord low = set.Add(Chord.Hold(KeyCode.W), priority: 0); Chord high = set.Add(Chord.Hold(KeyCode.W), priority: 1); var holdingW = new KeySnapshot(new KeyState(KeyCode.W), default, default); CheckFalse(set.ShouldFire(low, holdingW)); Check(set.ShouldFire(high, holdingW)); } public void OverlappingChordsNeitherSuperseding_BothShouldFire() { var set = new ChordSet(); Chord controlZ = set.Add(Chord.Press(KeyCode.Z) & Chord.HoldAny(KeyState.Control)); Chord shiftZ = set.Add(Chord.Press(KeyCode.Z) & Chord.HoldAny(KeyState.Shift)); KeySnapshot 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 ChordSet(); Chord zOrX = set.Add(Chord.Press(KeyCode.Z) | Chord.Press(KeyCode.X)); set.Add(Chord.Press(KeyCode.Z) & Chord.HoldAny(KeyState.Control)); var both = new KeySnapshot(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 ChordSet(); 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 ChordSet(); 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 = new KeySnapshot(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 ChordSet(); 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 = new KeySnapshot(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 ChordSet(); Chord walk = set.Add(Chord.Hold(KeyCode.W)); Chord sprint = set.Add(walk & Chord.Hold(KeyCode.ShiftLeft)); var sprinting = new KeySnapshot(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 ChordSet(); Chord walk = set.Add(Chord.Hold(KeyCode.W)); Chord longJump = set.Add(walk & Chord.Press(KeyCode.Space)); KeySnapshot jumping = Pressing(KeyCode.Space, alsoHeld: new KeyState(KeyCode.W)); Check(set.ShouldFire(walk, jumping)); Check(set.ShouldFire(longJump, jumping)); } public void UnregisteredChord_Throws() { CheckThrow(typeof(ArgumentException)); new ChordSet().ShouldFire(Delete, Pressing(KeyCode.Delete)); } public void Null_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSet().ShouldFire(null, Pressing(KeyCode.Delete)); } } public class IsEnabledTests : TestFixture { public void NewlyAdded_IsEnabled() { var set = new ChordSet(); set.Add(Delete); Check(set.IsEnabled(Delete)); } public void UnregisteredChord_Throws() { CheckThrow(typeof(ArgumentException)); new ChordSet().IsEnabled(Delete); } } public class SetEnabledTests : TestFixture { public void Disabling_IsReportedByIsEnabled() { var set = new ChordSet(); set.Add(Delete); set.SetEnabled(Delete, enabled: false); CheckFalse(set.IsEnabled(Delete)); } public void Disabled_NeverShouldFire() { var set = new ChordSet(); set.Add(Delete); set.SetEnabled(Delete, enabled: false); CheckFalse(set.ShouldFire(Delete, Pressing(KeyCode.Delete))); } public void DisabledMoreSpecificChord_NoLongerHidesTheLessSpecific() { var set = new ChordSet(); 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 ChordSet(); 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 ChordSet().SetEnabled(Delete, enabled: false); } } public class CountTests : TestFixture { public void EmptySet_IsZero() { CheckEqual(0, new ChordSet().Count); } public void CountsAddedChords() { var set = new ChordSet(); set.Add(Delete); set.Add(ShiftDelete); CheckEqual(2, set.Count); } public void DropsRemovedChords() { var set = new ChordSet(); set.Add(Delete); set.Add(ShiftDelete); set.Remove(Delete); CheckEqual(1, set.Count); } } public class GetEnumeratorTests : TestFixture { public static List Walk(ChordSet set) { var returnMe = new List(); foreach (Chord chord in set) { returnMe.Add(chord); } return returnMe; } public void EmptySet_YieldsNothing() { CheckEqual(0, Walk(new ChordSet()).Count); } public void YieldsEveryChordOnce() { var set = new ChordSet(); 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 ChordSet(); 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 ChordSet(); set.Add(Delete); set.SetEnabled(Delete, enabled: false); CheckEqual(1, Walk(set).Count); } public void AddingWhileWalking_Throws() { var set = new ChordSet(); set.Add(Delete); CheckThrow(typeof(InvalidOperationException)); foreach (Chord chord in set) { set.Add(Chord.Press(KeyCode.Z)); } } public void RemovingWhileWalking_Throws() { var set = new ChordSet(); set.Add(Delete); CheckThrow(typeof(InvalidOperationException)); foreach (Chord chord in set) { set.Remove(chord); } } public void EnablingAndDisablingWhileWalking_IsFine() { var set = new ChordSet(); 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 ContainsTests : TestFixture { public void AddedChord_IsContained() { var set = new ChordSet(); set.Add(Delete); Check(set.Contains(Delete)); } public void ChordNeverAdded_IsNotContained() { CheckFalse(new ChordSet().Contains(Delete)); } public void RemovedChord_IsNotContained() { var set = new ChordSet(); set.Add(Delete); set.Remove(Delete); CheckFalse(set.Contains(Delete)); } public void AnEquivalentChordThatWasAdded_IsNotTheSameChord() { var set = new ChordSet(); set.Add(Chord.Press(KeyCode.Delete)); CheckFalse(set.Contains(Delete)); } public void Null_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSet().Contains(null); } } public class AddAllTests : TestFixture { public void AddsEveryChordOfTheOtherSet() { var other = new ChordSet(); other.Add(Delete); other.Add(ShiftDelete); var set = new ChordSet(); set.AddAll(other); Check(set.Contains(Delete)); Check(set.Contains(ShiftDelete)); } public void AddedChords_CompeteLikeAnyOther() { var other = new ChordSet(); other.Add(Delete); other.Add(ShiftDelete); var set = new ChordSet(); set.AddAll(other); CheckFalse(set.ShouldFire(Delete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void AddedChords_CompeteWithTheOnesAlreadyThere() { var other = new ChordSet(); other.Add(ShiftDelete); var set = new ChordSet(); set.Add(Delete); set.AddAll(other); CheckFalse(set.ShouldFire(Delete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void TakesThePriorityGiven() { var other = new ChordSet(); Chord incoming = other.Add(Chord.Press(KeyCode.Delete)); var set = new ChordSet(); 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 ChordSet(); other.Add(Delete); var set = new ChordSet(); set.AddAll(other); set.Remove(Delete); Check(other.Contains(Delete)); Check(other.ShouldFire(Delete, Pressing(KeyCode.Delete))); } public void AChordAlreadyHere_ThrowsAndAddsNone() { var other = new ChordSet(); other.Add(ShiftDelete); other.Add(Delete); var set = new ChordSet(); set.Add(Delete); bool threw = false; try { set.AddAll(other); } catch (ArgumentException) { threw = true; } Check(threw); CheckFalse(set.Contains(ShiftDelete)); } public void AnEmptySet_AddsNothing() { var set = new ChordSet(); set.AddAll(new ChordSet()); CheckFalse(set.Contains(Delete)); } public void Null_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSet().AddAll(null); } } public class RemoveTests : TestFixture { public void RemovedChord_IsNoLongerRegistered() { var set = new ChordSet(); set.Add(Delete); set.Remove(Delete); CheckThrow(typeof(ArgumentException)); set.IsEnabled(Delete); } public void RemovedMoreSpecificChord_NoLongerHidesTheLessSpecific() { var set = new ChordSet(); 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 ChordSet(); 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 ChordSet(); set.Add(Delete); set.Add(ShiftDelete); set.Remove(Delete); Check(set.ShouldFire(ShiftDelete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void RemovedAndAddedAgain_CompetesAgain() { var set = new ChordSet(); 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 RemovingOneOfSeveralHiders_KeepsTheRestHiding() { var set = new ChordSet(); 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); KeySnapshot 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 ChordSet().Remove(Delete); } public void Null_Throws() { CheckThrow(typeof(ArgumentNullException)); new ChordSet().Remove(null); } } public class RemoveAllTests : TestFixture { public void RemovesEveryChordOfTheOtherSet() { var other = new ChordSet(); other.Add(Delete); other.Add(ShiftDelete); var set = new ChordSet(); set.AddAll(other); set.RemoveAll(other); CheckFalse(set.Contains(Delete)); CheckFalse(set.Contains(ShiftDelete)); } public void LeavesOtherChordsAlone() { var other = new ChordSet(); other.Add(ShiftDelete); var set = new ChordSet(); set.Add(Delete); set.AddAll(other); set.RemoveAll(other); Check(set.ShouldFire(Delete, Pressing(KeyCode.Delete, alsoHeld: new KeyState(KeyCode.ShiftLeft)))); } public void AChordNotHere_ThrowsAndRemovesNone() { var other = new ChordSet(); other.Add(Delete); other.Add(ShiftDelete); var set = new ChordSet(); 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 ChordSet().RemoveAll(null); } } } }