Repository: nvim-lua/plenary.nvim
Pull request #422 (2022-11-14 15:25:01)

docs(tests): fix outdated instructions
`:PlenaryBustedTest `  
is not a command.
#403


Patch:
diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml
index 47458a4..1014591 100644
--- a/.github/workflows/default.yml
+++ b/.github/workflows/default.yml
@@ -3,57 +3,49 @@ name: default
 on: [push, pull_request]
 
 jobs:
-  x64-ubuntu:
-    name: X64-ubuntu
-    runs-on: ubuntu-20.04
+  run_tests:
+    name: unit tests
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - os: ubuntu-22.04
+            rev: nightly/nvim-linux64.tar.gz
+          - os: ubuntu-22.04
+            rev: v0.7.2/nvim-linux64.tar.gz
+          - os: ubuntu-22.04
+            rev: v0.8.2/nvim-linux64.tar.gz
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v3
       - run: date +%F > todays-date
       - name: Restore cache for today's nightly.
-        uses: actions/cache@v2
+        uses: actions/cache@v3
         with:
           path: _neovim
-          key: ${{ runner.os }}-x64-${{ hashFiles('todays-date') }}
-
-      - name: Run tests
-        run: |
-          curl -OL https://raw.githubusercontent.com/norcalli/bot-ci/master/scripts/github-actions-setup.sh
-          source github-actions-setup.sh nightly-x64
-          make test
-
-  appimage-ubuntu:
-    name: Appimage-ubuntu
-    runs-on: ubuntu-20.04
-    steps:
-      - uses: actions/checkout@v2
-      - run: date +%F > todays-date
-      - name: Restore cache for today's nightly.
-        uses: actions/cache@v2
-        with:
-          path: build
-          key: ${{ runner.os }}-appimage-${{ hashFiles('todays-date') }}
-
+          key: ${{ runner.os }}-${{ matrix.rev }}-${{ hashFiles('todays-date') }}
       - name: Prepare
         run: |
