From f7d88a90f4d3ef35fd1b4e8b05b95690c54e4256 Mon Sep 17 00:00:00 2001 From: Hugo Thunnissen Date: Sat, 11 Sep 2021 01:57:23 +0200 Subject: [PATCH] Implement some general tests for the parser code --- README.md | 10 ++++ phpinspect.el | 16 +++-- test/fixtures/Block.el | 1 + test/fixtures/Block.php | 8 +++ test/fixtures/Functions.el | 1 + test/fixtures/Functions.php | 25 ++++++++ test/fixtures/NamespacedClass.el | 1 + test/fixtures/NamespacedClass.php | 84 +++++++++++++++++++++++++++ test/fixtures/NamespacedFunctions.el | 1 + test/fixtures/NamespacedFunctions.php | 27 +++++++++ test/phpinspect-test.el | 71 ++++++++++++++++++++++ test/util/generate-test-data.el | 13 +++++ 12 files changed, 254 insertions(+), 4 deletions(-) create mode 100644 test/fixtures/Block.el create mode 100644 test/fixtures/Block.php create mode 100644 test/fixtures/Functions.el create mode 100644 test/fixtures/Functions.php create mode 100644 test/fixtures/NamespacedClass.el create mode 100644 test/fixtures/NamespacedClass.php create mode 100644 test/fixtures/NamespacedFunctions.el create mode 100644 test/fixtures/NamespacedFunctions.php create mode 100644 test/phpinspect-test.el create mode 100644 test/util/generate-test-data.el diff --git a/README.md b/README.md index 3e80ef3..54004ff 100644 --- a/README.md +++ b/README.md @@ -23,3 +23,13 @@ Example config: (add-hook 'php-mode-hook #'my-php-personal-hook) ``` + +## Development + +### Running tests +Tests are implemented using `ert`. You can run them in batch mode with the following +command: + +```bash +emacs -batch -l ert -l ./phpinspect.el -l ./test/phpinspect-test.el -f ert-run-tests-batch-and-exit +``` diff --git a/phpinspect.el b/phpinspect.el index e62af15..1df1270 100644 --- a/phpinspect.el +++ b/phpinspect.el @@ -706,6 +706,12 @@ the properties of the class" (goto-char (buffer-end 1)) (insert (concat (apply 'format args) "\n"))))) +(defun phpinspect-parse-file (file) + (with-temp-buffer + (insert-file-contents-literally file) + (phpinspect-parse-current-buffer))) + + (defun phpinspect-parse-current-buffer () (phpinspect-parse-buffer-until-point (current-buffer) @@ -1507,6 +1513,9 @@ namespace if not provided" (phpinspect--get-project-root) class-fqn)) +(defun phpinspect-index-file (file-name) + (phpinspect--index-tokens (phpinspect-parse-file file-name))) + (defun phpinspect-get-or-create-cached-project-class (project-root class-fqn) (let ((existing-index (phpinspect-get-cached-project-class project-root @@ -1524,9 +1533,7 @@ namespace if not provided" (if visited-buffer (setq new-index (with-current-buffer visited-buffer (phpinspect--index-current-buffer))) - (setq new-index (with-temp-buffer - (insert-file-contents-literally class-file) - (phpinspect--index-current-buffer)))) + (setq new-index (phpinspect-index-file class-file))) (phpinspect--log "New index: %s" new-index) (dolist (class (alist-get 'classes new-index)) (when class @@ -2136,7 +2143,8 @@ level of START-FILE in stead of `default-directory`." ;; Use statements ;;;###autoload -(defun phpinspect-fix-uses-interactive () "Add missing use statements to a php file" +(defun phpinspect-fix-uses-interactive () + "Add missing use statements to the currently visited PHP file." (interactive) (save-buffer) (let* ((project-root (phpinspect--get-project-root)) diff --git a/test/fixtures/Block.el b/test/fixtures/Block.el new file mode 100644 index 0000000..427b1f1 --- /dev/null +++ b/test/fixtures/Block.el @@ -0,0 +1 @@ +(:root (:block (:word "return") (:word "new") (:word "Response") (:list (:variable "this") (:object-attrib (:word "twig")) (:object-attrib (:word "render")) (:list (:string "domain/manage.html.twig") (:comma ",") (:array (:string "domain") (:fat-arrow "=>") (:variable "this") (:object-attrib (:word "repo")) (:object-attrib (:word "find")) (:list (:variable "name")) (:comma ",") (:string "users") (:fat-arrow "=>") (:variable "this") (:object-attrib (:word "user_repo")) (:object-attrib (:word "findBy")) (:list (:array (:string "domain") (:fat-arrow "=>") (:variable "name")))))) (:terminator ";"))) diff --git a/test/fixtures/Block.php b/test/fixtures/Block.php new file mode 100644 index 0000000..c8cf052 --- /dev/null +++ b/test/fixtures/Block.php @@ -0,0 +1,8 @@ +{ + return new Response( + $this->twig->render('domain/manage.html.twig', [ + 'domain' => $this->repo->find($name), + 'users' => $this->user_repo->findBy(['domain' => $name]) + ]) + ); +} diff --git a/test/fixtures/Functions.el b/test/fixtures/Functions.el new file mode 100644 index 0000000..d8ca777 --- /dev/null +++ b/test/fixtures/Functions.el @@ -0,0 +1 @@ +(:root (:function (:declaration (:word "function") (:word "MergeTwoArraysAndSomeOtherStuff") (:list (:word "array") (:variable "array1") (:comma ",") (:variable "untyped_variable")) (:word "Response")) (:block (:variable "merged") (:assignment "=") (:word "array_merge") (:list (:variable "array_1") (:comma ",") (:variable "untyped_variable")) (:terminator ";") (:variable "mapped") (:assignment "=") (:word "arrap_map") (:list (:function (:declaration (:word "function") (:list (:variable "item"))) (:block (:word "return") (:variable "item") (:terminator ";"))) (:comma ",") (:variable "merged")) (:terminator ";") (:variable "user") (:assignment "=") (:variable "this") (:object-attrib (:word "user_repo")) (:object-attrib (:word "findOne")) (:list (:variable "req") (:object-attrib (:word "get")) (:list (:string "user"))) (:terminator ";") (:word "return") (:word "new") (:word "Response") (:list (:variable "this") (:object-attrib (:word "twig")) (:object-attrib (:word "render")) (:list (:string "address/create.html.twig") (:comma ",") (:array (:string "user") (:fat-arrow "=>") (:variable "user") (:comma ",")))) (:terminator ";"))) (:function (:declaration (:word "function") (:word "BeTheSecondFunctionInTheFile") (:list)) (:block (:word "return") (:array (:string "Very Impressive Result") (:fat-arrow "=>") (:variable "result")) (:terminator ";")))) diff --git a/test/fixtures/Functions.php b/test/fixtures/Functions.php new file mode 100644 index 0000000..374dcef --- /dev/null +++ b/test/fixtures/Functions.php @@ -0,0 +1,25 @@ +user_repo->findOne($req->get('user')); + + return new Response( + $this->twig->render('address/create.html.twig', [ + 'user' => $user, + ]) + ); +} + + +function BeTheSecondFunctionInTheFile() { + return [ "Very Impressive Result" => $result ]; +} diff --git a/test/fixtures/NamespacedClass.el b/test/fixtures/NamespacedClass.el new file mode 100644 index 0000000..8551d7b --- /dev/null +++ b/test/fixtures/NamespacedClass.el @@ -0,0 +1 @@ +(:root (:word "declare") (:list (:word "strict_types") (:assignment "=")) (:terminator ";") (:namespace (:word "App\\Controller") (:terminator ";") (:use (:word "Symfony\\Component\\HttpFoundation\\Response") (:terminator ";")) (:use (:word "App\\Entity\\Address") (:terminator ";")) (:use (:word "Symfony\\Component\\HttpFoundation\\RedirectResponse") (:terminator ";")) (:use (:word "App\\Repository\\AddressRepository") (:terminator ";")) (:use (:word "App\\Repository\\UserRepository") (:terminator ";")) (:use (:word "Doctrine\\ORM\\EntityManagerInterface") (:terminator ";")) (:use (:word "Twig\\Environment") (:terminator ";")) (:use (:word "Symfony\\Component\\HttpFoundation\\Request") (:terminator ";")) (:use (:word "Symfony\\Component\\Routing\\Annotation\\Route") (:terminator ";")) (:class (:declaration (:word "class") (:word "AddressController")) (:block (:const (:word "A_CONSTANT_FOR_THE_SAKE_OF_HAVING_ONE") (:assignment "=") (:string "a value") (:terminator ";")) (:public (:const (:word "ARRAY_CONSTANT") (:assignment "=") (:array (:string "key") (:fat-arrow "=>") (:string "value") (:comma ",") (:string "key") (:fat-arrow "=>")) (:terminator ";"))) (:private (:variable "repo") (:terminator ";")) (:private (:variable "user_repo") (:terminator ";")) (:private (:variable "twig") (:terminator ";")) (:private (:variable "em") (:terminator ";")) (:public (:function (:declaration (:word "function") (:word "__construct") (:list (:word "AddressRepository") (:variable "repo") (:comma ",") (:word "UserRepository") (:variable "user_repo") (:comma ",") (:word "Environment") (:variable "twig") (:comma ",") (:word "EntityManagerInterface") (:variable "em"))) (:block (:variable "this") (:object-attrib (:word "repo")) (:assignment "=") (:variable "repo") (:terminator ";") (:variable "this") (:object-attrib (:word "user_repo")) (:assignment "=") (:variable "user_repo") (:terminator ";") (:variable "this") (:object-attrib (:word "twig")) (:assignment "=") (:variable "twig") (:terminator ";") (:variable "this") (:object-attrib (:word "em")) (:assignment "=") (:variable "em") (:terminator ";")))) (:doc-block (:annotation "Route")) (:public (:function (:declaration (:word "function") (:word "addAddressPage") (:list (:word "Request") (:variable "req")) (:word "Response")) (:block (:variable "user") (:assignment "=") (:variable "this") (:object-attrib (:word "user_repo")) (:object-attrib (:word "findOne")) (:list (:variable "req") (:object-attrib (:word "get")) (:list (:string "user"))) (:terminator ";") (:word "return") (:word "new") (:word "Response") (:list (:variable "this") (:object-attrib (:word "twig")) (:object-attrib (:word "render")) (:list (:string "address/create.html.twig") (:comma ",") (:array (:string "user") (:fat-arrow "=>") (:variable "user") (:comma ",")))) (:terminator ";")))) (:doc-block (:annotation "Route")) (:public (:function (:declaration (:word "function") (:word "addAddressAction") (:list (:word "Request") (:variable "req")) (:word "Response")) (:block (:variable "user") (:assignment "=") (:variable "this") (:object-attrib (:word "user_repo")) (:object-attrib (:word "findOne")) (:list (:variable "req") (:object-attrib (:word "request")) (:object-attrib (:word "get")) (:list (:string "user"))) (:terminator ";") (:variable "address_string") (:assignment "=") (:variable "req") (:object-attrib (:word "request")) (:object-attrib (:word "get")) (:list (:string "address")) (:terminator ";") (:variable "address") (:assignment "=") (:word "new") (:word "Address") (:list (:variable "user") (:comma ",") (:variable "address_string")) (:terminator ";") (:variable "this") (:object-attrib (:word "em")) (:object-attrib (:word "persist")) (:list (:variable "address")) (:terminator ";") (:variable "this") (:object-attrib (:word "em")) (:object-attrib (:word "flush")) (:list) (:terminator ";") (:word "return") (:word "new") (:word "RedirectResponse") (:list (:string "/user/") (:variable "user") (:object-attrib (:word "getLoginName")) (:list) (:string "/manage")) (:terminator ";")))) (:doc-block (:annotation "Route")) (:public (:function (:declaration (:word "function") (:word "deleteAddressAction") (:list (:word "Request") (:variable "req")) (:word "Response")) (:block (:variable "address") (:assignment "=") (:variable "this") (:object-attrib (:word "repo")) (:object-attrib (:word "find")) (:list (:variable "req") (:object-attrib (:word "request")) (:object-attrib (:word "get")) (:list (:string "address"))) (:terminator ";") (:variable "this") (:object-attrib (:word "em")) (:object-attrib (:word "remove")) (:list (:variable "address")) (:terminator ";") (:variable "this") (:object-attrib (:word "em")) (:object-attrib (:word "flush")) (:list) (:terminator ";") (:word "return") (:word "new") (:word "RedirectResponse") (:list (:string "/user/") (:variable "address") (:object-attrib (:word "getUser")) (:list) (:object-attrib (:word "getLoginName")) (:list) (:string "/manage")) (:terminator ";")))))))) diff --git a/test/fixtures/NamespacedClass.php b/test/fixtures/NamespacedClass.php new file mode 100644 index 0000000..2ad5938 --- /dev/null +++ b/test/fixtures/NamespacedClass.php @@ -0,0 +1,84 @@ + 'value', + 'key' => 0 + ]; + + private $repo; + private $user_repo; + private $twig; + private $em; + + public function __construct( + AddressRepository $repo, + UserRepository $user_repo, + Environment $twig, + EntityManagerInterface $em + ) { + $this->repo = $repo; + $this->user_repo = $user_repo; + $this->twig = $twig; + $this->em = $em; + } + + /** + * @Route("/address/add", methods={"GET"}) + */ + public function addAddressPage(Request $req): Response + { + $user = $this->user_repo->findOne($req->get('user')); + + return new Response( + $this->twig->render('address/create.html.twig', [ + 'user' => $user, + ]) + ); + } + + /** + * @Route("/address/add", methods={"POST"}) + */ + public function addAddressAction(Request $req): Response + { + $user = $this->user_repo->findOne($req->request->get('user')); + $address_string = $req->request->get('address'); + + $address = new Address($user, $address_string); + + $this->em->persist($address); + $this->em->flush(); + + + return new RedirectResponse('/user/' . $user->getLoginName() . '/manage'); + } + + /** + * @Route("/address/delete", methods={"POST"}) + */ + public function deleteAddressAction(Request $req): Response + { + $address = $this->repo->find($req->request->get('address')); + + $this->em->remove($address); + $this->em->flush(); + + return new RedirectResponse('/user/' . $address->getUser()->getLoginName() . '/manage'); + } +} diff --git a/test/fixtures/NamespacedFunctions.el b/test/fixtures/NamespacedFunctions.el new file mode 100644 index 0000000..7a627e9 --- /dev/null +++ b/test/fixtures/NamespacedFunctions.el @@ -0,0 +1 @@ +(:root (:namespace (:word "App\\Functions") (:terminator ";") (:function (:declaration (:word "function") (:word "MergeTwoArraysAndSomeOtherStuff") (:list (:word "array") (:variable "array1") (:comma ",") (:variable "untyped_variable")) (:word "Response")) (:block (:variable "merged") (:assignment "=") (:word "array_merge") (:list (:variable "array_1") (:comma ",") (:variable "untyped_variable")) (:terminator ";") (:variable "mapped") (:assignment "=") (:word "arrap_map") (:list (:function (:declaration (:word "function") (:list (:variable "item"))) (:block (:word "return") (:variable "item") (:terminator ";"))) (:comma ",") (:variable "merged")) (:terminator ";") (:variable "user") (:assignment "=") (:variable "this") (:object-attrib (:word "user_repo")) (:object-attrib (:word "findOne")) (:list (:variable "req") (:object-attrib (:word "get")) (:list (:string "user"))) (:terminator ";") (:word "return") (:word "new") (:word "Response") (:list (:variable "this") (:object-attrib (:word "twig")) (:object-attrib (:word "render")) (:list (:string "address/create.html.twig") (:comma ",") (:array (:string "user") (:fat-arrow "=>") (:variable "user") (:comma ",")))) (:terminator ";"))) (:function (:declaration (:word "function") (:word "BeTheSecondFunctionInTheFile") (:list)) (:block (:word "return") (:array (:string "Very Impressive Result") (:fat-arrow "=>") (:variable "result")) (:terminator ";"))))) diff --git a/test/fixtures/NamespacedFunctions.php b/test/fixtures/NamespacedFunctions.php new file mode 100644 index 0000000..2c5ab55 --- /dev/null +++ b/test/fixtures/NamespacedFunctions.php @@ -0,0 +1,27 @@ +user_repo->findOne($req->get('user')); + + return new Response( + $this->twig->render('address/create.html.twig', [ + 'user' => $user, + ]) + ); +} + + +function BeTheSecondFunctionInTheFile() { + return [ "Very Impressive Result" => $result ]; +} diff --git a/test/phpinspect-test.el b/test/phpinspect-test.el new file mode 100644 index 0000000..8e8ceee --- /dev/null +++ b/test/phpinspect-test.el @@ -0,0 +1,71 @@ +;;; phpinspect-test.el --- Unit tests for phpinslect.el -*- lexical-binding: t; -*- + +;; Copyright (C) 2021 Free Software Foundation, Inc. + +;; Author: Hugo Thunnissen + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; + +;;; Code: + +(require 'ert) +(require 'phpinspect) + +(defvar phpinspect-test-php-file-directory + (concat + (file-name-directory + (or load-file-name + buffer-file-name)) + "/fixtures") + "Directory with syntax trees of example PHP files.") + +(defun phpinspect-test-read-fixture-tree (name) + (with-temp-buffer + (insert-file-contents-literally (concat phpinspect-test-php-file-directory "/" name ".el")) + (read (current-buffer)))) + +(defun phpinspect-test-parse-fixture-code (name) + (phpinspect-parse-file + (concat phpinspect-test-php-file-directory "/" name ".php"))) + +(ert-deftest phpinspect-parse-namespaced-class () + "Test phpinspect-parse on a namespaced class" + (should + (equal (phpinspect-test-read-fixture-tree "NamespacedClass") + (phpinspect-test-parse-fixture-code "NamespacedClass")))) + +(ert-deftest phpinspect-parse-block () + "Test phpinspect-parse for php blocks" + (should + (equal (phpinspect-test-read-fixture-tree "Block") + (phpinspect-test-parse-fixture-code "Block")))) + +(ert-deftest phpinspect-parse-functions () + "Test phpinspect-parse for php functions" + (should + (equal (phpinspect-test-read-fixture-tree "Functions") + (phpinspect-test-parse-fixture-code "Functions")))) + +(ert-deftest phpinspect-parse-namespaced-functions () + "Test phpinspect-parse for php blocks" + (should + (equal (phpinspect-test-read-fixture-tree "NamespacedFunctions") + (phpinspect-test-parse-fixture-code "NamespacedFunctions")))) + +(provide 'phpinspect-test) +;;; phpinspect-test.el ends here diff --git a/test/util/generate-test-data.el b/test/util/generate-test-data.el new file mode 100644 index 0000000..fcccd47 --- /dev/null +++ b/test/util/generate-test-data.el @@ -0,0 +1,13 @@ + +(require 'phpinspect) + +(let ((here (file-name-directory + (or load-file-name + buffer-file-name)))) + (dolist (file (directory-files (concat here "/../fixtures" ) t "\\.php$")) + (with-temp-buffer + (insert-file-contents-literally file) + (let ((result (phpinspect-parse-current-buffer))) + (with-temp-buffer + (insert (prin1-to-string result)) + (write-file (concat (string-remove-suffix ".php" file) ".el")))))))