Squashed 'test/mocker/' content from commit a1ddf87

git-subtree-dir: test/mocker
git-subtree-split: a1ddf87150f67306ae8da927117063c75588be30
This commit is contained in:
John Miller 2016-09-09 10:10:36 -05:00
commit 13d3f30f34
8 changed files with 914 additions and 0 deletions

10
.bumpversion.cfg Normal file
View file

@ -0,0 +1,10 @@
[bumpversion]
current_version = 0.3.1
commit = True
tag = True
tag_name = v{new_version}
[bumpversion:file:mocker.el]
[bumpversion:file:Cask]

1
.ert-runner Normal file
View file

@ -0,0 +1 @@
-L .

14
.travis.yml Normal file
View file

@ -0,0 +1,14 @@
language: emacs-lisp
sudo: no
env:
- EVM_EMACS=emacs-24.1-travis
- EVM_EMACS=emacs-24.2-travis
- EVM_EMACS=emacs-24.3-travis
- EVM_EMACS=emacs-24.4-travis
- EVM_EMACS=emacs-24.5-travis
- EVM_EMACS=emacs-25-pre-travis
before_install:
- curl -fsSkL https://gist.github.com/rejeep/ebcd57c3af83b049833b/raw > travis.sh && source ./travis.sh
- evm install "$EVM_EMACS" --use --skip
script:
make travis-ci

11
Cask Normal file
View file

@ -0,0 +1,11 @@
(source gnu)
(source melpa-stable)
(package "mocker" "0.3.1"
"mocking framework for emacs.")
(depends-on "eieio" "1.4")
(development
(depends-on "ert-runner")
(depends-on "shut-up"))

25
Makefile Normal file
View file

