diff --git a/sample/SampleConsoleApp/Demos/TimeDemo.cs b/sample/SampleConsoleApp/Demos/TimeDemo.cs deleted file mode 100644 index ae33e93..0000000 --- a/sample/SampleConsoleApp/Demos/TimeDemo.cs +++ /dev/null @@ -1,18 +0,0 @@ -using DarkMusicConcepts; - -namespace SampleConsoleApp.Demos; -internal class TimeDemo : Demo -{ - public override void Run() - { - PrintHeader("Time"); - - Print(Time.QuarterNote); - Print(Time.WholeNote); - Print(Time.Bar); - Print(Time.QuarterNote.GetDuration(Bpm.From(155))); - Print(Time.QuarterNote.Ticks); - Print(Time.QuarterNote * 3); - Print(Time.HalfNoteDotted + Time.Bar - (Time.SixteenthNote * 8)); - } -} diff --git a/sample/SampleConsoleApp/Demos/UnitsDemo.cs b/sample/SampleConsoleApp/Demos/UnitsDemo.cs new file mode 100644 index 0000000..63d618a --- /dev/null +++ b/sample/SampleConsoleApp/Demos/UnitsDemo.cs @@ -0,0 +1,31 @@ +using DarkMusicConcepts; + +namespace SampleConsoleApp.Demos; +internal class UnitsDemo : Demo +{ + public override void Run() + { + PrintHeader("Units"); + + PrintSubHeader("Overview"); + + Print(MidiNumber.Min); + Print(MidiNumber.Max); + Print(MidiNumber.From(127)); + Print(MidiNumber.IsValidValue(666)); + Print(MidiNumber.From(127) > MidiNumber.From(60)); + Print(MidiNumber.From(127) >= MidiNumber.From(60)); + Print(MidiNumber.From(127) < MidiNumber.From(60)); + Print(MidiNumber.From(127) <= MidiNumber.From(60)); + + PrintSubHeader("Time"); + + Print(Time.QuarterNote); + Print(Time.WholeNote); + Print(Time.Bar); + Print(Time.QuarterNote.GetDuration(Bpm.From(155))); + Print(Time.QuarterNote.Ticks); + Print(Time.QuarterNote * 3); + Print(Time.HalfNoteDotted + Time.Bar - (Time.SixteenthNote * 8)); + } +} diff --git a/sample/SampleConsoleApp/Program.cs b/sample/SampleConsoleApp/Program.cs index 16fee79..d6e9139 100644 --- a/sample/SampleConsoleApp/Program.cs +++ b/sample/SampleConsoleApp/Program.cs @@ -6,7 +6,7 @@ new IntervalsDemo(), new ScalesDemo(), new ChordsDemo(), - new TimeDemo() + new UnitsDemo() }; foreach (var demo in demos) diff --git a/src/DarkMusicConcepts.Units/Bpm.cs b/src/DarkMusicConcepts.Units/Bpm.cs index 9649ad3..697daf1 100644 --- a/src/DarkMusicConcepts.Units/Bpm.cs +++ b/src/DarkMusicConcepts.Units/Bpm.cs @@ -1,37 +1,17 @@ namespace DarkMusicConcepts; -public class Bpm : Unit +/// +/// Beats per minute. +/// In musical terminology, tempo (Italian, 'time'; plural tempos, or tempi from the Italian plural) also known as beats per minute, is the speed or pace of a given composition. In classical music, tempo is typically indicated with an instruction at the start of a piece (often using conventional Italian terms) and is usually measured in beats per minute (or bpm). In modern classical compositions, a "metronome mark" in beats per minute may supplement or replace the normal tempo marking, while in modern genres like electronic dance music, tempo will typically simply be stated in BPM. +/// +public class Bpm : Unit, IUnit { - public const double MinValue = 0; - public const double MaxValue = double.MaxValue; - - public static Bpm Min { get; } = From(MinValue); - public static Bpm Max { get; } = From(MaxValue); + public static double MinValue { get; } = 0; + public static double MaxValue { get; } = double.MaxValue; private Bpm(double value) : base(value) - { - } - - protected override double GetMinValue() => MinValue; - protected override double GetMaxValue() => MaxValue; - - public static Bpm From(double value) { - var bpm = new Bpm(value); - - bpm.Validate(); - - return bpm; } - public static bool TryFrom(double value, out Bpm bpm) - { - var x = new Bpm(value); - - bpm = x.TryValidate() - ? x - : null!; - - return bpm is not null; - } + static Bpm IUnit.Create(double value) => new(value); } \ No newline at end of file diff --git a/src/DarkMusicConcepts.Units/Frequency.cs b/src/DarkMusicConcepts.Units/Frequency.cs index 42ac56d..8ddd4aa 100644 --- a/src/DarkMusicConcepts.Units/Frequency.cs +++ b/src/DarkMusicConcepts.Units/Frequency.cs @@ -6,43 +6,16 @@ /// Note frequencies are mathematically related to each other, and are defined around the central note, A4 (A @ OneLine octave) . The current "standard pitch" or modern "concert pitch" for this note is 440 Hz. The formula for frequency calculatio is: /// f = 2^n/12 * 440Hz(n is the pitch distance to A4 or A @ OneLine). /// -public class Frequency : Unit +public class Frequency : Unit, IUnit { - public const double MinValue = 0; - public const double MaxValue = double.MaxValue; - - public static Frequency Min { get; } = From(MinValue); - public static Frequency Max { get; } = From(MaxValue); + public static double MinValue { get; } = 0; + public static double MaxValue { get; } = double.MaxValue; private Frequency(double value) : base(value) { } - protected override double GetMinValue() => MinValue; - protected override double GetMaxValue() => MaxValue; - - public override string ToString() - { - return $"{Value} Hz"; - } - - public static Frequency From(double value) - { - var frequency = new Frequency(value); - - frequency.Validate(); + static Frequency IUnit.Create(double value) => new(value); - return frequency; - } - - public static bool TryFrom(double value, out Frequency frequency) - { - var x = new Frequency(value); - - frequency = x.TryValidate() - ? x - : null!; - - return frequency is not null; - } + public override string ToString() => $"{Value} Hz"; } diff --git a/src/DarkMusicConcepts.Units/IUnit.cs b/src/DarkMusicConcepts.Units/IUnit.cs new file mode 100644 index 0000000..1a58daf --- /dev/null +++ b/src/DarkMusicConcepts.Units/IUnit.cs @@ -0,0 +1,11 @@ +namespace DarkMusicConcepts; + +public interface IUnit + where TValue : IComparable, IComparable, IEquatable +{ + public static abstract TValue MinValue { get; } + public static abstract TValue MaxValue { get; } + + internal static abstract TThis Create(TValue value); + public static abstract bool IsValidValue(TValue? value); +} diff --git a/src/DarkMusicConcepts.Units/MidiNumber.cs b/src/DarkMusicConcepts.Units/MidiNumber.cs index 34c6800..271f34a 100644 --- a/src/DarkMusicConcepts.Units/MidiNumber.cs +++ b/src/DarkMusicConcepts.Units/MidiNumber.cs @@ -5,38 +5,14 @@ /// In MIDI, each note is assigned a numeric value, which is transmitted with any Note-On/Off message. Middle C has a reference value of 60. The MIDI standard only says that the note number 60 is a C, it does not say of which octave. /// Due to the limitation of MIDI number's range (0-127), not all existing notes can be assigned one. There are more than 128 notes. /// -public class MidiNumber : Unit +public class MidiNumber : Unit, IUnit { - public const int MinValue = 0; - public const int MaxValue = 127; - - public static MidiNumber Min { get; } = From(MinValue); - public static MidiNumber Max { get; } = From(MaxValue); + public static int MinValue { get; } = 0; + public static int MaxValue { get; } = 127; private MidiNumber(int value) : base(value) { } - protected override int GetMinValue() => MinValue; - protected override int GetMaxValue() => MaxValue; - - public static MidiNumber From(int value) - { - var midiNumber = new MidiNumber(value); - - midiNumber.Validate(); - - return midiNumber; - } - - public static bool TryFrom(int value, out MidiNumber midiNumber) - { - var x = new MidiNumber(value); - - midiNumber = x.TryValidate() - ? x - : null!; - - return midiNumber is not null; - } + static MidiNumber IUnit.Create(int value) => new(value); } diff --git a/src/DarkMusicConcepts.Units/MidiVelocity.cs b/src/DarkMusicConcepts.Units/MidiVelocity.cs index 2d48f6b..41bfd7c 100644 --- a/src/DarkMusicConcepts.Units/MidiVelocity.cs +++ b/src/DarkMusicConcepts.Units/MidiVelocity.cs @@ -4,38 +4,14 @@ /// MIDI velocity indicates how hard the key was struck when the note was played, which usually corresponds to the note's loudness. /// The MIDI velocity range is from 0–127, with 127 being the loudest. /// -public class MidiVelocity : Unit +public class MidiVelocity : Unit, IUnit { - public const int MinValue = 0; - public const int MaxValue = 127; - - public static MidiVelocity Min { get; } = From(MinValue); - public static MidiVelocity Max { get; } = From(MaxValue); + public static int MinValue { get; } = 0; + public static int MaxValue { get; } = 127; private MidiVelocity(int value) : base(value) { } - protected override int GetMinValue() => MinValue; - protected override int GetMaxValue() => MaxValue; - - public static MidiVelocity From(int value) - { - var midiVelocity = new MidiVelocity(value); - - midiVelocity.Validate(); - - return midiVelocity; - } - - public static bool TryFrom(int value, out MidiVelocity midiVelocity) - { - var x = new MidiVelocity(value); - - midiVelocity = x.TryValidate() - ? x - : null!; - - return midiVelocity is not null; - } -} \ No newline at end of file + static MidiVelocity IUnit.Create(int value) => new(value); +} diff --git a/src/DarkMusicConcepts.Units/Time.cs b/src/DarkMusicConcepts.Units/Time.cs index c794e01..0071284 100644 --- a/src/DarkMusicConcepts.Units/Time.cs +++ b/src/DarkMusicConcepts.Units/Time.cs @@ -1,102 +1,79 @@ namespace DarkMusicConcepts; + /// /// Representation of musical time, the value is stored in MIDI ticks /// -public class Time : Unit +public class Time : Unit, IUnit { - public const long MinValue = 0; - public const long MaxValue = long.MaxValue; - - public static Time Min { get; } = From(MinValue); - public static Time Max { get; } = From(MaxValue); + public static long MinValue { get; } = 0; + public static long MaxValue { get; } = long.MaxValue; private Time(long value) : base(value) { } - protected override long GetMinValue() => MinValue; - protected override long GetMaxValue() => MaxValue; - - public static Time From(long ticks) - { - var time = new Time(ticks); - - time.Validate(); - - return time; - } - - public static bool TryFrom(long ticks, out Time time) - { - var x = new Time(ticks); - - time = x.TryValidate() - ? x - : null!; + static Time IUnit.Create(long value) => new(value); - return time is not null; - } - - private const long TicksPerQuarterNote = 960; - private const long TicksPerSixteenthNote = TicksPerQuarterNote / 4; - private const long TicksPerSixteenthNoteTriplet = TicksPerQuarterNote / 6; - private const long TicksPerSixteenthNoteDotted = 360; + private const long _ticksPerQuarterNote = 960; + private const long _ticksPerSixteenthNote = _ticksPerQuarterNote / 4; + private const long _ticksPerSixteenthNoteTriplet = _ticksPerQuarterNote / 6; + private const long _ticksPerSixteenthNoteDotted = 360; #region Helper members public long Ticks => Value; - public long Bars => Ticks / TicksPerSixteenthNote / 16 / 4; - - public long WholeNotes => Ticks / TicksPerSixteenthNote / 16; - public long HalfNotes => Ticks / TicksPerSixteenthNote / 8; - public long QuarterNotes => Ticks / TicksPerSixteenthNote / 4; - public long EightNotes => Ticks / TicksPerSixteenthNote / 2; - public long SixteenthNotes => Ticks / TicksPerSixteenthNote; - public long ThirtySecondNotes => Ticks / TicksPerSixteenthNote * 2; - public long SixtyForthNotes => Ticks / TicksPerSixteenthNote * 4; - - public long WholeNoteTriplets => Ticks / TicksPerSixteenthNoteTriplet / 16; - public long HalfNoteTriplets => Ticks / TicksPerSixteenthNoteTriplet / 8; - public long QuarterNoteTriplets => Ticks / TicksPerSixteenthNoteTriplet / 4; - public long EightNoteTriplets => Ticks / TicksPerSixteenthNoteTriplet / 2; - public long SixteenthNoteTriplets => Ticks / TicksPerSixteenthNoteTriplet; - public long ThirtySecondNoteTriplets => Ticks / TicksPerSixteenthNoteTriplet * 2; - public long SixtyForthNoteTriplets => Ticks / TicksPerSixteenthNoteTriplet * 4; - - public long WholeDottedNotes => Ticks / TicksPerSixteenthNoteDotted / 16; - public long HalfDottedNotes => Ticks / TicksPerSixteenthNoteDotted / 8; - public long QuarterDottedNotes => Ticks / TicksPerSixteenthNoteDotted / 4; - public long EightDottedNotes => Ticks / TicksPerSixteenthNoteDotted / 2; - public long SixteenthDottedNotes => Ticks / TicksPerSixteenthNoteDotted; - public long ThirtySecondDottedNotes => Ticks / TicksPerSixteenthNoteDotted * 2; - public long SixtyForthDottedNotes => Ticks / TicksPerSixteenthNoteDotted * 4; - - public static Time FromBars(long amount) => From(amount * TicksPerSixteenthNote * 64); - - public static Time FromWholeNotes(long amount) => From(amount * TicksPerSixteenthNote * 16); - public static Time FromHalfNotes(long amount) => From(amount * TicksPerSixteenthNote * 8); - public static Time FromQuarterNotes(long amount) => From(amount * TicksPerSixteenthNote * 4); - public static Time FromEightNotes(long amount) => From(amount * TicksPerSixteenthNote * 2); - public static Time FromSixteenthNotes(long amount) => From(amount * TicksPerSixteenthNote); - public static Time FromThirtySecondNotes(long amount) => From(amount * TicksPerSixteenthNote / 2); - public static Time FromSixtyForthNotes(long amount) => From(amount * TicksPerSixteenthNote / 4); - - public static Time FromWholeNoteTriplets(long amount) => From(amount * TicksPerSixteenthNoteTriplet * 16); - public static Time FromHalfNoteTriplets(long amount) => From(amount * TicksPerSixteenthNoteTriplet * 8); - public static Time FromQuarterNoteTriplets(long amount) => From(amount * TicksPerSixteenthNoteTriplet * 4); - public static Time FromEightNoteTriplets(long amount) => From(amount * TicksPerSixteenthNoteTriplet * 2); - public static Time FromSixteenthNoteTriplets(long amount) => From(amount * TicksPerSixteenthNoteTriplet); - public static Time FromThirtySecondNoteTriplets(long amount) => From(amount * TicksPerSixteenthNoteTriplet / 2); - public static Time FromSixtyForthNoteTriplets(long amount) => From(amount * TicksPerSixteenthNoteTriplet / 4); - - public static Time FromWholeNoteDotteds(long amount) => From(amount * TicksPerSixteenthNoteDotted * 16); - public static Time FromHalfNoteDotteds(long amount) => From(amount * TicksPerSixteenthNoteDotted * 8); - public static Time FromQuarterNoteDotteds(long amount) => From(amount * TicksPerSixteenthNoteDotted * 4); - public static Time FromEightNoteDotteds(long amount) => From(amount * TicksPerSixteenthNoteDotted * 2); - public static Time FromSixteenthNoteDotteds(long amount) => From(amount * TicksPerSixteenthNoteDotted); - public static Time FromThirtySecondNoteDotteds(long amount) => From(amount * TicksPerSixteenthNoteDotted / 2); - public static Time FromSixtyForthNoteDotteds(long amount) => From(amount * TicksPerSixteenthNoteDotted / 4); + public long Bars => Ticks / _ticksPerSixteenthNote / 16 / 4; + + public long WholeNotes => Ticks / _ticksPerSixteenthNote / 16; + public long HalfNotes => Ticks / _ticksPerSixteenthNote / 8; + public long QuarterNotes => Ticks / _ticksPerSixteenthNote / 4; + public long EightNotes => Ticks / _ticksPerSixteenthNote / 2; + public long SixteenthNotes => Ticks / _ticksPerSixteenthNote; + public long ThirtySecondNotes => Ticks / _ticksPerSixteenthNote * 2; + public long SixtyForthNotes => Ticks / _ticksPerSixteenthNote * 4; + + public long WholeNoteTriplets => Ticks / _ticksPerSixteenthNoteTriplet / 16; + public long HalfNoteTriplets => Ticks / _ticksPerSixteenthNoteTriplet / 8; + public long QuarterNoteTriplets => Ticks / _ticksPerSixteenthNoteTriplet / 4; + public long EightNoteTriplets => Ticks / _ticksPerSixteenthNoteTriplet / 2; + public long SixteenthNoteTriplets => Ticks / _ticksPerSixteenthNoteTriplet; + public long ThirtySecondNoteTriplets => Ticks / _ticksPerSixteenthNoteTriplet * 2; + public long SixtyForthNoteTriplets => Ticks / _ticksPerSixteenthNoteTriplet * 4; + + public long WholeDottedNotes => Ticks / _ticksPerSixteenthNoteDotted / 16; + public long HalfDottedNotes => Ticks / _ticksPerSixteenthNoteDotted / 8; + public long QuarterDottedNotes => Ticks / _ticksPerSixteenthNoteDotted / 4; + public long EightDottedNotes => Ticks / _ticksPerSixteenthNoteDotted / 2; + public long SixteenthDottedNotes => Ticks / _ticksPerSixteenthNoteDotted; + public long ThirtySecondDottedNotes => Ticks / _ticksPerSixteenthNoteDotted * 2; + public long SixtyForthDottedNotes => Ticks / _ticksPerSixteenthNoteDotted * 4; + + public static Time FromBars(long amount) => From(amount * _ticksPerSixteenthNote * 64); + + public static Time FromWholeNotes(long amount) => From(amount * _ticksPerSixteenthNote * 16); + public static Time FromHalfNotes(long amount) => From(amount * _ticksPerSixteenthNote * 8); + public static Time FromQuarterNotes(long amount) => From(amount * _ticksPerSixteenthNote * 4); + public static Time FromEightNotes(long amount) => From(amount * _ticksPerSixteenthNote * 2); + public static Time FromSixteenthNotes(long amount) => From(amount * _ticksPerSixteenthNote); + public static Time FromThirtySecondNotes(long amount) => From(amount * _ticksPerSixteenthNote / 2); + public static Time FromSixtyForthNotes(long amount) => From(amount * _ticksPerSixteenthNote / 4); + + public static Time FromWholeNoteTriplets(long amount) => From(amount * _ticksPerSixteenthNoteTriplet * 16); + public static Time FromHalfNoteTriplets(long amount) => From(amount * _ticksPerSixteenthNoteTriplet * 8); + public static Time FromQuarterNoteTriplets(long amount) => From(amount * _ticksPerSixteenthNoteTriplet * 4); + public static Time FromEightNoteTriplets(long amount) => From(amount * _ticksPerSixteenthNoteTriplet * 2); + public static Time FromSixteenthNoteTriplets(long amount) => From(amount * _ticksPerSixteenthNoteTriplet); + public static Time FromThirtySecondNoteTriplets(long amount) => From(amount * _ticksPerSixteenthNoteTriplet / 2); + public static Time FromSixtyForthNoteTriplets(long amount) => From(amount * _ticksPerSixteenthNoteTriplet / 4); + + public static Time FromWholeNoteDotteds(long amount) => From(amount * _ticksPerSixteenthNoteDotted * 16); + public static Time FromHalfNoteDotteds(long amount) => From(amount * _ticksPerSixteenthNoteDotted * 8); + public static Time FromQuarterNoteDotteds(long amount) => From(amount * _ticksPerSixteenthNoteDotted * 4); + public static Time FromEightNoteDotteds(long amount) => From(amount * _ticksPerSixteenthNoteDotted * 2); + public static Time FromSixteenthNoteDotteds(long amount) => From(amount * _ticksPerSixteenthNoteDotted); + public static Time FromThirtySecondNoteDotteds(long amount) => From(amount * _ticksPerSixteenthNoteDotted / 2); + public static Time FromSixtyForthNoteDotteds(long amount) => From(amount * _ticksPerSixteenthNoteDotted / 4); public static readonly Time Zero = From(0); @@ -158,15 +135,12 @@ public static bool TryFrom(long ticks, out Time time) /// The tempo to get duration for public TimeSpan GetDuration(Bpm bpm) { - var usPerQuarter = (double)60_000 / bpm.Value; - var usPerTick = (double)usPerQuarter / TicksPerQuarterNote; + var usPerQuarter = 60_000 / bpm.Value; + var usPerTick = (double)usPerQuarter / _ticksPerQuarterNote; var ms = (double)usPerTick * Ticks; var duration = TimeSpan.FromMilliseconds(ms); return duration; } - public override string ToString() - { - return $"{Ticks:n0} ticks"; - } + public override string ToString() => $"{Ticks:n0} ticks"; } \ No newline at end of file diff --git a/src/DarkMusicConcepts.Units/Unit.cs b/src/DarkMusicConcepts.Units/Unit.cs index 75ba4e2..ee22610 100644 --- a/src/DarkMusicConcepts.Units/Unit.cs +++ b/src/DarkMusicConcepts.Units/Unit.cs @@ -1,4 +1,6 @@ -namespace DarkMusicConcepts; +using System.Numerics; + +namespace DarkMusicConcepts; /// /// Number value object base class with a specific value range from Min to Max @@ -6,43 +8,49 @@ /// The actual value /// Self public abstract class Unit : IComparable, IComparable>, IEquatable> - where TThis : Unit - where TValue : IComparable, IComparable, IEquatable + where TThis : Unit, IUnit + where TValue : struct, IComparable, IComparable, IEquatable { + public static TThis Min { get; } = TThis.Create(TThis.MinValue); + public static TThis Max { get; } = TThis.Create(TThis.MaxValue); + protected Unit(TValue value) { Value = value; } - protected abstract TValue GetMinValue(); - protected abstract TValue GetMaxValue(); + public TValue Value { get; init; } - protected void Validate() + public static TThis From(TValue value) { - if (!IsValidValue(Value)) - { - throw new ArgumentOutOfRangeException(nameof(Value), Value, $"{nameof(Value)} has to be within range {GetMinValue()}-{GetMaxValue()}"); - } - } + var unit = TThis.Create(value); - protected bool TryValidate() - { - return IsValidValue(Value); + unit.Validate(); + + return unit; } - private bool IsValidValue(TValue? value) + public static bool TryFrom(TValue value, out TThis unit) { - if(value is null) + if (!IsValidValue(value)) { + unit = null!; return false; } - if (value.CompareTo(GetMinValue()) < 0) + unit = TThis.Create(value); + + return true; + } + + public static bool IsValidValue(TValue value) + { + if (value.CompareTo(TThis.MinValue) < 0) { return false; } - if (value.CompareTo(GetMaxValue()) > 0) + if (value.CompareTo(TThis.MaxValue) > 0) { return false; } @@ -50,7 +58,13 @@ private bool IsValidValue(TValue? value) return true; } - public TValue Value { get; } + protected void Validate() + { + if (!IsValidValue(Value)) + { + throw new ArgumentOutOfRangeException(nameof(Value), Value, $"{nameof(Value)} has to be within range {TThis.MinValue}-{TThis.MaxValue}"); + } + } #region Equality @@ -91,7 +105,7 @@ public override bool Equals(object? obj) return true; } - if(obj is not Unit other) + if (obj is not Unit other) { return false; } @@ -180,18 +194,7 @@ public int CompareTo(Unit? other) #endregion - public override int GetHashCode() - { - return EqualityComparer.Default.GetHashCode(Value); - } + public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Value); - public override string ToString() - { - if (Value is null) - { - return string.Empty; - } - - return Value.ToString() ?? string.Empty; - } + public override string ToString() => Value.ToString() ?? string.Empty; } \ No newline at end of file diff --git a/tests/DarkMusicConcepts.Tests/UnitsTests/TimeTests.cs b/tests/DarkMusicConcepts.Tests/UnitsTests/TimeTests.cs index dec3d90..6ecd10c 100644 --- a/tests/DarkMusicConcepts.Tests/UnitsTests/TimeTests.cs +++ b/tests/DarkMusicConcepts.Tests/UnitsTests/TimeTests.cs @@ -2,37 +2,8 @@ public class TimeTests { - [Fact] - public void From_ShouldThrow_WhenLessThanMin() - { - Assert.Throws(() => Time.From(Time.MinValue - 1)); - } - [Fact] - public void TryFrom_ShouldFail_WhenLessThanMin() - { - var success = Time.TryFrom(Time.MinValue - 1, out var invalidTime); - success.Should().Be(false); - invalidTime.Should().Be(null!); - } - - [Fact] - public void Comparison_ShouldReturnTrue_WhenValueMatches() - { - var time1 = Time.From(43); - var time2 = Time.From(43); - var result = time1 == time2; - result.Should().BeTrue(); - } - - [Fact] - public void Comparison_ShouldReturnFalse_WhenValueDoesntMatch() - { - var time1 = Time.From(43); - var time2 = Time.From(69); - var result = time1 == time2; - result.Should().BeFalse(); - } + #region Arithmetic operators [Fact] public void AdditionOperatorWithSelf_ShouldWork() @@ -60,6 +31,8 @@ public void MultiplicationOperatorWithInt_ShouldWork() result.Value.Should().Be(45); } + #endregion + [Fact] public void CalculatedProperties_NoneShouldThrowWhenAccessed() { diff --git a/tests/DarkMusicConcepts.Tests/UnitsTests/UnitTests.cs b/tests/DarkMusicConcepts.Tests/UnitsTests/UnitTests.cs index 96dbf0c..af0e71b 100644 --- a/tests/DarkMusicConcepts.Tests/UnitsTests/UnitTests.cs +++ b/tests/DarkMusicConcepts.Tests/UnitsTests/UnitTests.cs @@ -2,48 +2,12 @@ public class UnitTests { - private class DemoUnit : Unit - { - public const int MinValue = 0; - public const int MaxValue = int.MaxValue; - - public static DemoUnit Min { get; } = From(MinValue); - public static DemoUnit Max { get; } = From(MaxValue); - - private DemoUnit(int value) : base(value) - { - } - - protected override int GetMinValue() => MinValue; - protected override int GetMaxValue() => MaxValue; - - public static DemoUnit From(int value) - { - var demoUnit = new DemoUnit(value); - - demoUnit.Validate(); - - return demoUnit; - } - - public static bool TryFrom(int value, out DemoUnit demoUnit) - { - var x = new DemoUnit(value); - - demoUnit = x.TryValidate() - ? x - : null!; - - return demoUnit is not null; - } - } - [Fact] public void SimpleExample_ShouldWork() { - var item1 = DemoUnit.From(123456); - var item2 = DemoUnit.From(123456); - var item3 = DemoUnit.From(456789); + var item1 = Bpm.From(123456); + var item2 = Bpm.From(123456); + var item3 = Bpm.From(456789); item1.Should().Be(item2); item1.GetHashCode().Should().Be(item2.GetHashCode()); @@ -52,12 +16,30 @@ public void SimpleExample_ShouldWork() item1.GetHashCode().Should().NotBe(item3.GetHashCode()); } + #region Creation + + [Fact] + public void From_ShouldThrow_WhenLessThanMin() + { + Assert.Throws(() => Bpm.From(Bpm.MinValue - 1)); + } + + [Fact] + public void TryFrom_ShouldFail_WhenLessThanMin() + { + var success = Bpm.TryFrom(Bpm.MinValue - 1, out var invalidBpm); + success.Should().Be(false); + invalidBpm.Should().Be(null!); + } + + #endregion + #region Validate [Fact] public void Validate_ShouldWork() { - var act = new Action(() => DemoUnit.From(-99)); + var act = new Action(() => Bpm.From(-99)); act.Should().Throw(); } @@ -68,7 +50,7 @@ public void Validate_ShouldWork() [Fact] public void TryValidate_ShouldReturnFalse_WhenInvalid() { - var result = DemoUnit.TryFrom(-99, out var value); + var result = Bpm.TryFrom(-99, out var value); result.Should().BeFalse(); value.Should().BeNull(); } @@ -76,7 +58,7 @@ public void TryValidate_ShouldReturnFalse_WhenInvalid() [Fact] public void TryValidate_ShouldReturnTrue_WhenValid() { - var result = DemoUnit.TryFrom(25, out var value); + var result = Bpm.TryFrom(25, out var value); result.Should().BeTrue(); value.Should().NotBeNull(); } @@ -89,7 +71,7 @@ public void TryValidate_ShouldReturnTrue_WhenValid() public void ToString_ShouldValueToString() { var value = 123; - var result = DemoUnit.From(value).ToString(); + var result = Bpm.From(value).ToString(); result.Should().Be(value.ToString()); } @@ -100,25 +82,25 @@ public void ToString_ShouldValueToString() [Fact] public void StaticEquals_ShouldWork() { - DemoUnit nullItem = null!; - var item1 = DemoUnit.From(123456); - var item2 = DemoUnit.From(123456); - var item3 = DemoUnit.From(456789); + Bpm nullItem = null!; + var item1 = Bpm.From(123456); + var item2 = Bpm.From(123456); + var item3 = Bpm.From(456789); - (DemoUnit.Equals(item1, item2)).Should().BeTrue(); - (DemoUnit.Equals(item2, item1)).Should().BeTrue(); + (Bpm.Equals(item1, item2)).Should().BeTrue(); + (Bpm.Equals(item2, item1)).Should().BeTrue(); - (DemoUnit.Equals(item1, item3)).Should().BeFalse(); - (DemoUnit.Equals(item1, nullItem)).Should().BeFalse(); + (Bpm.Equals(item1, item3)).Should().BeFalse(); + (Bpm.Equals(item1, nullItem)).Should().BeFalse(); } [Fact] public void Equals_ShouldWork() { - DemoUnit nullItem = null!; - var item1 = DemoUnit.From(123456); - var item2 = DemoUnit.From(123456); - var item3 = DemoUnit.From(456789); + Bpm nullItem = null!; + var item1 = Bpm.From(123456); + var item2 = Bpm.From(123456); + var item3 = Bpm.From(456789); (item1.Equals(item2)).Should().BeTrue(); (item2.Equals(item1)).Should().BeTrue(); @@ -130,10 +112,10 @@ public void Equals_ShouldWork() [Fact] public void EqualOperator_ShouldWork() { - DemoUnit nullItem = null!; - var item1 = DemoUnit.From(123456); - var item2 = DemoUnit.From(123456); - var item3 = DemoUnit.From(456789); + Bpm nullItem = null!; + var item1 = Bpm.From(123456); + var item2 = Bpm.From(123456); + var item3 = Bpm.From(456789); (item1 == item2).Should().BeTrue(); (item2 == item1).Should().BeTrue(); @@ -146,10 +128,10 @@ public void EqualOperator_ShouldWork() [Fact] public void NotEqualOperator_ShouldWork() { - DemoUnit nullItem = null!; - var item1 = DemoUnit.From(123456); - var item2 = DemoUnit.From(123456); - var item3 = DemoUnit.From(456789); + Bpm nullItem = null!; + var item1 = Bpm.From(123456); + var item2 = Bpm.From(123456); + var item3 = Bpm.From(456789); (item1 != item2).Should().BeFalse(); (item2 != item1).Should().BeFalse(); @@ -166,12 +148,12 @@ public void NotEqualOperator_ShouldWork() [Fact] public void CompareTo_ShouldWork() { - var normal = DemoUnit.From(123456); + var normal = Bpm.From(123456); - DemoUnit nullItem = null!; - var little = DemoUnit.From(10); - var alsoNormal = DemoUnit.From(123456); - var large = DemoUnit.From(456789); + Bpm nullItem = null!; + var little = Bpm.From(10); + var alsoNormal = Bpm.From(123456); + var large = Bpm.From(456789); normal.CompareTo(nullItem).Should().Be(1); normal.CompareTo(little).Should().Be(1); @@ -182,13 +164,13 @@ public void CompareTo_ShouldWork() [Fact] public void CompareToObject_ShouldWork() { - var normal = DemoUnit.From(123456); + var normal = Bpm.From(123456); object nullItem = null!; - object anotherType = "I'm not a Demo Unit..."; - object little = DemoUnit.From(10); - object alsoNormal = DemoUnit.From(123456); - object large = DemoUnit.From(456789); + object anotherType = "I'm not a Bpm..."; + object little = Bpm.From(10); + object alsoNormal = Bpm.From(123456); + object large = Bpm.From(456789); normal.CompareTo(nullItem).Should().Be(1); normal.CompareTo(little).Should().Be(1); @@ -202,12 +184,12 @@ public void CompareToObject_ShouldWork() [Fact] public void GreaterThanOperator_ShouldWork() { - var normal = DemoUnit.From(123456); + var normal = Bpm.From(123456); - DemoUnit nullItem = null!; - var little = DemoUnit.From(10); - var alsoNormal = DemoUnit.From(123456); - var large = DemoUnit.From(456789); + Bpm nullItem = null!; + var little = Bpm.From(10); + var alsoNormal = Bpm.From(123456); + var large = Bpm.From(456789); (normal > nullItem).Should().BeTrue(); (normal > little).Should().BeTrue(); @@ -219,12 +201,12 @@ public void GreaterThanOperator_ShouldWork() [Fact] public void GreaterThanOrEqualOperator_ShouldWork() { - var normal = DemoUnit.From(123456); + var normal = Bpm.From(123456); - DemoUnit nullItem = null!; - var little = DemoUnit.From(10); - var alsoNormal = DemoUnit.From(123456); - var large = DemoUnit.From(456789); + Bpm nullItem = null!; + var little = Bpm.From(10); + var alsoNormal = Bpm.From(123456); + var large = Bpm.From(456789); (normal >= nullItem).Should().BeTrue(); (normal >= little).Should().BeTrue(); @@ -236,12 +218,12 @@ public void GreaterThanOrEqualOperator_ShouldWork() [Fact] public void SmallerThanOperator_ShouldWork() { - var normal = DemoUnit.From(123456); + var normal = Bpm.From(123456); - DemoUnit nullItem = null!; - var little = DemoUnit.From(10); - var alsoNormal = DemoUnit.From(123456); - var large = DemoUnit.From(456789); + Bpm nullItem = null!; + var little = Bpm.From(10); + var alsoNormal = Bpm.From(123456); + var large = Bpm.From(456789); (normal < nullItem).Should().BeFalse(); (normal < little).Should().BeFalse(); @@ -253,12 +235,12 @@ public void SmallerThanOperator_ShouldWork() [Fact] public void SmallerThanOrEqualOperator_ShouldWork() { - var normal = DemoUnit.From(123456); + var normal = Bpm.From(123456); - DemoUnit nullItem = null!; - var little = DemoUnit.From(10); - var alsoNormal = DemoUnit.From(123456); - var large = DemoUnit.From(456789); + Bpm nullItem = null!; + var little = Bpm.From(10); + var alsoNormal = Bpm.From(123456); + var large = Bpm.From(456789); (normal <= nullItem).Should().BeFalse(); (normal <= little).Should().BeFalse();