Replace index-thread with more generic and encapsulated worker type
continuous-integration/drone/push Build is failing Details

The new implementaiton makes the background thread more extensible in terms of
the types of tasks that it can execute. It also allows for the injection of a
worker as dependency of project instances, which will make automated testing
easier in the future.
WIP-incremental-parsing
Hugo Thunnissen 2 years ago
parent 1816495538
commit 56eaa3b36d

@ -56,7 +56,9 @@ then returned.")
((cache phpinspect--cache) (project-root string))
(or (phpinspect--cache-getproject cache project-root)
(puthash project-root
(phpinspect--make-project-cache :root project-root)
(phpinspect--make-project-cache
:root project-root
:worker (phpinspect-make-dynamic-worker))
(phpinspect--cache-projects cache))))
(provide 'phpinspect-cache)

@ -23,7 +23,9 @@
;;; Code:
(require 'cl-lib)
(require 'phpinspect-util)
(require 'phpinspect-project)
(require 'phpinspect-type)
(defun phpinspect--function-from-scope (scope)
@ -362,188 +364,6 @@ namespace if not provided"
"Index a PHP file for classes and the methods they have"
(phpinspect--index-tokens (phpinspect-parse-current-buffer)))
(cl-defstruct (phpinspect--queue-item
(:constructor phpinspect--make-queue-item))
(next nil
:type phpinspect--queue-item
:documentation
"The next item in the queue")
(thing nil
:type any
:documentation
"The thing stored in the queue")
(previous nil
:type phpinspect--queue-item
:documentation
"The previous item in the queue")
(subscription nil
:type function
:read-only t
:documentation
"A function that should be called when items are
enqueued."))
(defsubst phpinspect--make-queue (&optional subscription)
(phpinspect--make-queue-item :subscription subscription))
;; Recursion causes max-eval-depth error here for long queues. Hence the loop
;; implementation for these two functions.
(cl-defmethod phpinspect--queue-last ((item phpinspect--queue-item))
(while (phpinspect--queue-item-next item)
(setq item (phpinspect--queue-item-next item)))
item)
(cl-defmethod phpinspect--queue-first ((item phpinspect--queue-item))
(while (phpinspect--queue-item-previous item)
(setq item (phpinspect--queue-item-previous item)))
item)
(cl-defmethod phpinspect--queue-enqueue ((item phpinspect--queue-item) thing)
(let ((last (phpinspect--queue-last item)))
(if (not (phpinspect--queue-item-thing last))
(setf (phpinspect--queue-item-thing last) thing)
(setf (phpinspect--queue-item-next last)
(phpinspect--make-queue-item
:previous last
:thing thing
:subscription (phpinspect--queue-item-subscription item)))))
(funcall (phpinspect--queue-item-subscription item)))
(cl-defmethod phpinspect--queue-dequeue ((item phpinspect--queue-item))
(let* ((first (phpinspect--queue-first item))
(thing (phpinspect--queue-item-thing first))
(next (phpinspect--queue-item-next first)))
(when next (setf (phpinspect--queue-item-previous next) nil))
(cond ((and (eq item first) (not next))
(setf (phpinspect--queue-item-thing item)
nil))
((eq item first)
(setf (phpinspect--queue-item-thing item)
(phpinspect--queue-item-thing next))
(setf (phpinspect--queue-item-next item)
(phpinspect--queue-item-next next))))
thing))
(cl-defmethod phpinspect--queue-find
((item phpinspect--queue-item) thing comparison-func)
(setq item (phpinspect--queue-first item))
(let ((found))
(while (and item (not found))
(when (and (phpinspect--queue-item-thing item)
(funcall comparison-func thing (phpinspect--queue-item-thing item)))
(setq found item))
(setq item (phpinspect--queue-item-next item)))
found))
(cl-defmethod phpinspect--queue-enqueue-noduplicate
((item phpinspect--queue-item) thing comparison-func)
(when (not (phpinspect--queue-find item thing comparison-func))
(phpinspect--queue-enqueue item thing)))
(cl-defmethod phpinspect--queue-await-insert ((item phpinspect--queue-item))
(condition-wait (phpinspect--queue-item-insert item)))
(defvar phpinspect--index-queue (phpinspect--make-queue)
"Queue with indexation tasks. Each task is a list, the car of
which is a project directory path and the cadr of which is an
instance of `phpinspect--type`.")
(defvar phpinspect--index-thread nil
"Thread that executes index tasks from `phpinspect--index-queue`.")
(defvar phpinspect--index-thread-running nil
"Thread that executes index tasks from `phpinspect--index-queue`.")
(defun phpinspect--index-thread-function ()
(while phpinspect--index-thread-running
;; This error is used to wake up the thread when new tasks are added to the
;; queue.
(ignore-error 'phpinspect--wakeup-thread
(let* ((task (phpinspect--queue-dequeue phpinspect--index-queue))
(mx (make-mutex))
(continue (make-condition-variable mx))
(skip-pause))
(if task
(progn
(phpinspect--log "Indexing class %s for project in %s from index thread"
(phpinspect--index-task-type task)
(phpinspect--index-task-project-root task))
(let ((project (phpinspect--cache-get-project-create
(phpinspect--get-or-create-global-cache)
(phpinspect--index-task-project-root task)))
(is-native-type (phpinspect--type-is-native
(phpinspect--index-task-type task))))
(if is-native-type
(progn
(phpinspect--log "Skipping indexation of native type %s"
(phpinspect--index-task-type task))
;; We can skip pausing when a native type is encountered
;; and skipped, as we haven't done any intensive work that
;; may cause hangups.
(setq skip-pause t))
(let* ((type (phpinspect--index-task-type task))
(root-index (phpinspect--index-type-file project type)))
(when root-index
(phpinspect--project-add-index project root-index))))))
;; else: join with the main thread until wakeup is signaled
(thread-join main-thread))
;; Pause for a second after indexing something, to allow user input to
;; interrupt the thread.
(unless skip-pause
(phpinspect--index-thread-pause 1 mx continue))
(setq skip-pause nil))))
(phpinspect--log "Index thread exiting")
(message "phpinspect index thread exited"))
(defun phpinspect--index-thread-pause (pause-time mx continue)
(phpinspect--log "Index thead is paused for %d seconds" pause-time)
(run-with-idle-timer
pause-time
nil
(lambda () (with-mutex mx (condition-notify continue))))
(with-mutex mx (condition-wait continue))
(phpinspect--log "Index thread continuing"))
(define-error 'phpinspect--wakeup-thread
"This error is used to wakeup the index thread")
(defun phpinspect--wakeup-index-thread ()
(when (thread--blocker phpinspect--index-thread)
(thread-signal phpinspect--index-thread 'phpinspect--wakeup-thread nil)))
(defun phpinspect--ensure-index-thread ()
(interactive)
(when (or (not phpinspect--index-thread)
(not (thread-live-p phpinspect--index-thread)))
(setq phpinspect--index-queue (phpinspect--make-queue #'phpinspect--wakeup-index-thread))
(setq phpinspect--index-thread-running t)
(setq phpinspect--index-thread
(make-thread #'phpinspect--index-thread-function "phpinspect-index-thread"))))
(defun phpinspect--stop-index-thread ()
(interactive)
(setq phpinspect--index-thread-running nil))
(defalias 'phpinspect--index-task-project-root #'car)
(defalias 'phpinspect--index-task-type #'cadr)
(defun phpinspect--index-task= (task1 task2)
(and (phpinspect--type= (phpinspect--index-task-type task1)
(phpinspect--index-task-type task2))
(string= (phpinspect--index-task-project-root task1)
(phpinspect--index-task-project-root task2))))
(defsubst phpinspect--make-index-task (project-root type)
(list project-root type))
(cl-defmethod phpinspect--index-type-file ((project phpinspect--project)
(type phpinspect--type))
(condition-case error

@ -32,6 +32,10 @@
:documentation
"A `hash-table` that contains all of the currently
indexed classes in the project")
(worker nil
:type phpinspect-worker
:documentation
"The worker that this project may queue tasks for")
(root nil
:type string
:documentation
@ -65,9 +69,8 @@ indexed classes in the project")
(setq class (phpinspect--project-create-class project type)))
(phpinspect--log "Adding unpresent class %s to index queue" type)
(setf (phpinspect--class-index-queued class) t)
(phpinspect--index-thread-enqueue (phpinspect--make-index-task
(phpinspect--project-root project)
type)))))
(phpinspect-worker-enqueue (phpinspect--project-worker project)
(phpinspect-make-index-task project type)))))
(cl-defmethod phpinspect--project-add-class-attribute-types-to-index-queue
((project phpinspect--project) (class phpinspect--class))

@ -0,0 +1,342 @@
;;; phpinspect-worker.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
;; Copyright (C) 2021 Free Software Foundation, Inc
;; Author: Hugo Thunnissen <devel@hugot.nl>
;; Keywords: php, languages, tools, convenience
;; Version: 0
;; 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 <https://www.gnu.org/licenses/>.
;;; Commentary:
;;; Code:
(require 'cl-lib)
(require 'phpinspect-project)
(require 'phpinspect-index)
(require 'phpinspect-class)
(defvar phpinspect-worker nil
"Contains the phpinspect worker that is used by all projects.")
(cl-defstruct (phpinspect-queue-item
(:constructor phpinspect-make-queue-item))
(next nil
:type phpinspect-queue-item
:documentation
"The next item in the queue")
(thing nil
:type any
:documentation
"The thing stored in the queue")
(previous nil
:type phpinspect-queue-item
:documentation
"The previous item in the queue")
(subscription nil
:type function
:read-only t
:documentation
"A function that should be called when items are
enqueued."))
(defsubst phpinspect-make-queue (&optional subscription)
(phpinspect-make-queue-item :subscription subscription))
;; Recursion causes max-eval-depth error here for long queues. Hence the loop
;; implementation for these two functions.
(cl-defmethod phpinspect-queue-last ((item phpinspect-queue-item))
"Get the last item in the queue that ITEM is part of."
(while (phpinspect-queue-item-next item)
(setq item (phpinspect-queue-item-next item)))
item)
(cl-defmethod phpinspect-queue-first ((item phpinspect-queue-item))
"Get the first item in the queue that ITEM is part of."
(while (phpinspect-queue-item-previous item)
(setq item (phpinspect-queue-item-previous item)))
item)
(cl-defmethod phpinspect-queue-enqueue ((item phpinspect-queue-item) thing)
"Add THING to the end of the queue that ITEM is part of."
(let ((last (phpinspect-queue-last item)))
(if (not (phpinspect-queue-item-thing last))
(setf (phpinspect-queue-item-thing last) thing)
(setf (phpinspect-queue-item-next last)
(phpinspect-make-queue-item
:previous last
:thing thing
:subscription (phpinspect-queue-item-subscription item)))))
(when (phpinspect-queue-item-subscription item)
(funcall (phpinspect-queue-item-subscription item))))
(cl-defmethod phpinspect-queue-dequeue ((item phpinspect-queue-item))
"Remove the thing at the front of the queue that ITEM is part of an return it."
(let* ((first (phpinspect-queue-first item))
(thing (phpinspect-queue-item-thing first))
(next (phpinspect-queue-item-next first)))
(when next (setf (phpinspect-queue-item-previous next) nil))
(cond ((and (eq item first) (not next))
(setf (phpinspect-queue-item-thing item)
nil))
((eq item first)
(setf (phpinspect-queue-item-thing item)
(phpinspect-queue-item-thing next))
(setf (phpinspect-queue-item-next item)
(phpinspect-queue-item-next next))))
thing))
(defmacro phpinspect-doqueue (place-and-queue &rest body)
"Loop over queue defined in PLACE-AND-QUEUE executing BODY.
PLACE-AND-QUEUE is a two-member list. The first item should be
the place that the current thing in the queue should be assigned
to upon each iteration. The second item should be a queue-item
belonging to the queue that must be iterated over.
BODY can be any form."
(declare (indent defun))
(let ((item-sym (gensym))
(place (car place-and-queue))
(queue (cadr place-and-queue)))
`(let* ((,item-sym (phpinspect-queue-first ,queue))
(,place (phpinspect-queue-item-thing ,item-sym)))
(when ,place
,@body
(while (setq ,item-sym (phpinspect-queue-item-next ,item-sym))
(setq ,place (phpinspect-queue-item-thing ,item-sym))
,@body)))))
(cl-defmethod phpinspect-queue-find
((item phpinspect-queue-item) thing comparison-func)
"Find THING in the queue that ITEM is part of using COMPARISON-FUNC."
(catch 'found
(phpinspect-doqueue (current-thing item)
(when (funcall comparison-func current-thing thing)
(throw 'found current-thing)))))
(cl-defmethod phpinspect-queue-enqueue-noduplicate
((item phpinspect-queue-item) thing comparison-func)
(when (not (phpinspect-queue-find item thing comparison-func))
(phpinspect-queue-enqueue item thing)))
(cl-defmethod phpinspect-queue-await-insert ((item phpinspect-queue-item))
(condition-wait (phpinspect-queue-item-insert item)))
(cl-defstruct (phpinspect-worker
(:constructor phpinspect-make-worker-generated))
(queue nil
:type phpinspect-queue-item
:documentation
"The queue of tasks that are pending")
(thread nil
:type thread
:documentation
"The thread of this worker")
(continue-running nil
:type bool
:documentation
"Whether or not the thread should continue
running. If this is nil, the thread isstopped.")
(skip-next-pause nil
:type bool
:documentation
"Whether or not the thread should skip its next scheduled pause."))
(cl-defstruct (phpinspect-dynamic-worker
(:constructor phpinspect-make-dynamic-worker-generated))
"A dynamic worker is nothing other than an object that is
supported by all of the same methods as a `phpinspect-worker`,
but relies on an underlying, global worker to actually do the
work. The reason for its implementation is to allow users to
manage phpinspect's worker thread centrally in a dynamic
variable, while also making the behaviour of objects that depend
on the worker independent of dynamic variables during testing.")
(cl-defmethod phpinspect-resolve-dynamic-worker ((worker phpinspect-dynamic-worker))
phpinspect-worker)
(defsubst phpinspect-make-dynamic-worker ()
(phpinspect-make-dynamic-worker-generated))
(defsubst phpinspect-make-worker ()
"Create a new worker object."
(let ((worker (phpinspect-make-worker-generated)))
(setf (phpinspect-worker-queue worker)
(phpinspect-make-queue (phpinspect-worker-make-wakeup-function worker)))
worker))
(define-error 'phpinspect-wakeup-thread
"This error is used to wakeup the index thread")
(cl-defgeneric phpinspect-worker-make-wakeup-function (worker)
"Create a function that can be used to wake up WORKER's thread.")
(cl-defmethod phpinspect-worker-make-wakeup-function ((worker phpinspect-worker))
(lambda ()
(when (eq main-thread (thread--blocker (phpinspect-worker-thread worker)))
(thread-signal (phpinspect-worker-thread worker)
'phpinspect-wakeup-thread nil))))
(cl-defmethod phpinspect-worker-make-wakeup-function ((worker phpinspect-dynamic-worker))
(phpinspect-worker-make-wakeup-function (phpinspect-resolve-dynamic-worker worker)))
(cl-defgeneric phpinspect-worker-live-p (worker)
"Just a shorthand to check whether or not the WORKER's thread is running.")
(cl-defmethod phpinspect-worker-live-p ((worker phpinspect-worker))
(when (phpinspect-worker-thread worker)
(thread-live-p (phpinspect-worker-thread worker))))
(cl-defmethod phpinspect-worker-live-p ((worker phpinspect-dynamic-worker))
(phpinspect-worker-live-p (phpinspect-resolve-dynamic-worker worker)))
(cl-defgeneric phpinspect-worker-enqueue (worker task)
"Enqueue a TASK to be executed by WORKER.")
(cl-defmethod phpinspect-worker-enqueue ((worker phpinspect-worker) task)
(phpinspect-queue-enqueue (phpinspect-worker-queue worker) task))
(cl-defmethod phpinspect-worker-enqueue ((worker phpinspect-dynamic-worker) task)
(phpinspect-worker-enqueue (phpinspect-resolve-dynamic-worker worker)
task))
(defun phpinspect-thread-pause (pause-time mx continue)
"Pause current thread using MX and CONTINUE for PAUSE-TIME idle seconds.
PAUSE-TIME must be the idle time that the thread should pause for.
MX must be a mutex
CONTINUE must be a condition-variable"
(phpinspect--log "Worker thead is paused for %d seconds" pause-time)
(run-with-idle-timer
pause-time
nil
(lambda () (with-mutex mx (condition-notify continue))))
(with-mutex mx (condition-wait continue))
(phpinspect--log "Index thread continuing"))
(cl-defgeneric phpinspect-worker-make-thread-function (worker)
"Create a function that can be used to start WORKER's thread.")
(cl-defmethod phpinspect-worker-make-thread-function ((worker phpinspect-worker))
(lambda ()
(while (phpinspect-worker-continue-running worker)
;; This error is used to wake up the thread when new tasks are added to the
;; queue.
(ignore-error 'phpinspect-wakeup-thread
(let* ((task (phpinspect-queue-dequeue (phpinspect-worker-queue worker)))
(mx (make-mutex))
(continue (make-condition-variable mx)))
(if task
(phpinspect-task-execute task worker)
;; else: join with the main thread until wakeup is signaled
(thread-join main-thread))
;; Pause for a second after indexing something, to allow user input to
;; interrupt the thread.
(unless (phpinspect-worker-skip-next-pause worker)
(phpinspect-thread-pause 1 mx continue))
(setf (phpinspect-worker-skip-next-pause worker) nil))))
(phpinspect--log "Worker thread exiting")
(message "phpinspect worker thread exited")))
(cl-defmethod phpinspect-worker-make-thread-function ((worker phpinspect-dynamic-worker))
(phpinspect-worker-make-thread-function
(phpinspect-resolve-dynamic-worker worker)))
(cl-defgeneric phpinspect-worker-start (worker)
"Start WORKER's thread.")
(cl-defmethod phpinspect-worker-start ((worker phpinspect-worker))
(if (phpinspect-worker-live-p worker)
(error "Attempt to start a worker that is already running")
(progn
(setf (phpinspect-worker-continue-running worker) t)
(setf (phpinspect-worker-thread worker)
(make-thread (phpinspect-worker-make-thread-function worker))))))
(cl-defmethod phpinspect-worker-start ((worker phpinspect-dynamic-worker))
(phpinspect-worker-start (phpinspect-resolve-dynamic-worker worker)))
(cl-defgeneric phpinspect-worker-stop (worker)
"Stop the worker")
(cl-defmethod phpinspect-worker-stop ((worker phpinspect-worker))
(setf (phpinspect-worker-continue-running worker) nil))
(cl-defmethod phpinspect-worker-stop ((worker phpinspect-dynamic-worker))
(phpinspect-worker-stop (phpinspect-resolve-dynamic-worker worker)))
(defun phpinspect-ensure-worker ()
(interactive)
(when (not phpinspect-worker)
(setq phpinspect-worker (phpinspect-make-worker)))
(when (not (phpinspect-worker-live-p phpinspect-worker))
(phpinspect-worker-start phpinspect-worker)))
(defun phpinspect-stop-worker ()
(interactive)
(phpinspect-worker-stop phpinspect-worker))
(cl-defstruct (phpinspect-index-task
(:constructor phpinspect-make-index-task-generated))
"Represents an index task that can be executed by a `phpinspect-worker`."
(project nil
:type phpinspect--project
:documentation
"The project that the task should be executed for.")
(type nil
:type phpinspect--type
:documentation
"The type whose file should be indexed."))
(cl-defgeneric phpinspect-make-index-task ((project phpinspect--project)
(type phpinspect--type))
(phpinspect-make-index-task-generated
:project project
:type type))
(cl-defgeneric phpinspect-task-execute (task worker)
"Execute TASK for WORKER.")
(cl-defmethod phpinspect-task-execute ((task phpinspect-index-task)
(worker phpinspect-worker))
"Execute index TASK for WORKER."
(let ((project (phpinspect-index-task-project task))
(is-native-type (phpinspect--type-is-native
(phpinspect-index-task-type task))))
(phpinspect--log "Indexing class %s for project in %s from index thread"
(phpinspect-index-task-type task)
(phpinspect--project-root project))
(if is-native-type
(progn
(phpinspect--log "Skipping indexation of native type %s"
(phpinspect-index-task-type task))
;; We can skip pausing when a native type is encountered
;; and skipped, as we haven't done any intensive work that
;; may cause hangups.
(setf (phpinspect-worker-skip-next-pause worker) t))
(let* ((type (phpinspect-index-task-type task))
(root-index (phpinspect--index-type-file project type)))
(when root-index
(phpinspect--project-add-index project root-index))))))
(provide 'phpinspect-worker)
;;; phpinspect-worker.el ends here

@ -37,6 +37,7 @@
(require 'phpinspect-type)
(require 'phpinspect-index)
(require 'phpinspect-class)
(require 'phpinspect-worker)
(defvar-local phpinspect--buffer-index nil
"The result of the last successfull parse + index action
@ -719,7 +720,7 @@ more recent"
(eldoc-add-command 'c-electric-paren)
(eldoc-add-command 'c-electric-backspace)
(phpinspect--ensure-index-thread)
(phpinspect-ensure-worker)
(phpinspect--after-save-action)
(add-hook 'after-save-hook #'phpinspect--after-save-action nil 'local))

@ -26,6 +26,13 @@
(require 'ert)
(require 'phpinspect)
(defvar phpinspect-test-directory
(file-name-directory
(or load-file-name
buffer-file-name))
"Directory that phpinspect tests reside in.")
(defvar phpinspect-test-php-file-directory
(concat
(file-name-directory
@ -487,5 +494,7 @@ class Thing
(phpinspect--get-last-statement-in-token
(phpinspect-parse-string php-code-bare))))))
(load-file (concat phpinspect-test-directory "/test-worker.el"))
(provide 'phpinspect-test)
;;; phpinspect-test.el ends here

@ -0,0 +1,92 @@
;;; test-worker.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
;; Copyright (C) 2021 Free Software Foundation, Inc.
;; Author: Hugo Thunnissen <devel@hugot.nl>
;; 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 <https://www.gnu.org/licenses/>.
;;; Commentary:
;;
;;; Code:
(require 'ert)
(require 'phpinspect-index)
(require 'phpinspect-worker)
(ert-deftest phpinspect-queue-enqueue ()
(let ((queue (phpinspect-make-queue)))
(phpinspect-queue-enqueue queue "one")
(phpinspect-queue-enqueue queue "two")
(phpinspect-queue-enqueue queue "three")
(should (string= "one" (phpinspect-queue-dequeue queue)))
(should (string= "two" (phpinspect-queue-dequeue queue)))
(should (string= "three" (phpinspect-queue-dequeue queue)))))
(ert-deftest phpinspect-queue-subscribe ()
(let ((be-called nil))
(let ((queue (phpinspect-make-queue (lambda () (setq be-called t)))))
(phpinspect-queue-enqueue queue "one"))
(should be-called)))
(ert-deftest phpinspect-queue-find ()
(let ((queue (phpinspect-make-queue)))
(phpinspect-queue-enqueue queue "one")
(phpinspect-queue-enqueue queue "two")
(phpinspect-queue-enqueue queue "three")
(should (string= "one" (phpinspect-queue-find queue "one" 'string=)))
(should (string= "two" (phpinspect-queue-find queue "two" 'string=)))
(should (string= "three" (phpinspect-queue-find queue "three" 'string=)))))
(ert-deftest phpinspect-doqueue ()
;; Iterate over a populated queue
(let ((queue (phpinspect-make-queue)))
(phpinspect-queue-enqueue queue "one")
(phpinspect-queue-enqueue queue "two")
(phpinspect-queue-enqueue queue "three")
(phpinspect-queue-enqueue queue "four")
(let ((expected-things '("one" "two" "three" "four"))
(things))
(phpinspect-doqueue (thing queue)
(push thing things))
(should (equal expected-things (nreverse things)))))
;; attempt to iterate over an empty queue
(let ((have-iterated nil))
(phpinspect-doqueue (thing (phpinspect-make-queue))
(setq have-iterated t))
(should-not have-iterated)))
(ert-deftest phpinspect-queue-enqueue-noduplicate ()
(let ((queue (phpinspect-make-queue))
(expected-things '("one" "two"))
(things))
(phpinspect-queue-enqueue-noduplicate queue "one" 'string=)
(phpinspect-queue-enqueue-noduplicate queue "two" 'string=)
(phpinspect-queue-enqueue-noduplicate queue "two" 'string=)
(phpinspect-queue-enqueue-noduplicate queue "one" 'string=)
(phpinspect-doqueue (thing queue)
(push thing things))
(should (equal expected-things (nreverse things)))))
Loading…
Cancel
Save