From c2c9a6bcc2a1f42cb6a64c49c16a1db1816a5cc3 Mon Sep 17 00:00:00 2001 From: Starfflame Date: Sun, 23 May 2021 22:22:27 -0500 Subject: [PATCH] Add tests for core words --- .gitignore | 1 + CoreWords.tl | 2 +- ForthMachine.tl | 4 +- tests.lua | 203 ++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 185 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index a9711c7..76ac12d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ *.lua !*tlconfig.lua !*tests.lua +luacov.* diff --git a/CoreWords.tl b/CoreWords.tl index 02fda28..e165a64 100644 --- a/CoreWords.tl +++ b/CoreWords.tl @@ -78,7 +78,7 @@ end -- I/O operations local function dot(state: Environment) local out = state.activeDataStack:pop() - io.write(out as string.."\n") + io.write(tostring(out) as string.."\n") end local function twoDup(state: Environment) over(state) diff --git a/ForthMachine.tl b/ForthMachine.tl index 23c9f33..71a8fb8 100644 --- a/ForthMachine.tl +++ b/ForthMachine.tl @@ -9,9 +9,9 @@ local Environment = require("DataStructures").Environment --interpreters.start(fileStream("codetest.txt")) local core_dicts = {CoreWords} ---local env = Environment:new(istream:new(standardInputRefill), core_dicts) +local env = Environment:new(istream:new(standardInputRefill), core_dicts) -local env = Environment:new(oneReadInputStream("14 11 + ."), core_dicts) +--local env = Environment:new(oneReadInputStream("14 11 + ."), core_dicts) interpreters.start(env) diff --git a/tests.lua b/tests.lua index e42b037..445bb8f 100644 --- a/tests.lua +++ b/tests.lua @@ -4,12 +4,21 @@ local Stack, Dictionary, WordInfo, Environment = ds.Stack, ds.Dictionary, ds.Wor local CoreHelpers = require("CoreHelpers") local CoreWords = require("CoreWords") local interpreters = require("Interpreters") + +local TEST_REPEAT_COUNT = 200 +local NUM_RANGE = 100000000000000 +local STACK_RANGE = 100 + + + + + function pushThree(s) s:push(1) s:push(2) s:push(3) end - +math.randomseed( os.time()) describe("Stack Tests", function() local stack = Stack:new() @@ -72,7 +81,7 @@ describe("InputStream tests", function() for i = 1, #inputstr do assert.are.same(inputstr:sub(i, i), input:next()) end - for i = 1, 1000 do + for i = 1, TEST_REPEAT_COUNT do assert.are.same(nil, input:next()) end end) @@ -83,7 +92,7 @@ describe("InputStream tests", function() for i = 1, #inputstr do assert.are.same(inputstr:sub(i, i), input:next()) end - for i = 1, 1000 do + for i = 1, TEST_REPEAT_COUNT do assert.are.same(nil, input:next()) end end) @@ -94,7 +103,7 @@ describe("InputStream tests", function() for i = 1, #expected_output do assert.are.same(expected_output:sub(i,i), input:next()) end - for i = 1, 1000 do + for i = 1, TEST_REPEAT_COUNT do assert.are.same(nil, input:next()) end end) @@ -137,13 +146,25 @@ function buildEnvironment(testString) return testEnv end -local twoRandom = function() return math.random(-100000, 100000), math.random(-100000, 100000) end +local twoRandom = function() return math.random(-NUM_RANGE, NUM_RANGE), math.random(-NUM_RANGE, NUM_RANGE) end + + +local function randomDataStackEnv(lowerRange, upperRange) + local stackDepth = math.random(lowerRange, upperRange) + local stackTop = 0 + local testEnv = Environment:new(nil, {CoreWords}) + for j = 1, stackDepth do + stackTop = math.random(-NUM_RANGE,NUM_RANGE) + testEnv.activeDataStack:push(stackTop) + end + return testEnv, stackDepth +end describe("Interpreter tests", function() describe("Arithmetic tests", function() it("can add two numbers, and leave the sum on the stack", function() - for i=1, 1000 do + for i=1, TEST_REPEAT_COUNT do local a, b = twoRandom() local expected_sum = a + b local testString = a.." "..b.." +" @@ -153,7 +174,7 @@ describe("Interpreter tests", function() end end) it("can subtract two numbers, and leave the difference on the stack", function() - for i=1, 1000 do + for i=1, TEST_REPEAT_COUNT do local a, b = twoRandom() local expected_diff = a -b local testString = a.." "..b.." -" @@ -164,7 +185,7 @@ describe("Interpreter tests", function() end) it("can multiply two numbers, leaving product on the stack", function() - for i = 1, 1000 do + for i = 1, TEST_REPEAT_COUNT do local a, b = twoRandom() local expected_prod = a*b local testString = a.." "..b.." *" @@ -175,7 +196,7 @@ describe("Interpreter tests", function() end) it("can divide two numbers, leaving the quotient on the stack", function() - for i = 1, 1000 do + for i = 1, TEST_REPEAT_COUNT do local a, b = twoRandom() local expected_quo = a/b local testString = a.." "..b.." /" @@ -185,19 +206,19 @@ describe("Interpreter tests", function() end end) it("can evaluate a arithmetic expression with multiple operations", function() - for i = 1, 1000 do - operands = {math.random(-100000,100000), math.random(-100000,100000), math.random(-100000,100000), math.random(-100000,100000)} + for i = 1, TEST_REPEAT_COUNT do + operands = {math.random(-NUM_RANGE,NUM_RANGE), math.random(-NUM_RANGE,NUM_RANGE), math.random(-NUM_RANGE,NUM_RANGE), math.random(-NUM_RANGE,NUM_RANGE)} local operations = {math.random(4), math.random(4), math.random(4)} local testString = ""..operands[1] local expectedResult = operands[1] for j, op in ipairs(operations) do - if j == 1 then + if op == 1 then testString = testString.." "..operands[j + 1].." +" expectedResult = expectedResult + operands[j+1] - elseif j == 2 then + elseif op == 2 then testString = testString.." "..operands[j + 1].." -" expectedResult = expectedResult - operands[j+1] - elseif j == 3 then + elseif op == 3 then testString = testString.." "..operands[j + 1].." *" expectedResult = expectedResult * operands[j+1] else @@ -219,18 +240,156 @@ describe("Interpreter tests", function() assert.are.same(1, testEnv.activeDataStack.contents[2]) end) it("DUPs the top item of a stack with random quantities of random numbers on it.", function() - for i = 1, 1000 do - local stack_depth = math.random(100) - local stack_top = 0 - for j = 1, stack_depth do - stack_top = math.random(-100000,100000) - testEnv.activeDataStack:push(stack_top) + for i = 1, TEST_REPEAT_COUNT do + local stackDepth = math.random(STACK_RANGE) + local stackTop = 0 + local testEnv = buildEnvironment("DUP") + for j = 1, stackDepth do + stackTop = math.random(-NUM_RANGE,NUM_RANGE) + testEnv.activeDataStack:push(stackTop) end - local testEnv = buildEnvironment(stack_top.." DUP") - assert.are.same(stack_top, testEnv.activeDataStack.contents[j+1]) + interpreters.start(testEnv) + assert.are.same(stackTop, testEnv.activeDataStack.contents[stackDepth+1]) end end) end) + describe("SWAP tests", function() + it("SWAPS the top two items of a stack with two items on it.", function() + local testEnv = buildEnvironment("1 2 SWAP") + interpreters.start(testEnv) + assert.are.same(2, testEnv.activeDataStack.contents[1]) + assert.are.same(1, testEnv.activeDataStack.contents[2]) + end) + it("SWAPS the top two items of a stack with random quantities of random numbers on it.", function() + for i = 1, TEST_REPEAT_COUNT do + local testEnv, stackDepth = randomDataStackEnv(2, STACK_RANGE) + testEnv.activeInputStream = CoreHelpers.oneReadInputStream("SWAP") + local a = testEnv.activeDataStack.contents[stackDepth] + local b = testEnv.activeDataStack.contents[stackDepth-1] + interpreters.start(testEnv) + assert.are.equal(testEnv.activeDataStack.contents[stackDepth-1], a) + assert.are.equal(testEnv.activeDataStack.contents[stackDepth], b) + end + end) + end) + describe("ROT tests", function() + it("ROTs the top three items of a stack with three items on it.", function() + local testEnv = buildEnvironment("1 2 3 ROT") + interpreters.start(testEnv) + assert.are.same({2, 3, 1}, testEnv.activeDataStack.contents) + end) + end) + describe("DROP tests", function() + it("Drops the top item of the stack, when stack size is 1.", function() + local testEnv = buildEnvironment("1 DROP") + interpreters.start(testEnv) + assert.are.same(nil, testEnv.activeDataStack.contents[1]) + end) + -- TODO: add better tests + end) + describe("OVER tests", function() + it("Places a copy of the item below the top of the stack onto the top, when stack size is 2.", function() + local testEnv = buildEnvironment("1 2 OVER") + interpreters.start(testEnv) + assert.are.same({1, 2, 1}, testEnv.activeDataStack.contents) + end) + end) + + -- TODO: test output of DOT. + describe("DOT tests", function() + it("Drops the top item of the stack.", function() + local testEnv = buildEnvironment("1 .") + interpreters.start(testEnv) + assert.are.same({}, testEnv.activeDataStack.contents) + end) + end) + describe("2DUP tests", function() + it("Duplicates the top two items on the stack.", function() + local testEnv = buildEnvironment("1 2 2DUP") + interpreters.start(testEnv) + assert.are.same({1, 2, 1, 2}, testEnv.activeDataStack.contents) + + end) + + end) + describe("2SWAP tests", function() + it("Exchanges the top two cell pairs.", function() + local testEnv = buildEnvironment("1 2 3 4 2SWAP") + interpreters.start(testEnv) + assert.are.same({3, 4, 1, 2}, testEnv.activeDataStack.contents) + end) + end) + describe("2OVER tests", function() + it("copies the two items below the top two items onto the top of the stack.", function() + local testEnv = buildEnvironment("1 2 3 4 2OVER") + interpreters.start(testEnv) + assert.are.same({1, 2, 3, 4, 1, 2}, testEnv.activeDataStack.contents) + end) + end) + describe("NIP tests", function() + it("drops the first item below the top of stack, when stack size == 2", function() + local testEnv = buildEnvironment("1 2 NIP") + interpreters.start(testEnv) + assert.are.same({2}, testEnv.activeDataStack.contents) + end) + end) + describe("TUCK tests", function() + it("Copies the top below the item just below the top, in a stack of size 2.", function() + local testEnv = buildEnvironment("1 2 TUCK") + interpreters.start(testEnv) + assert.are.same({2, 1, 2}, testEnv.activeDataStack.contents) + end) + end) + describe("ROLL tests", function() + it("is NOOP when u = 0", function() + local testEnv = buildEnvironment("1 2 4 1 1 0 ROLL") + interpreters.start(testEnv) + assert.are.same({1, 2, 4, 1, 1}, testEnv.activeDataStack.contents) + end) + it("is SWAP when u = 1", function() + local testEnv = buildEnvironment("1 2 3 4 5 1 ROLL") + interpreters.start(testEnv) + assert.are.same({1, 2, 3, 5, 4}, testEnv.activeDataStack.contents) + end) + it("is ROT when u = 2", function() + local testEnv = buildEnvironment("1 2 3 4 5 2 ROLL") + interpreters.start(testEnv) + assert.are.same({1, 2, 4, 5, 3}, testEnv.activeDataStack.contents) + end) + it("works properly for u = [3, 100]", function() + local testStr = "" + local expected_output = {} + for i = 3, 100 do + for j = 1, i+1 do + testStr = testStr.." "..j + table.insert(expected_output, j) + end + testStr = testStr.." "..i.." ROLL" + local bottom = table.remove(expected_output, #expected_output - i) + table.insert(expected_output, bottom) + local testEnv = buildEnvironment(testStr) + interpreters.start(testEnv) + assert.are.same(expected_output, testEnv.activeDataStack.contents) + end + end) + end) + describe("' tests", function() + it("places an xt on top of the stack replacing the next word", function() + local cw = CoreWords + local testFunc = function(env) local a, b return a + b end + local customWord = CoreHelpers.defineWord(cw, "TEST", testFunc, false) + local testEnv = buildEnvironment("' TEST") + interpreters.start(testEnv) + assert.are.same(testFunc, testEnv.activeDataStack.contents[1]) + end) + end) + describe("EXECUTE tests", function() + it("Given an `add` XT on top of the stack with two numbers beneath it, execute it.", function() + local testEnv = buildEnvironment("1 2 ' + EXECUTE") + interpreters.start(testEnv) + assert.are.equal(3, testEnv.activeDataStack.contents[1]) + end) + end) end) end)