-          test -d build || {
-            mkdir -p build
-            wget https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage
-            chmod +x nvim.appimage
-            mv nvim.appimage ./build/nvim
+          test -d _neovim || {
+            mkdir -p _neovim
+            curl -sL "https://github.com/neovim/neovim/releases/download/${{ matrix.rev }}" | tar xzf - --strip-components=1 -C "${PWD}/_neovim"
           }
 
       - name: Run tests
         run: |
-            export PATH="${PWD}/build/:${PATH}"
-            make test
+          export PATH="${PWD}/_neovim/bin:${PATH}"
+          export VIM="${PWD}/_neovim/share/nvim/runtime"
+          nvim --version
+          make test
 
   stylua:
     name: stylua
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-22.04
     steps:
-      - uses: actions/checkout@v2
-      - uses: JohnnyMorganz/stylua-action@v1
+      - uses: actions/checkout@v3
+      - uses: JohnnyMorganz/stylua-action@v2
         with:
           token: ${{ secrets.GITHUB_TOKEN }}
+          version: latest
           # CLI arguments
           args: --color always --check .
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..089c028
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,20 @@
+name: "release"
+on:
+  push:
+    tags:
+      - 'v*'
+jobs:
+  luarocks-upload:
+    runs-on: ubuntu-22.04
+    steps:
+      - uses: actions/checkout@v3
+      - uses: leafo/gh-actions-lua@v9
+      - uses: leafo/gh-actions-luarocks@v4
+      - name: Install dkjson
+        run: luarocks install dkjson
+      - name: Luarocks Upload
+        env: 
+          LUAROCKS_API_KEY: ${{ secrets.LUAROCKS_API_KEY }}
+        run: make luarocks_upload
+      - name: Install release
+        run: make test_luarocks_install 
diff --git a/Makefile b/Makefile
index 46b875b..bc1f3d7 100644
--- a/Makefile
+++ b/Makefile
@@ -3,3 +3,9 @@ test:
 
 generate_filetypes:
 	nvim --headless -c 'luafile scripts/update_filetypes_from_github.lua' -c 'qa!'
+
+luarocks_upload:
+	bash ./scripts/luarocks-upload.sh
+
+test_luarocks_install:
+	bash ./scripts/test-luarocks-install.sh
diff --git a/lua/plenary/async/control.lua b/lua/plenary/async/control.lua
index cf741fe..a03685f 100644
--- a/lua/plenary/async/control.lua
+++ b/lua/plenary/async/control.lua
@@ -74,7 +74,7 @@ end
 ---async function, blocks until a permit can be acquired
 ---example:
 ---local semaphore = Semaphore.new(1024)
----local permit = await(semaphore:acquire())
+---local permit = semaphore:acquire()
 ---permit:forget()
 ---when a permit can be acquired returns it
 ---call permit:forget() to forget the permit
@@ -176,14 +176,14 @@ M.channel.counter = function()
 
   Receiver.recv = function()
     if counter == 0 then
-      await(condvar:wait())
+      condvar:wait()
     end
     counter = counter - 1
   end
 
   Receiver.last = function()
     if counter == 0 then
-      await(condvar:wait())
+      condvar:wait()
     end
     counter = 0
   end
diff --git a/lua/plenary/busted.lua b/lua/plenary/busted.lua
index 6d1e91e..b827ff8 100644
--- a/lua/plenary/busted.lua
+++ b/lua/plenary/busted.lua
@@ -218,9 +218,9 @@ mod.run = function(file)
   print("\n" .. HEADER)
   print("Testing: ", file)
 
-  local ok, msg = pcall(dofile, file)
+  local loaded, msg = loadfile(file)
 
-  if not ok then
+  if not loaded then
     print(HEADER)
     print "FAILED TO LOAD FILE"
     print(color_string("red", msg))
@@ -232,33 +232,37 @@ mod.run = function(file)
     end
   end
 
-  -- If nothing runs (empty file without top level describe)
-  if not results.pass then
-    if is_headless then
-      return vim.cmd "0cq"
-    else
-      return
+  coroutine.wrap(function()
+    loaded()
+
+    -- If nothing runs (empty file without top level describe)
+    if not results.pass then
+      if is_headless then
+        return vim.cmd "0cq"
+      else
+        return
+      end
     end
-  end
 
-  mod.format_results(results)
+    mod.format_results(results)
 
-  if #results.errs ~= 0 then
-    print("We had an unexpected error: ", vim.inspect(results.errs), vim.inspect(results))
-    if is_headless then
-      return vim.cmd "2cq"
-    end
-  elseif #results.fail > 0 then
-    print "Tests Failed. Exit: 1"
+    if #results.errs ~= 0 then
+      print("We had an unexpected error: ", vim.inspect(results.errs), vim.inspect(results))
+      if is_headless then
+        return vim.cmd "2cq"
+      end
+    elseif #results.fail > 0 then
+      print "Tests Failed. Exit: 1"
 
-    if is_headless then
-      return vim.cmd "1cq"
-    end
-  else
-    if is_headless then
-      return vim.cmd "0cq"
+      if is_headless then
+        return vim.cmd "1cq"
+      end
+    else
+      if is_headless then
+        return vim.cmd "0cq"
+      end
     end
-  end
+  end)()
 end
 
 return mod
diff --git a/lua/plenary/popup/init.lua b/lua/plenary/popup/init.lua
index 92dec7f..90eb5c9 100644
--- a/lua/plenary/popup/init.lua
+++ b/lua/plenary/popup/init.lua
@@ -422,7 +422,7 @@ function popup.create(what, vim_options)
       bufnr,
       "n",
       "<CR>",
-      '<cmd>lua require"popup".execute_callback(' .. bufnr .. ")<CR>",
+      '<cmd>lua require"plenary.popup".execute_callback(' .. bufnr .. ")<CR>",
       { noremap = true }
     )
   end
diff --git a/scripts/luarocks-upload.sh b/scripts/luarocks-upload.sh
new file mode 100644
index 0000000..fae54a7
--- /dev/null
+++ b/scripts/luarocks-upload.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+
+# Expects the LUAROCKS_API_KEY secret to be set
+
+TMP_DIR=$(mktemp -d)
+MODREV=$(git describe --tags --always --first-parent | tr -d "v")
+DEST_ROCKSPEC="$TMP_DIR/plenary.nvim-$MODREV-1.rockspec"
+cp "plenary.nvim-scm-1.rockspec" "$DEST_ROCKSPEC"
+sed -i "s/'scm'/'$MODREV'/g" "$DEST_ROCKSPEC"
+luarocks upload "$DEST_ROCKSPEC" --api-key="$LUAROCKS_API_KEY"


