From 161a5b47077b3e3dfb9652cca55c5431a127735c Mon Sep 17 00:00:00 2001 From: SquidDev Date: Fri, 15 May 2020 10:03:47 +0100 Subject: [PATCH] Document and test the redstone library The tests may be a little agressive, but I wanted some sanity checks for the 1.15 API rewrite. --- doc/stub/redstone.lua | 114 +++++++++++++++++- illuaminate.sexp | 5 +- .../computercraft/core/apis/RedstoneAPI.java | 24 ++-- .../rom/modules/main/cc/shell/completion.lua | 12 +- .../test-rom/spec/apis/redstone_spec.lua | 92 ++++++++++++++ 5 files changed, 221 insertions(+), 26 deletions(-) create mode 100644 src/test/resources/test-rom/spec/apis/redstone_spec.lua diff --git a/doc/stub/redstone.lua b/doc/stub/redstone.lua index 217d41766f..d19a804878 100644 --- a/doc/stub/redstone.lua +++ b/doc/stub/redstone.lua @@ -1,14 +1,120 @@ +--[[- Interact with redstone attached to this computer. + +The @{redstone} library exposes three "types" of redstone control: + - Binary input/output (@{setOutput}/@{getInput}): These simply check if a + redstone wire has any input or output. A signal strength of 1 and 15 are + treated the same. + - Analogue input/output (@{setAnalogueOutput}/@{getAnalogueInput}): These + work with the actual signal strength of the redstone wired, from 0 to 15. + - Bundled cables (@{setBundledOutput}/@{getBundledInput}): These interact with + "bundled" cables, such as those from Project:Red. These allow you to send + 16 separate on/off signals. Each channel corresponds to a colour, with the + first being @{colors.white} and the last @{colors.black}. + +Whenever a redstone input changes, a `redstone` event will be fired. This may +be used in or + +This module may also be referred to as `rs`. For example, one may call +`rs.getSides()` instead of @{redstone.getSides}. + +@module redstone +@usage Toggle the redstone signal above the computer every 0.5 seconds. + + while true do + redstone.setOutput("top", not redstone.getOutput("top")) + sleep(0.5) + end +@usage Mimic a redstone comparator in [subtraction mode][comparator]. + + while true do + local rear = rs.getAnalogueInput("back") + local sides = math.max(rs.getAnalogueInput("left"), rs.getAnalogueInput("right")) + rs.setAnalogueOutput("front", math.max(rear - sides, 0)) + + os.pullEvent("redstone") -- Wait for a change to inputs. + end + +[comparator]: https://minecraft.gamepedia.com/Redstone_Comparator#Subtract_signal_strength "Redstone Comparator on the Minecraft wiki." +]] + +--- Returns a table containing the six sides of the computer. Namely, "top", +-- "bottom", "left", "right", "front" and "back". +-- +-- @treturn { string... } A table of valid sides. function getSides() end + +--- Turn the redstone signal of a specific side on or off. +-- +-- @tparam string side The side to set. +-- @tparam boolean on Whether the redstone signal should be on or off. When on, +-- a signal strength of 15 is emitted. function setOutput(side, on) end + +--- Get the current redstone output of a specific side. +-- +-- @tparam string side The side to get. +-- @treturn boolean Whether the redstone output is on or off. +-- @see setOutput function getOutput(side) end + +--- Get the current redstone input of a specific side. +-- +-- @tparam string side The side to get. +-- @treturn boolean Whether the redstone input is on or off. function getInput(side) end -function setBundledOutput(side, output) end -function getBundledOutput(side) end -function getBundledInput(side) end -function testBundledInput(side, mask) end + +--- Set the redstone signal strength for a specific side. +-- +-- @tparam string side The side to set. +-- @tparam number value The signal strength, between 0 and 15. +-- @throws If `value` is not between 0 and 15. function setAnalogOutput(side, value) end setAnalogueOutput = setAnalogOutput + +--- Get the redstone output signal strength for a specific side. +-- +-- @tparam string side The side to get. +-- @treturn number The output signal strength, between 0 and 15. +-- @see setAnalogueOutput function getAnalogOutput(sid) end getAnalogueOutput = getAnalogOutput + +--- Get the redstone input signal strength for a specific side. +-- +-- @tparam string side The side to get. +-- @treturn number The input signal strength, between 0 and 15. function getAnalogInput(side) end getAnalogueInput = getAnalogInput + +--- Set the bundled cable output for a specific side. +-- +-- @tparam string side The side to set. +-- @tparam number The colour bitmask to set. +-- @see colors.subtract For removing a colour from the bitmask. +-- @see colors.combine For adding a colour to the bitmask. +function setBundledOutput(side, output) end + +--- Get the bundled cable output for a specific side. +-- +-- @tparam string side The side to get. +-- @treturn number The bundled cable's output. +function getBundledOutput(side) end + +--- Get the bundled cable input for a specific side. +-- +-- @tparam string side The side to get. +-- @treturn number The bundled cable's input. +-- @see testBundledInput To determine if a specific colour is set. +function getBundledInput(side) end + +--- Determine if a specific combination of colours are on for the given side. +-- +-- @tparam string side The side to test. +-- @tparam number mask The mask to test. +-- @see getBundledInput +-- @see colors.combine For adding a colour to the bitmask. +-- @usage Check if @{colors.white} and @{colors.black} are on for above the +-- computer. +-- +-- print(redstone.testBundledInput("top", colors.combine(colors.white, colors.black))) +function testBundledInput(side, mask) end diff --git a/illuaminate.sexp b/illuaminate.sexp index 631a9eb589..769eba1756 100644 --- a/illuaminate.sexp +++ b/illuaminate.sexp @@ -73,12 +73,10 @@ (/doc/stub/fs.lua /doc/stub/http.lua /doc/stub/os.lua - /doc/stub/redstone.lua /doc/stub/term.lua /doc/stub/turtle.lua /src/main/resources/*/computercraft/lua/rom/apis/io.lua - /src/main/resources/*/computercraft/lua/rom/apis/window.lua - /src/main/resources/*/computercraft/lua/rom/modules/main/cc/shell/completion.lua) + /src/main/resources/*/computercraft/lua/rom/apis/window.lua) (linters -doc:undocumented -doc:undocumented-arg)) @@ -87,7 +85,6 @@ (/src/main/resources/*/computercraft/lua/rom/apis/textutils.lua /src/main/resources/*/computercraft/lua/rom/modules/main/cc/completion.lua /src/main/resources/*/computercraft/lua/rom/modules/main/cc/shell/completion.lua - /src/main/resources/*/computercraft/lua/rom/programs/advanced/multishell.lua /src/main/resources/*/computercraft/lua/rom/programs/shell.lua) (linters -doc:unresolved-reference)) diff --git a/src/main/java/dan200/computercraft/core/apis/RedstoneAPI.java b/src/main/java/dan200/computercraft/core/apis/RedstoneAPI.java index 1a987d5190..fbb8feffab 100644 --- a/src/main/java/dan200/computercraft/core/apis/RedstoneAPI.java +++ b/src/main/java/dan200/computercraft/core/apis/RedstoneAPI.java @@ -16,11 +16,11 @@ public class RedstoneAPI implements ILuaAPI { - private IAPIEnvironment m_environment; + private final IAPIEnvironment environment; public RedstoneAPI( IAPIEnvironment environment ) { - m_environment = environment; + this.environment = environment; } @Override @@ -63,31 +63,31 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O // setOutput ComputerSide side = parseSide( args ); boolean output = getBoolean( args, 1 ); - m_environment.setOutput( side, output ? 15 : 0 ); + environment.setOutput( side, output ? 15 : 0 ); return null; } case 2: // getOutput - return new Object[] { m_environment.getOutput( parseSide( args ) ) > 0 }; + return new Object[] { environment.getOutput( parseSide( args ) ) > 0 }; case 3: // getInput - return new Object[] { m_environment.getInput( parseSide( args ) ) > 0 }; + return new Object[] { environment.getInput( parseSide( args ) ) > 0 }; case 4: { // setBundledOutput ComputerSide side = parseSide( args ); int output = getInt( args, 1 ); - m_environment.setBundledOutput( side, output ); + environment.setBundledOutput( side, output ); return null; } case 5: // getBundledOutput - return new Object[] { m_environment.getBundledOutput( parseSide( args ) ) }; + return new Object[] { environment.getBundledOutput( parseSide( args ) ) }; case 6: // getBundledInput - return new Object[] { m_environment.getBundledInput( parseSide( args ) ) }; + return new Object[] { environment.getBundledInput( parseSide( args ) ) }; case 7: { // testBundledInput ComputerSide side = parseSide( args ); int mask = getInt( args, 1 ); - int input = m_environment.getBundledInput( side ); + int input = environment.getBundledInput( side ); return new Object[] { (input & mask) == mask }; } case 8: @@ -100,15 +100,15 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O { throw new LuaException( "Expected number in range 0-15" ); } - m_environment.setOutput( side, output ); + environment.setOutput( side, output ); return null; } case 10: case 11: // getAnalogOutput/getAnalogueOutput - return new Object[] { m_environment.getOutput( parseSide( args ) ) }; + return new Object[] { environment.getOutput( parseSide( args ) ) }; case 12: case 13: // getAnalogInput/getAnalogueInput - return new Object[] { m_environment.getInput( parseSide( args ) ) }; + return new Object[] { environment.getInput( parseSide( args ) ) }; default: return null; } diff --git a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/shell/completion.lua b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/shell/completion.lua index 6be54fe2fb..91d9ea7a4b 100644 --- a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/shell/completion.lua +++ b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/shell/completion.lua @@ -141,12 +141,12 @@ return { program = program, -- Re-export various other functions - help = wrap(help.completeTopic), - choice = wrap(completion.choice), - peripheral = wrap(completion.peripheral), - side = wrap(completion.side), - setting = wrap(completion.setting), - command = wrap(completion.command), + help = wrap(help.completeTopic), --- Wraps @{help.completeTopic} as a @{build} compatible function. + choice = wrap(completion.choice), --- Wraps @{cc.completion.choice} as a @{build} compatible function. + peripheral = wrap(completion.peripheral), --- Wraps @{cc.completion.peripheral} as a @{build} compatible function. + side = wrap(completion.side), --- Wraps @{cc.completion.side} as a @{build} compatible function. + setting = wrap(completion.setting), --- Wraps @{cc.completion.setting} as a @{build} compatible function. + command = wrap(completion.command), --- Wraps @{cc.completion.command} as a @{build} compatible function. build = build, } diff --git a/src/test/resources/test-rom/spec/apis/redstone_spec.lua b/src/test/resources/test-rom/spec/apis/redstone_spec.lua new file mode 100644 index 0000000000..17d8acab0e --- /dev/null +++ b/src/test/resources/test-rom/spec/apis/redstone_spec.lua @@ -0,0 +1,92 @@ +local function it_side(func, ...) + local arg = table.pack(...) + it("requires a specific side", function() + expect.error(func, 0):eq("bad argument #1 (string expected, got number)") + expect.error(func, "blah", table.unpack(arg)):eq("Invalid side.") + + func("top", table.unpack(arg)) + func("Top", table.unpack(arg)) + func("toP", table.unpack(arg)) + end) +end + +describe("The redstone library", function() + describe("redstone.setOutput", function() + it_side(redstone.setOutput, false) + + it("sets the output strength correctly", function() + redstone.setOutput("top", false) + expect(redstone.getAnalogueOutput("top")):eq(0) + + redstone.setOutput("top", true) + expect(redstone.getAnalogueOutput("top")):eq(15) + end) + end) + + describe("redstone.getOutput", function() + it_side(redstone.getOutput) + + it("gets the output strength correctly", function() + redstone.setAnalogueOutput("top", 0) + expect(redstone.getOutput("top")):eq(false) + + redstone.setAnalogueOutput("top", 1) + expect(redstone.getOutput("top")):eq(true) + + redstone.setAnalogueOutput("top", 15) + expect(redstone.getOutput("top")):eq(true) + end) + end) + + describe("redstone.getInput", function() + it_side(redstone.getInput) + end) + + describe("redstone.setAnalogueOutput", function() + it_side(redstone.setAnalogueOutput, 0) + + it("checks the strength parameter", function() + expect.error(redstone.setAnalogueOutput, "top", true):eq("bad argument #2 (number expected, got boolean)") + expect.error(redstone.setAnalogueOutput, "top", 0 / 0):eq("bad argument #2 (number expected, got nan)") + expect.error(redstone.setAnalogueOutput, "top", math.huge):eq("bad argument #2 (number expected, got inf)") + expect.error(redstone.setAnalogueOutput, "top", -1):eq("Expected number in range 0-15") + expect.error(redstone.setAnalogueOutput, "top", 16):eq("Expected number in range 0-15") + end) + end) + + describe("redstone.getAnalogueOutput", function() + it_side(redstone.getAnalogueOutput) + end) + + describe("redstone.getAnalogueInput", function() + it_side(redstone.getAnalogueInput) + end) + + describe("redstone.setBundledOutput", function() + it_side(redstone.setBundledOutput, 0) + + it("checks the mask parameter", function() + expect.error(redstone.setBundledOutput, "top", true):eq("bad argument #2 (number expected, got boolean)") + expect.error(redstone.setBundledOutput, "top", 0 / 0):eq("bad argument #2 (number expected, got nan)") + expect.error(redstone.setBundledOutput, "top", math.huge):eq("bad argument #2 (number expected, got inf)") + end) + end) + + describe("redstone.getBundledOutput", function() + it_side(redstone.getBundledOutput) + end) + + describe("redstone.getBundledInput", function() + it_side(redstone.getBundledInput) + end) + + describe("redstone.testBundledInput", function() + it_side(redstone.testBundledInput, 0) + + it("checks the mask parameter", function() + expect.error(redstone.testBundledInput, "top", true):eq("bad argument #2 (number expected, got boolean)") + expect.error(redstone.testBundledInput, "top", 0 / 0):eq("bad argument #2 (number expected, got nan)") + expect.error(redstone.testBundledInput, "top", math.huge):eq("bad argument #2 (number expected, got inf)") + end) + end) +end)