Skip to content

Commit 698d139

Browse files
author
BRUNER Patrick
committed
ledservice, removed leds from logtabwindow to service, so class is smaller and less packed
1 parent 44f2957 commit 698d139

15 files changed

Lines changed: 1700 additions & 394 deletions

src/LogExpert.Resources/Resources.Designer.cs

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/LogExpert.Resources/Resources.de.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2115,4 +2115,10 @@ Fortfahren?</value>
21152115

21162116
LogExpert neu starten, um die Änderungen zu übernehmen?</value>
21172117
</data>
2118+
<data name="LogExpert_Common_Error_Message_ServiceNotInitialized" xml:space="preserve">
2119+
<value>{0} nicht initialisiert</value>
2120+
</data>
2121+
<data name="LogExpert_Common_Error_Message_ServiceIsAlreadyInitialized" xml:space="preserve">
2122+
<value>{0} ist bereits initialisiert</value>
2123+
</data>
21182124
</root>

src/LogExpert.Resources/Resources.resx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2124,4 +2124,13 @@ Restart LogExpert to apply changes?</value>
21242124
<data name="MissingFilesDialog_UI_Filter_Title" xml:space="preserve">
21252125
<value>Locate: {0}</value>
21262126
</data>
2127+
<data name="LogExpert_Common_Error_Message_ServiceNotInitialized" xml:space="preserve">
2128+
<value>{0} not initialized</value>
2129+
</data>
2130+
<data name="LogExpert_Common_Error_Message_ServiceIsAlreadyInitialized" xml:space="preserve">
2131+
<value>{0} is already initialized</value>
2132+
</data>
2133+
<data name="LogExpert_Common_Error_Message_ServiceMustBeCreatedOnUIThread" xml:space="preserve">
2134+
<value>{0} must be created on UI thread</value>
2135+
</data>
21272136
</root>
Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
using System.Runtime.Versioning;
2+
3+
using LogExpert.UI.Services;
4+
5+
using NUnit.Framework;
6+
7+
namespace LogExpert.Tests.Services;
8+
9+
[TestFixture]
10+
[Apartment(ApartmentState.STA)] // Required for UI components
11+
[SupportedOSPlatform("windows")]
12+
public class LedIndicatorServiceTests
13+
{
14+
private LedIndicatorService? _service;
15+
private ApplicationContext? _appContext;
16+
17+
[SetUp]
18+
public void Setup ()
19+
{
20+
// Ensure we have a WindowsFormsSynchronizationContext for the UI thread
21+
if (SynchronizationContext.Current == null)
22+
{
23+
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
24+
}
25+
26+
// Create an application context to ensure we have a proper UI context
27+
_appContext = new ApplicationContext();
28+
29+
// Must be created on STA thread with synchronization context
30+
_service = new LedIndicatorService();
31+
}
32+
33+
[TearDown]
34+
public void TearDown ()
35+
{
36+
_service?.Dispose();
37+
_appContext?.Dispose();
38+
}
39+
40+
[Test]
41+
public void Initialize_WithValidColor_Succeeds ()
42+
{
43+
// Act
44+
_service!.Initialize(Color.Blue);
45+
46+
// Assert - no exception thrown
47+
Assert.That(_service, Is.Not.Null);
48+
}
49+
50+
[Test]
51+
public void Initialize_CalledTwice_ThrowsException ()
52+
{
53+
// Arrange
54+
_service!.Initialize(Color.Blue);
55+
56+
// Act & Assert
57+
_ = Assert.Throws<InvalidOperationException>(() => _service.Initialize(Color.Red));
58+
}
59+
60+
[Test]
61+
public void GetIcon_WithZeroDiff_ReturnsOffLevelIcon ()
62+
{
63+
// Arrange
64+
_service!.Initialize(Color.Blue);
65+
var state = new LedState
66+
{
67+
IsDirty = false,
68+
TailState = TailFollowState.On,
69+
SyncState = TimeSyncState.NotSynced
70+
};
71+
72+
// Act
73+
var icon = _service.GetIcon(0, state);
74+
75+
// Assert
76+
Assert.That(icon, Is.Not.Null);
77+
Assert.That(icon.Width, Is.EqualTo(16));
78+
Assert.That(icon.Height, Is.EqualTo(16));
79+
}
80+
81+
[Test]
82+
public void GetIcon_WithMaxDiff_ReturnsHighestLevelIcon ()
83+
{
84+
// Arrange
85+
_service!.Initialize(Color.Blue);
86+
var state = new LedState
87+
{
88+
IsDirty = false,
89+
TailState = TailFollowState.On,
90+
SyncState = TimeSyncState.NotSynced
91+
};
92+
93+
// Act
94+
var icon = _service.GetIcon(100, state);
95+
96+
// Assert
97+
Assert.That(icon, Is.Not.Null);
98+
}
99+
100+
[Test]
101+
public void GetIcon_WithDirtyState_ReturnsDirtyIcon ()
102+
{
103+
// Arrange
104+
_service!.Initialize(Color.Blue);
105+
var state = new LedState
106+
{
107+
IsDirty = true,
108+
TailState = TailFollowState.On,
109+
SyncState = TimeSyncState.NotSynced
110+
};
111+
112+
// Act
113+
var icon = _service.GetIcon(50, state);
114+
115+
// Assert
116+
Assert.That(icon, Is.Not.Null);
117+
}
118+
119+
[Test]
120+
public void GetDeadIcon_ReturnsNonNullIcon ()
121+
{
122+
// Arrange
123+
_service!.Initialize(Color.Blue);
124+
125+
// Act
126+
var icon = _service.GetDeadIcon();
127+
128+
// Assert
129+
Assert.That(icon, Is.Not.Null);
130+
Assert.That(icon.Width, Is.EqualTo(16));
131+
}
132+
133+
[Test]
134+
public void StartStop_DoesNotThrowException ()
135+
{
136+
// Arrange
137+
_service!.Initialize(Color.Blue);
138+
139+
// Act
140+
_service.Start();
141+
Thread.Sleep(500); // Let timer tick a few times
142+
_service.Stop();
143+
144+
// Assert - no exception
145+
Assert.That(true, Is.True, "Service started and stopped without exceptions");
146+
}
147+
148+
[Test]
149+
public void RegisterWindow_AddsWindowToTracking ()
150+
{
151+
// Arrange
152+
_service!.Initialize(Color.Blue);
153+
154+
// We can't easily mock LogWindow since it has no parameterless constructor
155+
// and is internal, so we just test that registering null throws
156+
// Act & Assert
157+
_ = Assert.Throws<ArgumentNullException>(() => _service.RegisterWindow(null!));
158+
}
159+
160+
[Test]
161+
public void UpdateWindowActivity_WithoutRegisteringWindow_DoesNotThrow ()
162+
{
163+
// Arrange
164+
_service!.Initialize(Color.Blue);
165+
166+
// Act & Assert - Updating an unregistered window should not throw
167+
// (it just won't raise events)
168+
Assert.DoesNotThrow(() => _service.UpdateWindowActivity(null, 10));
169+
}
170+
171+
[Test]
172+
public void RegenerateIcons_WithNoWindows_DoesNotThrow ()
173+
{
174+
// Arrange
175+
_service!.Initialize(Color.Blue);
176+
177+
int eventCount = 0;
178+
_service.IconChanged += (s, e) => eventCount++;
179+
180+
// Act
181+
_service.RegenerateIcons(Color.Red);
182+
183+
// Assert - No windows registered, so no events should be raised
184+
Assert.That(eventCount, Is.EqualTo(0));
185+
}
186+
187+
[Test]
188+
public void Dispose_DisposesAllResources ()
189+
{
190+
// Arrange
191+
_service!.Initialize(Color.Blue);
192+
_service.Start();
193+
194+
// Act
195+
_service.Dispose();
196+
197+
// Assert - After dispose, trying to use the service will throw an exception
198+
var exception = Assert.Catch(() => _service.GetIcon(0, new LedState()));
199+
Assert.That(exception, Is.Not.Null, "Should throw an exception after disposal");
200+
}
201+
202+
[Test]
203+
public void GetIcon_WithoutInitialize_ThrowsException ()
204+
{
205+
// Arrange - don't initialize
206+
207+
// Act & Assert
208+
_ = Assert.Throws<InvalidOperationException>(() => _service!.GetIcon(0, new LedState()));
209+
}
210+
211+
[Test]
212+
public void Start_WithoutInitialize_ThrowsException ()
213+
{
214+
// Arrange - don't initialize
215+
216+
// Act & Assert
217+
_ = Assert.Throws<InvalidOperationException>(() => _service!.Start());
218+
}
219+
220+
[Test]
221+
public void RegisterWindow_WithNullWindow_ThrowsException ()
222+
{
223+
// Arrange
224+
_service!.Initialize(Color.Blue);
225+
226+
// Act & Assert
227+
_ = Assert.Throws<ArgumentNullException>(() => _service.RegisterWindow(null!));
228+
}
229+
230+
[Test]
231+
public void UnregisterWindow_WithNullWindow_DoesNotThrow ()
232+
{
233+
// Arrange
234+
_service!.Initialize(Color.Blue);
235+
236+
// Act & Assert - Unregistering null should not throw
237+
Assert.DoesNotThrow(() => _service.UnregisterWindow(null));
238+
}
239+
240+
[Test]
241+
public void CurrentTailColor_AfterInitialize_ReturnsInitializedColor ()
242+
{
243+
// Arrange
244+
var expectedColor = Color.FromArgb(50, 100, 200);
245+
_service!.Initialize(expectedColor);
246+
247+
// Act
248+
var actualColor = _service.CurrentTailColor;
249+
250+
// Assert
251+
Assert.That(actualColor, Is.EqualTo(expectedColor));
252+
}
253+
254+
[Test]
255+
public void CurrentTailColor_AfterRegenerateIcons_ReturnsNewColor ()
256+
{
257+
// Arrange
258+
_service!.Initialize(Color.Blue);
259+
var newColor = Color.FromArgb(255, 128, 0);
260+
261+
// Act
262+
_service.RegenerateIcons(newColor);
263+
264+
// Assert
265+
Assert.That(_service.CurrentTailColor, Is.EqualTo(newColor));
266+
}
267+
268+
[Test]
269+
public void CurrentTailColor_BeforeInitialize_ThrowsException ()
270+
{
271+
// Arrange - don't initialize
272+
273+
// Act & Assert
274+
_ = Assert.Throws<InvalidOperationException>(() => _ = _service!.CurrentTailColor);
275+
}
276+
277+
[Test]
278+
public void CurrentTailColor_AfterDispose_ThrowsObjectDisposedException ()
279+
{
280+
// Arrange
281+
_service!.Initialize(Color.Blue);
282+
_service.Dispose();
283+
284+
// Act & Assert
285+
Assert.Throws<ObjectDisposedException>(() => _ = _service.CurrentTailColor);
286+
}
287+
288+
[Test]
289+
public void GetIcon_WithSyncedState_ReturnsSyncedIcon ()
290+
{
291+
// Arrange
292+
_service!.Initialize(Color.Blue);
293+
var stateSynced = new LedState
294+
{
295+
IsDirty = false,
296+
TailState = TailFollowState.On,
297+
SyncState = TimeSyncState.Synced
298+
};
299+
var stateNotSynced = new LedState
300+
{
301+
IsDirty = false,
302+
TailState = TailFollowState.On,
303+
SyncState = TimeSyncState.NotSynced
304+
};
305+
306+
// Act
307+
var iconSynced = _service.GetIcon(50, stateSynced);
308+
var iconNotSynced = _service.GetIcon(50, stateNotSynced);
309+
310+
// Assert
311+
Assert.That(iconSynced, Is.Not.Null);
312+
Assert.That(iconNotSynced, Is.Not.Null);
313+
// The icons should be different (synced has blue indicator on left side)
314+
Assert.That(iconSynced, Is.Not.EqualTo(iconNotSynced));
315+
}
316+
}

0 commit comments

Comments
 (0)