Test patch:
diff --git a/TESTS_README.md b/TESTS_README.md
index 1103ca2..955cdf9 100644
--- a/TESTS_README.md
+++ b/TESTS_README.md
@@ -34,11 +34,21 @@ The test **some test** checks that a functions output is as expected based on th
 
 # Running tests
 
-Run the test using `:PlenaryBustedTest <file>`. 
+Run the test using `:PlenaryBustedFile <file>`. 
 
 ```vimscript
 " Run the test in the current buffer
-:PlenaryBustedTest %
+:PlenaryBustedFile %
+" Run all tests in the directory "tests/plenary/"
+:PlenaryBustedDirectory tests/plenary/
+```
+
+Or you can run tests in headless mode to see output in terminal:
+
+```bash
+# run all tests in terminal
+cd plenary.nvim
+nvim --headless -c 'PlenaryBustedDirectory tests'
 ```
 
 # mocking with luassert
@@ -125,3 +135,19 @@ To test this in your `~/.config/nvim` configuration, try the suggested file stru
 lua/example/module.lua
 lua/spec/example/module_spec.lua
 ```
+
+# Asynchronous testing
+
+Tests run in a coroutine, which can be yielded and resumed. This can be used to
+test code that uses asynchronous Neovim functionalities. For example, this can
+be done inside a test:
+
+```lua
+local co = coroutine.running()
+vim.defer_fn(function()
+  coroutine.resume(co)
+end, 1000)
+--The test will reach here immediately.
+coroutine.yield()
+--The test will only reach here after one second, when the deferred function runs.
+```
diff --git a/scripts/test-luarocks-install.sh b/scripts/test-luarocks-install.sh
new file mode 100644
index 0000000..be140b0
--- /dev/null
+++ b/scripts/test-luarocks-install.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+MODREV=$(git describe --tags --always --first-parent | tr -d "v")
+luarocks install "plenary.nvim" "$MODREV"
diff --git a/tests/plenary/async_testing_spec.lua b/tests/plenary/async_testing_spec.lua
new file mode 100644
index 0000000..c1c991b
--- /dev/null
+++ b/tests/plenary/async_testing_spec.lua
@@ -0,0 +1,75 @@
+local Job = require "plenary.job"
+
+local Timing = {}
+
+function Timing:log(name)
+  self[name] = vim.loop.uptime()
+end
+
+function Timing:check(from, to, min_elapsed)
+  assert(self[from], "did not log " .. from)
+  assert(self[to], "did not log " .. to)
+  local elapsed = self[to] - self[from]
+  assert(
+    min_elapsed <= elapsed,
+    string.format("only took %s to get from %s to %s - expected at least %s", elapsed, from, to, min_elapsed)
+  )
+end
+
+describe("Async test", function()
+  it("can resume testing with vim.defer_fn", function()
+    local co = coroutine.running()
+    assert(co, "not running inside a coroutine")
+
+    local timing = setmetatable({}, { __index = Timing })
+
+    vim.defer_fn(function()
+      coroutine.resume(co)
+    end, 200)
+    timing:log "before"
+    coroutine.yield()
+    timing:log "after"
+    timing:check("before", "after", 0.1)
+  end)
+
+  it("can resume testing from job callback", function()
+    local co = coroutine.running()
+    assert(co, "not running inside a coroutine")
+
+    local timing = setmetatable({}, { __index = Timing })
+
+    Job:new({
+      command = "bash",
+      args = {
+        "-ce",
+        [[
+        sleep 0.2
+        echo hello
+        sleep 0.2
+        echo world
+        sleep 0.2
+        exit 42
+      ]],
+      },
+      on_stdout = function(_, data)
+        timing:log(data)
+      end,
+      on_exit = function(_, exit_status)
+        timing:log "exit"
+        --This is required so that the rest of the test will run in a proper context
+        vim.schedule(function()
+          coroutine.resume(co, exit_status)
+        end)
+      end,
+    }):start()
+    timing:log "job started"
+    local exit_status = coroutine.yield()
+    timing:log "job finished"
+    assert.are.equal(exit_status, 42)
+
+    timing:check("job started", "job finished", 0.3)
+    timing:check("job started", "hello", 0.1)
+    timing:check("hello", "world", 0.1)
+    timing:check("world", "job finished", 0.1)
+  end)
+end)