Skip to content

Commit a593955

Browse files
add ability to react to inputs changing (closes #5)
1 parent 77e68c0 commit a593955

File tree

14 files changed

+364
-32
lines changed

14 files changed

+364
-32
lines changed

SimpleGPIO.Examples.Input/Program.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Threading;
32
using SimpleGPIO.Boards;
43

54
namespace SimpleGPIO.Examples.Input
@@ -8,12 +7,14 @@ public static class Program
87
{
98
public static void Main()
109
{
11-
var pi = new RaspberryPi();
12-
var button = pi.GPIO17;
13-
while (true)
10+
using (var pi = new RaspberryPi())
1411
{
15-
Console.WriteLine(button.Power);
16-
Thread.Sleep(50);
12+
var button = pi.Pin11;
13+
var led = pi.Pin16;
14+
button.OnPowerOn(() => led.Toggle());
15+
16+
Console.WriteLine("Press any key to exit...");
17+
Console.ReadKey();
1718
}
1819
}
1920
}

SimpleGPIO.Tests/GPIO/LinuxPinInterfaceTests.cs

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using NSubstitute;
3+
using NSubstitute.Extensions;
34
using SimpleGPIO.GPIO;
45
using SimpleGPIO.IO;
56
using SimpleGPIO.OS;
@@ -247,7 +248,7 @@ public void GettingPowerDoesNotGetCachedValueWhenInput()
247248
//arrange
248249
var fs = Substitute.For<IFileSystem>();
249250
fs.Read("/sys/class/gpio/gpio123/value").Returns("0");
250-
251+
251252
var pinInterface = new LinuxPinInterface(123, fs)
252253
{
253254
Direction = Direction.In
@@ -377,7 +378,7 @@ public void CanToggleForADuration()
377378
var pinInterface = new LinuxPinInterface(123, fs);
378379

379380
//act
380-
pinInterface.Toggle(100, TimeSpan.FromMilliseconds(100));
381+
pinInterface.Toggle(1000, TimeSpan.FromMilliseconds(10));
381382

382383
//assert
383384
fs.Received(20).Write("/sys/class/gpio/gpio123/value", Arg.Any<string>());
@@ -399,6 +400,117 @@ public void CanToggleForSetIterations()
399400
fs.Received(20).Write("/sys/class/gpio/gpio123/value", Arg.Any<string>());
400401
}
401402

403+
[Fact]
404+
public void OnPowerOnEnablesIfNotEnabled()
405+
{
406+
//arrange
407+
var fs = Substitute.For<IFileSystem>();
408+
fs.Read("/sys/class/gpio/gpio123/direction").Returns("in");
409+
var pinInterface = new LinuxPinInterface(123, fs);
410+
411+
//act
412+
pinInterface.OnPowerOn(null);
413+
414+
//assert
415+
fs.Received().Write("/sys/class/gpio/export", "123");
416+
}
417+
418+
[Fact]
419+
public void OnPowerOnPerformsAction()
420+
{
421+
//arrange
422+
var fs = Substitute.For<IFileSystem>();
423+
fs.Read("/sys/class/gpio/gpio123/direction").Returns("in");
424+
fs.When(f => f.Watch(Arg.Any<string>(), Arg.Any<Func<bool>>(), Arg.Any<Action>())).Do(c =>
425+
{
426+
if (c.Arg<Func<bool>>().Invoke()) c.Arg<Action>().Invoke();
427+
});
428+
var pinInterface = new LinuxPinInterface(123, fs)
429+
{
430+
Power = PowerValue.On
431+
};
432+
var called = false;
433+
434+
//act
435+
pinInterface.OnPowerOn(() => called = true);
436+
437+
//assert
438+
Assert.True(called);
439+
}
440+
441+
[Fact]
442+
public void OnPowerOffEnablesIfNotEnabled()
443+
{
444+
//arrange
445+
var fs = Substitute.For<IFileSystem>();
446+
fs.Read("/sys/class/gpio/gpio123/direction").Returns("in");
447+
var pinInterface = new LinuxPinInterface(123, fs);
448+
449+
//act
450+
pinInterface.OnPowerOff(null);
451+
452+
//assert
453+
fs.Received().Write("/sys/class/gpio/export", "123");
454+
}
455+
456+
[Fact]
457+
public void OnPowerOffPerformsAction()
458+
{
459+
//arrange
460+
var fs = Substitute.For<IFileSystem>();
461+
fs.Read("/sys/class/gpio/gpio123/direction").Returns("in");
462+
fs.When(f => f.Watch(Arg.Any<string>(), Arg.Any<Func<bool>>(), Arg.Any<Action>())).Do(c =>
463+
{
464+
if (c.Arg<Func<bool>>().Invoke()) c.Arg<Action>().Invoke();
465+
});
466+
var pinInterface = new LinuxPinInterface(123, fs)
467+
{
468+
Power = PowerValue.Off
469+
};
470+
var called = false;
471+
472+
//act
473+
pinInterface.OnPowerOff(() => called = true);
474+
475+
//assert
476+
Assert.True(called);
477+
}
478+
479+
[Fact]
480+
public void OnPowerChangeEnablesIfNotEnabled()
481+
{
482+
//arrange
483+
var fs = Substitute.For<IFileSystem>();
484+
fs.Read("/sys/class/gpio/gpio123/direction").Returns("in");
485+
var pinInterface = new LinuxPinInterface(123, fs);
486+
487+
//act
488+
pinInterface.OnPowerChange(null);
489+
490+
//assert
491+
fs.Received().Write("/sys/class/gpio/export", "123");
492+
}
493+
494+
[Fact]
495+
public void OnPowerChangePerformsAction()
496+
{
497+
//arrange
498+
var fs = Substitute.For<IFileSystem>();
499+
fs.Read("/sys/class/gpio/gpio123/direction").Returns("in");
500+
fs.When(f => f.Watch(Arg.Any<string>(), Arg.Any<Func<bool>>(), Arg.Any<Action>())).Do(c =>
501+
{
502+
if (c.Arg<Func<bool>>().Invoke()) c.Arg<Action>().Invoke();
503+
});
504+
var pinInterface = new LinuxPinInterface(123, fs);
505+
var called = false;
506+
507+
//act
508+
pinInterface.OnPowerChange(() => called = true);
509+
510+
//assert
511+
Assert.True(called);
512+
}
513+
402514
[Fact]
403515
public void DisablesOnDispose()
404516
{

SimpleGPIO.Tests/GPIO/StubPinInterface.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,17 @@ public class StubPinInterface : IPinInterface
2020

2121
public void Enable() => throw new NotImplementedException();
2222
public void Disable() => throw new NotImplementedException();
23+
2324
public void TurnOn() => throw new NotImplementedException();
2425
public void TurnOff() => throw new NotImplementedException();
26+
2527
public void Toggle() => throw new NotImplementedException();
2628
public void Toggle(double hz, TimeSpan duration) => throw new NotImplementedException();
2729
public void Toggle(double hz, ulong iterations) => throw new NotImplementedException();
30+
31+
public void OnPowerOn(Action action) => throw new NotImplementedException();
32+
public void OnPowerOff(Action action) => throw new NotImplementedException();
33+
public void OnPowerChange(Action action) => throw new NotImplementedException();
2834

2935
public void Dispose()
3036
{

SimpleGPIO.Tests/IO/IOHelpersTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using SimpleGPIO.IO;
1+
using System.ComponentModel;
2+
using SimpleGPIO.IO;
23
using Xunit;
34

45
namespace SimpleGPIO.Tests.IO

SimpleGPIO.Tests/OS/FileSystemTests.cs

Lines changed: 79 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ public class FileSystemTests
1212
public void ReadTrimsBlankLines()
1313
{
1414
//arrange
15-
var fs = new FileSystem();
15+
var fileInfo = Substitute.For<IFileInfoWrapper>();
16+
var nfw = Substitute.For<Func<IFileSystem, string, Func<bool>, Action, IFileWatcher>>();
17+
var fs = new FileSystem(path => fileInfo, nfw);
1618

1719
//act
1820
var data = fs.Read("readme.txt");
@@ -25,7 +27,9 @@ public void ReadTrimsBlankLines()
2527
public void WriteTrimsBlankLines()
2628
{
2729
//arrange
28-
var fs = new FileSystem();
30+
var fileInfo = Substitute.For<IFileInfoWrapper>();
31+
var nfw = Substitute.For<Func<IFileSystem, string, Func<bool>, Action, IFileWatcher>>();
32+
var fs = new FileSystem(path => fileInfo, nfw);
2933

3034
//act
3135
fs.Write("writeme.txt", "456\n");
@@ -38,7 +42,9 @@ public void WriteTrimsBlankLines()
3842
public void ExistsWhenDirectoryExists()
3943
{
4044
//arrange
41-
var fs = new FileSystem();
45+
var fileInfo = Substitute.For<IFileInfoWrapper>();
46+
var nfw = Substitute.For<Func<IFileSystem, string, Func<bool>, Action, IFileWatcher>>();
47+
var fs = new FileSystem(path => fileInfo, nfw);
4248

4349
//act
4450
var exists = fs.Exists(Directory.GetCurrentDirectory());
@@ -51,7 +57,9 @@ public void ExistsWhenDirectoryExists()
5157
public void ExistsWhenFileExists()
5258
{
5359
//arrange
54-
var fs = new FileSystem();
60+
var fileInfo = Substitute.For<IFileInfoWrapper>();
61+
var nfw = Substitute.For<Func<IFileSystem, string, Func<bool>, Action, IFileWatcher>>();
62+
var fs = new FileSystem(path => fileInfo, nfw);
5563

5664
//act
5765
var exists = fs.Exists("readme.txt");
@@ -64,7 +72,9 @@ public void ExistsWhenFileExists()
6472
public void DoesNotExistWhenNeitherDirectoryNorFileExists()
6573
{
6674
//arrange
67-
var fs = new FileSystem();
75+
var fileInfo = Substitute.For<IFileInfoWrapper>();
76+
var nfw = Substitute.For<Func<IFileSystem, string, Func<bool>, Action, IFileWatcher>>();
77+
var fs = new FileSystem(path => fileInfo, nfw);
6878

6979
//act
7080
var exists = fs.Exists("other");
@@ -77,10 +87,13 @@ public void DoesNotExistWhenNeitherDirectoryNorFileExists()
7787
public void WaitForPassesWhenFileExists()
7888
{
7989
//arrange
80-
var fs = new FileSystem();
90+
var fileInfo = Substitute.For<IFileInfoWrapper>();
91+
fileInfo.Exists.Returns(true);
92+
var nfw = Substitute.For<Func<IFileSystem, string, Func<bool>, Action, IFileWatcher>>();
93+
var fs = new FileSystem(path => fileInfo, nfw);
8194

8295
//act
83-
fs.WaitFor("readme.txt", TimeSpan.FromMilliseconds(1));
96+
fs.WaitFor("readme.txt", TimeSpan.FromMilliseconds(10));
8497

8598
//assert
8699
Assert.True(true);
@@ -90,10 +103,12 @@ public void WaitForPassesWhenFileExists()
90103
public void WaitForFailsWhenFileDoesNotExist()
91104
{
92105
//arrange
93-
var fs = new FileSystem();
106+
var fileInfo = Substitute.For<IFileInfoWrapper>();
107+
var nfw = Substitute.For<Func<IFileSystem, string, Func<bool>, Action, IFileWatcher>>();
108+
var fs = new FileSystem(path => fileInfo, nfw);
94109

95110
//act
96-
var wait = new Action(() => fs.WaitFor("other", TimeSpan.FromMilliseconds(1)));
111+
var wait = new Action(() => fs.WaitFor("other", TimeSpan.FromMilliseconds(10)));
97112

98113
//assert
99114
Assert.Throws<TimeoutException>(wait);
@@ -103,10 +118,14 @@ public void WaitForFailsWhenFileDoesNotExist()
103118
public void WaitForWriteablePassesWhenFileIsWriteable()
104119
{
105120
//arrange
106-
var fs = new FileSystem();
121+
var fileInfo = Substitute.For<IFileInfoWrapper>();
122+
fileInfo.Exists.Returns(true);
123+
fileInfo.IsReadOnly.Returns(false);
124+
var nfw = Substitute.For<Func<IFileSystem, string, Func<bool>, Action, IFileWatcher>>();
125+
var fs = new FileSystem(path => fileInfo, nfw);
107126

108127
//act
109-
fs.WaitForWriteable("readme.txt", TimeSpan.FromMilliseconds(1));
128+
fs.WaitForWriteable("readme.txt", TimeSpan.FromMilliseconds(10));
110129

111130
//assert
112131
Assert.True(true);
@@ -118,13 +137,60 @@ public void WaitForWriteableFailsWhenFileIsReadOnly()
118137
//arrange
119138
var fileInfo = Substitute.For<IFileInfoWrapper>();
120139
fileInfo.IsReadOnly.Returns(true);
121-
var fs = new FileSystem(path => fileInfo);
140+
var nfw = Substitute.For<IFileWatcher>();
141+
var fs = new FileSystem(path => fileInfo, (fileSystem, path, predicate, action) => nfw);
122142

123143
//act
124-
var wait = new Action(() => fs.WaitForWriteable("readonly.txt", TimeSpan.FromMilliseconds(1)));
144+
var wait = new Action(() => fs.WaitForWriteable("readonly.txt", TimeSpan.FromMilliseconds(10)));
125145

126146
//assert
127147
Assert.Throws<TimeoutException>(wait);
128148
}
149+
150+
[Fact]
151+
public void WatchStartsWatcherWhenActionIsNotNull()
152+
{
153+
//arrange
154+
var fileInfo = Substitute.For<IFileInfoWrapper>();
155+
var nfw = Substitute.For<IFileWatcher>();
156+
var fs = new FileSystem(path => fileInfo, (fileSystem, path, predicate, action) => nfw);
157+
158+
//act
159+
fs.Watch("", () => true, () => { });
160+
161+
//assert
162+
nfw.Received().Watch();
163+
}
164+
165+
[Fact]
166+
public void WatchStopsWatcherWhenActionIsNull()
167+
{
168+
//arrange
169+
var fileInfo = Substitute.For<IFileInfoWrapper>();
170+
var nfw = Substitute.For<IFileWatcher>();
171+
var fs = new FileSystem(path => fileInfo, (fileSystem, path, predicate, action) => nfw);
172+
173+
//act
174+
fs.Watch("", () => true, null);
175+
176+
//assert
177+
nfw.Received().Stop();
178+
}
179+
180+
[Fact]
181+
public void WatchUsesCachedWatcherWhenExists()
182+
{
183+
//arrange
184+
var fileInfo = Substitute.For<IFileInfoWrapper>();
185+
var nfw = Substitute.For<Func<IFileSystem, string, Func<bool>, Action, IFileWatcher>>();
186+
var fs = new FileSystem(path => fileInfo, nfw);
187+
fs.Watch("", () => true, () => { });
188+
189+
//act
190+
fs.Watch("", () => true, () => { });
191+
192+
//assert
193+
nfw.Received(1).Invoke(Arg.Any<IFileSystem>(), Arg.Any<string>(), Arg.Any<Func<bool>>(), Arg.Any<Action>());
194+
}
129195
}
130196
}

0 commit comments

Comments
 (0)