From 4787168aac1ff6a5ddb14755f221a28678106aaf Mon Sep 17 00:00:00 2001 From: Ryan Hartlage <2488333+ryanplusplus@users.noreply.github.com> Date: Sun, 3 May 2026 08:53:26 -0400 Subject: [PATCH] Spec generators for grains, rotational-cipher, and space-age --- exercises/practice/grains/.meta/example.lua | 3 +- .../practice/grains/.meta/spec_generator.lua | 17 ++++ exercises/practice/grains/grains_spec.lua | 80 ++++++++++++------- .../.meta/spec_generator.lua | 10 +++ .../rotational-cipher_spec.lua | 22 ++--- .../practice/space-age/.meta/example.lua | 13 ++- .../space-age/.meta/spec_generator.lua | 21 +++++ exercises/practice/space-age/.meta/tests.toml | 1 - .../practice/space-age/space-age_spec.lua | 51 ++++++------ 9 files changed, 143 insertions(+), 75 deletions(-) create mode 100644 exercises/practice/grains/.meta/spec_generator.lua create mode 100644 exercises/practice/rotational-cipher/.meta/spec_generator.lua create mode 100644 exercises/practice/space-age/.meta/spec_generator.lua diff --git a/exercises/practice/grains/.meta/example.lua b/exercises/practice/grains/.meta/example.lua index 1d42257a..4f716c84 100644 --- a/exercises/practice/grains/.meta/example.lua +++ b/exercises/practice/grains/.meta/example.lua @@ -1,11 +1,12 @@ local grains = {} function grains.square(n) + assert(n >= 1 and n <= 64, 'square must be between 1 and 64') return 2 ^ (n - 1) end function grains.total() - return grains.square(65) - 1 + return 2 ^ 64 - 1 end return grains diff --git a/exercises/practice/grains/.meta/spec_generator.lua b/exercises/practice/grains/.meta/spec_generator.lua new file mode 100644 index 00000000..7034054c --- /dev/null +++ b/exercises/practice/grains/.meta/spec_generator.lua @@ -0,0 +1,17 @@ +return { + module_name = 'grains', + + generate_test = function(case) + if type(case.expected) == 'table' then + local template = [[ + assert.has_error(function() grains.%s(%s) end, '%s')]] + + return template:format(case.property, case.input and case.input.square or '', case.expected.error) + else + local template = [[ + assert.are.equal(%s, grains.%s(%s))]] + + return template:format(tostring(case.expected), case.property, case.input and case.input.square or '') + end + end +} diff --git a/exercises/practice/grains/grains_spec.lua b/exercises/practice/grains/grains_spec.lua index 675070eb..0e568bf3 100644 --- a/exercises/practice/grains/grains_spec.lua +++ b/exercises/practice/grains/grains_spec.lua @@ -1,35 +1,55 @@ local grains = require('grains') describe('grains', function() - it('square 1', function() - assert.are.equals(1, grains.square(1)) - end) - - it('square 2', function() - assert.are.equals(2, grains.square(2)) - end) - - it('square 3', function() - assert.are.equals(4, grains.square(3)) - end) - - it('square 4', function() - assert.are.equals(8, grains.square(4)) - end) - - it('square 16', function() - assert.are.equals(32768, grains.square(16)) - end) - - it('square 32', function() - assert.are.equals(2147483648, grains.square(32)) - end) - - it('square 64', function() - assert.are.equals(9223372036854775808, grains.square(64)) - end) - - it('total', function() - assert.are.equals(18446744073709551615, grains.total()) + describe('returns the number of grains on the square', function() + it('grains on square 1', function() + assert.are.equal(1, grains.square(1)) + end) + + it('grains on square 2', function() + assert.are.equal(2, grains.square(2)) + end) + + it('grains on square 3', function() + assert.are.equal(4, grains.square(3)) + end) + + it('grains on square 4', function() + assert.are.equal(8, grains.square(4)) + end) + + it('grains on square 16', function() + assert.are.equal(32768, grains.square(16)) + end) + + it('grains on square 32', function() + assert.are.equal(2147483648, grains.square(32)) + end) + + it('grains on square 64', function() + assert.are.equal(9.2233720368547758e+18, grains.square(64)) + end) + + it('square 0 is invalid', function() + assert.has_error(function() + grains.square(0) + end, 'square must be between 1 and 64') + end) + + it('negative square is invalid', function() + assert.has_error(function() + grains.square(-1) + end, 'square must be between 1 and 64') + end) + + it('square greater than 64 is invalid', function() + assert.has_error(function() + grains.square(65) + end, 'square must be between 1 and 64') + end) + end) + + it('returns the total number of grains on the board', function() + assert.are.equal(1.8446744073709552e+19, grains.total()) end) end) diff --git a/exercises/practice/rotational-cipher/.meta/spec_generator.lua b/exercises/practice/rotational-cipher/.meta/spec_generator.lua new file mode 100644 index 00000000..18217bde --- /dev/null +++ b/exercises/practice/rotational-cipher/.meta/spec_generator.lua @@ -0,0 +1,10 @@ +return { + module_name = 'rotational_cipher', + + generate_test = function(case) + local template = [[ + assert.are.equal("%s", rotational_cipher.%s("%s", %s))]] + + return template:format(tostring(case.expected), case.property, case.input.text, case.input.shiftKey) + end +} diff --git a/exercises/practice/rotational-cipher/rotational-cipher_spec.lua b/exercises/practice/rotational-cipher/rotational-cipher_spec.lua index bf09a78a..b930bfb7 100644 --- a/exercises/practice/rotational-cipher/rotational-cipher_spec.lua +++ b/exercises/practice/rotational-cipher/rotational-cipher_spec.lua @@ -2,43 +2,43 @@ local rotational_cipher = require('rotational-cipher') describe('rotational-cipher', function() it('rotate a by 0, same output as input', function() - assert.are.equal('a', rotational_cipher.rotate('a', 0)) + assert.are.equal("a", rotational_cipher.rotate("a", 0)) end) it('rotate a by 1', function() - assert.are.equal('b', rotational_cipher.rotate('a', 1)) + assert.are.equal("b", rotational_cipher.rotate("a", 1)) end) it('rotate a by 26, same output as input', function() - assert.are.equal('a', rotational_cipher.rotate('a', 26)) + assert.are.equal("a", rotational_cipher.rotate("a", 26)) end) it('rotate m by 13', function() - assert.are.equal('z', rotational_cipher.rotate('m', 13)) + assert.are.equal("z", rotational_cipher.rotate("m", 13)) end) it('rotate n by 13 with wrap around alphabet', function() - assert.are.equal('a', rotational_cipher.rotate('n', 13)) + assert.are.equal("a", rotational_cipher.rotate("n", 13)) end) it('rotate capital letters', function() - assert.are.equal('TRL', rotational_cipher.rotate('OMG', 5)) + assert.are.equal("TRL", rotational_cipher.rotate("OMG", 5)) end) it('rotate spaces', function() - assert.are.equal('T R L', rotational_cipher.rotate('O M G', 5)) + assert.are.equal("T R L", rotational_cipher.rotate("O M G", 5)) end) it('rotate numbers', function() - assert.are.equal('Xiwxmrk 1 2 3 xiwxmrk', rotational_cipher.rotate('Testing 1 2 3 testing', 4)) + assert.are.equal("Xiwxmrk 1 2 3 xiwxmrk", rotational_cipher.rotate("Testing 1 2 3 testing", 4)) end) it('rotate punctuation', function() - assert.are.equal('Gzo\'n zvo, Bmviyhv!', rotational_cipher.rotate('Let\'s eat, Grandma!', 21)) + assert.are.equal("Gzo'n zvo, Bmviyhv!", rotational_cipher.rotate("Let's eat, Grandma!", 21)) end) it('rotate all letters', function() - assert.are.equal('Gur dhvpx oebja sbk whzcf bire gur ynml qbt.', - rotational_cipher.rotate('The quick brown fox jumps over the lazy dog.', 13)) + assert.are.equal("Gur dhvpx oebja sbk whzcf bire gur ynml qbt.", + rotational_cipher.rotate("The quick brown fox jumps over the lazy dog.", 13)) end) end) diff --git a/exercises/practice/space-age/.meta/example.lua b/exercises/practice/space-age/.meta/example.lua index 7a24dd94..bd6e49d6 100644 --- a/exercises/practice/space-age/.meta/example.lua +++ b/exercises/practice/space-age/.meta/example.lua @@ -18,14 +18,19 @@ local function round(val, decimal) end function SpaceAge:new(seconds) - self.__index = self - local meta_tbl = { seconds = seconds } + local o = {} for k, v in pairs(SpaceAge.ORBITAL_PERIODS) do local val = round(seconds / SpaceAge.EARTH_ORBITAL_PERIOD / v, 2) - meta_tbl['on_' .. k] = load('return ' .. val) + o['on_' .. k] = load('return ' .. val) end - return setmetatable(meta_tbl, self) + return setmetatable(o, { + __index = function(_, key) + return function() + error('not a planet') + end + end + }) end return SpaceAge diff --git a/exercises/practice/space-age/.meta/spec_generator.lua b/exercises/practice/space-age/.meta/spec_generator.lua new file mode 100644 index 00000000..61d66c94 --- /dev/null +++ b/exercises/practice/space-age/.meta/spec_generator.lua @@ -0,0 +1,21 @@ +return { + module_name = 'SpaceAge', + + generate_test = function(case) + require'pl.pretty'.dump(case) + + if type(case.expected) == 'table' then + local template = [[ + local age = SpaceAge:new(%s) + assert.has_error(function() age.on_%s() end, '%s')]] + + return template:format(tostring(case.input.seconds), case.input.planet:lower(), case.expected.error) + else + local template = [[ + local age = SpaceAge:new(%s) + assert.are.equal(%s, age.on_%s())]] + + return template:format(tostring(case.input.seconds), case.expected, case.input.planet:lower()) + end + end +} diff --git a/exercises/practice/space-age/.meta/tests.toml b/exercises/practice/space-age/.meta/tests.toml index a62017e4..7957bb77 100644 --- a/exercises/practice/space-age/.meta/tests.toml +++ b/exercises/practice/space-age/.meta/tests.toml @@ -35,4 +35,3 @@ description = "age on Neptune" [57b96e2a-1178-40b7-b34d-f3c9c34e4bf4] description = "invalid planet causes error" -include = false diff --git a/exercises/practice/space-age/space-age_spec.lua b/exercises/practice/space-age/space-age_spec.lua index 1a324556..d5db3ba4 100644 --- a/exercises/practice/space-age/space-age_spec.lua +++ b/exercises/practice/space-age/space-age_spec.lua @@ -1,55 +1,50 @@ local SpaceAge = require('space-age') describe('space-age', function() - it('age in seconds', function() - local age = SpaceAge:new(1000000) - assert.are.equal(1000000, age.seconds) - end) - - it('age in Earth years', function() + it('age on earth', function() local age = SpaceAge:new(1000000000) assert.are.equal(31.69, age.on_earth()) end) - it('age in Mercury years', function() + it('age on mercury', function() local age = SpaceAge:new(2134835688) - assert.are.equal(67.65, age.on_earth()) assert.are.equal(280.88, age.on_mercury()) end) - it('age in Venus years', function() + it('age on venus', function() local age = SpaceAge:new(189839836) - assert.are.equal(6.02, age.on_earth()) assert.are.equal(9.78, age.on_venus()) end) - it('age in Mars years', function() - local age = SpaceAge:new(2329871239) - assert.are.equal(73.83, age.on_earth()) - assert.are.equal(39.25, age.on_mars()) + it('age on mars', function() + local age = SpaceAge:new(2129871239) + assert.are.equal(35.88, age.on_mars()) end) - it('age in Jupiter years', function() + it('age on jupiter', function() local age = SpaceAge:new(901876382) - assert.are.equal(28.58, age.on_earth()) assert.are.equal(2.41, age.on_jupiter()) end) - it('age in Saturn years', function() - local age = SpaceAge:new(3000000000) - assert.are.equal(95.06, age.on_earth()) - assert.are.equal(3.23, age.on_saturn()) + it('age on saturn', function() + local age = SpaceAge:new(2000000000) + assert.are.equal(2.15, age.on_saturn()) + end) + + it('age on uranus', function() + local age = SpaceAge:new(1210123456) + assert.are.equal(0.46, age.on_uranus()) end) - it('age in Uranus years', function() - local age = SpaceAge:new(3210123456) - assert.are.equal(101.72, age.on_earth()) - assert.are.equal(1.21, age.on_uranus()) + it('age on neptune', function() + local age = SpaceAge:new(1821023456) + assert.are.equal(0.35, age.on_neptune()) end) - it('age in Neptune year', function() - local age = SpaceAge:new(8210123456) - assert.are.equal(260.16, age.on_earth()) - assert.are.equal(1.58, age.on_neptune()) + it('invalid planet causes error', function() + local age = SpaceAge:new(680804807) + assert.has_error(function() + age.on_sun() + end, 'not a planet') end) end)