From f25797f86976cbccf697d6df953154c5e59656e6 Mon Sep 17 00:00:00 2001 From: Rui Abreu Ferreira Date: Sun, 25 Sep 2016 18:46:37 +0100 Subject: [PATCH 1/2] api: Nvim version, API level #5386 The API level is disconnected from the NVIM version. The API metadata holds the current API level, and the lowest backwards-compatible level supported by this instance. Release 0.1.6 will be the first release reporting the Nvim version and API level. metadata['version'] = { major: 0, minor: 1, patch: 6, prerelease: true, api_level: 1, api_compatible: 0, } The API level may remain unchanged across Neovim releases if the API has not changed. When changing the API the CMake variable NVIM_API_PRERELEASE is set to true, and NVIM_API_CURRENT/NVIM_API_COMPATIBILITY are incremented accordingly. The functional tests check the API table against fixtures of past versions of Neovim. It compares all the functions in the old table with the new one, it does ignore some metadata attributes that do not alter the function signature or were removed since 0.1.5. Currently the only fixture is 0.mpack, generated from Neovim 0.1.5 with nvim --api-info. --- CMakeLists.txt | 6 ++ config/versiondef.h.in | 4 ++ runtime/doc/api.txt | 1 + runtime/doc/msgpack_rpc.txt | 3 + scripts/release.sh | 2 + src/nvim/api/private/helpers.c | 13 ++++ test/functional/api/compatibility_spec.lua | 65 ++++++++++++++++++ test/functional/eval/api_functions_spec.lua | 3 +- .../eval/msgpack_functions_spec.lua | 2 +- test/functional/fixtures/api-info/0.mpack | Bin 0 -> 7873 bytes 10 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 test/functional/api/compatibility_spec.lua create mode 100644 test/functional/fixtures/api-info/0.mpack diff --git a/CMakeLists.txt b/CMakeLists.txt index 4aa6a2de32..3b139025d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,12 @@ set(NVIM_VERSION_MINOR 1) set(NVIM_VERSION_PATCH 6) set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers +# Neovim API version. When changing the API, bump CURRENT if +# PRERELEASE is false, and set PRERELEASE as true +set(NVIM_API_CURRENT 1) +set(NVIM_API_COMPATIBILITY 0) +set(NVIM_API_PRERELEASE true) + file(TO_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR}/.git FORCED_GIT_DIR) include(GetGitRevisionDescription) get_git_head_revision(GIT_REFSPEC NVIM_VERSION_COMMIT) diff --git a/config/versiondef.h.in b/config/versiondef.h.in index c91bb29c90..465d90b674 100644 --- a/config/versiondef.h.in +++ b/config/versiondef.h.in @@ -7,6 +7,10 @@ #define NVIM_VERSION_PRERELEASE "@NVIM_VERSION_PRERELEASE@" #cmakedefine NVIM_VERSION_MEDIUM "@NVIM_VERSION_MEDIUM@" +#define NVIM_API_CURRENT @NVIM_API_CURRENT@ +#define NVIM_API_COMPATIBILITY @NVIM_API_COMPATIBILITY@ +#cmakedefine NVIM_API_PRERELEASE + #define NVIM_VERSION_CFLAGS "@NVIM_VERSION_CFLAGS@" #define NVIM_VERSION_BUILD_TYPE "@NVIM_VERSION_BUILD_TYPE@" diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index c3d7fdb35b..dd48a0c2a5 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -51,6 +51,7 @@ Tabpage -> enum value kObjectTypeTabpage Nvim exposes metadata about the API as a Dictionary with the following keys: +api_level API version compatibility information functions calling signature of the API functions types The custom handle types defined by Nvim error_types The possible kinds of errors an API function can exit with. diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt index b3fed9e756..bfd4100f15 100644 --- a/runtime/doc/msgpack_rpc.txt +++ b/runtime/doc/msgpack_rpc.txt @@ -168,6 +168,9 @@ API metadata object ~ API clients exist to hide msgpack-rpc details. The API metadata object contains information that makes this task easier (see also |rpc-types|): + - The "api_level" key contais API compatibility information. The "current" + key holds the API version supported Neovim. The "compatibility" key holds + the oldest supported API version. - The "functions" key contains a list of metadata objects for individual functions. - Each function metadata object has |rpc-types| information about the return diff --git a/scripts/release.sh b/scripts/release.sh index 5a5b5a6498..0dd840074a 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -5,6 +5,7 @@ # Steps: # Create the "release" commit: # - CMakeLists.txt: Unset NVIM_VERSION_PRERELEASE +# - CMakeLists.txt: Unset NVIM_API_PRERELEASE # - Tag the commit. # Create the "version bump" commit: # - CMakeLists.txt: Set NVIM_VERSION_PRERELEASE to "-dev" @@ -46,6 +47,7 @@ __BUMP_MSG="version bump" echo "Most recent tag: ${__LAST_TAG}" echo "Release version: ${__VERSION}" $__sed -i.bk 's/(NVIM_VERSION_PRERELEASE) "-dev"/\1 ""/' CMakeLists.txt +$__sed -i.bk 's/(NVIM_API_PRERELEASE) true/\1 false/' CMakeLists.txt echo "Building changelog since ${__LAST_TAG}..." __CHANGELOG="$(./scripts/git-log-pretty-since.sh "$__LAST_TAG" 'vim-patch:\S')" diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index c0ee735d1a..7fda2802c8 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -20,6 +20,7 @@ #include "nvim/option_defs.h" #include "nvim/eval/typval_encode.h" #include "nvim/lib/kvec.h" +#include "auto/versiondef.h" /// Helper structure for vim_to_object typedef struct { @@ -766,6 +767,7 @@ Dictionary api_metadata(void) init_function_metadata(&metadata); init_error_type_metadata(&metadata); init_type_metadata(&metadata); + init_api_level_metadata(&metadata); } return copy_object(DICTIONARY_OBJ(metadata)).data.dictionary; @@ -825,6 +827,17 @@ static void init_type_metadata(Dictionary *metadata) PUT(*metadata, "types", DICTIONARY_OBJ(types)); } +static void init_api_level_metadata(Dictionary *metadata) +{ + Dictionary version = ARRAY_DICT_INIT; + PUT(version, "current", INTEGER_OBJ(NVIM_API_CURRENT)); + PUT(version, "compatibility", INTEGER_OBJ(NVIM_API_COMPATIBILITY)); +#ifdef NVIM_API_PRERELEASE + PUT(version, "prerelease", BOOLEAN_OBJ(true)); +#endif + + PUT(*metadata, "api_level", DICTIONARY_OBJ(version)); +} /// Creates a deep clone of an object Object copy_object(Object obj) diff --git a/test/functional/api/compatibility_spec.lua b/test/functional/api/compatibility_spec.lua new file mode 100644 index 0000000000..e0fc625b46 --- /dev/null +++ b/test/functional/api/compatibility_spec.lua @@ -0,0 +1,65 @@ + +local helpers = require('test.functional.helpers')(after_each) +local lfs = require('lfs') +local mpack = require('mpack') +local clear, eq, neq = helpers.clear, helpers.eq, helpers.neq + +local read_mpack_file = function(fname) + local fd = io.open(fname, 'rb') + local data = fd:read('*a') + fd:close() + local unpack = mpack.Unpacker() + return unpack(data) +end + +-- ignore metadata in API function spec +local remove_function_metadata = function(fspec) + fspec['can_fail'] = nil + fspec['async'] = nil + fspec['method'] = nil + fspec['since'] = nil + fspec['deprecated_since'] = nil + fspec['receives_channel_id'] = nil + for idx,_ in ipairs(fspec['parameters']) do + fspec['parameters'][idx][2] = '' + end +end + +clear() +local api_level = helpers.call('api_info')['api_level'] + +describe('api compatibility', function() + before_each(clear) + + it("version metadata is sane", function() + local info = helpers.call('api_info') + local current = info['api_level']['current'] + local compatibility = info['api_level']['compatibility'] + neq(current, nil) + neq(compatibility, nil) + assert(current >= compatibility) + end) + + for ver = api_level['compatibility'], api_level['current'] do + local path = 'test/functional/fixtures/api-info/' .. tostring(ver) .. '.mpack' + it('are backwards compatible with api level '..ver, function() + if lfs.attributes(path,"mode") ~= "file" then + pending("No fixture found, skipping test") + return + end + + local old_api = read_mpack_file(path) + local api = helpers.call('api_info') + + for _, fspec in ipairs(old_api['functions']) do + remove_function_metadata(fspec) + for _, fspec_new in ipairs(api['functions']) do + if fspec['name'] == fspec_new['name'] then + remove_function_metadata(fspec_new) + eq(fspec, fspec_new) + end + end + end + end) + end +end) diff --git a/test/functional/eval/api_functions_spec.lua b/test/functional/eval/api_functions_spec.lua index 2ef67e7808..5345c9782d 100644 --- a/test/functional/eval/api_functions_spec.lua +++ b/test/functional/eval/api_functions_spec.lua @@ -106,7 +106,7 @@ describe('api functions', function() it('have metadata accessible with api_info()', function() local api_keys = eval("sort(keys(api_info()))") - eq({'error_types', 'functions', 'types'}, api_keys) + eq({'api_level', 'error_types', 'functions', 'types'}, api_keys) end) it('are highlighted by vim.vim syntax file', function() @@ -144,5 +144,4 @@ describe('api functions', function() ]]) screen:detach() end) - end) diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua index 5b87b05652..ae7e1b5609 100644 --- a/test/functional/eval/msgpack_functions_spec.lua +++ b/test/functional/eval/msgpack_functions_spec.lua @@ -460,7 +460,7 @@ describe('msgpackparse() function', function() eval(cmd) eval(cmd) -- do it again (try to force segfault) local api_info = eval(cmd) -- do it again - eq({'error_types', 'functions', 'types'}, api_info) + eq({'api_level', 'error_types', 'functions', 'types'}, api_info) end) it('fails when called with no arguments', function() diff --git a/test/functional/fixtures/api-info/0.mpack b/test/functional/fixtures/api-info/0.mpack new file mode 100644 index 0000000000000000000000000000000000000000..75b236a3c11ac8f492e6798e8360221c973a9636 GIT binary patch literal 7873 zcmb_h%aYqf6b*a{OH##(KVS+LtWs1`umRICl2rd%-Eg>GoHyL zVbA;qA49)=Taw$7n8hsQk@yI*0x$di9A84(rBkyw;u^K~xBgs_VeGx=>n7=OK7A8CxWfik#Ti#s7g zV@FZfPX(j-1b{AUpi5_XMe~%r2yO5#HcsaSO@W0saSKb!=~M+KG?$X>uuFAwog8{! z3mW1XO-M9!+bi*_B_~Rz5nuJBErtibY%-K*e>tLf^JH3PE>?xn}YBnPAI&i}q6vAQE zP!$o6R6{l5pei^ALF=K3Civ$-8ttgGs;Sl0sUYRDwz5Ef+PPlY?xHy-EH1@$ri~=J zsGnvsMUBp0DPiNLi5=2uc;AhheO;}NtUhlM|`=|4y$eh?&k2dNfFLWO)+eFGE>&UmBo4p$6 z$a)9$9v15Jnf}c4xf6I);t4@2TgOrWi{rRbc;)#{zYLT0dbxRK&^8v;76i00WGdES zT{d)A)NBkL^idmaqkikW8anHv7L8YLosC^~qTRGMNdbl3%*nwqbK{J*&Y3#vUv(1B zD~hlXIGOJF-7%67Iwu?@Q=I2g5oy|X3aFR&8{bWTSPWUXUD*GJ@D- zv}2Kyhhg!`Kwe451Y00RIQ;=ANLT0DmKN8Z?e3Y(U(V@Fo}gwkoTs~xww$5r(UVGG zlT7FM4n^^JIH6f|%&WnP(Oz(3(NU2v1WBoDA?FZ#lM!7&tUKr3BkqHx1nA}$; zkngnja+XNBN~xSKqF{?C$NkMAzYjpwcugwC)THTW=LZR4t|TD$%*zF(_KRCpbZM@;Ht0orCpbHxcaHaf^a}FIdDoCeJZpR>u{U3}Q#Y2wwf(ay{irS5!85#{i^F!rG z-xvl%W$Q9z_U&Mr1TnGKjg5f>M}ols;jta1Cl_pZ^J3qTWa!%q&w!lw&@S7MrT5To z?T}soVwXbrT}>Q%XfPmznT`z)&5M0KH1yZz%K^C?cm8{3P7Xm5by+Byb|c-NMYokg zC;xUO;0C8fX?m&Ogn6`Y_H@&c2v0w{$9Og338N#@b#JWqxSts-GWU0Ux&*5Hp}~U$ zIhlu%)oX~_ACM^IlA5*ms~+wF*2S0dmQ`L^VYX<%>1tF^^3I@arpT89E~(rl$3-NN z_wXXcY&cE8TT_gBig##RJ0<{*%t%nyrePqgtUF%B@3IbDf5dIrialhbUP@9f0^-Xy z5+`M0H$v!(nYuX;8WY!m zdH2Gg8j`IP(JiGAkT>0??&g$4O{9u_yg;-!xGh8%X-*QG3#KXp7`ED zy8}fa8c3SIKESUbcW3C=P{ttB~*C_x1 literal 0 HcmV?d00001 From c5f5f427c6f8d1b4136bf41171f8f0980561080e Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 26 Oct 2016 15:20:00 +0200 Subject: [PATCH 2/2] api: api_info()['version'] API level is disconnected from NVIM version. The API metadata holds the current API level, and the lowest backwards-compatible level supported by this instance. Release 0.1.6 is the first release that reports the Nvim version and API level. metadata['version'] = { major: 0, minor: 1, patch: 6, api_level: 1, api_compatible: 0, api_prerelease: false, } The API level may remain unchanged across Nvim releases if the API has not changed. When changing the API, - set NVIM_API_PRERELEASE to true - increment NVIM_API_LEVEL (at most once per Nvim version) - adjust NVIM_API_LEVEL_COMPAT if backwards-compatibility was broken api_level_0.mpack was generated from Nvim 0.1.5 with: nvim --api-info --- CMakeLists.txt | 9 +-- config/versiondef.h.in | 6 +- runtime/doc/api.txt | 8 +- runtime/doc/msgpack_rpc.txt | 5 +- src/nvim/api/private/helpers.c | 15 +--- src/nvim/version.c | 12 +++ test/functional/api/compatibility_spec.lua | 65 ---------------- test/functional/api/version_spec.lua | 71 ++++++++++++++++++ test/functional/eval/api_functions_spec.lua | 2 +- .../eval/msgpack_functions_spec.lua | 2 +- .../{api-info/0.mpack => api_level_0.mpack} | Bin 11 files changed, 100 insertions(+), 95 deletions(-) delete mode 100644 test/functional/api/compatibility_spec.lua create mode 100644 test/functional/api/version_spec.lua rename test/functional/fixtures/{api-info/0.mpack => api_level_0.mpack} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b139025d7..75c0bd0ecd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,16 +59,15 @@ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Dev" "Release" "MinSizeRel" "RelWithDebInfo") # If not in a git repo (e.g., a tarball) these tokens define the complete -# version string, else it is combined with the result of `git describe`. +# version string, else they are combined with the result of `git describe`. set(NVIM_VERSION_MAJOR 0) set(NVIM_VERSION_MINOR 1) set(NVIM_VERSION_PATCH 6) set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers -# Neovim API version. When changing the API, bump CURRENT if -# PRERELEASE is false, and set PRERELEASE as true -set(NVIM_API_CURRENT 1) -set(NVIM_API_COMPATIBILITY 0) +# API level +set(NVIM_API_LEVEL 1) # Bump this after any API change. +set(NVIM_API_LEVEL_COMPAT 0) # Adjust this after a _breaking_ API change. set(NVIM_API_PRERELEASE true) file(TO_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR}/.git FORCED_GIT_DIR) diff --git a/config/versiondef.h.in b/config/versiondef.h.in index 465d90b674..b9565735b3 100644 --- a/config/versiondef.h.in +++ b/config/versiondef.h.in @@ -7,9 +7,9 @@ #define NVIM_VERSION_PRERELEASE "@NVIM_VERSION_PRERELEASE@" #cmakedefine NVIM_VERSION_MEDIUM "@NVIM_VERSION_MEDIUM@" -#define NVIM_API_CURRENT @NVIM_API_CURRENT@ -#define NVIM_API_COMPATIBILITY @NVIM_API_COMPATIBILITY@ -#cmakedefine NVIM_API_PRERELEASE +#define NVIM_API_LEVEL @NVIM_API_LEVEL@ +#define NVIM_API_LEVEL_COMPAT @NVIM_API_LEVEL_COMPAT@ +#define NVIM_API_PRERELEASE @NVIM_API_PRERELEASE@ #define NVIM_VERSION_CFLAGS "@NVIM_VERSION_CFLAGS@" #define NVIM_VERSION_BUILD_TYPE "@NVIM_VERSION_BUILD_TYPE@" diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index dd48a0c2a5..976b852bdb 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -51,10 +51,10 @@ Tabpage -> enum value kObjectTypeTabpage Nvim exposes metadata about the API as a Dictionary with the following keys: -api_level API version compatibility information -functions calling signature of the API functions -types The custom handle types defined by Nvim -error_types The possible kinds of errors an API function can exit with. +version Nvim version, API level/compatibility +functions API function signatures +types Custom handle types defined by Nvim +error_types Possible error types returned by API functions This metadata is mostly useful for external programs accessing the API via RPC, see |rpc-api|. diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt index bfd4100f15..757f5574d4 100644 --- a/runtime/doc/msgpack_rpc.txt +++ b/runtime/doc/msgpack_rpc.txt @@ -168,9 +168,8 @@ API metadata object ~ API clients exist to hide msgpack-rpc details. The API metadata object contains information that makes this task easier (see also |rpc-types|): - - The "api_level" key contais API compatibility information. The "current" - key holds the API version supported Neovim. The "compatibility" key holds - the oldest supported API version. + - The "version" key contains the Nvim version, API level, and API + backwards-compatibility level. - The "functions" key contains a list of metadata objects for individual functions. - Each function metadata object has |rpc-types| information about the return diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 7fda2802c8..208c3b53c8 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -18,9 +18,9 @@ #include "nvim/map.h" #include "nvim/option.h" #include "nvim/option_defs.h" +#include "nvim/version.h" #include "nvim/eval/typval_encode.h" #include "nvim/lib/kvec.h" -#include "auto/versiondef.h" /// Helper structure for vim_to_object typedef struct { @@ -764,10 +764,10 @@ Dictionary api_metadata(void) static Dictionary metadata = ARRAY_DICT_INIT; if (!metadata.size) { + PUT(metadata, "version", DICTIONARY_OBJ(version_dict())); init_function_metadata(&metadata); init_error_type_metadata(&metadata); init_type_metadata(&metadata); - init_api_level_metadata(&metadata); } return copy_object(DICTIONARY_OBJ(metadata)).data.dictionary; @@ -827,17 +827,6 @@ static void init_type_metadata(Dictionary *metadata) PUT(*metadata, "types", DICTIONARY_OBJ(types)); } -static void init_api_level_metadata(Dictionary *metadata) -{ - Dictionary version = ARRAY_DICT_INIT; - PUT(version, "current", INTEGER_OBJ(NVIM_API_CURRENT)); - PUT(version, "compatibility", INTEGER_OBJ(NVIM_API_COMPATIBILITY)); -#ifdef NVIM_API_PRERELEASE - PUT(version, "prerelease", BOOLEAN_OBJ(true)); -#endif - - PUT(*metadata, "api_level", DICTIONARY_OBJ(version)); -} /// Creates a deep clone of an object Object copy_object(Object obj) diff --git a/src/nvim/version.c b/src/nvim/version.c index 498afa9656..7ab8c84569 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -7,6 +7,7 @@ #include #include +#include "nvim/api/private/helpers.h" #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/iconv.h" @@ -2514,6 +2515,17 @@ bool has_vim_patch(int n) return false; } +Dictionary version_dict(void) { + Dictionary d = ARRAY_DICT_INIT; + PUT(d, "major", INTEGER_OBJ(NVIM_VERSION_MAJOR)); + PUT(d, "minor", INTEGER_OBJ(NVIM_VERSION_MINOR)); + PUT(d, "patch", INTEGER_OBJ(NVIM_VERSION_PATCH)); + PUT(d, "api_level", INTEGER_OBJ(NVIM_API_LEVEL)); + PUT(d, "api_compatible", INTEGER_OBJ(NVIM_API_LEVEL_COMPAT)); + PUT(d, "api_prerelease", BOOLEAN_OBJ(NVIM_API_PRERELEASE)); + return d; +} + void ex_version(exarg_T *eap) { // Ignore a ":version 9.99" command. diff --git a/test/functional/api/compatibility_spec.lua b/test/functional/api/compatibility_spec.lua deleted file mode 100644 index e0fc625b46..0000000000 --- a/test/functional/api/compatibility_spec.lua +++ /dev/null @@ -1,65 +0,0 @@ - -local helpers = require('test.functional.helpers')(after_each) -local lfs = require('lfs') -local mpack = require('mpack') -local clear, eq, neq = helpers.clear, helpers.eq, helpers.neq - -local read_mpack_file = function(fname) - local fd = io.open(fname, 'rb') - local data = fd:read('*a') - fd:close() - local unpack = mpack.Unpacker() - return unpack(data) -end - --- ignore metadata in API function spec -local remove_function_metadata = function(fspec) - fspec['can_fail'] = nil - fspec['async'] = nil - fspec['method'] = nil - fspec['since'] = nil - fspec['deprecated_since'] = nil - fspec['receives_channel_id'] = nil - for idx,_ in ipairs(fspec['parameters']) do - fspec['parameters'][idx][2] = '' - end -end - -clear() -local api_level = helpers.call('api_info')['api_level'] - -describe('api compatibility', function() - before_each(clear) - - it("version metadata is sane", function() - local info = helpers.call('api_info') - local current = info['api_level']['current'] - local compatibility = info['api_level']['compatibility'] - neq(current, nil) - neq(compatibility, nil) - assert(current >= compatibility) - end) - - for ver = api_level['compatibility'], api_level['current'] do - local path = 'test/functional/fixtures/api-info/' .. tostring(ver) .. '.mpack' - it('are backwards compatible with api level '..ver, function() - if lfs.attributes(path,"mode") ~= "file" then - pending("No fixture found, skipping test") - return - end - - local old_api = read_mpack_file(path) - local api = helpers.call('api_info') - - for _, fspec in ipairs(old_api['functions']) do - remove_function_metadata(fspec) - for _, fspec_new in ipairs(api['functions']) do - if fspec['name'] == fspec_new['name'] then - remove_function_metadata(fspec_new) - eq(fspec, fspec_new) - end - end - end - end) - end -end) diff --git a/test/functional/api/version_spec.lua b/test/functional/api/version_spec.lua new file mode 100644 index 0000000000..3efd00ddbe --- /dev/null +++ b/test/functional/api/version_spec.lua @@ -0,0 +1,71 @@ +local helpers = require('test.functional.helpers')(after_each) +local mpack = require('mpack') +local clear, funcs, eq = helpers.clear, helpers.funcs, helpers.eq + +local function read_mpack_file(fname) + local fd = io.open(fname, 'rb') + local data = fd:read('*a') + fd:close() + local unpack = mpack.Unpacker() + return unpack(data) +end + +-- Remove metadata that is not essential to backwards-compatibility. +local function remove_function_metadata(fspec) + fspec['can_fail'] = nil + fspec['async'] = nil + fspec['method'] = nil + fspec['since'] = nil + fspec['deprecated_since'] = nil + fspec['receives_channel_id'] = nil + for idx, _ in ipairs(fspec['parameters']) do + fspec['parameters'][idx][2] = '' -- Remove parameter name. + end + return fspec +end + +describe("api_info()['version']", function() + before_each(clear) + + it("returns API level", function() + local version = helpers.call('api_info')['version'] + local current = version['api_level'] + local compat = version['api_compatible'] + eq("number", type(current)) + eq("number", type(compat)) + assert(current >= compat) + end) + + it("returns Nvim version", function() + local version = helpers.call('api_info')['version'] + local major = version['major'] + local minor = version['minor'] + local patch = version['patch'] + eq("number", type(major)) + eq("number", type(minor)) + eq("number", type(patch)) + eq(1, funcs.has("nvim-"..major.."."..minor.."."..patch)) + eq(0, funcs.has("nvim-"..major.."."..minor.."."..(patch + 1))) + eq(0, funcs.has("nvim-"..major.."."..(minor + 1).."."..patch)) + eq(0, funcs.has("nvim-"..(major + 1).."."..minor.."."..patch)) + end) + + it("api_compatible level is valid", function() + local api = helpers.call('api_info') + local compat = api['version']['api_compatible'] + local path = 'test/functional/fixtures/api_level_' + ..tostring(compat)..'.mpack' + + -- Verify that the current API function signatures match those of the API + -- level for which we claim compatibility. + local old_api = read_mpack_file(path) + for _, fn_old in ipairs(old_api['functions']) do + for _, fn_new in ipairs(api['functions']) do + if fn_old['name'] == fn_new['name'] then + eq(remove_function_metadata(fn_old), + remove_function_metadata(fn_new)) + end + end + end + end) +end) diff --git a/test/functional/eval/api_functions_spec.lua b/test/functional/eval/api_functions_spec.lua index 5345c9782d..21dd228145 100644 --- a/test/functional/eval/api_functions_spec.lua +++ b/test/functional/eval/api_functions_spec.lua @@ -106,7 +106,7 @@ describe('api functions', function() it('have metadata accessible with api_info()', function() local api_keys = eval("sort(keys(api_info()))") - eq({'api_level', 'error_types', 'functions', 'types'}, api_keys) + eq({'error_types', 'functions', 'types', 'version'}, api_keys) end) it('are highlighted by vim.vim syntax file', function() diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua index ae7e1b5609..f11c08de05 100644 --- a/test/functional/eval/msgpack_functions_spec.lua +++ b/test/functional/eval/msgpack_functions_spec.lua @@ -460,7 +460,7 @@ describe('msgpackparse() function', function() eval(cmd) eval(cmd) -- do it again (try to force segfault) local api_info = eval(cmd) -- do it again - eq({'api_level', 'error_types', 'functions', 'types'}, api_info) + eq({'error_types', 'functions', 'types', 'version'}, api_info) end) it('fails when called with no arguments', function() diff --git a/test/functional/fixtures/api-info/0.mpack b/test/functional/fixtures/api_level_0.mpack similarity index 100% rename from test/functional/fixtures/api-info/0.mpack rename to test/functional/fixtures/api_level_0.mpack