Skip to content

lp/pdtest

Repository files navigation

Pure Data testing external

Async Functional testing for Pure Data using Lua scripting.

PdTest is intended at developers of Pure Data externals that needs functional testing for their project. Functional tests are organised as test suites, grouping test cases that coordinates the many individual tests. The pdtest external features mandatory inlet type creation arguments, adding test inlets for receiving results of the specifed types, while the active left inlet is used for loading test suites, starting, stopping and resetting the test loop. Test are built using standard Lua scripts, with provided helper functions for declaring the test structure. Refer to Lua documentation to learn more about test scripts syntax.

Test Suite Syntax:

Building a test suite is easy when you know its basics:

Suites functions

mysuite = Suite("my test suite")
mysuite.setup(function()                                -- set a setup function to be called
    _.outlet({"SOME","SETUP","MESSAGE"})                -- before all of this suite tests
end)
mysuite.teardown(function()                             -- set a cleanup function to be called
    _.outlet({"SOME","CLEANUP","MESSAGE"})              -- after all of this suite tests
end)
mysuite.case("my test case")                            -- define a new test case

Cases functions

mycase = mysuite.case("my test case")
mycase.setup(function()                                 -- set a setup function to be called
    _.outlet({"SOME","SETUP","MESSAGE"})                -- before all of this case tests
end)
mycase.teardown(function()                              -- set a cleanup function to be called
    _.outlet({"SOME","CLEANUP","MESSAGE"})              -- after all of this case tests
end)
mycase.test({"MY","TEST","MESSAGE"})                    -- lua table as pd list message to outlet
mycase.test("MESSAGE")                                  -- lua string as pd symbol message to outlet
mycase.test(3.1416)                                     -- lua number as pd float message to outlet
mycase.test(function(test)                              -- long test form, function body can contain only one
    test({"MY","TEST","MESSAGE"})                       -- test() call, but unlimited _.outlet().
end)

Tests functions

mycase.test({"MY","TEST","MESSAGE"}                     -- tests are made by calling condition methods
    ).should:equal(                                     -- like equal() from the .should namespace
        {"TEST","DESIRED","OUTPUT","MESSAGE"})          -- notice the colon for calling condition methods
mycase.test({"MY","TEST","MESSAGE"}                     -- tests can also be reversed by calling them
    ).should.nt:equal(                                  -- from the .should.nt namespace
        {"TEST","NOT","DESIRED","OUTPUT","MESSAGE"})

Conditions methods

equality

.should:equal(                                          -- equal method test for message content equality
    {"TEST","DESIRED","OUTPUT","MESSAGE"})
.should.nt:equal(                                       -- not equal method
    {"TEST","UNDESIRED","OUTPUT","MESSAGE"})

similarity

.should:resemble(                                       -- resemble is close to equal, 2 list will resemble if 
    {"SOME","MESSAGE","LIST"})                          -- they have the same members even with different indexes,
                                                        -- numbers will be rounded according to a decimal precision
.should:resemble(26.6666, 3)                            -- argument, strings will match event when case differs,
                                                        -- a number and a string will match if their string values
.should.not:resemble(                                   -- are equal and strings or numbers will match a tables first
    {"SOME", "OTHER", "MESSAGE", "LIST"})               -- member

possibility

.should:be_any({"A","B","C"})                           -- passes if any of the table's members equals the returned value
                                                        -- tables of tables are allowed to match table results
.should.nt:be_any({{1,2,3},{2,3,4},{3,4,5}})            -- not be any_

pattern matching

According to Lua’s pattern matching syntax

.should:match("%d+%s%a+")                               -- match method matches individual result atoms
.should.nt:match("%d+%s%a+")                            -- not match method

numeric comparisons

>
.should:be_more(10)                                     -- the greater than test, all four examples are equivalent
.should:be_more("10")                                   -- and results are treated in a similar manner.  Everything gets
.should:be_more({10})                                   -- converted to number and in the case of list only the first
.should:be_more({"10"})                                 -- is compared
.should.nt:be_more(22)                                  -- negation works as well
<
.should:be_less(10)
.should:be_less("10")
.should:be_less({10})
.should:be_less({"10"})
.should.nt:be_less(22)
>=
.should:be_more_or_equal(10)
.should:be_more_or_equal("10")
.should:be_more_or_equal({10})
.should:be_more_or_equal({"10"})
.should.nt:be_more_or_equal(22)
<=
.should:be_less_or_equal(10)
.should:be_less_or_equal("10")
.should:be_less_or_equal({10})
.should:be_less_or_equal({"10"})
.should.nt:be_less_or_equal(22)

