summaryrefslogtreecommitdiff
path: root/indra/newview/scripts/lua/qtest.lua
blob: 009446d0c3160b748e4b1a8227354e5f0a718895 (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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
-- Exercise the Queue, WaitQueue, ErrorQueue family

Queue = require('Queue')
WaitQueue = require('WaitQueue')
ErrorQueue = require('ErrorQueue')
util = require('util')

inspect = require('inspect')

-- resume() wrapper to propagate errors
function resume(co, ...)
    -- if there's an idiom other than table.pack() to assign an arbitrary
    -- number of return values, I don't yet know it
    local ok_result = table.pack(coroutine.resume(co, ...))
    if not ok_result[1] then
        -- if [1] is false, then [2] is the error message
        error(ok_result[2])
    end
    -- ok is true, whew, just return the rest of the values
    return table.unpack(ok_result, 2)
end

-- ------------------ Queue variables are instance-specific ------------------
q1 = Queue:new()
q2 = Queue:new()

q1:Enqueue(17)

assert(not q1:IsEmpty())
assert(q2:IsEmpty())
assert(q1:Dequeue() == 17)
assert(q1:Dequeue() == nil)
assert(q2:Dequeue() == nil)

-- ----------------------------- test WaitQueue ------------------------------
q1 = WaitQueue:new()
q2 = WaitQueue:new()
result = {}
values = { 1, 1, 2, 3, 5, 8, 13, 21 }

for i, value in pairs(values) do
    q1:Enqueue(value)
end
-- close() while not empty tests that queue drains before reporting done
q1:close()

-- ensure that WaitQueue instance variables are in fact independent
assert(q2:IsEmpty())

-- consumer() coroutine to pull from the passed q until closed
function consumer(desc, q)
    print(string.format('consumer(%s) start', desc))
    local value = q:Dequeue()
    while value ~= nil do
        print(string.format('consumer(%s) got %q', desc, value))
        table.insert(result, value)
        value = q:Dequeue()
    end
    print(string.format('consumer(%s) done', desc))
end

-- run two consumers
coa = coroutine.create(consumer)
cob = coroutine.create(consumer)
-- Since consumer() doesn't yield while it can still retrieve values,
-- consumer(a) will dequeue all values from q1 and return when done.
resume(coa, 'a', q1)
-- consumer(b) will wake up to find the queue empty and closed.
resume(cob, 'b', q1)
coroutine.close(coa)
coroutine.close(cob)

print('values:', inspect(values))
print('result:', inspect(result))

assert(util.equal(values, result))

-- try incrementally enqueueing values
q3 = WaitQueue:new()
result = {}
values = { 'This', 'is', 'a', 'test', 'script' }

coros = {}
for _, name in {'a', 'b'} do
    local coro = coroutine.create(consumer)
    table.insert(coros, coro)
    -- Resuming both coroutines should leave them both waiting for a queue item.
    resume(coro, name, q3)
end

for _, s in pairs(values) do
    print(string.format('Enqueue(%q)', s))
    q3:Enqueue(s)
end
q3:close()

function joinall(coros)
    local running
    local errors = 0
    repeat
        running = false
        for i, coro in pairs(coros) do
            if coroutine.status(coro) == 'suspended' then
                running = true
                -- directly call coroutine.resume() instead of our resume()
                -- wrapper because we explicitly check for errors here
                local ok, message = coroutine.resume(coro)
                if not ok then
                    print('*** ' .. message)
                    errors += 1
                end
                if coroutine.status(coro) == 'dead' then
                    coros[i] = nil
                end
            end
        end
    until not running
    return errors
end

joinall(coros)

print(string.format('%q', table.concat(result, ' ')))
assert(util.equal(values, result))

-- ----------------------------- test ErrorQueue -----------------------------
q4 = ErrorQueue:new()
result = {}
values = { 'This', 'is', 'a', 'test', 'script' }

coros = {}
for _, name in {'a', 'b'} do
    local coro = coroutine.create(consumer)
    table.insert(coros, coro)
    -- Resuming both coroutines should leave them both waiting for a queue item.
    resume(coro, name, q4)
end

for i = 1, 4 do
    print(string.format('Enqueue(%q)', values[i]))
    q4:Enqueue(values[i])
end
q4:Error('something went wrong')

assert(joinall(coros) == 2)