-- test/test-clod.lua
--
-- Configuration languge organised (by) dots.
--
-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org>
--
-- For Licence terms, see COPYING
--

-- Step one, start coverage

pcall(require, "luacov")

local clod = require 'clod'

local testnames = {}

local real_assert = assert
local total_asserts = 0
local function assert(...)
   local retval = real_assert(...)
   total_asserts = total_asserts + 1
   return retval
end

local function add_test(suite, name, value)
   rawset(suite, name, value)
   testnames[#testnames+1] = name
end

local suite = setmetatable({}, {__newindex = add_test})

function suite.test_parse_empty()
   local conf = assert(clod.parse(""))
end

function suite.test_parse_error()
   local conf, err = clod.parse("foo=")
   assert(not conf)
end

function suite.test_runtime_error()
   local conf, err = clod.parse("foo {}")
   assert(not conf)
end

function suite.test_runtime_error2()
   local conf, err = clod.parse("foo(function()end)")
   assert(not conf)
end

function suite.test_runtime_error3()
   local conf, err = clod.parse("foo = {}")
   assert(not conf)
end

function suite.test_runtime_error3()
   local conf, err = clod.parse("foo = function() end")
   assert(not conf)
end

function suite.test_parse_simple_string()
   local conf = assert(clod.parse("test 'any'"))
   assert(conf.settings.test == "any")
end

function suite.test_parse_simple_string_assign()
   local conf = assert(clod.parse("test = 'any'"))
   assert(conf.settings.test == "any")
end

function suite.test_parse_dotted_key()
   local conf = assert(clod.parse("one.two 'three'"))
   assert(conf.settings.one.two == "three")
end

function suite.test_blank_lines()
   local conf = assert(clod.parse("a 'b'\n\nc 'd'"))
   assert(conf.settings.a == "b")
   assert(conf.settings.c == "d")
end

function suite.one_line_serialise()
   local input = 'foo.bar "baz"\n'
   local conf = assert(clod.parse(input))
   assert(conf:serialise() == input)
end

function suite.one_line_serialise_number()
   local input = 'foo.bar = 5\n'
   local conf = assert(clod.parse(input))
   assert(conf:serialise() == input)
end

function suite.one_line_serialise_boolean_true()
   local input = 'foo.bar = true\n'
   local conf = assert(clod.parse(input))
   assert(conf:serialise() == input)
end

function suite.one_line_serialise_boolean_false()
   local input = 'foo.bar = false\n'
   local conf = assert(clod.parse(input))
   assert(conf:serialise() == input)
end

function suite.three_line_serialise()
   local input = 'foo.bar "baz"\n\njeff "cake"\n'
   local conf = assert(clod.parse(input))
   assert(conf:serialise() == input)
end

function suite.set_extant_value()
   local conf = assert(clod.parse("foo 'bar'"))
   conf.settings.foo = "baz"
   assert(conf:serialise() == 'foo "baz"\n')
end

function suite.set_dotted_extant_value()
   local conf = assert(clod.parse("foo.bar 'bar'"))
   conf.settings.foo.bar = "baz"
   assert(conf:serialise() == 'foo.bar "baz"\n')
end

function suite.bad_set_extant_value()
   local conf = assert(clod.parse("foo 'bar'"))
   assert(not pcall(function()conf.settings.foo = {}end))
end

function suite.clear_extant_entry()
   local conf = assert(clod.parse("foo 'br'"))
   conf.settings.foo = nil
   assert(conf:serialise() == "")
end

function suite.clear_extant_entry_mid_gap()
   local conf = assert(clod.parse("foo 'br'\n\nbar 'ba'\n\nwibble 'wobble'\n"))
   conf.settings.bar = nil
   assert(conf:serialise() == 'foo "br"\n\nwibble "wobble"\n')
end

function suite.clear_extant_entry_last_one()
   local conf = assert(clod.parse("foo 'br'\n\nbar 'ba'\n\nwibble 'wobble'\n"))
   conf.settings.wibble = nil
   assert(conf:serialise() == 'foo "br"\n\nbar "ba"\n')
end

function suite.insert_into_blank()
   local conf = assert(clod.parse(""))
   conf.settings.foo = "bar"
   assert(conf:serialise() == 'foo "bar"\n')
end

function suite.insert_into_blank_two_similar()
   local conf = assert(clod.parse(""))
   conf.settings["person.name"] = "Lars"
   conf.settings["person.age"] = 42
   assert(conf:serialise() == 'person.name "Lars"\nperson.age = 42\n')
end

function suite.insert_into_blank_two_dissimilar()
   local conf = assert(clod.parse(""))
   conf.settings["person.name"] = "Lars"
   conf.settings["cat.age"] = 42
   assert(conf:serialise() == 'person.name "Lars"\n\ncat.age = 42\n')
end

function suite.insert_into_blank_two_similar_one_dissimilar()
   local conf = assert(clod.parse(""))
   conf.settings["person.name"] = "Lars"
   conf.settings["person.age"] = 42
   conf.settings["pants.style"] = "Y-Front"
   assert(conf:serialise() == 'person.name "Lars"\nperson.age = 42\n\npants.style "Y-Front"\n')
end

function suite.insert_into_blank_two_similar_one_dissimilar_variant()
   local conf = assert(clod.parse(""))
   conf.settings["person.name"] = "Lars"
   conf.settings["pants.style"] = "Y-Front"
   conf.settings["person.age"] = 42
   assert(conf:serialise() == 'person.name "Lars"\nperson.age = 42\n\npants.style "Y-Front"\n')
end

function suite.parse_wild_key()
   local conf = assert(clod.parse('foo["*"] "bar"'))
   assert(conf.settings.foo.i_1 == "bar")
end

function suite.insert_wild_key()
   local conf = assert(clod.parse(""))
   conf.settings["foo.*"] = "bar"
   assert(conf.settings.foo.i_1 == "bar")
end

function suite.insert_two_wild_keys()
   local conf = assert(clod.parse(""))
   conf.settings["foo.*"] = "bar"
   conf.settings["foo.*"] = "baz"
   assert(conf.settings.foo.i_1 == "bar")
   assert(conf.settings.foo.i_2 == "baz")
end

function suite.serialise_wild_keys()
   local input_str = 'foo["*"] "bar"\n'
   local conf = assert(clod.parse(input_str))
   assert(conf:serialise() == input_str)
end

function suite.iterate_all()
   local input_str = [[
project.name "Clod"
project.description "Badgering stoats"

owner.name "Daniel"
]]
   local conf = assert(clod.parse(input_str))
   local had_name, had_desc, had_owner
   for k, v in conf:each() do
      if k == "project.name" then
	 had_name = true
      elseif k == "project.description" then
	 had_desc = true
      elseif k == "owner.name" then
	 had_owner = true
      else
	 assert(false)
      end
   end
   assert(had_name)
   assert(had_desc)
   assert(had_owner)
end

function suite.iterate_subset()
   local input_str = [[
project.name "Clod"
project.description "Badgering stoats"

owner.name "Daniel"
]]
   local conf = assert(clod.parse(input_str))
   local had_name, had_desc, had_owner
   for k, v in conf:each("project") do
      if k == "project.name" then
	 had_name = true
      elseif k == "project.description" then
	 had_desc = true
      elseif k == "owner.name" then
	 had_owner = true
      else
	 assert(false)
      end
   end
   assert(had_name)
   assert(had_desc)
   assert(not had_owner)
end

function suite.get_list()
   local input_str = [[
foo["*"] "Hello"
foo["*"] "World"
]]
   local conf = clod.parse(input_str)
   local list = conf:get_list('foo')
   assert(#list == 2)
   assert(list[1] == "Hello")
   assert(list[2] == "World")
end

function suite.set_list_same_length()
   local input_str = [[
foo["*"] "Hello"
foo["*"] "World"
]]
   local conf = clod.parse(input_str)
   local list = { "Yeesh", "Gawd" }
   conf:set_list("foo", list)
   local output_str = [[
foo["*"] "Yeesh"
foo["*"] "Gawd"
]]
   assert(conf:serialise() == output_str)
end

function suite.set_list_longer_length()
   local input_str = [[
foo["*"] "Hello"
foo["*"] "World"
]]
   local conf = clod.parse(input_str)
   local list = { "Yeesh", "Gawd", "Cripes" }
   conf:set_list("foo", list)
   local output_str = [[
foo["*"] "Yeesh"
foo["*"] "Gawd"
foo["*"] "Cripes"
]]
   assert(conf:serialise() == output_str)
end

function suite.set_list_shorter_length()
   local input_str = [[
foo["*"] "Hello"
foo["*"] "World"
foo["*"] "Badger"
]]
   local conf = clod.parse(input_str)
   local list = { "Yeesh", "Gawd" }
   conf:set_list("foo", list)
   local output_str = [[
foo["*"] "Yeesh"
foo["*"] "Gawd"
]]
   assert(conf:serialise() == output_str)
end

function suite.migrate_tables_assign()
   local input_str = [[
tab = { "hello", "world" }
]]
   local conf = assert(clod.parse(input_str, "@str", true))
   local tab = conf:get_list('tab')
   assert(#tab == 2)
   assert(tab[1] == "hello")
   assert(tab[2] == "world")
   local output_str = [[
tab["*"] "hello"
tab["*"] "world"
]]
   assert(conf:serialise() == output_str)
end

function suite.migrate_tables_call()
   local input_str = [[
tab { "hello", "world" }
]]
   local conf = assert(clod.parse(input_str, "@str", true))
   local tab = conf:get_list('tab')
   assert(#tab == 2)
   assert(tab[1] == "hello")
   assert(tab[2] == "world")
   local output_str = [[
tab["*"] "hello"
tab["*"] "world"
]]
   assert(conf:serialise() == output_str)
end

function suite.locate_inputs()
   local input_str = [[
foo "bar"
baz "meta"
qux "fish"
]]
   local conf = assert(clod.parse(input_str, "@str", true))
   assert(conf:locate("foo") == 1)
   assert(conf:locate("baz") == 2)
   assert(conf:locate("qux") == 3)
end

function suite.locate_inputs_even_after_delete()
   local input_str = [[
foo "bar"
baz "meta"
qux "fish"
]]
   local conf = assert(clod.parse(input_str, "@str", true))
   assert(conf:locate("foo") == 1)
   conf.settings["baz"] = nil
   assert(conf:locate("qux") == 3)
end


local count_ok = 0
for _, testname in ipairs(testnames) do
--   print("Run: " .. testname)
   local ok, err = xpcall(suite[testname], debug.traceback)
   if not ok then
      print(err)
      print()
   else
      count_ok = count_ok + 1
   end
end

print(tostring(count_ok) .. "/" .. tostring(#testnames) .. " [" .. tostring(total_asserts) .. "] OK")

os.exit(count_ok == #testnames and 0 or 1)
