spec/util_throttle_spec.lua
author Kim Alvefur <zash@zash.se>
Wed, 27 Mar 2024 19:33:11 +0100
changeset 13471 c2a476f4712a
parent 13403 f4415f76727b
permissions -rw-r--r--
util.startup: Fix exiting on pidfile trouble prosody.shutdown() relies on prosody.main_thread, which has not been set yet at this point. Doing a clean shutdown might actually be harmful in case it tears down things set up by the conflicting Prosody, such as the very pidfile we were looking at. Thanks again SigmaTel71 for noticing


-- luacheck: ignore 411/a

-- Mock util.time
local now = 0; -- wibbly-wobbly... timey-wimey... stuff
local function later(n)
	now = now + n; -- time passes at a different rate
end
package.loaded["util.time"] = {
	now = function() return now; end
}


local throttle = require "util.throttle";

describe("util.throttle", function()
	describe("#create()", function()
		it("should be created with correct values", function()
			now = 5;
			local a = throttle.create(3, 10);
			assert.same(a, { balance = 3, max = 3, rate = 0.3, t = 5 });

			local a = throttle.create(3, 5);
			assert.same(a, { balance = 3, max = 3, rate = 0.6, t = 5 });

			local a = throttle.create(1, 1);
			assert.same(a, { balance = 1, max = 1, rate = 1, t = 5 });

			local a = throttle.create(10, 10);
			assert.same(a, { balance = 10, max = 10, rate = 1, t = 5 });

			local a = throttle.create(10, 1);
			assert.same(a, { balance = 10, max = 10, rate = 10, t = 5 });
		end);
	end);

	describe("#update()", function()
		it("does nothing when no time has passed, even if balance is not full", function()
			now = 5;
			local a = throttle.create(10, 10);
			for i=1,5 do
				a:update();
				assert.same(a, { balance = 10, max = 10, rate = 1, t = 5 });
			end
			a.balance = 0;
			for i=1,5 do
				a:update();
				assert.same(a, { balance = 0, max = 10, rate = 1, t = 5 });
			end
		end);
		it("updates only time when time passes but balance is full", function()
			now = 5;
			local a = throttle.create(10, 10);
			for i=1,5 do
				later(5);
				a:update();
				assert.same(a, { balance = 10, max = 10, rate = 1, t = 5 + i*5 });
			end
		end);
		it("updates balance when balance has room to grow as time passes", function()
			now = 5;
			local a = throttle.create(10, 10);
			a.balance = 0;
			assert.same(a, { balance = 0, max = 10, rate = 1, t = 5 });

			later(1);
			a:update();
			assert.same(a, { balance = 1, max = 10, rate = 1, t = 6 });

			later(3);
			a:update();
			assert.same(a, { balance = 4, max = 10, rate = 1, t = 9 });

			later(10);
			a:update();
			assert.same(a, { balance = 10, max = 10, rate = 1, t = 19 });
		end);
		it("handles 10 x 0.1s updates the same as 1 x 1s update ", function()
			now = 5;
			local a = throttle.create(1, 1);

			a.balance = 0;
			later(1);
			a:update();
			assert.same(a, { balance = 1, max = 1, rate = 1, t = now });

			a.balance = 0;
			for i=1,10 do
				later(0.1);
				a:update();
			end
			assert(math.abs(a.balance - 1) < 0.0001); -- incremental updates cause rounding errors
		end);
	end);

	-- describe("po")

	describe("#poll()", function()
		it("should only allow successful polls until cost is hit", function()
			now = 5;

			local a = throttle.create(3, 10);
			assert.same(a, { balance = 3, max = 3, rate = 0.3, t = 5 });

			assert.is_true(a:poll(1));  -- 3 -> 2
			assert.same(a, { balance = 2, max = 3, rate = 0.3, t = 5 });

			assert.is_true(a:poll(2));  -- 2 -> 1
			assert.same(a, { balance = 0, max = 3, rate = 0.3, t = 5 });

			assert.is_false(a:poll(1)); -- MEEP, out of credits!
			assert.is_false(a:poll(1)); -- MEEP, out of credits!
			assert.same(a, { balance = 0, max = 3, rate = 0.3, t = 5 });
		end);

		it("should not allow polls more than the cost", function()
			now = 0;

			local a = throttle.create(10, 10);
			assert.same(a, { balance = 10, max = 10, rate = 1, t = 0 });

			assert.is_false(a:poll(11));
			assert.same(a, { balance = 10, max = 10, rate = 1, t = 0 });

			assert.is_true(a:poll(6));
			assert.same(a, { balance = 4, max = 10, rate = 1, t = 0 });

			assert.is_false(a:poll(5));
			assert.same(a, { balance = 4, max = 10, rate = 1, t = 0 });

			-- fractional
			assert.is_true(a:poll(3.5));
			assert.same(a, { balance = 0.5, max = 10, rate = 1, t = 0 });

			assert.is_true(a:poll(0.25));
			assert.same(a, { balance = 0.25, max = 10, rate = 1, t = 0 });

			assert.is_false(a:poll(0.3));
			assert.same(a, { balance = 0.25, max = 10, rate = 1, t = 0 });

			assert.is_true(a:poll(0.25));
			assert.same(a, { balance = 0, max = 10, rate = 1, t = 0 });

			assert.is_false(a:poll(0.1));
			assert.same(a, { balance = 0, max = 10, rate = 1, t = 0 });

			assert.is_true(a:poll(0));
			assert.same(a, { balance = 0, max = 10, rate = 1, t = 0 });
		end);
	end);
end);