@ -0,0 +1,25 @@
EMACS ?= emacs
CASK ?= cask
test: unit-tests
unit-tests: elpa
${CASK} exec ert-runner
elpa:
mkdir -p elpa
${CASK} install 2> elpa/install.log
clean-elpa:
rm -rf elpa
clean-elc:
rm -f *.elc test/*.elc
clean: clean-elpa clean-elc
print-deps:
${EMACS} --version
@echo CASK=${CASK}
travis-ci: print-deps test

206
README.markdown Normal file
View file

@ -0,0 +1,206 @@
[![Build Status](https://travis-ci.org/sigma/mocker.el.png?branch=master)](https://travis-ci.org/sigma/mocker.el)
Mocker.el is a mocking framework for Emacs lisp.
Its single entry point, `mocker-let` provides an `let` like interface to
defining mock objects. Actually, `mocker-let` is a wrapper around `flet`, which
can be seen as a way to manually generate mocks.
## Usage
### Basic usage
Let's start with a simple example:
```lisp
(mocker-let ((foo (x y z)
((:input '(1 2 3) :output 4)
(:input '(4 5 6) :output 10)))
(bar (x)
((:input '(42) :output 4))))
(+ (foo 1 2 3)
(foo 4 5 6)
(bar 42)))
```
Each mock is defined in a function-style, and is associated with a set of
"records" that map expected inputs to desired outputs.
### Call order
By default, the order of definition within a mock has to be respected by the
wrapped code, so that in this situation it would be an error to observe `(foo
4 5 6)` before `(foo 1 2 3)`.
```lisp
(mocker-let ((foo (x y z)
((:input '(1 2 3) :output 4)
(:input '(4 5 6) :output 10)))
(bar (x)
((:input '(42) :output 4))))
(+ (foo 4 5 6)
(foo 1 2 3)
(bar 42)))
```
In such a situation, you'll get a typed error with a message like
```
(mocker-record-error "Violated record while mocking `foo'. Expected input like: `(1 2 3)', got: `(4 5 6)' instead")
...
```
If order is not important, you can obtain the same effect as before by
specifying it:
```lisp
(mocker-let ((foo (x y z)
:ordered nil
((:input '(1 2 3) :output 4)
(:input '(4 5 6) :output 10)))
(bar (x)
((:input '(42) :output 4))))
(+ (foo 4 5 6)
(foo 1 2 3)
(bar 42)))
```
### Counting calls
In many situations it can be pretty repetitive to list all the expected calls
to a mock. In some, the count might even be a range rather than a fixed number.
The `:min-occur` and `:max-occur` options allow to tune that. By default, they
are both set to 1, so that exactly 1 call is expected. As a special case,
setting `:max-occur` to nil will accept any number of calls.
An `:occur` shorthand is also provided, to expect an exact number of calls.
```lisp
(mocker-let ((foo (x)
((:input '(1) :output 1 :min-occur 1 :max-occur 3))))
(+ (foo 1) (foo 1)))
```
This example will accept between 1 and 3 calls to `(foo 1)`, and complain if
that constraint is not fulfilled.
Note the applied algorithm is greedy, so that as many calls as possible will
count as part of the earliest constraints.
### Flexible input/output
The examples above are fine, but they suppose input and output are just
constant expressions. A useful addition is the ability to match arbitrary input
and generate arbitrary output.
To this end, the `:input-matcher` and `:output-generator` options can be used
instead (actually think of `:input` and `:output` as convenience shortcuts for
constant matcher/generator).
```lisp
(mocker-let ((foo (x)
:ordered nil
((:input-matcher 'oddp :output-generator 'identity :max-occur 2)
(:input-matcher 'evenp :output 0))))
(+ (foo 1) (foo 2) (foo 3)))
```
Both `:input-matcher` and `:output-generator` values need to be functions (or
function symbols) accepting the same arguments as the mocked function itself.
## Extensibility
Each record definition actually builds a `mocker-record` object, that's
responsible for checking the actual behavior. By providing alternative
implementations of those records, one can adapt the mocking to special needs.
### Stubs
As a quick proof of concept, an implementation of a stub is provided with the
class `mocker-stub-record` which casualy ignores any input and always emits the
same output:
```lisp
(mocker-let ((foo (x)
((:record-cls 'mocker-stub-record :output 42))))
(foo 12345))
```
### Passthrough
In some occasions, you might want to mock only some calls for a function, and
let other calls invoke the real one. This can be achieved by using the
`mocker-passthrough-record`. In the following example, the first call to
`ignore` uses the real implementation, while the second one is mocked to return
`t`:
```lisp
(mocker-let ((ignore (x)
:records ((:record-cls mocker-passthrough-record
:input '(42))
(:input '(58) :output t))))
(or (ignore 42)
(ignore 58)))
```
### Provide your own
Customized classes can be provided, that can even introduce a mini-language for
describing the stub. This can be achieved by overloading
`mocker-read-record` correctly.
In case the customized record class is meant to be used in many tests, it might
be more convenient to use a pattern like:
```lisp
(let ((mocker-mock-default-record-cls 'mocker-stub-record))
(mocker-let ((foo (x)
((:output 42)))
(bar (x y)
((:output 1))))
(+ (foo 12345)
(bar 5 14))))
```
Also note that `mocker-stub-record` set their `:min-occur` to 0 and
`:max-occur` to nil, if not specified otherwise.
## Comparison to other mocking solutions
* el-mock.el (http://www.emacswiki.org/emacs/EmacsLispMock)
* el-mock.el uses a small DSL for recording behavior, which is great for
conciseness. mocker.el instead uses regular lisp as much as possible, which
is more flexible.
* el-mock.el does not allow recording multiple behaviors (the same call will
always return the same value). This makes it difficult to use in real
situation, where different call sites for the same function might have to
behave differently.
## Examples
```lisp
;;; automatically answer some `y-or-n-p' questions
(mocker-let ((y-or-n-p (prompt)
((:input '("Really?") :output t)
(:input '("I mean... for real?") :output nil))))
...)
```
```lisp
;;; blindly accept all `yes-or-no-p' questions
(mocker-let ((yes-or-no-p (prompt)
((:record-cls mocker-stub-record :output t))))
...)
```
```lisp
;;; make `foo' generate the fibonacci suite, no matter how it's called
(mocker-let ((foo (x)
((:input-matcher (lambda (x) t)
:output-generator (lexical-let ((x 0) (y 1))
(lambda (any)
(let ((z (+ x y)))
(setq x y y z))))
:max-occur nil))))
...)
```

368
mocker.el Normal file
View file

@ -0,0 +1,368 @@
;;; mocker.el --- mocking framework for emacs
;; Copyright (C) 2011 Yann Hodique.
;; Author: Yann Hodique <yann.hodique@gmail.com>
;; Keywords: lisp, testing
;; Version: 0.3.1
;; Package-Requires: ((eieio "1.3") (el-x "0.2.4"))
;; This file 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 2, or (at your option)
;; any later version.
;; This file 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 GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;
;;; Code:
(eval-when-compile
(require 'cl))
(require 'eieio)
(eval-and-compile
;; use dflet from el-x if available
(if (require 'dflet nil t)
(defalias 'mocker-flet 'dflet)
;; fallback to regular flet, hoping it's still there
(defalias 'mocker-flet 'flet)))
(defvar mocker-mock-default-record-cls 'mocker-record)
(put 'mocker-mock-error 'error-conditions '(mocker-mock-error error))
(put 'mocker-mock-error 'error-message "Mocker mock error")
(put 'mocker-record-error 'error-conditions '(mocker-record-error error))
(put 'mocker-record-error 'error-message "Mocker record error")
(defun mocker--plist-remove (plist key)
;; courtesy of pjb
(if (eq (car plist) key) (cdr (cdr plist))
(cons (car plist)
(cons (cadr plist)
(mocker--plist-remove (cddr plist) key)))))
;;; Mock object
(defclass mocker-mock ()
((function :initarg :function :type symbol)
(orig-def :initarg :orig-def :initform nil)
(argspec :initarg :argspec :initform nil :type list)
(ordered :initarg :ordered :initform t)
(records :initarg :records :initform nil :type list)))
(defmethod constructor :static ((mock mocker-mock) newname &rest args)
(let* ((obj (call-next-method))
(recs (oref obj :records))
(func (oref obj :function)))
(oset obj :orig-def (when (fboundp func) (symbol-function func)))
(oset obj :records nil)
(mapc #'(lambda (r)
(apply 'mocker-add-record obj r))
recs)
obj))
(defmethod mocker-add-record ((mock mocker-mock) &rest args)
(object-add-to-list mock :records
(let ((cls mocker-mock-default-record-cls)
(tmp (plist-get args :record-cls)))
(when tmp
(setq cls tmp
args (mocker-read-record cls
(mocker--plist-remove
args :record-cls))))
(apply 'make-instance cls :-mock mock
:-sym (make-symbol "unique") args))
t))
(defmethod mocker-fail-mock ((mock mocker-mock) args)
(signal 'mocker-mock-error
(list (format (concat "Unexpected call to mock `%s'"
" with input `%s'")
(oref mock :function) args))))
(defvar mocker-inhibit nil)
(defmethod mocker-run ((mock mocker-mock) &rest args)
(if (not mocker-inhibit)
(let* ((mocker-inhibit t)
(rec (mocker-find-active-record mock args))
(ordered (oref mock :ordered)))
(cond ((null rec)
(mocker-fail-mock mock args))
((or (not ordered) (mocker-test-record rec args))
(mocker-run-record rec args))
(t
(mocker-fail-record rec args))))
(apply (oref mock :orig-def) args)))
(defmethod mocker-find-active-record ((mock mocker-mock) args)
(let ((first-match (lambda (pred seq)
(let ((x nil))
(while (and seq
(not (setq x (funcall pred (pop seq))))))
x))))
(let* ((ordered (oref mock :ordered))
rec)
(if ordered
(setq rec (funcall
first-match
#'(lambda (r)
(when (oref r :-active)
(if (mocker-test-record r args)
(progn
(mocker-use-record r)
r)
(mocker-skip-record r args))))
(oref mock :records)))
(setq rec (funcall
first-match
#'(lambda (r)
(and
(oref r :-active)
(mocker-test-record r args)
(progn
(mocker-use-record r)
r)))
(oref mock :records))))
rec)))
(defmethod mocker-verify ((mock mocker-mock))
(mapc #'(lambda (r) (when (and (oref r :-active)
(< (oref r :-occurrences)
(oref r :min-occur)))
(signal 'mocker-record-error
(list (format
(concat "Expected call to mock `%s',"
" with input like %s,"
" was not run.")
(oref mock :function)
(mocker-get-record-expectations r))))))
(oref mock :records)))
;;; Mock record base object
(defclass mocker-record-base ()
((min-occur :initarg :min-occur :initform 1 :type number)
(max-occur :initarg :max-occur :initform nil :type (or null number))
(-occur :initarg :occur :initform nil :type (or null number))
(-occurrences :initarg :-occurrences :initform 0 :type number
:protection :protected)
(-mock :initarg :-mock)
(-active :initarg :-active :initform t :protection :protected)
(-sym :initarg :-sym)))
(defmethod constructor :static ((rec mocker-record-base) newname &rest args)
(let* ((obj (call-next-method))
(occur (oref obj :occur)))
(when occur
(oset obj :min-occur (max (oref obj :min-occur)
occur))
(oset obj :max-occur (if (oref obj :max-occur)
(min (oref obj :max-occur) occur)
occur)))
obj))
(defmethod mocker-read-record :static ((rec mocker-record-base) spec)
spec)
(defmethod mocker-use-record ((rec mocker-record-base))
(let ((max (oref rec :max-occur))
(n (1+ (oref rec :-occurrences))))
(oset rec :-occurrences n)
(when (and (not (null max))
(= n max))
(oset rec :-active nil))))
(defmethod mocker-skip-record ((rec mocker-record-base) args)
(if (>= (oref rec :-occurrences)
(oref rec :min-occur))
(oset rec :-active nil)
(mocker-fail-record rec args)))
(defmethod mocker-test-record ((rec mocker-record-base) args)
(error "not implemented in base class"))
(defmethod mocker-run-record ((rec mocker-record-base) args)
(error "not implemented in base class"))
(defmethod mocker-get-record-expectations ((rec mocker-record-base)))
(defmethod mocker-fail-record ((rec mocker-record-base) args)
(signal 'mocker-record-error
(list (format (concat "Violated record while mocking `%s'."
" Expected input like: %s, got: `%s' instead")
(oref (oref rec :-mock) :function)
(mocker-get-record-expectations rec)
args))))
;;; Mock input recognizer
(defclass mocker-input-record (mocker-record-base)
((input :initarg :input :initform nil :type list)
(input-matcher :initarg :input-matcher :initform nil)))
(defmethod constructor :static ((rec mocker-input-record) newname &rest args)
(let* ((obj (call-next-method)))
(when (or (not (slot-boundp obj :max-occur))
(and (oref obj :max-occur)
(< (oref obj :max-occur)
(oref obj :min-occur))))
(oset obj :max-occur (oref obj :min-occur)))
obj))
(defmethod mocker-test-record ((rec mocker-input-record) args)
(let ((matcher (oref rec :input-matcher))
(input (oref rec :input)))
(cond (matcher
(apply matcher args))
(t
(equal input args)))))
(defmethod mocker-get-record-expectations ((rec mocker-input-record))
(format "`%s'" (or (oref rec :input-matcher) (oref rec :input))))
;;; Mock record default object
(defclass mocker-record (mocker-input-record)
((output :initarg :output :initform nil)
(output-generator :initarg :output-generator :initform nil)))
(defmethod mocker-run-record ((rec mocker-record) args)
(let ((generator (oref rec :output-generator))
(output (oref rec :output)))
(cond (generator
(apply generator args))
(t
output))))
;;; Mock simple stub object
(defclass mocker-stub-record (mocker-record-base)
((output :initarg :output :initform nil)))
(defmethod constructor :static ((rec mocker-stub-record) newname &rest args)
(let* ((obj (call-next-method)))
(unless (slot-boundp obj :min-occur)
(oset obj :min-occur 0))
(unless (slot-boundp obj :max-occur)
(oset obj :max-occur nil))
obj))
(defmethod mocker-test-record ((rec mocker-stub-record) args)
t)
(defmethod mocker-run-record ((rec mocker-stub-record) args)
(oref rec :output))
(defmethod mocker-get-record-expectations ((rec mocker-stub-record))
"anything")
;;; Mock passthrough record
(defclass mocker-passthrough-record (mocker-input-record)
())
(defmethod mocker-run-record ((rec mocker-passthrough-record) args)
(let* ((mock (oref rec :-mock))
(def (oref mock :orig-def)))
(when def
(apply def args))))
;;; Helpers
(defun mocker-gen-mocks (mockspecs)
"helper to generate mocks from the input of `mocker-let'"
(mapcar #'(lambda (m)
(let* ((func (car m))
(argspec (cadr m))
(rest (cddr m))
(sym (make-symbol (concat (symbol-name func) "--mock"))))
(list sym
(apply 'make-instance 'mocker-mock
:function func
:argspec argspec
(let* ((order (if (plist-member rest :ordered)
(prog1
(plist-get rest :ordered)
(setq rest
(mocker--plist-remove
rest :ordered)))
(oref-default 'mocker-mock
:ordered))))
(list :ordered order)))
(if (plist-member rest :records)
(plist-get rest :records)
(car rest)))))
mockspecs))
;;;###autoload
(defmacro mocker-let (mockspecs &rest body)
"Generate temporary bindings according to MOCKSPECS then eval
BODY. The value of the last form in BODY is returned.
Each element of MOCKSPECS is a list (FUNC ARGS [OPTIONS]
RECORDS).
FUNC is the name of the function to bind, whose original
definition must accept arguments compatible with ARGS.
OPTIONS can be :ordered nil if the records can be executed out of
order (by default, order is enforced).
RECORDS is a list ([:record-cls CLASS] ARG1 ARG2...).
Each element of RECORDS will generate a record for the
corresponding mock. By default, records are objects of the
`mocker-record' class, but CLASS is used instead if specified.
The rest of the arguments are used to construct the record
object. They will be passed to method `mocker-read-record' for
the used CLASS. This method must return a valid list of
parameters for the CLASS constructor. This allows to implement
specialized mini-languages for specific record classes.
"
(declare (indent 1) (debug t))
(let* ((mocks (mocker-gen-mocks mockspecs))
(vars (mapcar #'(lambda (m)
`(,(car m) ,(cadr m)))
mocks))
(specs (mapcar
#'(lambda (m)
(let* ((mock-sym (car m))
(mock (cadr m))
(func (oref mock :function))
(spec (oref mock :argspec))
(call (or (and (member '&rest spec) 'apply)
'funcall))
(args (loop for el in spec
if (or (not (symbolp el))
(not (equal
(elt (symbol-name el) 0)
?&)))
collect el)))
(list func
spec
`(,call #'mocker-run ,mock-sym ,@args))))
mocks))
(inits (mapcar #'(lambda (m)
(cons 'progn
(mapcar #'(lambda (rec)
`(mocker-add-record ,(car m)
,@rec))
(nth 2 m))))
mocks))
(verifs (mapcar #'(lambda (m)
`(mocker-verify ,(car m)))
mocks)))
`(let (,@vars)
,@inits
(prog1
,(macroexpand `(mocker-flet (,@specs)
,@body))
,@verifs))))
(provide 'mocker)
;;; mocker.el ends here

279
test/mocker-test.el Normal file
View file

@ -0,0 +1,279 @@
;;; mocker-tests.el --- tests for mocker.el
;; Copyright (C) 2011 Free Software Foundation, Inc.
;; Author: Yann Hodique <yann.hodique@gmail.com>
;; Keywords: lisp, testing
;; This file 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 2, or (at your option)
;; any later version.
;; This file 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 GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; This file contains tests for mocker.el
;;; Code:
(eval-when-compile
(require 'cl))
(require 'mocker)
(eval-and-compile
(when (null (ignore-errors (require 'ert)))
(defmacro* ert-deftest (name () &body docstring-keys-and-body)
(message "Skipping tests, ERT is not available"))))
(ert-deftest mocker-let-basic ()
(should
(eq t (mocker-let () t))))
(ert-deftest mocker-let-single ()
(should
(eq t
(mocker-let ((foo () :records ((:output t))))
(foo)))))
(ert-deftest mocker-let-single-implicit-records ()
(should
(eq t
(mocker-let ((foo ()
((:output t))))
(foo)))))
(ert-deftest mocker-let-no-record ()
(should-error
(mocker-let ((foo () :records ()))
(foo))
:type 'mocker-mock-error))
(ert-deftest mocker-let-multiple ()
(should
(eq 42
(mocker-let ((foo () :records ((:output 6)))
(bar () :records ((:output 7))))
(* (foo) (bar))))))
(ert-deftest mocker-let-nested ()
(should
(eq 42
(mocker-let ((foo () :records ((:output 6))))
(mocker-let ((bar () :records ((:output 7))))
(* (foo) (bar)))))))
(ert-deftest mocker-let-multiple-inputs ()
(should
(eq 42
(mocker-let ((foo (x) :records ((:input '(1) :output 6)))
(bar (x) :records ((:input '(2) :output 7))))
(* (foo 1) (bar 2))))))
(ert-deftest mocker-let-multiple-inputs-invalid ()
(should-error
(mocker-let ((foo (x) :records ((:input '(1) :output 6)))
(bar (x) :records ((:input '(2) :output 7))))
(* (foo 2) (bar 2)))
:type 'mocker-record-error))
(ert-deftest mocker-let-single-input-matcher ()
(should
(eq t
(mocker-let ((foo (x)
:records ((:input-matcher 'integerp :output t))))
(foo 4)))))
(ert-deftest mocker-let-single-input-matcher-invalid ()
(should-error
(mocker-let ((foo (x)
:records ((:input-matcher 'integerp :output t))))
(foo t))
:type 'mocker-record-error))
(ert-deftest mocker-let-multiple-output-generator ()
(should
(eq 2
(mocker-let ((foo (x)
:records ((:input '(2)
:output-generator (function identity))
(:input '(4)
:output-generator (lambda (x) 0)))))
(+ (foo 2) (foo 4))))))
(ert-deftest mocker-let-multiple-calls-min ()
(should
(eq 4
(mocker-let ((foo (x)
:records ((:input '(2)
:output-generator (function identity)
:min-occur 2))))
(+ (foo 2) (foo 2))))))
(ert-deftest mocker-let-multiple-calls-illimited ()
(should
(eq 8
(mocker-let ((foo (x)
:records ((:input '(2)
:output-generator (function identity)
:max-occur nil))))
(+ (foo 2) (foo 2) (foo 2) (foo 2))))))
(ert-deftest mocker-let-multiple-calls-exact ()
(should
(eq 8
(mocker-let ((foo (x)
:records ((:input '(2)
:output-generator (function identity)
:occur 4))))
(+ (foo 2) (foo 2) (foo 2) (foo 2))))))
(ert-deftest mocker-let-multiple-calls-multiple-records ()
(should
(eq 12
(mocker-let ((foo (x)
:records ((:input '(2)
:output-generator (function identity)
:max-occur 2)
(:input '(2)
:output-generator (lambda (x) (* 2 x))
:occur 2))))
(+ (foo 2) (foo 2) (foo 2) (foo 2))))))
(ert-deftest mocker-let-multiple-calls-multiple-same-records ()
(should
(eq 8
(mocker-let ((foo (x)
:records ((:input '(2)
:output-generator (function identity)
:max-occur 2)
(:input '(2)
:output-generator (function identity)
:max-occur 2))))
(+ (foo 2) (foo 2) (foo 2) (foo 2))))))
(ert-deftest mocker-let-multiple-calls-unexpected ()
(should-error
(mocker-let ((foo (x)
:records ((:input '(2)
:output-generator (function identity)
:max-occur 2))))
(+ (foo 2) (foo 2) (foo 2) (foo 2))))
:type 'mocker-record-error)
(ert-deftest mocker-let-multiple-calls-unexpected-exact ()
(should-error
(mocker-let ((foo (x)
:records ((:input '(2)
:output-generator (function identity)
:occur 2))))
(+ (foo 2) (foo 2) (foo 2) (foo 2))))
:type 'mocker-record-error)
(ert-deftest mocker-let-stub-simple ()
(should
(let ((mocker-mock-default-record-cls 'mocker-stub-record))
(eq t
(mocker-let ((foo (x) :records ((:output t))))
(and (foo 1) (foo 42) (foo 666)))))))
(ert-deftest mocker-let-stub-simple-explicit ()
(should
(eq t
(mocker-let ((foo (x) :records ((:record-cls mocker-stub-record
:output t))))
(and (foo 1) (foo 42) (foo 666))))))
(ert-deftest mocker-let-stub-limited ()
(should-error
(let ((mocker-mock-default-record-cls 'mocker-stub-record))
(mocker-let ((foo (x) :records ((:output t :max-occur 2))))
(and (foo 1) (foo 42) (foo 666))))
:type 'mocker-mock-error))
(ert-deftest mocker-let-multiple-calls-unordered ()
(should
(eq 18
(mocker-let ((foo (x y z)
:ordered nil
((:input '(1 2 3) :output 4)
(:input '(4 5 6) :output 10)))
(bar (x)
((:input '(42) :output 4))))
(+ (foo 4 5 6)
(foo 1 2 3)
(bar 42))))))
(ert-deftest mocker-passthrough-basic ()
(should
(not
(mocker-let ((ignore (x)
:records ((:record-cls mocker-passthrough-record
:input '(42)))))
(ignore 42)))))
(ert-deftest mocker-passthrough-mixed ()
(should
(mocker-let ((ignore (x)
:records ((:record-cls mocker-passthrough-record
:input '(42))
(:input '(58) :output t))))
(or (ignore 42)
(ignore 58)))))
(ert-deftest mocker-passthrough-mixed-error ()
(should-error
(mocker-let ((ignore (x)
:records ((:record-cls mocker-passthrough-record
:input '(42))
(:input '(58) :output t))))
(or (ignore 42)
(ignore 42)))
:type 'mocker-record-error))
(ert-deftest mocker-passthrough-multiple ()
(should
(mocker-let ((ignore (x)
((:input-matcher (lambda (x) t)
:output t :max-occur 2)
(:record-cls mocker-passthrough-record
:input '(42) :max-occur nil))))
(and (ignore 1)
(ignore 2)
(not (or
(ignore 42) (ignore 42) (ignore 42) (ignore 42)))))))
(ert-deftest mocker-inhibit-mock-not-consumed ()
(should-error
(let ((mocker-inhibit t))
(mocker-let ((ignore (x)
((:input '(42) :output t))))
(ignore 42)))
:type 'mocker-record-error))
(ert-deftest mocker-inhibit-mocking ()
(should
(not
(mocker-let ((ignore (x)
((:input '(42) :output t))))
(and (ignore 42)
(let ((mocker-inhibit t))
(ignore 42)))))))
(ert-deftest mocker-rest-args ()
(should
(mocker-let ((f (a b &rest args) ((:input '(1 2 3 4) :output t))))
(f 1 2 3 4))))
(provide 'mocker-tests)
;;; mocker-tests.el ends here