summaryrefslogtreecommitdiffstats
path: root/test/lua/busted/context.lua
blob: 4cf0f3b53f5d1073820d4882e2a03c4643b225e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
local tablex = require 'pl.tablex'

local function save()
  local g = {}
  for k,_ in next, _G, nil do
    g[k] = rawget(_G, k)
  end
  return {
    gmt = getmetatable(_G),
    g = g,
    loaded = tablex.copy(package.loaded)
  }
end

local function restore(state)
  setmetatable(_G, state.gmt)
  for k,_ in next, _G, nil do
    rawset(_G, k, state.g[k])
  end
  for k,_ in pairs(package.loaded) do
    package.loaded[k] = state.loaded[k]
  end
end

return function()
  local context = {}

  local data = {}
  local parents = {}
  local children = {}
  local stack = {}

  function context.ref()
    local ref = {}
    local ctx = data

    function ref.get(key)
      if not key then return ctx end
      return ctx[key]
    end

    function ref.set(key, value)
      ctx[key] = value
    end

    function ref.clear()
      data = {}
      parents = {}
      children = {}
      stack = {}
      ctx = data
    end

    function ref.attach(child)
      if not children[ctx] then children[ctx] = {} end
      parents[child] = ctx
      table.insert(children[ctx], child)
    end

    function ref.children(parent)
      return children[parent] or {}
    end

    function ref.parent(child)
      return parents[child]
    end

    function ref.push(current)
      if not parents[current] then error('Detached child. Cannot push.') end
      if ctx ~= current and current.descriptor == 'file' then
        current.state = save()
      end
      table.insert(stack, ctx)
      ctx = current
    end

    function ref.pop()
      local current = ctx
      ctx = table.remove(stack)
      if ctx ~= current and current.state then
        restore(current.state)
        current.state = nil
      end
      if not ctx then error('Context stack empty. Cannot pop.') end
    end

    return ref
  end

  return context
end