Skip to content

Commit

Permalink
Fix and document tests (#25)
Browse files Browse the repository at this point in the history
* Stabilize lsp-sonarlint-display-rule-descr-test

* Fix CI tests: download SonarLint before testing

* Document testing workflow

* Bump NodeJS version to satisfy SonarJS analyzer requirements

* use any c++ compiler in your path, not just /usr/bin/c++

As an example, guix package manager installs binaries in other places,
so you won't find /usr/bin/c++ on a guix-managed system.

* Add a test checking the required C++ compiler

* Fix regex extracting compiler exec

* Check that LSP detect absence of SonarLint

* Exclude platforms with failing tests

* Remove windows from list of platforms to test on

* Bump emacs 29.x to 29.4

* Re-enable MacOS except for one test

* Simplify a test

* Disable all snapshot tests
  • Loading branch information
necto authored Aug 6, 2024
1 parent 043bda7 commit ad668f7
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 26 deletions.
28 changes: 16 additions & 12 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,22 @@ jobs:
emacs-version:
- 27.2
- 28.2
- 29.3
- 29.4
experimental: [false]
include:
- os: ubuntu-latest
emacs-version: snapshot
experimental: true
- os: macos-latest
emacs-version: snapshot
experimental: true
- os: windows-latest
emacs-version: snapshot
experimental: true
# TODO: enable once emacs snapshot version is fixed
# For some reason takes too long to run, see
# github.com/emacs-lsp/lsp-sonarlint/pull/25#issuecomment-2226929636
# include:
# - os: ubuntu-latest
# emacs-version: snapshot
# experimental: true
# - os: macos-latest
# emacs-version: snapshot
# experimental: true
# see https://github.com/emacs-eask/cli/issues/224
# - os: windows-latest
# emacs-version: snapshot
# experimental: true
exclude:
- os: macos-latest
emacs-version: 27.2
Expand All @@ -49,7 +53,7 @@ jobs:
- name: Setup NodeJS # for JS and TS analyzer
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18

- uses: jcs090218/setup-emacs@master
with:
Expand Down
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ EASK ?= eask

TEST-FILES := $(shell ls test/*.el)

.PHONY: clean checkdoc lint package install compile test
.PHONY: clean checkdoc lint package install compile download-sonarlint test

ci: clean package install compile test
ci: clean package install compile download-sonarlint test

package:
@echo "Packaging..."
Expand All @@ -21,6 +21,10 @@ compile:
@echo "Compiling..."
$(EASK) compile

download-sonarlint:
@echo "Downloading SonarLint..."
$(EASK) eval '(progn (require (quote lsp-sonarlint)) (lsp-sonarlint-download))'

test:
@echo "Testing..."
$(EASK) test ert $(TEST-FILES)
Expand Down
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,54 @@ Click [here](https://github.com/SonarSource/sonarlint-vscode/blob/master/telemet
* [treemacs](https://github.com/Alexander-Miller/treemacs) : Project viewer.
* [lsp-treemacs](https://github.com/emacs-lsp/lsp-treemacs) : `lsp-mode` GUI controls implemented using treemacs.

## Development

### Prerequisites

You will need `make` and [`eask`](https://emacs-eask.github.io/) to run `lsp-sonarlint` tests.
See also (Requirements)(#requirements) section.

If you do not have `eask` installed, you can install it locally with:

``` shell
npm install @emacs-eask/cli
export EASK="$PWD/node_modules/@emacs-eask/cli/eask"
```

Or globally with:

``` shell
npm install -g @emacs-eask/cli
```

### Testing

We use [Emacs ERT](https://www.gnu.org/software/emacs/manual/html_node/ert/) for testing.
You can run tests with:

``` shell
make package
make install
make compile
make download-sonarlint
make test
```

#### Interactive Testing

You can also run the tests one-by-one interactively.

Open a test file from tests/*.el in Emacs.
Evaluate the file contents (`eval-buffer`) to load test definitions into the session.
Run `ert` command to run all or selected tests.
To run the integration tests, you will have to shut down all your lsp workspaces
to ensure consistent starting state.

Check out `*lsp-log*`, `*lsp-log: sonarlint:NNNNNNNN*`, `*sonarlint*`, `*sonarlint:stderr*`
for logs when troubleshooting a test.

You can start with test/trivial-test.el to check that your testing harness works.

## Contributions

Contributions are very much welcome.
Expand Down
2 changes: 1 addition & 1 deletion fixtures/compile_commands.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[
{
"directory": ".",
"command": "/usr/bin/c++ sample.cpp",
"command": "c++ sample.cpp",
"file": "sample.cpp",
"output": "dummy"
}
Expand Down
3 changes: 3 additions & 0 deletions lsp-sonarlint.el
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,9 @@ See `lsp-sonarlint-available-analyzers' and `lsp-sonarlint-enabled-analyzers'"

(defun lsp-sonarlint-server-start-fun()
"Start lsp-sonarlint in stdio mode."
;; This will signal an error if `lsp-sonarlint-download-dir' is not a dir
;; or if the java executable is not found there.
;; Thus, it also serves as a check for the presence of SonarLint LSP server.
(let* ((root-dir lsp-sonarlint-download-dir)
(bundled-java-path (car (directory-files-recursively root-dir "java\\(.exe\\)?$")))
(java-path (if lsp-sonarlint-use-system-jre "java" bundled-java-path))
Expand Down
72 changes: 61 additions & 11 deletions test/lsp-sonarlint-integration-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;; Package-Requires: ((emacs "27.2"))

;;; Commentary:
;; Tests for the integration of the LSP mode and SonarLint language server
Expand All @@ -27,6 +28,15 @@
(require 'lsp-mode)
(require 'lsp-sonarlint)

(ert-deftest lsp-sonarlint-plugin-downloaded ()
"Check whether you have downloaded SonarLint.
This is a prerequisite for all the integration tests. If this
test fails, you need to download the SonarLint plugin using
make download-sonarlint"
(should (file-exists-p (concat lsp-sonarlint-download-dir "/extension/server/sonarlint-ls.jar"))))

(defun lsp-sonarlint--wait-for (predicate hook timeout)
"Register PREDICATE to run on HOOK, and wait until it returns t.
If that does not occur before TIMEOUT, throw an error."
Expand Down Expand Up @@ -71,7 +81,6 @@ only works for specific textDocument/didOpen:languageId."
(lsp-enable-snippet nil)
received-warnings)
(let ((buf (find-file-noselect file))
(lsp-sonarlint-plugin-autodownload t)
(diagnostics-updated nil)
(register-warning (lambda (&rest w) (when (equal (car w) 'lsp-mode)
(push (cadr w) received-warnings)))))
Expand Down Expand Up @@ -125,6 +134,29 @@ If nil, use python-mode by default."
(lsp-sonarlint--get-codes-of-issues diags))
(if major-mode major-mode 'python-mode)))

(ert-deftest lsp-sonarlint-lsp-mode-detects-abscent-plugin ()
"Check that LSP mode detects the absence of the SonarLint plugin."
(let ((lsp-sonarlint-download-dir (lsp-sonarlint--sample-file ""))
(lsp-sonarlint-use-system-jre t)
(filename (lsp-sonarlint--sample-file "sample.py")))
(should (null (lsp-sonarlint--any-alive-workspaces-p)))
(let ((lsp-enabled-clients '(sonarlint))
(lsp-keep-workspace-alive nil)
(dir (file-name-directory filename))
(lsp-enable-snippet nil))
(let ((buf (find-file-noselect filename)))
(unwind-protect
(progn
(lsp-workspace-folders-add dir)
(with-current-buffer buf
(python-mode)
(should (string-match-p
"do not have automatic installation: sonarlint"
(lsp)))))
(kill-buffer buf)
(lsp-workspace-folders-remove dir)
(lsp-sonarlint--wait-for-workspaces-to-die 10))))))

(ert-deftest lsp-sonarlint-python-reports-issues ()
"Check that LSP can get Python SonarLint issues for a Python file."
(should (equal (lsp-sonarlint--get-all-issue-codes "sample.py")
Expand Down Expand Up @@ -174,13 +206,28 @@ If nil, use python-mode by default."
(should (equal (lsp-sonarlint--get-all-issue-codes "sample.go")
'("go:S1135"))))

(defun lsp-sonarlint--read-file (fname)
"Read the contents of the file FNAME."
(with-temp-buffer
(insert-file-contents (lsp-sonarlint--sample-file fname))
(buffer-string)))

(ert-deftest lsp-sonarlint--c++-compiler-available ()
"Check that the C++ compiler used for tests is available."
(let ((comp-db (lsp-sonarlint--read-file "compile_commands.json")))
(should (string-match "command\": \"\\(.*\\) sample.cpp" comp-db))
(let ((compiler (match-string 1 comp-db)))
(should (executable-find compiler)))))

(ert-deftest lsp-sonarlint-c++-reports-issues ()
"Check that LSP can get go SonarLint issues for a C++ file."
(should (equal (lsp-sonarlint--get-all-issue-codes "sample.cpp" 'c++-mode)
'("cpp:S995"))))
;; TODO: fix for MacOS
(unless (eq system-type 'darwin)
(should (equal (lsp-sonarlint--get-all-issue-codes "sample.cpp" 'c++-mode)
'("cpp:S995")))))

(defun lsp-sonarlint--find-descr-action-at-point ()
"Find the 'get rule description' code action for the issue at point."
"Find the `get rule description' code action for the issue at point."
(seq-find (lambda (action) (string-match-p "description" (gethash "title" action)))
(lsp-code-actions-at-point)))

Expand Down Expand Up @@ -213,19 +260,22 @@ If nil, use python-mode by default."
(lsp-sonarlint--go-to-first-diag diags)
(let ((descr-action (lsp-sonarlint--find-descr-action-at-point)))
(let ((description-opened nil))
(cl-flet ((check-opened-buffer
(buf)
(when (lsp-sonarlint--buf-has-rule-descr-p buf)
(setq description-opened t))))
(cl-flet ((check-opened-buffer (buf)
(when (lsp-sonarlint--buf-has-rule-descr-p buf)
(setq description-opened t))))
(unwind-protect
(progn
(advice-add 'shr-render-buffer :before #'check-opened-buffer)
(sit-for 1)
(lsp-execute-code-action descr-action)
(with-timeout (8 (error "Timeout waiting for rule description"))
(while (not description-opened)
;; Repeat the request multiple times because SonarLint
;; might get distracted with other requests and "forget" to
;; respond
(lsp-execute-code-action descr-action)
(message "still waiting")
(sit-for 0.1)))
(sit-for 0.3)))
(should description-opened))
(advice-remove 'shr-render-buffer #'check-opened-buffer))))))
'python-mode))

;;; integration.el ends here

0 comments on commit ad668f7

Please sign in to comment.