version_spec.lua 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. local helpers = require('test.functional.helpers')(after_each)
  2. local mpack = require('mpack')
  3. local clear, funcs, eq = helpers.clear, helpers.funcs, helpers.eq
  4. local call = helpers.call
  5. local meths = helpers.meths
  6. local function read_mpack_file(fname)
  7. local fd = io.open(fname, 'rb')
  8. if fd == nil then
  9. return nil
  10. end
  11. local data = fd:read('*a')
  12. fd:close()
  13. local unpack = mpack.Unpacker()
  14. return unpack(data)
  15. end
  16. describe("api_info()['version']", function()
  17. before_each(clear)
  18. it("returns API level", function()
  19. local version = call('api_info')['version']
  20. local current = version['api_level']
  21. local compat = version['api_compatible']
  22. eq("number", type(current))
  23. eq("number", type(compat))
  24. assert(current >= compat)
  25. end)
  26. it("returns Nvim version", function()
  27. local version = call('api_info')['version']
  28. local major = version['major']
  29. local minor = version['minor']
  30. local patch = version['patch']
  31. eq("number", type(major))
  32. eq("number", type(minor))
  33. eq("number", type(patch))
  34. eq(1, funcs.has("nvim-"..major.."."..minor.."."..patch))
  35. eq(0, funcs.has("nvim-"..major.."."..minor.."."..(patch + 1)))
  36. eq(0, funcs.has("nvim-"..major.."."..(minor + 1).."."..patch))
  37. eq(0, funcs.has("nvim-"..(major + 1).."."..minor.."."..patch))
  38. end)
  39. end)
  40. describe("api metadata", function()
  41. before_each(clear)
  42. local function name_table(entries)
  43. local by_name = {}
  44. for _,e in ipairs(entries) do
  45. by_name[e.name] = e
  46. end
  47. return by_name
  48. end
  49. -- Remove metadata that is not essential to backwards-compatibility.
  50. local function filter_function_metadata(f)
  51. f.deprecated_since = nil
  52. for idx, _ in ipairs(f.parameters) do
  53. f.parameters[idx][2] = '' -- Remove parameter name.
  54. end
  55. if string.sub(f.name, 1, 4) ~= "nvim" then
  56. f.method = nil
  57. end
  58. return f
  59. end
  60. local function check_ui_event_compatible(old_e, new_e)
  61. -- check types of existing params are the same
  62. -- adding parameters is ok, but removing params is not (gives nil error)
  63. eq(old_e.since, new_e.since, old_e.name)
  64. for i,p in ipairs(old_e.parameters) do
  65. eq(new_e.parameters[i][1], p[1], old_e.name)
  66. end
  67. end
  68. -- Level 0 represents methods from 0.1.5 and earlier, when 'since' was not
  69. -- yet defined, and metadata was not filtered of internal keys like 'async'.
  70. local function clean_level_0(metadata)
  71. for _, f in ipairs(metadata.functions) do
  72. f.can_fail = nil
  73. f.async = nil
  74. f.receives_channel_id = nil
  75. f.since = 0
  76. end
  77. end
  78. local api, compat, stable, api_level
  79. local old_api = {}
  80. setup(function()
  81. api = meths.get_api_info()[2]
  82. compat = api.version.api_compatible
  83. api_level = api.version.api_level
  84. if api.version.api_prerelease then
  85. stable = api_level-1
  86. else
  87. stable = api_level
  88. end
  89. for level = compat, stable do
  90. local path = ('test/functional/fixtures/api_level_'..
  91. tostring(level)..'.mpack')
  92. old_api[level] = read_mpack_file(path)
  93. if old_api[level] == nil then
  94. local errstr = "missing metadata fixture for stable level "..level..". "
  95. if level == api_level and not api.version.api_prerelease then
  96. errstr = (errstr.."If NVIM_API_CURRENT was bumped, "..
  97. "don't forget to set NVIM_API_PRERELEASE to true.")
  98. end
  99. error(errstr)
  100. end
  101. if level == 0 then
  102. clean_level_0(old_api[level])
  103. end
  104. end
  105. end)
  106. it("functions are compatible with old metadata or have new level", function()
  107. local funcs_new = name_table(api.functions)
  108. local funcs_compat = {}
  109. for level = compat, stable do
  110. for _,f in ipairs(old_api[level].functions) do
  111. if funcs_new[f.name] == nil then
  112. if f.since >= compat then
  113. error('function '..f.name..' was removed but exists in level '..
  114. f.since..' which nvim should be compatible with')
  115. end
  116. else
  117. eq(filter_function_metadata(f),
  118. filter_function_metadata(funcs_new[f.name]))
  119. end
  120. end
  121. funcs_compat[level] = name_table(old_api[level].functions)
  122. end
  123. for _,f in ipairs(api.functions) do
  124. if f.since <= stable then
  125. local f_old = funcs_compat[f.since][f.name]
  126. if f_old == nil then
  127. if string.sub(f.name, 1, 4) == "nvim" then
  128. local errstr = ("function "..f.name.." has too low since value. "..
  129. "For new functions set it to "..(stable+1)..".")
  130. if not api.version.api_prerelease then
  131. errstr = (errstr.." Also bump NVIM_API_CURRENT and set "..
  132. "NVIM_API_PRERELEASE to true in CMakeLists.txt.")
  133. end
  134. error(errstr)
  135. else
  136. error("function name '"..f.name.."' doesn't begin with 'nvim_'")
  137. end
  138. end
  139. elseif f.since > api_level then
  140. if api.version.api_prerelease then
  141. error("New function "..f.name.." should use since value "..
  142. api_level)
  143. else
  144. error("function "..f.name.." has since value > api_level. "..
  145. "Bump NVIM_API_CURRENT and set "..
  146. "NVIM_API_PRERELEASE to true in CMakeLists.txt.")
  147. end
  148. end
  149. end
  150. end)
  151. it("UI events are compatible with old metadata or have new level", function()
  152. local ui_events_new = name_table(api.ui_events)
  153. local ui_events_compat = {}
  154. -- UI events were formalized in level 3
  155. for level = 3, stable do
  156. for _,e in ipairs(old_api[level].ui_events) do
  157. local new_e = ui_events_new[e.name]
  158. if new_e ~= nil then
  159. check_ui_event_compatible(e, new_e)
  160. end
  161. end
  162. ui_events_compat[level] = name_table(old_api[level].ui_events)
  163. end
  164. for _,e in ipairs(api.ui_events) do
  165. if e.since <= stable then
  166. local e_old = ui_events_compat[e.since][e.name]
  167. if e_old == nil then
  168. local errstr = ("UI event "..e.name.." has too low since value. "..
  169. "For new events set it to "..(stable+1)..".")
  170. if not api.version.api_prerelease then
  171. errstr = (errstr.." Also bump NVIM_API_CURRENT and set "..
  172. "NVIM_API_PRERELEASE to true in CMakeLists.txt.")
  173. end
  174. error(errstr)
  175. end
  176. elseif e.since > api_level then
  177. if api.version.api_prerelease then
  178. error("New UI event "..e.name.." should use since value "..
  179. api_level)
  180. else
  181. error("UI event "..e.name.." has since value > api_level. "..
  182. "Bump NVIM_API_CURRENT and set "..
  183. "NVIM_API_PRERELEASE to true in CMakeLists.txt.")
  184. end
  185. end
  186. end
  187. end)
  188. it("ui_options are preserved from older levels", function()
  189. local available_options = {}
  190. for _, option in ipairs(api.ui_options) do
  191. available_options[option] = true
  192. end
  193. -- UI options were versioned from level 4
  194. for level = 4, stable do
  195. for _, option in ipairs(old_api[level].ui_options) do
  196. if not available_options[option] then
  197. error("UI option "..option.." from stable metadata is missing")
  198. end
  199. end
  200. end
  201. end)
  202. end)