telescope-frecency.nvim/lua/frecency/tests/frecency_spec.lua
JINNOUCHI Yasushi 1f32091e2b
refactor!: use OO & add tests (#100)
* I did an overhall for all codes and added typing by Lua-language-server and tests. It also works on CI.
* Now it searches files on the workspace completely asynchronously. It does not block your text input. (Fix #106)
Make count = 1 when you open a file you've never opened (Fix #107)
2023-08-06 16:02:37 +09:00

374 lines
15 KiB
Lua

---@diagnostic disable: invisible
local Frecency = require "frecency.frecency"
local Picker = require "frecency.picker"
local util = require "frecency.tests.util"
local Path = require "plenary.path"
local log = require "plenary.log"
---@param files string[]
---@param callback fun(frecency: Frecency, dir: PlenaryPath): nil
---@return nil
local function with_files(files, callback)
local dir, close = util.make_tree(files)
local frecency = Frecency.new { db_root = dir.filename }
frecency.picker = Picker.new(
frecency.database,
frecency.finder,
frecency.fs,
frecency.recency,
{ editing_bufnr = 0, filter_delimiter = ":", show_unindexed = false, workspaces = {} }
)
callback(frecency, dir)
close()
end
local function filepath(dir, file)
return dir:joinpath(file):absolute()
end
---@param frecency Frecency
---@param dir PlenaryPath
---@return fun(file: string, datetime: string, reset: boolean?): nil
local function make_register(frecency, dir)
return function(file, datetime, reset)
local path = filepath(dir, file)
vim.cmd.edit(path)
local bufnr = assert(vim.fn.bufnr(path))
if reset then
frecency.buf_registered[bufnr] = nil
end
frecency:register(bufnr, datetime)
end
end
---comment
---@param frecency Frecency
---@param dir PlenaryPath
---@param callback fun(register: fun(file: string, datetime: string?): nil): nil
---@return nil
local function with_fake_register(frecency, dir, callback)
local bufnr = 0
local buffers = {}
local original_nvim_buf_get_name = vim.api.nvim_buf_get_name
---@diagnostic disable-next-line: redefined-local, duplicate-set-field
vim.api.nvim_buf_get_name = function(bufnr)
return buffers[bufnr]
end
local function register(file, datetime)
local path = filepath(dir, file)
Path.new(path):touch()
bufnr = bufnr + 1
buffers[bufnr] = path
frecency:register(bufnr, datetime)
end
callback(register)
vim.api.nvim_buf_get_name = original_nvim_buf_get_name
end
---@param choice "y"|"n"
---@param callback fun(called: fun(): integer): nil
---@return nil
local function with_fake_vim_ui_select(choice, callback)
local original_vim_ui_select = vim.ui.select
local count = 0
local function called()
return count
end
---@diagnostic disable-next-line: duplicate-set-field
vim.ui.select = function(_, opts, on_choice)
count = count + 1
log.info(opts.prompt)
log.info(opts.format_item(choice))
on_choice(choice)
end
callback(called)
vim.ui.select = original_vim_ui_select
end
describe("frecency", function()
describe("register", function()
describe("when opening files", function()
with_files({ "hoge1.txt", "hoge2.txt" }, function(frecency, dir)
local register = make_register(frecency, dir)
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
register("hoge2.txt", "2023-07-29T01:00:00+09:00")
it("has valid records in DB", function()
local results = frecency.picker:fetch_results(nil, "2023-07-29T02:00:00+09:00")
assert.are.same({
{ count = 1, id = 1, path = filepath(dir, "hoge1.txt"), score = 10 },
{ count = 1, id = 2, path = filepath(dir, "hoge2.txt"), score = 10 },
}, results)
end)
end)
end)
describe("when opening again", function()
with_files({ "hoge1.txt", "hoge2.txt" }, function(frecency, dir)
local register = make_register(frecency, dir)
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
register("hoge2.txt", "2023-07-29T01:00:00+09:00")
register("hoge1.txt", "2023-07-29T02:00:00+09:00", true)
it("increases the score", function()
local results = frecency.picker:fetch_results(nil, "2023-07-29T03:00:00+09:00")
assert.are.same({
{ count = 2, id = 1, path = filepath(dir, "hoge1.txt"), score = 40 },
{ count = 1, id = 2, path = filepath(dir, "hoge2.txt"), score = 10 },
}, results)
end)
end)
end)
describe("when opening again but the same instance", function()
with_files({ "hoge1.txt", "hoge2.txt" }, function(frecency, dir)
local register = make_register(frecency, dir)
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
register("hoge2.txt", "2023-07-29T01:00:00+09:00")
register("hoge1.txt", "2023-07-29T02:00:00+09:00")
it("does not increase the score", function()
local results = frecency.picker:fetch_results(nil, "2023-07-29T03:00:00+09:00")
assert.are.same({
{ count = 1, id = 1, path = filepath(dir, "hoge1.txt"), score = 10 },
{ count = 1, id = 2, path = filepath(dir, "hoge2.txt"), score = 10 },
}, results)
end)
end)
end)
describe("when opening more than 10 times", function()
with_files({ "hoge1.txt", "hoge2.txt" }, function(frecency, dir)
local register = make_register(frecency, dir)
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
register("hoge1.txt", "2023-07-29T00:01:00+09:00", true)
register("hoge1.txt", "2023-07-29T00:02:00+09:00", true)
register("hoge1.txt", "2023-07-29T00:03:00+09:00", true)
register("hoge1.txt", "2023-07-29T00:04:00+09:00", true)
register("hoge1.txt", "2023-07-29T00:05:00+09:00", true)
register("hoge1.txt", "2023-07-29T00:06:00+09:00", true)
register("hoge1.txt", "2023-07-29T00:07:00+09:00", true)
register("hoge1.txt", "2023-07-29T00:08:00+09:00", true)
register("hoge1.txt", "2023-07-29T00:09:00+09:00", true)
register("hoge1.txt", "2023-07-29T00:10:00+09:00", true)
register("hoge1.txt", "2023-07-29T00:11:00+09:00", true)
it("calculates score from the recent 10 times", function()
local results = frecency.picker:fetch_results(nil, "2023-07-29T00:12:00+09:00")
assert.are.same({
{ count = 12, id = 1, path = filepath(dir, "hoge1.txt"), score = 12 * (10 * 100) / 10 },
}, results)
end)
end)
end)
end)
describe("benchmark", function()
describe("after registered over >5000 files", function()
with_files({}, function(frecency, dir)
with_fake_register(frecency, dir, function(register)
local file_count = 6000
if not os.getenv "CI" then
log.info "It works not on CI. Files is decreaed into 10 count."
file_count = 10
end
local expected = {}
for i = 1, file_count do
local file = ("hoge%08d.txt"):format(i)
table.insert(expected, { count = 1, id = i, path = filepath(dir, file), score = 10 })
register(file, "2023-07-29T00:00:00+09:00")
end
local start = os.clock()
local results = frecency.picker:fetch_results(nil, "2023-07-29T00:01:00+09:00")
table.sort(results, function(a, b)
return a.path < b.path
end)
local elapsed = os.clock() - start
log.info(("it takes %f seconds in fetching all results"):format(elapsed))
it("returns appropriate latency (<1.0 second)", function()
assert.are.is_true(elapsed < 1.0)
end)
it("returns valid response", function()
assert.are.same(expected, results)
end)
end)
end)
end)
end)
describe("validate_database", function()
describe("when no files are unlinked", function()
with_files({ "hoge1.txt", "hoge2.txt" }, function(frecency, dir)
local register = make_register(frecency, dir)
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
register("hoge2.txt", "2023-07-29T00:01:00+09:00")
it("removes no entries", function()
local results = frecency.picker:fetch_results(nil, "2023-07-29T02:00:00+09:00")
assert.are.same({
{ count = 1, id = 1, path = filepath(dir, "hoge1.txt"), score = 10 },
{ count = 1, id = 2, path = filepath(dir, "hoge2.txt"), score = 10 },
}, results)
end)
end)
end)
describe("when with not force", function()
describe("when files are unlinked but it is less than threshold", function()
with_files({ "hoge1.txt", "hoge2.txt", "hoge3.txt", "hoge4.txt", "hoge5.txt" }, function(frecency, dir)
local register = make_register(frecency, dir)
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
register("hoge2.txt", "2023-07-29T00:01:00+09:00")
register("hoge3.txt", "2023-07-29T00:02:00+09:00")
register("hoge4.txt", "2023-07-29T00:03:00+09:00")
register("hoge5.txt", "2023-07-29T00:04:00+09:00")
frecency.config.db_validate_threshold = 3
dir:joinpath("hoge1.txt"):rm()
dir:joinpath("hoge2.txt"):rm()
frecency:validate_database()
it("removes no entries", function()
local results = frecency.picker:fetch_results(nil, "2023-07-29T02:00:00+09:00")
table.sort(results, function(a, b)
return a.path < b.path
end)
assert.are.same({
{ count = 1, id = 1, path = filepath(dir, "hoge1.txt"), score = 10 },
{ count = 1, id = 2, path = filepath(dir, "hoge2.txt"), score = 10 },
{ count = 1, id = 3, path = filepath(dir, "hoge3.txt"), score = 10 },
{ count = 1, id = 4, path = filepath(dir, "hoge4.txt"), score = 10 },
{ count = 1, id = 5, path = filepath(dir, "hoge5.txt"), score = 10 },
}, results)
end)
end)
end)
describe("when files are unlinked and it is more than threshold", function()
describe('when the user response "yes"', function()
with_files({ "hoge1.txt", "hoge2.txt", "hoge3.txt", "hoge4.txt", "hoge5.txt" }, function(frecency, dir)
local register = make_register(frecency, dir)
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
register("hoge2.txt", "2023-07-29T00:01:00+09:00")
register("hoge3.txt", "2023-07-29T00:02:00+09:00")
register("hoge4.txt", "2023-07-29T00:03:00+09:00")
register("hoge5.txt", "2023-07-29T00:04:00+09:00")
frecency.config.db_validate_threshold = 3
dir:joinpath("hoge1.txt"):rm()
dir:joinpath("hoge2.txt"):rm()
dir:joinpath("hoge3.txt"):rm()
with_fake_vim_ui_select("y", function(called)
frecency:validate_database()
it("called vim.ui.select()", function()
assert.are.same(1, called())
end)
end)
it("removes entries", function()
local results = frecency.picker:fetch_results(nil, "2023-07-29T02:00:00+09:00")
table.sort(results, function(a, b)
return a.path < b.path
end)
assert.are.same({
{ count = 1, id = 4, path = filepath(dir, "hoge4.txt"), score = 10 },
{ count = 1, id = 5, path = filepath(dir, "hoge5.txt"), score = 10 },
}, results)
end)
end)
end)
describe('when the user response "no"', function()
with_files({ "hoge1.txt", "hoge2.txt", "hoge3.txt", "hoge4.txt", "hoge5.txt" }, function(frecency, dir)
local register = make_register(frecency, dir)
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
register("hoge2.txt", "2023-07-29T00:01:00+09:00")
register("hoge3.txt", "2023-07-29T00:02:00+09:00")
register("hoge4.txt", "2023-07-29T00:03:00+09:00")
register("hoge5.txt", "2023-07-29T00:04:00+09:00")
frecency.config.db_validate_threshold = 3
dir:joinpath("hoge1.txt"):rm()
dir:joinpath("hoge2.txt"):rm()
dir:joinpath("hoge3.txt"):rm()
with_fake_vim_ui_select("n", function(called)
frecency:validate_database()
it("called vim.ui.select()", function()
assert.are.same(1, called())
end)
end)
it("removes no entries", function()
local results = frecency.picker:fetch_results(nil, "2023-07-29T02:00:00+09:00")
table.sort(results, function(a, b)
return a.path < b.path
end)
assert.are.same({
{ count = 1, id = 1, path = filepath(dir, "hoge1.txt"), score = 10 },
{ count = 1, id = 2, path = filepath(dir, "hoge2.txt"), score = 10 },
{ count = 1, id = 3, path = filepath(dir, "hoge3.txt"), score = 10 },
{ count = 1, id = 4, path = filepath(dir, "hoge4.txt"), score = 10 },
{ count = 1, id = 5, path = filepath(dir, "hoge5.txt"), score = 10 },
}, results)
end)
end)
end)
end)
end)
describe("when with force", function()
describe("when db_safe_mode is true", function()
with_files({ "hoge1.txt", "hoge2.txt" }, function(frecency, dir)
local register = make_register(frecency, dir)
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
register("hoge2.txt", "2023-07-29T00:01:00+09:00")
dir:joinpath("hoge1.txt"):rm()
with_fake_vim_ui_select("y", function(called)
frecency:validate_database(true)
it("called vim.ui.select()", function()
assert.are.same(1, called())
end)
end)
it("needs confirmation for removing entries", function()
local results = frecency.picker:fetch_results(nil, "2023-07-29T02:00:00+09:00")
assert.are.same({
{ count = 1, id = 2, path = filepath(dir, "hoge2.txt"), score = 10 },
}, results)
end)
end)
end)
describe("when db_safe_mode is false", function()
with_files({ "hoge1.txt", "hoge2.txt" }, function(frecency, dir)
local register = make_register(frecency, dir)
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
register("hoge2.txt", "2023-07-29T00:01:00+09:00")
dir:joinpath("hoge1.txt"):rm()
with_fake_vim_ui_select("y", function(called)
frecency.config.db_safe_mode = false
frecency:validate_database(true)
it("did not call vim.ui.select()", function()
assert.are.same(0, called())
end)
end)
it("needs no confirmation for removing entries", function()
local results = frecency.picker:fetch_results(nil, "2023-07-29T02:00:00+09:00")
assert.are.same({
{ count = 1, id = 2, path = filepath(dir, "hoge2.txt"), score = 10 },
}, results)
end)
end)
end)
end)
end)
end)