From d98af659020b8610bc64db2809a38a3d4e90caf7 Mon Sep 17 00:00:00 2001 From: mgreiff <marcusgreiff.93@hotmail.com> Date: Thu, 5 Oct 2017 22:14:51 +0200 Subject: [PATCH] Created tests for the SysLed item and refactor code --- src/BeagleBone/GPIO.jl | 132 +++++++++------------- src/BeagleBone/IO_Object.jl | 48 +++++++- src/BeagleBone/SysLED.jl | 51 +++++++-- test/BeagleBone/GPIO_test.jl | 194 ++++++++++++++++---------------- test/BeagleBone/PWM_test.jl | 13 ++- test/BeagleBone/SYS_LED_test.jl | 74 ++++++++++++ test/BeagleBone/Sys_LED_test.jl | 39 ------- test/runtests.jl | 3 +- 8 files changed, 326 insertions(+), 228 deletions(-) create mode 100644 test/BeagleBone/SYS_LED_test.jl delete mode 100644 test/BeagleBone/Sys_LED_test.jl diff --git a/src/BeagleBone/GPIO.jl b/src/BeagleBone/GPIO.jl index 3cca111..6537cf8 100644 --- a/src/BeagleBone/GPIO.jl +++ b/src/BeagleBone/GPIO.jl @@ -1,99 +1,42 @@ """ Lowest form of communication with the GPIO pins. The available pins are -listed in the "channel" parameter, and appear as directories in /sys/class/gpio. - -Any of these GPIOs can be run in various ways, with any operation specified by an -Int32 value. +listed in the "channel" parameter, and appear as directories in /sys/class/gpio +after being exported. For instance, to setup a GPIO on "gpio112", configure it as an output pin and set it to high, the following code would be used. -gpio = GPIO(1) -write!(gpio, (2,"out")) -write!(gpio, (1, "1")) + gpio = GPIO(1) + write!(gpio, (2,"out")) + write!(gpio, (1, "1")) The operation of reading the current output value of the GPIO is done by -read(gpio, 1) + read(gpio, 1) +See the test/BeagleBone/GPIO_test.jl for more examples. """ + type GPIO <: IO_Object i::Int32 basedir::String filestreams::Array{IOStream,1} function GPIO(i::Int32) - (i <= 0 || i > length(channels)) && error("Invalid GPIO index: $i") + (i <= 0 || i > length(gpio_channels)) && error("Invalid GPIO index: $i") # If the tests re being run, a dummy filesystem is created - if isdefined(:RUNNING_TESTS) - # Export a dummy file system for testing - basedir = "$(pwd())/testfilesystem/gpio" - try - println("$(basedir)/$(channels[i])") - mkpath("$(basedir)/$(channels[i])") - catch - error("Could not export the GPIO device for channel $(channels[i]) for testing as the directory $(basedir)/$(channels[i]) already exists.") - end - try - f = open("$(basedir)/$(channels[i])/value", "w"); write(f,"0"); close(f); - f = open("$(basedir)/$(channels[i])/direction", "w"); write(f,"0"); close(f); - f = open("$(basedir)/$(channels[i])/edge", "w"); write(f,"0"); close(f); - catch - error("Could not open the requested GPIO testfiles for channel $(channels[i]).") - end - else - basedir = "/sys/class/gpio" - # TODO Export for real-time use - end + basedir = export_gpio(i) # setup IOstreams - value_filestream = open("$(basedir)/$(channels[i])/value","r+") - direction_filestream = open("$(basedir)/$(channels[i])/direction","r+") - edge_filestream = open("$(basedir)/$(channels[i])/edge","r") + value_filestream = open("$(basedir)/$(gpio_channels[i])/value","r+") + direction_filestream = open("$(basedir)/$(gpio_channels[i])/direction","r+") + edge_filestream = open("$(basedir)/$(gpio_channels[i])/edge","r") # Initialize object return new(i, basedir, [value_filestream, direction_filestream, edge_filestream]) end end -writeOperations = [["1", "0"],["in", "out"],["none","rising","falling","both"]] - -channels =[ -"gpio112" -"gpio114" -"gpio115" -"gpio116" -"gpio14" -"gpio15" -"gpio2" -"gpio20" -"gpio22" -"gpio23" -"gpio26" -"gpio27" -"gpio3" -"gpio30" -"gpio31" -"gpio4" -"gpio44" -"gpio45" -"gpio46" -"gpio47" -"gpio48" -"gpio49" -"gpio5" -"gpio50" -"gpio51" -"gpio60" -"gpio61" -"gpio65" -"gpio66" -"gpio67" -"gpio68" -"gpio69" -"gpio7" -] - """ write!(gpio::GPIO, args::Tuple{Int32,String}, debug::Bool=false) Writes an entry to an operation on a GPIO, of the form args = (operation, entry). @@ -101,10 +44,12 @@ Writes an entry to an operation on a GPIO, of the form args = (operation, entry) function write!(gpio::GPIO, args::Tuple{Int32,String}, debug::Bool=false) debug && return operation, entry = args[1], args[2] - (operation < 1 || operation > length(gpio.filestreams)) && error("Invalid GPIO operation: $operation") - if entry in writeOperations[operation] - write(gpio.filestreams[operation], entry) + # Only filestreams 1 and 2 are writable + operation ∉ [1,2] && error("Invalid GPIO operation $operation for writing") + if entry in gpio_operations[operation] seekstart(gpio.filestreams[operation]) + write(gpio.filestreams[operation], "$entry\n") + flush(gpio.filestreams[operation]) else error("Invalid entry for GPIO operation $(operation): $(entry)") end @@ -116,9 +61,10 @@ Reads the current value from an operation on a GPIO. """ function read(gpio::GPIO, operation::Int32, debug::Bool=false) debug && return - (operation < 1 || operation > length(gpio.filestreams)) && error("Invalid GPIO operation: $operation") - l = readline(gpio.filestreams[operation]) + # Filestreams 1, 2 and 3 are readable + operation ∉ [1,2,3] && error("Invalid GPIO operation: $operation for reading") seekstart(gpio.filestreams[operation]) + l = readline(gpio.filestreams[operation]) return l end @@ -139,16 +85,44 @@ function teardown(gpio::GPIO, debug::Bool=false) # Remove the dummy file system for testing basedir = "$(pwd())/testfilesystem/gpio" try - rm("$(gpio.basedir)/$(channels[gpio.i])"; recursive=true) + rm("$(gpio.basedir)/$(gpio_channels[gpio.i])"; recursive=true) catch - error("Could not remove the requested GPIO testfiles for channel $(channels[i]).") + error("Could not remove the requested GPIO testfiles for channel $(gpio_channels[i]).") end else # Remove the file system filename = "/sys/class/gpio/unexport" - #TODO Verify if this is the correct command to send to unexport... - write(filename, channels[gpio.i]) + #TODO Verify if this is the correct command to send to unexport + write(filename, gpio_channels[gpio.i]) + end +end + +""" + export_gpio(i::Int32, debug::Bool=false) +Export the GPIO file system, either for real-time or testing usecases. +""" +function export_gpio(i::Int32) + if isdefined(:RUNNING_TESTS) + # Export a dummy file system for testing + basedir = "$(pwd())/testfilesystem/gpio" + try + #println("$(basedir)/$(gpio_channels[i])") + mkpath("$(basedir)/$(gpio_channels[i])") + catch + error("Could not export the GPIO device for channel $(gpio_channels[i]) for testing as the directory $(basedir)/$(gpio_channels[i]) already exists.") + end + try + f = open("$(basedir)/$(gpio_channels[i])/value", "w"); write(f,"0"); close(f); + f = open("$(basedir)/$(gpio_channels[i])/direction", "w"); write(f,"0"); close(f); + f = open("$(basedir)/$(gpio_channels[i])/edge", "w"); write(f,"0"); close(f); + catch + error("Could not open the requested GPIO testfiles for channel $(gpio_channels[i]).") + end + else + basedir = "/sys/class/gpio" + # TODO Export for real-time use end + return basedir end """ diff --git a/src/BeagleBone/IO_Object.jl b/src/BeagleBone/IO_Object.jl index bd99d85..4080669 100644 --- a/src/BeagleBone/IO_Object.jl +++ b/src/BeagleBone/IO_Object.jl @@ -1,2 +1,48 @@ -"""Define abstract type for pins/LEDS on the BeagleBone""" +""" +Define abstract type for pins/LEDS on the BeagleBone +""" abstract type IO_Object end + +""" +GPIO interfaces +""" +const gpio_operations = [ + ["1", "0"], + ["in", "out"], + ["none","rising","falling","both"] +] +const gpio_channels =[ + "gpio112" + "gpio114" + "gpio115" + "gpio116" + "gpio14" + "gpio15" + "gpio2" + "gpio20" + "gpio22" + "gpio23" + "gpio26" + "gpio27" + "gpio3" + "gpio30" + "gpio31" + "gpio4" + "gpio44" + "gpio45" + "gpio46" + "gpio47" + "gpio48" + "gpio49" + "gpio5" + "gpio50" + "gpio51" + "gpio60" + "gpio61" + "gpio65" + "gpio66" + "gpio67" + "gpio68" + "gpio69" + "gpio7" +] diff --git a/src/BeagleBone/SysLED.jl b/src/BeagleBone/SysLED.jl index 3418dea..d5d26c1 100644 --- a/src/BeagleBone/SysLED.jl +++ b/src/BeagleBone/SysLED.jl @@ -5,12 +5,17 @@ i ∈ [1,2,3,4]. """ type SysLED <: IO_Object i::Int32 + basedir::String filestream::IOStream function SysLED(i::Int32) i ∉ [1,2,3,4] && error("Invalid SysLED index: $i") - #Note, in the future we should interface to config and retrieve IOStream from there - brightness_filestream = open("/sys/class/leds/beaglebone:green:usr$(i-1)/brightness","r+") - return new(i, brightness_filestream) + + # Export system for testing + basedir = export_led(i) + + # open filestream + brightness_filestream = open("$(basedir)/beaglebone:green:usr$(i-1)/brightness","r+") + return new(i, basedir, brightness_filestream) end end @@ -20,9 +25,10 @@ Turns the LED 'SysLed' on/off for val = true/false respectively. """ function write!(led::SysLED, entry::String, debug::Bool=false) debug && return - entry ∉ ["0", "1"] && error("Invalid SysLED entry $(entry), valid options are 0 and 1 (string)") - write(led.filestream, entry) + entry ∉ ["0", "1"] && error("Invalid SysLED entry $(entry), valid options are 0 and 1 ::String") seekstart(led.filestream) + write(led.filestream, "$entry\n") + flush(led.filestream) end """ @@ -31,10 +37,8 @@ Reads the current brightness value from the LED 'SysLED'. """ function read(led::SysLED, debug::Bool=false) debug && return - l = read(filestream, Char) - (l != '1' && l != '0') && error("Invalid value \"$l\" read from SysLed") seekstart(led.filestream) - return l + return readline(led.filestream) end """ @@ -44,4 +48,35 @@ Closes all open filestreams for the SysLED 'led'. function teardown(led::SysLED, debug::Bool=false) debug && return close(led.filestream) + + if isdefined(:RUNNING_TESTS) + # Remove the dummy file system for testing + try + #println("$(led.basedir)/beaglebone:green:usr$(led.i-1)") + rm("$(led.basedir)/beaglebone:green:usr$(led.i-1)"; recursive=true) + catch + error("Could not remove the requested LED testfiles for channel beaglebone:green:usr$(led.i-1).") + end + end +end + +function export_led(i::Int32) + if isdefined(:RUNNING_TESTS) + # Export a dummy file system for testing + basedir = "$(pwd())/testfilesystem/leds" + try + #println("$(basedir)/beaglebone:green:usr$(i-1)") + mkpath("$(basedir)/beaglebone:green:usr$(i-1)") + catch + error("Could not export the LED device for beaglebone:green:usr$(i-1)) for testing as the directory $(basedir)/beaglebone:green:usr$(i-1) already exists.") + end + try + f = open("$(basedir)/beaglebone:green:usr$(i-1)/brightness", "w"); write(f,"0"); close(f); + catch + error("Could not open the requested LED testfiles for beaglebone:green:usr$(i-1)/brightness.") + end + else + basedir = "/sys/class/leds" + end + return basedir end diff --git a/test/BeagleBone/GPIO_test.jl b/test/BeagleBone/GPIO_test.jl index bbbda3c..bbe9dae 100644 --- a/test/BeagleBone/GPIO_test.jl +++ b/test/BeagleBone/GPIO_test.jl @@ -1,115 +1,121 @@ using LabConnections.BeagleBone -import LabConnections.BeagleBone: initdev, listdev, closedev, printdev, write!, read! +import LabConnections.BeagleBone: initdev, listdev, closedev, printdev, write!, read, gpio_channels using Base.Test -@testset "GPIO Inialization and termination" begin - # Initialize three devices - initdev("gpio", 1) - @test sum(listdev()) == 1 +@testset "GPIO tests" begin - initdev("gpio", 3) - @test sum(listdev()) == 2 + @testset "Inialization/Termination" begin + # Initialize three devices + initdev("gpio", 1) + @test sum(listdev()) == 1 - initdev("gpio", 5) - @test sum(listdev()) == 3 + initdev("gpio", 3) + @test sum(listdev()) == 2 - #printdev("gpio", 3) + initdev("gpio", 5) + @test sum(listdev()) == 3 - # Attempt to initialize a device which has already been initialized - @test_throws ErrorException initdev("gpio", 1) - @test_throws ErrorException initdev("gpio", 3) - @test_throws ErrorException initdev("gpio", 5) + #printdev("gpio", 3) - # Attempt to initialize a device with a very high index (no matching channel) - @test_throws ErrorException initdev("gpio", 1000) + # Attempt to initialize a device which has already been initialized + @test_throws ErrorException initdev("gpio", 1) + @test_throws ErrorException initdev("gpio", 3) + @test_throws ErrorException initdev("gpio", 5) - # Attempt to remove devices which have not been initialized - @test_throws ErrorException closedev("gpio", 2) - @test_throws ErrorException closedev("gpio", 4) - @test_throws ErrorException closedev("gpio", 6) + # Attempt to initialize a device with a very high index (no matching channel) + @test_throws ErrorException initdev("gpio", 1000) - #printdev("gpio", 3) + # Attempt to remove devices which have not been initialized + @test_throws ErrorException closedev("gpio", 2) + @test_throws ErrorException closedev("gpio", 4) + @test_throws ErrorException closedev("gpio", 6) - # Remove devices from TOC - closedev("gpio", 1) - @test sum(listdev()) == 2 + #printdev("gpio", 3) - closedev("gpio", 3) - @test sum(listdev()) == 1 + # Remove devices from TOC + closedev("gpio", 1) + @test sum(listdev()) == 2 - closedev("gpio", 5) - @test sum(listdev()) == 0 + closedev("gpio", 3) + @test sum(listdev()) == 1 -end - -@testset "GPIO Read and write" begin - - # Fixture - device = initdev("gpio", 1) - - # Test that an exception is thrown when an invalid operation is given - # supported operations are 1,2,3 - @test_throws ErrorException write!(device, (0, "something")) - @test_throws ErrorException write!(device, (4, "something")) - - # Test that exceptions are thrown for each individual operation - @test_throws ErrorException write!(device, (1, "bad_entry")) - @test_throws ErrorException write!(device, (2, "bad_entry")) - @test_throws ErrorException write!(device, (3, "bad_entry")) - - # Test operation 1 - sleep(0.001);write!(device, (1, "1")) - sleep(0.001);@test read(device, 1) == "1" - sleep(0.001);write!(device, (1, "0")) - sleep(0.001);@test read(device, 1) == "0" - sleep(0.001);write!(device, (1, "1")) - sleep(0.001);@test read(device, 1) == "1" - sleep(0.001);write!(device, (1, "0")) - sleep(0.001);@test read(device, 1) == "0" - - if false - # TODO fix write function to clear files before writing otherwise writing "xy" when a file containt "abcd" results in "xycd" and a system failure - # Test operation 2 - sleep(0.001);write!(device, (2, "in")) - sleep(0.001);@test read(device, 2) == "in" - sleep(0.001);write!(device, (2, "out")) - sleep(0.001);@test read(device, 2) == "out" - sleep(0.001);write!(device, (2, "in")) - sleep(0.001);@test read(device, 2) == "in" - sleep(0.001);write!(device, (2, "out")) - sleep(0.001);@test read(device, 2) == "out" + closedev("gpio", 5) + @test sum(listdev()) == 0 - # Test operation 3 - sleep(0.001);write!(device, (3, "none")) - sleep(0.001);@test read(device, 3) == "none" - sleep(0.001);write!(device, (3, "rising")) - sleep(0.001);@test read(device, 3) == "rising" - sleep(0.001);write!(device, (3, "falling")) - sleep(0.001);@test read(device, 3) == "falling" - sleep(0.001);write!(device, (3, "both")) - sleep(0.001);@test read(device, 3) == "both" end - # Close Gpio - closedev("gpio", 1) - end - -@testset "IO Communication" begin - # Instanciate all possible leds and perform 10 read/write commands - # with the set high/low operation ("value") + @testset "Read/Write" begin + + # Fixture + device = initdev("gpio", 1) + + # Test that an exception is thrown when an invalid operation is given + # supported operations are 1,2,3 + @test_throws ErrorException write!(device, (0, "something")) + @test_throws ErrorException write!(device, (4, "something")) + + # Test that exceptions are thrown for each individual operation + @test_throws ErrorException write!(device, (1, "bad_entry")) + @test_throws ErrorException write!(device, (2, "bad_entry")) + @test_throws ErrorException write!(device, (3, "bad_entry")) + + # Test operation 1 + write!(device, (1, "1")) + @test read(device, 1) == "1" + write!(device, (1, "0")) + @test read(device, 1) == "0" + write!(device, (1, "1")) + @test read(device, 1) == "1" + write!(device, (1, "0")) + @test read(device, 1) == "0" + + write!(device, (2, "in")) + @test read(device, 2) == "in" + write!(device, (2, "out")) + @test read(device, 2) == "out" + write!(device, (2, "in")) + @test read(device, 2) == "in" + write!(device, (2, "out")) + @test read(device, 2) == "out" - # Configure the GPIO for output usage - device = initdev("gpio", 1) - - # Operation 2 -> in/out, set out - write!(device, (2, "out")) - - # Configure the GPIO for output usage - for i = 1:10 - state = "$(i%2)" - sleep(0.10);write!(device, (1, state)) - sleep(0.001);@test read(device, 1) == state + # Test operation 3 + @test_throws ErrorException write!(device, (3, "none")) + @test_throws ErrorException write!(device, (3, "rising")) + @test_throws ErrorException write!(device, (3, "falling")) + @test_throws ErrorException write!(device, (3, "both")) + + # Close Gpio + closedev("gpio", 1) + end + + @testset "All channels" begin + # Instanciate all possible leds and perform 10 read/write commands + # with the set high/low operation ("value") + + # Configure the GPIO for output usage + devices = [] + for ii = 1:length(gpio_channels) + device = initdev("gpio", ii) + # Operation 2 -> in/out, set out + write!(device, (2, "out")) + # Append to list + append!(devices, [device]) + end + + # Sets all available GPIO pins high/low in an alternating pattern + for i = 1:10 + for ii = 1:length(gpio_channels) + state = "$(i%2)" + write!(devices[ii], (1, state)) + @test read(devices[ii], 1) == state + end + sleep(0.10) + end + + # Closes all devices + for ii = 1:length(gpio_channels) + closedev("gpio", ii) + end end - closedev("gpio", 1) end diff --git a/test/BeagleBone/PWM_test.jl b/test/BeagleBone/PWM_test.jl index ea50d45..f456186 100644 --- a/test/BeagleBone/PWM_test.jl +++ b/test/BeagleBone/PWM_test.jl @@ -1,10 +1,11 @@ using Base.Test -# TODO write tests for PWM -@testset "PWM Inialization and termination" begin - @test 1 == 1 -end +@testset "PWM tests" begin + @testset "Inialization and termination" begin + @test 1 == 1 + end -@testset "PWM Read and write" begin - @test 1 == 1 + @testset "Read and write" begin + @test 1 == 1 + end end diff --git a/test/BeagleBone/SYS_LED_test.jl b/test/BeagleBone/SYS_LED_test.jl new file mode 100644 index 0000000..6109ec7 --- /dev/null +++ b/test/BeagleBone/SYS_LED_test.jl @@ -0,0 +1,74 @@ +using LabConnections.BeagleBone +import LabConnections.BeagleBone: getdev, write!, closedev, read, initdev, printdev, listdev + +using Base.Test + +@testset "SYS LED Tests" begin + + @testset "Inialization/Termination" begin + # Initialize three devices + initdev("sysled", 1) + @test sum(listdev()) == 1 + + initdev("sysled", 3) + @test sum(listdev()) == 2 + + # Attempt to initialize a device which has already been initialized + @test_throws ErrorException initdev("sysled", 1) + @test_throws ErrorException initdev("sysled", 3) + + # Attempt to initialize a device with a very high index (no matching channel) + @test_throws ErrorException initdev("sysled", 1000) + + # Attempt to remove devices which have not been initialized + @test_throws ErrorException closedev("sysled", 2) + @test_throws ErrorException closedev("sysled", 4) + + # Remove devices from TOC + closedev("sysled", 1) + @test sum(listdev()) == 1 + + closedev("sysled", 3) + @test sum(listdev()) == 0 + end + + @testset "Error Handling" begin + + device = initdev("sysled", 1) + + # Test that an exception is thrown when a faulty ID is given + @test_throws ErrorException write!(device, "bad_entry") + + # Close device + closedev("sysled", 1) + end + + @testset "IO Communication" begin + # Instanciate all possible leds and perform 10 read/write commands + println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") + device1 = initdev("sysled", 1) + device2 = initdev("sysled", 2) + device3 = initdev("sysled", 3) + device4 = initdev("sysled", 4) + + for i = 1:10 + stateA = "$(i%2)" + stateB = "$((i+1)%2)" + write!(device1, stateA) + @test read(device1) == stateA + write!(device2, stateB) + @test read(device2) == stateB + write!(device3, stateA) + @test read(device3) == stateA + write!(device4, stateB) + @test read(device4) == stateB + sleep(0.1) + end + + println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") + closedev("sysled", 1) + closedev("sysled", 2) + closedev("sysled", 3) + closedev("sysled", 4) + end +end diff --git a/test/BeagleBone/Sys_LED_test.jl b/test/BeagleBone/Sys_LED_test.jl deleted file mode 100644 index c18b85f..0000000 --- a/test/BeagleBone/Sys_LED_test.jl +++ /dev/null @@ -1,39 +0,0 @@ -using LabConnections.BeagleBone -import LabConnections.BeagleBone: getdev, write!, closedev, read, initdev - -using Base.Test - -@testset "SYS LED Tests" begin - @testset "Error Handling" begin - - device = initdev("sysled", 1) - - # Test that an exception is thrown when a faulty ID is given - @test_throws ErrorException write!(device, "bad_entry") - - # Close device - closedev("sysled", 1) - end - - @testset "IO Communication" begin - # Instanciate all possible leds and perform 10 read/write commands - device1 = initdev("sysled", 1) - device2 = initdev("sysled", 2) - device3 = initdev("sysled", 3) - device4 = initdev("sysled", 4) - - for i = 1:10 - state = - write!(device1, "$(i%2)") - write!(device2, "$((i+1)%2)") - write!(device3, "$(i%2)") - write!(device4, "$((i+1)%2)") - sleep(0.1) - end - - closedev("sysled", 1) - closedev("sysled", 2) - closedev("sysled", 3) - closedev("sysled", 4) - end -end diff --git a/test/runtests.jl b/test/runtests.jl index 53aaeb3..e834a19 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,7 +2,8 @@ using Base.Test # This flag is enabled if a dummy filesystem should be used for testing (for online testing) # disabling the flag allows the BBB to be run in the loop, in this case blinking LEDS -RUNNING_TESTS = true +#RUNNING_TESTS = true +include("BeagleBone/SYS_LED_test.jl") include("BeagleBone/GPIO_test.jl") include("BeagleBone/PWM_test.jl") -- GitLab