Skip to content

Commit 10bbbab

Browse files
Add last-lap exercise
1 parent 9210b05 commit 10bbbab

File tree

2 files changed

+245
-20
lines changed

2 files changed

+245
-20
lines changed

exercises/practice/split-second-stopwatch/SplitSecondStopwatch.cs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,65 @@ public enum StopwatchState
22
{
33
Ready,
44
Running,
5-
Paused,
6-
Stopped
5+
Paused
76
}
87

98
public class SplitSecondStopwatch(TimeProvider time)
109
{
10+
private readonly List<TimeSpan> _previousLapSegments = new();
11+
private DateTimeOffset? _currentSegmentStart;
12+
1113
public StopwatchState State { get; private set; }
1214

15+
public List<TimeSpan> PreviousLaps { get; } = new();
16+
17+
public TimeSpan CurrentLap => PreviousLapSegments + CurrentSegment;
18+
19+
private TimeSpan PreviousLapSegments => _previousLapSegments.Aggregate(TimeSpan.Zero, (total, segment) => total + segment);
20+
21+
private TimeSpan CurrentSegment => _currentSegmentStart is {} start ? time.GetUtcNow() - start : TimeSpan.Zero;
22+
1323
public void Start()
1424
{
15-
throw new NotImplementedException();
25+
if (State == StopwatchState.Paused)
26+
_currentSegmentStart = null;
27+
28+
_currentSegmentStart ??= time.GetUtcNow();
29+
30+
State = StopwatchState.Running;
1631
}
1732

1833
public void Stop()
1934
{
20-
throw new NotImplementedException();
35+
if (State == StopwatchState.Ready)
36+
return;
37+
38+
PreviousLaps.Add(CurrentLap);
39+
_currentSegmentStart = null;
40+
State = StopwatchState.Ready;
2141
}
2242

2343
public void Split()
2444
{
25-
throw new NotImplementedException();
45+
AddSegment();
2646
}
2747

2848
public void Pause()
2949
{
30-
throw new NotImplementedException();
50+
State = StopwatchState.Paused;
51+
AddSegment();
52+
}
53+
54+
public void Reset()
55+
{
56+
Stop();
57+
_previousLapSegments.Clear();
58+
Start();
59+
}
60+
61+
private void AddSegment()
62+
{
63+
_previousLapSegments.Add(CurrentSegment);
64+
_currentSegmentStart = null;
3165
}
3266
}

exercises/practice/split-second-stopwatch/SplitSecondStopwatchTests.cs

