require "bootstrap-tests"

local effil = require "effil"

test.channel_stress.tear_down = default_tear_down

test.channel_stress.with_multiple_threads = function ()
    local exchange_channel, result_channel = effil.channel(), effil.channel()

    local threads_number = 20 * tonumber(os.getenv("STRESS"))
    local threads = {}
    for i = 1, threads_number do
        threads[i] = effil.thread(function(exchange_channel, result_channel, indx)
            if indx % 2 == 0 then
                for i = 1, 10000 do
                    exchange_channel:push(indx .. "_".. i)
                end
            else
                repeat
                    local ret = exchange_channel:pop(10)
                    if ret then
                        result_channel:push(ret)
                    end
                until ret == nil
            end
        end
        )(exchange_channel, result_channel, i)
    end

    local data = {}
    for i = 1, (threads_number / 2) * 10000 do
        local ret = result_channel:pop(10)
        test.is_not_nil(ret)
        test.is_string(ret)
        test.is_nil(data[ret])
        data[ret] = true
    end

    for thr_id = 2, threads_number, 2 do
        for iter = 1, 10000 do
            test.is_true(data[thr_id .. "_".. iter])
        end
    end

    for _, thread in ipairs(threads) do
        thread:wait()
    end
end

test.channel_stress.timed_read = function ()
    local chan = effil.channel()
    local delayed_writer = function(channel, delay)
        require("effil").sleep(delay)
        channel:push("hello!")
    end
    effil.thread(delayed_writer)(chan, 70)

    local function check_time(real_time, use_time, metric, result)
        local start_time = os.time()
        test.equal(chan:pop(use_time, metric), result)
        test.almost_equal(os.time(), start_time + real_time, 2)
    end
    check_time(2, 2, nil, nil) -- second by default
    check_time(2, 2, 's', nil)
    check_time(60, 1, 'm', nil)

    local start_time = os.time()
    test.equal(chan:pop(10), "hello!")
    test.is_true(os.time() < start_time + 10)
end

-- regress for channel returns
test.channel_stress.retun_tables = function ()
    local function worker()
        local effil = require "effil"
        local ch = effil.channel()
        for i = 1, 1000 do
            ch:push(effil.table())
            local ret = { ch:pop() }
        end
    end

    local threads = {}

    for i = 1, 20 do
        table.insert(threads, effil.thread(worker)())
    end
    for _, thr in ipairs(threads) do
        thr:wait()
    end
end

-- regress for multiple wait on channel
test.channel_stress.regress_for_multiple_waiters = function ()
    for i = 1, 20 do
        local chan = effil.channel()
        local function receiver()
            return chan:pop(5) ~= nil
        end

        local threads = {}
        for i = 1, 10 do
            table.insert(threads, effil.thread(receiver)())
        end

        effil.sleep(0.1)
        for i = 1, 100 do
            chan:push(1)
        end

        for _, thr in ipairs(threads) do
            local ret = thr:get()
            test.is_true(ret)
            if not ret then
                return
            end
        end
    end
end
