Skip to content

Commit a1fc370

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

File tree

2 files changed

+253
-19
lines changed

2 files changed

+253
-19
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: 213 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,53 +3,253 @@
33
public class SplitSecondStopwatchTests
44
{
55
[Fact]
6-
public void InitalStateIsReady()
6+
public void StateIsReadyForNewStopwatch()
77
{
88
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
99
Assert.Equal(StopwatchState.Ready, stopwatch.State);
1010
}
11-
11+
12+
[Fact]
13+
public void PreviousLapsIsEmptyForNewStopwatch()
14+
{
15+
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
16+
Assert.Empty(stopwatch.PreviousLaps);
17+
}
18+
1219
[Fact]
13-
public void StartChangesStateToRunning()
20+
public void CurrentLapIsZeroForNewStopwatch()
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
}
19-
33+
2034
[Fact]
21-
public void PauseChangesStateToPaused()
35+
public void StartFromReadyStateDoesNotChangePreviousLaps()
2236
{
2337
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
2438
stopwatch.Start();
25-
stopwatch.Pause();
26-
Assert.Equal(StopwatchState.Paused, stopwatch.State);
39+
Assert.Empty(stopwatch.PreviousLaps);
2740
}
41+
42+
[Fact]
43+
public void StartFromReadyStateStartsTimeTrackingInCurrentLap()
44+
{
45+
var timeProvider = new FakeTimeProvider();
46+
var stopwatch = new SplitSecondStopwatch(timeProvider);
47+
stopwatch.Start();
2848

49+
var elapsed = TimeSpan.FromSeconds(55.5);
50+
timeProvider.Advance(elapsed);
51+
52+
Assert.Equal(elapsed, stopwatch.CurrentLap);
53+
}
54+
55+
56+
57+
58+
59+
2960
[Fact]
30-
public void SplitKeepsStateAsRunning()
61+
public void StartFromPausedStateResumesTimeTrackingInCurrentLap()
3162
{
3263
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
3364
stopwatch.Start();
34-
stopwatch.Split();
65+
stopwatch.Pause();
66+
67+
stopwatch.Start();
68+
3569
Assert.Equal(StopwatchState.Running, stopwatch.State);
3670
}
71+
72+
[Fact]
73+
public void StartFromStartStateIsInvalid()
74+
{
75+
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
76+
stopwatch.Start();
77+
78+
Assert.Throws<InvalidOperationException>(() => stopwatch.Start());
79+
}
80+
81+
82+
83+
84+
85+
86+
[Fact]
87+
public void StartFromPausedStateDoesNotChangePreviousLaps()
88+
{
89+
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
90+
stopwatch.Start();
91+
stopwatch.Pause();
92+
93+
stopwatch.Start();
94+
95+
Assert.Empty(stopwatch.PreviousLaps);
96+
}
97+
98+
99+
public void StopFromReadyStateIsInvalid()
100+
{
101+
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
102+
Assert.Throws<InvalidOperationException>(() => stopwatch.Stop());
103+
}
37104

38105
[Fact]
39-
public void StopChangesStateToStop()
106+
public void StopFromRunningStateChangesStateToReady()
40107
{
41108
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
42109
stopwatch.Start();
110+
43111
stopwatch.Stop();
44-
Assert.Equal(StopwatchState.Stopped, stopwatch.State);
112+
113+
Assert.Equal(StopwatchState.Ready, stopwatch.State);
45114
}
46115

