root/trunk/bknr/datastore/src/data/object-tests.lisp

Revision 3950, 10.1 kB (checked in by hans, 2 months ago)

Make it possible to restore datastores when packages have been deleted
which are referenced by objects in the store.

Line 
1 (in-package :bknr.datastore.tests)
2
3 (defun delete-directory (pathname)
4   (when (probe-file pathname)
5     #+cmu
6     (loop for file in (directory pathname)
7        when (pathname-name file)
8        do (delete-file file)
9        unless (pathname-name file)
10        do (delete-directory file))
11     #+allegro
12     (excl:delete-directory-and-files pathname)
13     #+cmu
14     (unix:unix-rmdir (namestring pathname))
15     #+sbcl
16     (loop for file in (directory
17                        (merge-pathnames
18                         (make-pathname
19                          :name    :wild
20                          :type    :wild
21                          :version :wild)
22                         pathname))
23        when (pathname-name file) do (delete-file file)
24        unless (pathname-name file) do (delete-directory file))
25     #+sbcl
26     (sb-posix:rmdir (namestring pathname))
27     #+openmcl
28     (ccl::recursive-delete-directory pathname)))
29
30 (defvar *test-datastore* nil)
31
32 (define-test-class datastore-test-class)
33
34 (defun make-test-store-directory ()
35   ;; bknr.utils:make-temporary-pathname does not really return a new
36   ;; directory pathname that is guaranteed to be distinct.  Chances
37   ;; that it returns the name of an existing directory are so slim
38   ;; that we accept the risk.
39   (ensure-directories-exist (format nil "~A/" (bknr.utils:make-temporary-pathname :defaults "/tmp/" :name "store-test"))))
40
41 (defmethod run-test :around ((test datastore-test-class) &optional (output *debug-io*))
42   (let ((directory (make-test-store-directory))
43         error)
44     (make-instance 'mp-store :directory directory)
45     (handler-bind ((error (lambda (e)
46                             (declare (ignore e))
47                             (setf error t))))
48       (call-next-method))
49     (close-store)
50     (if error
51         (format output ";; store directory ~A not deleted~%" directory)
52         (delete-directory directory))))
53
54 (defvar *tests* (make-hash-table))
55
56 (defun do-run-test (thunk)
57   "Run the test in THUNK, then verify that the store contains the
58 `same' objects after a restore and after snapshot and a restore."
59   (let ((bknr.datastore::*store-verbose* nil) initial-objects)
60     (funcall thunk)
61     (let ((next-object-id (bknr.datastore::next-object-id (bknr.datastore::store-object-subsystem))))
62       (setf initial-objects (object-classes-and-ids))
63       (restore)
64       (test-equal initial-objects (object-classes-and-ids))
65       (test-equal next-object-id (bknr.datastore::next-object-id (bknr.datastore::store-object-subsystem)))
66       (snapshot)
67       (restore)
68       (test-equal initial-objects (object-classes-and-ids))
69       (test-equal next-object-id (bknr.datastore::next-object-id (bknr.datastore::store-object-subsystem))))))
70  
71 (defmacro defdstest (name args &body body)
72   (when args
73     (error "unexpected arguments ~A to defdstest ~A" args name))
74   `(setf (gethash ',name *tests*)
75          (make-instance 'datastore-test-class
76                         :unit :datastore
77                         :name ',name
78                         :body (lambda () (do-run-test (lambda () ,@body))))))
79  
80 (defdstest store-setup ()
81   (test-assert *store*))
82
83 (defdstest create-object ()
84   (let ((obj (make-instance 'store-object)))
85     (test-assert obj)
86     (test-equal (list obj) (all-store-objects))))
87
88 (defdstest create-multiple-objects ()
89   (let ((o1 (make-instance 'store-object))
90         (o2 (make-instance 'store-object)))
91     (test-assert o1)
92     (test-assert o2)
93     (test-equal (length (all-store-objects)) 2)
94     (test-assert (subsetp (list o1 o2) (all-store-objects)))))
95
96 (defdstest delete-multiple-objects ()
97   (let ((o1 (make-instance 'store-object))
98         (o2 (make-instance 'store-object)))
99     (test-assert o1)
100     (test-assert o2)
101     (test-equal (length (all-store-objects)) 2)
102     (test-assert (subsetp (list o1 o2) (all-store-objects)))
103     (delete-object o1)
104     (test-equal (all-store-objects) (list o2))
105     (delete-object o2)
106     (test-equal (all-store-objects) nil)))
107
108 (defdstest restore ()
109   (let ((object-id (store-object-id (make-instance 'store-object))))
110     (restore)
111     (test-equal 1 (length (all-store-objects)))
112     (test-equal object-id (store-object-id (first (all-store-objects))))))
113
114 (defdstest snapshot-and-restore ()
115   (let ((object-id (store-object-id (make-instance 'store-object))))
116     (snapshot)
117     (restore)
118     (test-equal 1 (length (all-store-objects)))
119     (test-equal object-id (store-object-id (first (all-store-objects))))))
120
121 (defdstest restore-multiple-objects ()
122   (dotimes (i 10)
123     (make-instance 'store-object))
124   (restore)
125   (test-equal 10 (length (all-store-objects))))
126
127 (defdstest snapshot-restore-multiple-objects ()
128   (dotimes (i 10)
129     (make-instance 'store-object))
130   (snapshot)
131   (restore)
132   (test-equal (length (all-store-objects)) 10))
133
134 (defconstant +stress-size+ 10000)
135
136 (defdstest stress-test ()
137   (format t "Creating ~A objects in two threads~%" +stress-size+)
138   (time (bknr.datastore::without-sync ()
139           (labels ((stress ()
140                      (dotimes (i +stress-size+)
141                        (make-instance 'store-object))))
142             (let ((threads (list (bt:make-thread #'stress)
143                                  (bt:make-thread #'stress))))
144               (loop while (some #'bt:thread-alive-p threads)
145                  do (sleep 1))))))
146   (test-equal (length (all-store-objects)) (* 2 +stress-size+)))
147
148 (defdstest stress-test-2 ()
149   (bknr.datastore::without-sync ()
150     (format t "Creating ~A objects~%" +stress-size+)
151     (time (dotimes (i +stress-size+)
152             (make-instance 'store-object)))
153     (format t "Deleting ~A objects~%" (length (all-store-objects)))
154     (time (map-store-objects #'delete-object))
155     (test-equal (all-store-objects) nil)))
156
157 (defdstest holes-test ()
158   (dotimes (i +stress-size+)
159     (let ((delete (zerop (random 2))))
160       (with-transaction (:foo)
161         (funcall (if delete #'delete-object #'identity)
162                  (make-instance 'store-object))))))
163
164 (defdstest make-instance-in-anon-txn ()
165   (with-transaction ()
166     (make-instance 'store-object))
167   (restore)
168   (test-equal 1 (length (class-instances 'store-object))))
169
170 (defdstest make-instance-in-anon-txn ()
171   (with-transaction ()
172     (test-assert (make-instance 'store-object))))
173
174 (define-persistent-class parent ()
175   ((child :update :initform nil :initarg nil)))
176
177 (define-persistent-class child ()
178   ())
179
180 (defun object-classes-and-ids ()
181   "Return a list of conses with the car being a class name and the cdr
182   being the object id for all persistent objects in the store"
183   (sort (mapcar (lambda (object)
184                   (cons (class-name (class-of object))
185                         (store-object-id object)))
186                 (all-store-objects))
187         #'< :key #'cdr))
188
189 (defdstest make-referenced-object-in-anon-tx ()
190   (with-transaction (:make)
191     (make-instance 'parent :child (make-instance 'child))))
192
193 (defdstest serialize-circular-in-anon-txn ()
194   (let ((parent (make-instance 'parent)))
195     (with-transaction (:circular)
196       (setf (parent-child parent) (make-instance 'child))))
197   (restore)
198   (test-equal (find-class 'child)
199               (class-of (parent-child (first (class-instances 'parent))))))
200
201 (defdstest serialize-self-circular-in-anon-txn ()
202   (let ((object (make-instance 'parent)))
203     (with-transaction (:circular)
204       (setf (parent-child object) object)))
205   (restore)
206   (let ((object (first (class-instances 'store-object))))
207     (test-assert object)
208     (test-equal object (parent-child object)))
209   (snapshot)
210   (restore)
211   (let ((object (first (class-instances 'store-object))))
212     (test-assert object)
213     (test-equal object (parent-child object))))
214
215 (defdstest delete-object-in-anon-txn ()
216   (let (object)
217     (with-transaction (:make)
218       (setf object (make-instance 'child)))
219     (with-transaction (:delete)
220       (delete-object object))
221     (restore)
222     (test-assert (object-destroyed-p object))))
223
224 (defdstest delete-object-and-check-object-id-of-next-1 ()
225   (let (object-id)
226     (with-transaction (:make)
227       (let ((object (make-instance 'store-object)))
228         (setf object-id (store-object-id object))
229         (delete-object object)))
230     (restore)
231     (test-assert (< object-id (store-object-id (make-instance 'store-object))))))
232
233 (defdstest delete-object-and-check-object-id-of-next-2 ()
234   (let (object-id)
235     (with-transaction (:make)
236       (let ((object (make-instance 'store-object)))
237         (setf object-id (store-object-id object))))
238     (snapshot)
239     (restore)
240     (test-assert (< object-id (store-object-id (make-instance 'store-object))))))
241
242 (defdstest delete-object-and-check-object-id-of-next-3 ()
243   (let (object-id)
244     (with-transaction (:make)
245       (let ((object (make-instance 'store-object)))
246         (setf object-id (store-object-id object))
247         (delete-object object)))
248     (snapshot)
249     (restore)
250     (test-assert (< object-id (store-object-id (make-instance 'store-object))))))
251
252 (define-persistent-class class-with-transient-slot ()
253   ((slot :update
254          :transient t
255          :initform 0)))
256
257 (defdstest test-transient-slots ()
258   (let ((object-id (store-object-id (make-instance 'class-with-transient-slot))))
259     (restore)
260     (test-equal 0 (class-with-transient-slot-slot (find-store-object object-id)))
261     (setf (class-with-transient-slot-slot (find-store-object object-id)) 1)
262     (restore)
263     (test-equal 0 (class-with-transient-slot-slot (find-store-object object-id)))
264     (snapshot)
265     (restore)
266     (test-equal 0 (class-with-transient-slot-slot (find-store-object object-id)))))
267
268 (define-persistent-class persistent-mixin ()
269   ((mixin-slot :update
270                :initform 2)))
271
272 (define-persistent-class inherit-multiple (persistent-mixin parent)
273   ())
274
275 (defdstest multiple-inheritance-test ()
276   (let* ((o1 (make-instance 'inherit-multiple :child (make-instance 'child)))
277          (o2 (make-instance 'inherit-multiple :child o1)))
278     (test-equal o1 (parent-child o2))))
279
280 (defdstest abort-anonymous-transaction ()
281   (let ((parent (make-instance 'parent :child nil)))
282     (ignore-errors
283       (with-transaction (:abort)
284         (setf (parent-child parent) (make-instance 'child))
285         (error "abort")))
286     (test-equal nil (parent-child parent))
287     (test-equal nil (class-instances 'child))))
288
289 (defun run-datastore-test (name)
290   (unit-test:run-test (gethash name *tests*)))
Note: See TracBrowser for help on using the browser.