Lines changed: 205 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,53 +3,244 @@
33
public class SplitSecondStopwatchTests
44
{
55
[Fact]
6-
public void InitalStateIsReady()
6+
public void StateIsInitiallyReady()
77
{
88
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
99
Assert.Equal(StopwatchState.Ready, stopwatch.State);
1010
}
11-
11+
12+
[Fact]
13+
public void PreviousLapsIsInitiallyEmpty()
14+
{
15+
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
16+
Assert.Empty(stopwatch.PreviousLaps);
17+
}
18+
1219
[Fact]
13-
public void StartChangesStateToRunning()
20+
public void CurrentLapIsInitiallyZero()
21+
{
22+
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
23+
Assert.Equal(TimeSpan.Zero, stopwatch.CurrentLap);
24+
}
25+
26+
[Fact]
27+
public void StartFromReadyStateChangesStateToRunning()
1428
{
1529
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
1630
stopwatch.Start();
1731
Assert.Equal(StopwatchState.Running, stopwatch.State);
1832
}
33+
34+
[Fact]
35+
public void StartFromReadyStateDoesNotChangePreviousLaps()
36+
{
37+
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
38+
stopwatch.Start();
39+
Assert.Empty(stopwatch.PreviousLaps);
40+
}
41+
42+
[Fact]
43+
public void StartFromReadyStateStartsTimeTrackingInCurrentLap()
44+
{
45+
var timeProvider = new FakeTimeProvider();
46+
var stopwatch = new SplitSecondStopwatch(timeProvider);
47+
stopwatch.Start();
48+
49+
var elapsed = TimeSpan.FromSeconds(55.5);
50+
timeProvider.Advance(elapsed);
51+
52+
Assert.Equal(elapsed, stopwatch.CurrentLap);
53+
}
1954

2055
[Fact]
21-
public void PauseChangesStateToPaused()
56+
public void StopFromRunningStateChangesStateToReady()
2257
{
2358
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
2459
stopwatch.Start();
25-
stopwatch.Pause();
26-
Assert.Equal(StopwatchState.Paused, stopwatch.State);
60+
61+
stopwatch.Stop();
62+
63+
Assert.Equal(StopwatchState.Ready, stopwatch.State);
64+
}
65+
66+
[Fact]
67+
public void StopFromRunningStateAddsCurrentLapToPreviousLaps()
68+
{
69+
var timeProvider = new FakeTimeProvider();
70+
var stopwatch = new SplitSecondStopwatch(timeProvider);
71+
stopwatch.Start();
72+
73+
var elapsed = TimeSpan.FromSeconds(33.6);
74+
timeProvider.Advance(elapsed);
75+
76+
stopwatch.Stop();
77+
78+
var previousLap = Assert.Single(stopwatch.PreviousLaps);
79+
Assert.Equal(elapsed, previousLap);
2780
}
2881

2982
[Fact]
30-
public void SplitKeepsStateAsRunning()
83+
public void StopFromRunningStateResetsCurrentLapToZero()
84+
{
85+
var timeProvider = new FakeTimeProvider();
86+
var stopwatch = new SplitSecondStopwatch(timeProvider);
87+
stopwatch.Start();
88+
89+
stopwatch.Stop();
90+
91+
Assert.Equal(TimeSpan.Zero, stopwatch.CurrentLap);
92+
}
93+
94+
95+
96+
97+
98+
[Fact]
99+
public void StartFromPausedStateResumesTimeTrackingInCurrentLap()
31100
{
32101
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
33102
stopwatch.Start();
34-
stopwatch.Split();
103+
stopwatch.Pause();
104+
105+
stopwatch.Start();
106+
35107
Assert.Equal(StopwatchState.Running, stopwatch.State);
36108
}
37-
109+
110+
[Fact]
111+
public void StartFromStartStateIsInvalid()
112+
{
113+
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
114+
stopwatch.Start();
115+
116+
Assert.Throws<InvalidOperationException>(() => stopwatch.Start());
117+
}
118+
119+
120+
121+
122+
123+
38124
[Fact]
39-
public void StopChangesStateToStop()
125+
public void StartFromPausedStateDoesNotChangePreviousLaps()
40126
{
41127
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
42128
stopwatch.Start();
129+
stopwatch.Pause();
130+
131+
stopwatch.Start();
132+
133+
Assert.Empty(stopwatch.PreviousLaps);
134+
}
135+
136+
137+
public void StopFromReadyStateIsInvalid()
138+
{
139+
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
140+
Assert.Throws<InvalidOperationException>(() => stopwatch.Stop());
141+
}
142+
143+
[Fact]
144+
public void StopFromPausedStateChangesStateToReady()
145+
{
146+
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
147+
stopwatch.Start();
148+
stopwatch.Pause();
149+
43150
stopwatch.Stop();
44-
Assert.Equal(StopwatchState.Stopped, stopwatch.State);
151+
152+
Assert.Equal(StopwatchState.Ready, stopwatch.State);
45153
}
46154

47155
[Fact]
48-
public void StopAddsLapTime()
156+
public void StopFromPausedStateResetsCurrentLap()
49157
{
50-
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
158+
var timeProvider = new FakeTimeProvider();
159+
var stopwatch = new SplitSecondStopwatch(timeProvider);
51160
stopwatch.Start();
161+
stopwatch.Pause();
162+
163+
stopwatch.Stop();
164+
165+
Assert.Equal(TimeSpan.Zero, stopwatch.CurrentLap);
166+
}
167+
168+
[Fact]
169+
public void StopFromPausedStateAddsCurrentLapToPreviousLaps()
170+
{
171+
var timeProvider = new FakeTimeProvider();
172+
var stopwatch = new SplitSecondStopwatch(timeProvider);
173+
stopwatch.Start();
174+
175+
var elapsed1 = TimeSpan.FromSeconds(33.6);
176+
timeProvider.Advance(elapsed1);
177+
stopwatch.Pause();
178+
179+
var elapsed2 = TimeSpan.FromSeconds(44.4);
180+
timeProvider.Advance(elapsed2);
52181
stopwatch.Stop();
53-
Assert.Equal(StopwatchState.Stopped, stopwatch.State);
182+
183+
var recordedLap = Assert.Single(stopwatch.PreviousLaps);
184+
Assert.Equal(elapsed1 + elapsed2, recordedLap);
185+
}
186+
187+
188+
189+
190+
191+
192+
193+
[Fact]
194+
public void CurrentLapDoesNotTrackElapsedTimeInReadyState()
195+
{
196+
var timeProvider = new FakeTimeProvider();
197+
var stopwatch = new SplitSecondStopwatch(timeProvider);
198+
199+
timeProvider.Advance(TimeSpan.FromSeconds(55.5));
200+
201+
Assert.Equal(TimeSpan.Zero, stopwatch.CurrentLap);
202+
}
203+
204+
[Fact]
205+
public void CurrentLapDoesNotTrackElapsedTimeInPausedState()
206+
{
207+
var timeProvider = new FakeTimeProvider();
208+
var stopwatch = new SplitSecondStopwatch(timeProvider);
209+
stopwatch.Start();
210+
var elapsed = TimeSpan.FromSeconds(7);
211+
timeProvider.Advance(elapsed);
212+
213+
stopwatch.Pause();
214+
timeProvider.Advance(TimeSpan.FromSeconds(10));
215+
216+
Assert.Equal(elapsed, stopwatch.CurrentLap);
54217
}
218+
219+
220+
// [Fact]
221+
// public void EachStartStopCycleAddsToPreviousLaps()
222+
// {
223+
// var timeProvider = new FakeTimeProvider();
224+
// var stopwatch = new SplitSecondStopwatch(timeProvider);
225+
//
226+
// List<TimeSpan> expectedLapTimes = [];
227+
// const int numberOfLaps = 8;
228+
//
229+
// for (var i = 0; i < numberOfLaps; i++)
230+
// {
231+
// stopwatch.Start();
232+
//
233+
// var elapsed = TimeSpan.FromSeconds(i * 1.1);
234+
// timeProvider.Advance(elapsed);
235+
//
236+
// stopwatch.Stop();
237+
//
238+
// expectedLapTimes.Add(elapsed);
239+
// }
240+
//
241+
// for (var i = 0; i < numberOfLaps; i++)
242+
// {
243+
// Assert.Equal(expectedLapTimes[i], stopwatch.PreviousLaps[i]);
244+
// }
245+
// }
55246
}

0 commit comments

Comments
 (0)