47116
[Fact]
48-
public void StopAddsLapTime()
117+
public void StopFromRunningStateResetsCurrentLap()
118+
{
119+
var timeProvider = new FakeTimeProvider();
120+
var stopwatch = new SplitSecondStopwatch(timeProvider);
121+
stopwatch.Start();
122+
123+
stopwatch.Stop();
124+
125+
Assert.Equal(TimeSpan.Zero, stopwatch.CurrentLap);
126+
}
127+
128+
[Fact]
129+
public void StopFromPausedStateChangesStateToReady()
49130
{
50131
var stopwatch = new SplitSecondStopwatch(new FakeTimeProvider());
51132
stopwatch.Start();
133+
stopwatch.Pause();
134+
52135
stopwatch.Stop();
53-
Assert.Equal(StopwatchState.Stopped, stopwatch.State);
136+
137+
Assert.Equal(StopwatchState.Ready, stopwatch.State);
138+
}
139+
140+
[Fact]
141+
public void StopFromPausedStateResetsCurrentLap()
142+
{
143+
var timeProvider = new FakeTimeProvider();
144+
var stopwatch = new SplitSecondStopwatch(timeProvider);
145+
stopwatch.Start();
146+
stopwatch.Pause();
147+
148+
stopwatch.Stop();
149+
150+
Assert.Equal(TimeSpan.Zero, stopwatch.CurrentLap);
151+
}
152+
153+
[Fact]
154+
public void StopFromPausedStateAddsCurrentLapToPreviousLaps()
155+
{
156+
var timeProvider = new FakeTimeProvider();
157+
var stopwatch = new SplitSecondStopwatch(timeProvider);
158+
stopwatch.Start();
159+
160+
var elapsed1 = TimeSpan.FromSeconds(33.6);
161+
timeProvider.Advance(elapsed1);
162+
stopwatch.Pause();
163+
164+
var elapsed2 = TimeSpan.FromSeconds(44.4);
165+
timeProvider.Advance(elapsed2);
166+
stopwatch.Stop();
167+
168+
var recordedLap = Assert.Single(stopwatch.PreviousLaps);
169+
Assert.Equal(elapsed1 + elapsed2, recordedLap);
170+
}
171+
172+
173+
174+
175+
176+
177+
178+
[Fact]
179+
public void CurrentLapDoesNotTrackTimeElapsedInReadyState()
180+
{
181+
var timeProvider = new FakeTimeProvider();
182+
var stopwatch = new SplitSecondStopwatch(timeProvider);
183+
184+
timeProvider.Advance(TimeSpan.FromSeconds(55.5));
185+
186+
Assert.Equal(TimeSpan.Zero, stopwatch.CurrentLap);
187+
}
188+
189+
[Fact]
190+
public void CurrentLapTracksTimeElapsedInRunningState()
191+
{
192+
var timeProvider = new FakeTimeProvider();
193+
var stopwatch = new SplitSecondStopwatch(timeProvider);
194+
stopwatch.Start();
195+
196+
var elapsed1 = TimeSpan.FromSeconds(5.2);
197+
timeProvider.Advance(elapsed1);
198+
199+
Assert.Equal(elapsed1, stopwatch.CurrentLap);
200+
201+
var elapsed2 = TimeSpan.FromSeconds(3.4);
202+
timeProvider.Advance(elapsed2);
203+
204+
Assert.Equal(elapsed1 + elapsed2, stopwatch.CurrentLap);
205+
}
206+
207+
[Fact]
208+
public void CurrentLapDoesNotTrackTimeElapsedInPausedState()
209+
{
210+
var timeProvider = new FakeTimeProvider();
211+
var stopwatch = new SplitSecondStopwatch(timeProvider);
212+
stopwatch.Start();
213+
214+
stopwatch.Pause();
215+
timeProvider.Advance(TimeSpan.FromSeconds(55.5));
216+
217+
Assert.Equal(TimeSpan.Zero, stopwatch.CurrentLap);
54218
}
219+
220+
221+
222+
223+
224+
225+
226+
227+
228+
229+
// [Fact]
230+
// public void EachStartStopCycleAddsToPreviousLaps()
231+
// {
232+
// var timeProvider = new FakeTimeProvider();
233+
// var stopwatch = new SplitSecondStopwatch(timeProvider);
234+
//
235+
// List<TimeSpan> expectedLapTimes = [];
236+
// const int numberOfLaps = 8;
237+
//
238+
// for (var i = 0; i < numberOfLaps; i++)
239+
// {
240+
// stopwatch.Start();
241+
//
242+
// var elapsed = TimeSpan.FromSeconds(i * 1.1);
243+
// timeProvider.Advance(elapsed);
244+
//
245+
// stopwatch.Stop();
246+
//
247+
// expectedLapTimes.Add(elapsed);
248+
// }
249+
//
250+
// for (var i = 0; i < numberOfLaps; i++)
251+
// {
252+
// Assert.Equal(expectedLapTimes[i], stopwatch.PreviousLaps[i]);
253+
// }
254+
// }
55255
}

0 commit comments

Comments
 (0)