custom truth

.should:be_true(function(result)                        -- be_true method test if provided function returns
    if result[1] == "OK" then                           -- true or false
        return true
    else
        return false
    end
end)
.should.nt:be_true(function(result)                     -- not be_true (be_false...)
    if result[1] == "ERROR" then
        return true
    else
        return false
    end
end)

The test namespace

A test function is made available as only argument inside the test function for sending test messages through the pdtest outlet. Specifically used inside a function() passed to the case.test() function. These messages are registered as test messages, pdtest will expect a test result for each of these test messages. General rule: 1 test call for each case.test().

test({"YOUR","MESSAGE","LIST"})               -- sends lua table as test message list through outlet
test("STRING")                                    -- sends lua string as test message symbol through outlet
test(3.1416)                                      -- sends lua number as test message float through outlet
test("BANG")                                      -- sends bang as test message through outlet

The _ namespace: direct pd calls

_.post("string to post")                          -- post to pd console
_.error("string to signal")                       -- sends error to pd console
_.outlet                                          -- namespace for sending untested messages to the pdtest outlet 

The _.outlet namespace → untested messages

Sending raw messages through the pdtest outlet. These unregistered messages go unnoticed through the test system. Meant to be used inside setup()/teardown() or as test helpers inside case.test() function()_. General rule: 1 ignored inlet message for each *.outlet* call.

_.outlet({"YOUR","MESSAGE","LIST"})             -- sends lua table as raw message list through outlet
_.outlet("STRING")                                -- sends lua string as raw message symbol through outlet
_.outlet(3.1416)                                  -- sends lua number as raw message float through outlet
_.outlet("BANG")                                  -- sends bang as raw message through outlet

Example Tests Suite

examplesuite.lua
mysuite = Suite("TestSuite") -- initializing a new test suite named "TestSuite"
mysuite.setup(function()                -- called before every test in the suite
  _.outlet({"command","flushdb"})
end)
mysuite.teardown(function()             -- called after every test in the suite
  _.outlet({"command","flushdb"})
end)
mysuite.case("Server Info"              -- new test case named "Server Info"
  ).test({"command", "INFO"}            -- will output message "list command INFO"
    ).should:match("^redis_version")    -- test will pass if result received on right 
                                        -- inlet starts with the string "redis_version"
mysuite.case("Reality Check"            -- second test case named "Reality Check"
  ).test({"command", "dummy"}           -- will output message "list command dummy"
    ).should:match("ERR"                -- test will pass if result received on right
  ).test({"command", "dbsize"}          -- inlet match the string "ERR"
    ).should.nt:match("ERR")            -- second test will pass if result doesn't match "ERR"
mycase = mysuite.case("Basic tests")    -- new test case named "Basic tests"
mycase.setup(                           -- called before every test in the case
  function() _.outlet({"command","SET","FOO","BAR"})
end)
mycase.test({"command", "GET", "FOO"}   -- test will pass if result equals "BAR"
  ).should:equal("BAR")
mycase.test(function(test)              -- here a test function is passed instead
  _.outlet({"command","DEL","FOO"})         -- notice its only argument being a test function
  test({"command","EXISTS","FOO"})
end).should:equal(0)
mycase.test(
  {"command","SETNX","FOO","BAT"}
  ).should:be_true(function(result)     -- test method '.should:be_true' takes a function
    if result == 1 then                 -- for which it provides a 'result' argument
      return true                       -- the function must return true or false
    else
      return false
    end
  end)
pd console
pdtest: loading testfile examplesuite.lua
pdtest: TestSuite -> Server Info < command, INFO > 
pdtest: -> OK
pdtest: TestSuite -> Reality Check < command, dummy > 
pdtest: -> OK
pdtest: TestSuite -> Reality Check < command, dbsize > 
pdtest: -> OK
pdtest: TestSuite -> Basic tests < command, GET, FOO > 
pdtest: -> OK
pdtest: TestSuite -> Basic tests < function > 
pdtest: -> OK
pdtest: TestSuite -> Basic tests < command, SETNX, FOO, BAT > 
pdtest: x> FAILED |> 0 is not true function
pdtest: !!! Test Completed !!!
pdtest: 1 Suites | 3 Cases | 6 Tests
pdtest: 5 tests passed
pdtest: 1 tests failed

About

Pure Data testing external

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published