profile_spec.lua (7252B)
1 local t = require('test.unit.testutil') 2 local itp = t.gen_itp(it) 3 4 local cimport = t.cimport 5 local ffi = t.ffi 6 local eq = t.eq 7 local neq = t.neq 8 9 local prof = cimport('./src/nvim/profile.h') 10 11 local function split(inputstr, sep) 12 if sep == nil then 13 sep = '%s' 14 end 15 16 local q, i = {}, 1 17 for str in string.gmatch(inputstr, '([^' .. sep .. ']+)') do 18 q[i] = str 19 i = i + 1 20 end 21 22 return q 23 end 24 25 local function trim(s) 26 local from = s:match '^%s*()' 27 return from > #s and '' or s:match('.*%S', from) 28 end 29 30 local function starts(str, start) 31 return string.sub(str, 1, string.len(start)) == start 32 end 33 34 local function cmp_assert(v1, v2, op, opstr) 35 local res = op(v1, v2) 36 if res == false then 37 print(string.format('expected: %f %s %f', v1, opstr, v2)) 38 end 39 assert.is_true(res) 40 end 41 42 local function lt(a, b) -- luacheck: ignore 43 cmp_assert(a, b, function(x, y) 44 return x < y 45 end, '<') 46 end 47 48 local function lte(a, b) -- luacheck: ignore 49 cmp_assert(a, b, function(x, y) 50 return x <= y 51 end, '<=') 52 end 53 54 local function gt(a, b) -- luacheck: ignore 55 cmp_assert(a, b, function(x, y) 56 return x > y 57 end, '>') 58 end 59 60 local function gte(a, b) 61 cmp_assert(a, b, function(x, y) 62 return x >= y 63 end, '>=') 64 end 65 66 -- missing functions: 67 -- profile_self 68 -- profile_get_wait 69 -- profile_set_wait 70 -- profile_sub_wait 71 describe('profiling related functions', function() 72 local function profile_start() 73 return prof.profile_start() 74 end 75 local function profile_end(q) 76 return prof.profile_end(q) 77 end 78 local function profile_zero() 79 return prof.profile_zero() 80 end 81 local function profile_setlimit(ms) 82 return prof.profile_setlimit(ms) 83 end 84 local function profile_passed_limit(q) 85 return prof.profile_passed_limit(q) 86 end 87 local function profile_add(t1, t2) 88 return prof.profile_add(t1, t2) 89 end 90 local function profile_sub(t1, t2) 91 return prof.profile_sub(t1, t2) 92 end 93 local function profile_divide(q, cnt) 94 return prof.profile_divide(q, cnt) 95 end 96 local function profile_cmp(t1, t2) 97 return prof.profile_cmp(t1, t2) 98 end 99 local function profile_msg(q) 100 return ffi.string(prof.profile_msg(q)) 101 end 102 103 local function toseconds(q) -- luacheck: ignore 104 local str = trim(profile_msg(q)) 105 local spl = split(str, '.') 106 local s, us = spl[1], spl[2] 107 return tonumber(s) + tonumber(us) / 1000000 108 end 109 110 -- this is quite difficult to test, as it would rely on other functions in 111 -- the profiling package. Those functions in turn will probably be tested 112 -- using profile_cmp... circular reasoning. 113 describe('profile_cmp', function() 114 itp('can compare subsequent starts', function() 115 local s1, s2 = profile_start(), profile_start() 116 assert.is_true(profile_cmp(s1, s2) > 0) 117 assert.is_true(profile_cmp(s2, s1) < 0) 118 end) 119 120 itp('can compare the zero element', function() 121 assert.is_true(profile_cmp(profile_zero(), profile_zero()) == 0) 122 end) 123 124 itp('correctly orders divisions', function() 125 local start = profile_start() 126 assert.is_true(profile_cmp(start, profile_divide(start, 10)) <= 0) 127 end) 128 end) 129 130 describe('profile_divide', function() 131 itp('actually performs division', function() 132 -- note: the routine actually performs floating-point division to get 133 -- better rounding behaviour, we have to take that into account when 134 -- checking. (check range, not exact number). 135 local divisor = 10 136 137 local start = profile_start() 138 local divided = profile_divide(start, divisor) 139 140 local res = divided 141 for _ = 1, divisor - 1 do 142 res = profile_add(res, divided) 143 end 144 145 -- res should be in the range [start - divisor, start + divisor] 146 local start_min, start_max = profile_sub(start, divisor), profile_add(start, divisor) 147 assert.is_true(profile_cmp(start_min, res) >= 0) 148 assert.is_true(profile_cmp(start_max, res) <= 0) 149 end) 150 end) 151 152 describe('profile_zero', function() 153 itp('returns the same value on each call', function() 154 eq(0, profile_zero()) 155 end) 156 end) 157 158 describe('profile_start', function() 159 itp('increases', function() 160 local last = profile_start() 161 for _ = 1, 100 do 162 local curr = profile_start() 163 gte(curr, last) 164 last = curr 165 end 166 end) 167 end) 168 169 describe('profile_end', function() 170 itp('the elapsed time cannot be zero', function() 171 neq(profile_zero(), profile_end(profile_start())) 172 end) 173 174 itp('outer elapsed >= inner elapsed', function() 175 for _ = 1, 100 do 176 local start_outer = profile_start() 177 local start_inner = profile_start() 178 local elapsed_inner = profile_end(start_inner) 179 local elapsed_outer = profile_end(start_outer) 180 181 gte(elapsed_outer, elapsed_inner) 182 end 183 end) 184 end) 185 186 describe('profile_setlimit', function() 187 itp('sets no limit when 0 is passed', function() 188 eq(profile_setlimit(0), profile_zero()) 189 end) 190 191 itp('sets a limit in the future otherwise', function() 192 local future = profile_setlimit(1000) 193 local now = profile_start() 194 assert.is_true(profile_cmp(future, now) < 0) 195 end) 196 end) 197 198 describe('profile_passed_limit', function() 199 itp('start is in the past', function() 200 local start = profile_start() 201 eq(true, profile_passed_limit(start)) 202 end) 203 204 itp('start + start is in the future', function() 205 local start = profile_start() 206 local future = profile_add(start, start) 207 eq(false, profile_passed_limit(future)) 208 end) 209 end) 210 211 describe('profile_msg', function() 212 itp('prints the zero time as 0.00000', function() 213 local str = trim(profile_msg(profile_zero())) 214 eq('0.000000', str) 215 end) 216 217 itp('prints the time passed, in seconds.microsends', function() 218 local start = profile_start() 219 local endt = profile_end(start) 220 local str = trim(profile_msg(endt)) 221 local spl = split(str, '.') 222 223 -- string has two parts (before dot and after dot) 224 eq(2, #spl) 225 226 local s, us = spl[1], spl[2] 227 228 -- zero seconds have passed (if this is not true, either LuaJIT is too 229 -- slow or the profiling functions are too slow and need to be fixed) 230 eq('0', s) 231 232 -- more or less the same goes for the microsecond part, if it doesn't 233 -- start with 0, it's too slow. 234 assert.is_true(starts(us, '0')) 235 end) 236 end) 237 238 describe('profile_add', function() 239 itp('adds profiling times', function() 240 local start = profile_start() 241 assert.equals(start, profile_add(profile_zero(), start)) 242 end) 243 end) 244 245 describe('profile_sub', function() 246 itp('subtracts profiling times', function() 247 -- subtracting zero does nothing 248 local start = profile_start() 249 assert.equals(start, profile_sub(start, profile_zero())) 250 251 local start1, start2, start3 = profile_start(), profile_start(), profile_start() 252 local cmp = profile_cmp(profile_sub(start2, start1), profile_sub(start3, start1)) 253 -- t2 >= t1 => profile_cmp(t1, t2) >= 0 254 assert.is_true(cmp >= 0) 255 256 cmp = profile_cmp(profile_sub(start3, start1), profile_sub(start2, start1)) 257 -- t2 <= t1 => profile_cmp(t1, t2) <= 0 258 assert.is_true(cmp <= 0) 259 end) 260 end) 261 end)