1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; ======================================================================
3 ;;;; File: test-init.lisp
4 ;;;; Authors: Marcus Pearce <m.t.pearce@city.ac.uk>, Kevin Rosenberg
5 ;;;; Created: 30/03/2004
8 ;;;; Initialisation utilities for running regression tests on CLSQL.
10 ;;;; This file is part of CLSQL.
12 ;;;; CLSQL users are granted the rights to distribute and use this software
13 ;;;; as governed by the terms of the Lisp Lesser GNU Public License
14 ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
15 ;;;; ======================================================================
17 (in-package #:clsql-tests
)
19 (defvar *report-stream
* *standard-output
* "Stream to send text report.")
20 (defvar *sexp-report-stream
* nil
"Stream to send sexp report.")
21 (defvar *rt-internal
*)
23 (defvar *rt-connection
*)
31 (defvar *test-database-type
* nil
)
32 (defvar *test-database-underlying-type
* nil
)
33 (defvar *test-database-user
* nil
)
34 (defvar *test-start-utime
* nil
)
35 (defvar *test-connection-spec
* nil
)
36 (defvar *test-connection-db-type
* nil
)
39 ((extraterrestrial :initform nil
:initarg
:extraterrestrial
)))
41 (def-view-class person
(thing)
42 ((height :db-kind
:base
:accessor height
:type float
44 (married :db-kind
:base
:accessor married
:type boolean
46 (birthday :type clsql
:wall-time
:initarg
:birthday
)
47 (bd-utime :type clsql
:universal-time
:initarg
:bd-utime
)
48 (hobby :db-kind
:virtual
:initarg
:hobby
:initform nil
)))
50 (def-view-class employee
(person)
53 :db-constraints
(:not-null
:unique
)
58 :db-constraints
:not-null
70 :accessor employee-email
77 :accessor employee-company
79 :db-info
(:join-class company
81 :foreign-key companyid
87 :accessor employee-manager
89 :db-info
(:join-class employee
94 :accessor employee-addresses
96 :db-info
(:join-class employee-address
101 (:base-table employee
))
103 (def-view-class company
()
106 :db-constraints
:not-null
111 :db-constraints
:not-null
119 :initarg
:presidentid
)
123 :db-info
(:join-class employee
124 :home-key presidentid
128 :reader company-employees
130 :db-info
(:join-class employee
131 :home-key
(companyid groupid
)
132 :foreign-key
(ecompanyid groupid
)
135 (def-view-class address
()
138 :db-constraints
:not-null
143 :initarg
:street-number
)
147 :initarg
:street-name
)
150 :void-value
"no city"
157 :initarg
:postal-code
))
160 ;; many employees can reside at many addressess
161 (def-view-class employee-address
()
162 ((aemplid :type integer
:initarg
:emplid
)
163 (aaddressid :type integer
:initarg
:addressid
)
164 (verified :type boolean
:initarg
:verified
)
165 (address :db-kind
:join
166 :db-info
(:join-class address
168 :foreign-key addressid
169 :retrieval
:immediate
))
170 (employee :db-kind
:join
171 :db-info
(:join-class employee
174 :retrieval
:immediate
)))
175 (:base-table
"ea_join"))
177 (def-view-class deferred-employee-address
()
178 ((aemplid :type integer
:initarg
:emplid
)
179 (aaddressid :type integer
:initarg
:addressid
)
180 (verified :type boolean
:initarg
:verified
)
181 (address :db-kind
:join
182 :db-info
(:join-class address
184 :foreign-key addressid
187 (:base-table
"ea_join"))
189 (def-view-class big
()
190 ((i :type integer
:initarg
:i
)
191 (bi :type bigint
:initarg
:bi
)))
193 (defun test-connect-to-database (db-type spec
)
194 (when (clsql-sys:db-backend-has-create
/destroy-db? db-type
)
195 (ignore-errors (destroy-database spec
:database-type db-type
))
196 (ignore-errors (create-database spec
:database-type db-type
)))
198 (setf *test-database-type
* db-type
)
199 (setf *test-database-user
*
201 ((eq :oracle db-type
) (second spec
))
202 ((>= (length spec
) 3) (third spec
))))
204 ;; Connect to the database
206 :database-type db-type
210 ;; Ensure database is empty
211 (truncate-database :database
*default-database
*)
213 (setf *test-database-underlying-type
*
214 (clsql-sys:database-underlying-type
*default-database
*))
218 (defparameter company1 nil
)
219 (defparameter employee1 nil
)
220 (defparameter employee2 nil
)
221 (defparameter employee3 nil
)
222 (defparameter employee4 nil
)
223 (defparameter employee5 nil
)
224 (defparameter employee6 nil
)
225 (defparameter employee7 nil
)
226 (defparameter employee8 nil
)
227 (defparameter employee9 nil
)
228 (defparameter employee10 nil
)
229 (defparameter address1 nil
)
230 (defparameter address2 nil
)
231 (defparameter employee-address1 nil
)
232 (defparameter employee-address2 nil
)
233 (defparameter employee-address3 nil
)
234 (defparameter employee-address4 nil
)
235 (defparameter employee-address5 nil
)
237 (defun test-initialise-database ()
238 (test-basic-initialize)
239 (let ((*backend-warning-behavior
*
240 (if (member *test-database-type
* '(:postgresql
:postgresql-socket
))
243 (clsql:create-view-from-class
'employee
)
244 (clsql:create-view-from-class
'company
)
245 (clsql:create-view-from-class
'address
)
246 (clsql:create-view-from-class
'employee-address
)
247 (clsql:create-view-from-class
'big
))
249 (setq *test-start-utime
* (get-universal-time))
250 (let* ((*db-auto-sync
* t
)
251 (now-time (clsql:utime-
>time
*test-start-utime
*)))
252 (setf company1
(make-instance 'company
256 :name
"Widgets Inc.")
257 employee1
(make-instance 'employee
261 :height
(1+ (random 1.00))
262 :bd-utime
*test-start-utime
*
264 :first-name
"Vladimir"
266 :email
"lenin@soviet.org"
268 employee2
(make-instance 'employee
271 :height
(1+ (random 1.00))
273 :bd-utime
*test-start-utime
*
277 :email
"stalin@soviet.org"
280 employee3
(make-instance 'employee
283 :height
(1+ (random 1.00))
285 :bd-utime
*test-start-utime
*
289 :email
"trotsky@soviet.org"
292 employee4
(make-instance 'employee
295 :height
(1+ (random 1.00))
297 :bd-utime
*test-start-utime
*
300 :last-name
"Kruschev"
301 :email
"kruschev@soviet.org"
304 employee5
(make-instance 'employee
308 :height
(1+ (random 1.00))
309 :bd-utime
*test-start-utime
*
312 :last-name
"Brezhnev"
313 :email
"brezhnev@soviet.org"
316 employee6
(make-instance 'employee
320 :height
(1+ (random 1.00))
321 :bd-utime
*test-start-utime
*
324 :last-name
"Andropov"
325 :email
"andropov@soviet.org"
328 employee7
(make-instance 'employee
331 :height
(1+ (random 1.00))
333 :bd-utime
*test-start-utime
*
335 :first-name
"Konstantin"
336 :last-name
"Chernenko"
337 :email
"chernenko@soviet.org"
340 employee8
(make-instance 'employee
343 :height
(1+ (random 1.00))
345 :bd-utime
*test-start-utime
*
347 :first-name
"Mikhail"
348 :last-name
"Gorbachev"
349 :email
"gorbachev@soviet.org"
352 employee9
(make-instance 'employee
356 :height
(1+ (random 1.00))
357 :bd-utime
*test-start-utime
*
361 :email
"yeltsin@soviet.org"
364 employee10
(make-instance 'employee
368 :height
(1+ (random 1.00))
369 :bd-utime
*test-start-utime
*
371 :first-name
"Vladimir"
373 :email
"putin@soviet.org"
376 address1
(make-instance 'address
379 :street-name
"Park Place"
382 address2
(make-instance 'address
384 employee-address1
(make-instance 'employee-address
388 employee-address2
(make-instance 'employee-address
392 employee-address3
(make-instance 'employee-address
396 employee-address4
(make-instance 'employee-address
400 employee-address5
(make-instance 'employee-address
404 (let ((max (expt 2 60)))
406 (make-instance 'big
:i
(1+ i
) :bi
(truncate max
(1+ i
))))))
408 ;; sleep to ensure birthdays are no longer at current time
412 ;; Lenin manages everyone
413 (clsql:add-to-relation employee2
'manager employee1
)
414 (clsql:add-to-relation employee3
'manager employee1
)
415 (clsql:add-to-relation employee4
'manager employee1
)
416 (clsql:add-to-relation employee5
'manager employee1
)
417 (clsql:add-to-relation employee6
'manager employee1
)
418 (clsql:add-to-relation employee7
'manager employee1
)
419 (clsql:add-to-relation employee8
'manager employee1
)
420 (clsql:add-to-relation employee9
'manager employee1
)
421 (clsql:add-to-relation employee10
'manager employee1
)
422 ;; Everyone works for Widgets Inc.
423 (clsql:add-to-relation company1
'employees employee1
)
424 (clsql:add-to-relation company1
'employees employee2
)
425 (clsql:add-to-relation company1
'employees employee3
)
426 (clsql:add-to-relation company1
'employees employee4
)
427 (clsql:add-to-relation company1
'employees employee5
)
428 (clsql:add-to-relation company1
'employees employee6
)
429 (clsql:add-to-relation company1
'employees employee7
)
430 (clsql:add-to-relation company1
'employees employee8
)
431 (clsql:add-to-relation company1
'employees employee9
)
432 (clsql:add-to-relation company1
'employees employee10
)
433 ;; Lenin is president of Widgets Inc.
434 (clsql:add-to-relation company1
'president employee1
)
437 ;; store these instances
439 (clsql:update-records-from-instance employee1
)
440 (clsql:update-records-from-instance employee2
)
441 (clsql:update-records-from-instance employee3
)
442 (clsql:update-records-from-instance employee4
)
443 (clsql:update-records-from-instance employee5
)
444 (clsql:update-records-from-instance employee6
)
445 (clsql:update-records-from-instance employee7
)
446 (clsql:update-records-from-instance employee8
)
447 (clsql:update-records-from-instance employee9
)
448 (clsql:update-records-from-instance employee10
)
449 (clsql:update-records-from-instance company1
)
450 (clsql:update-records-from-instance address1
)
451 (clsql:update-records-from-instance address2
)
455 (defvar *error-count
* 0)
456 (defvar *error-list
* nil
)
458 (defun run-function-append-report-file (function report-file
)
459 (let* ((report-path (etypecase report-file
460 (pathname report-file
)
461 (string (parse-namestring report-file
))))
462 (sexp-report-path (make-pathname :defaults report-path
464 (with-open-file (rs report-path
:direction
:output
466 :if-does-not-exist
:create
)
467 (with-open-file (srs sexp-report-path
:direction
:output
469 :if-does-not-exist
:create
)
470 (funcall function
:report-stream rs
:sexp-report-stream srs
)))))
472 (defun run-tests-append-report-file (report-file)
473 (run-function-append-report-file 'run-tests report-file
))
476 (defun run-tests (&key
(report-stream *standard-output
*) (sexp-report-stream nil
))
477 ;; clear SQL-OUTPUT cache
478 (setq clsql-sys
::*output-hash
* (make-hash-table :test
#'equal
))
479 (let ((specs (read-specs))
480 (*report-stream
* report-stream
)
481 (*sexp-report-stream
* sexp-report-stream
)
485 (warn "Not running tests because test configuration file is missing")
486 (return-from run-tests
:skipped
))
487 (load-necessary-systems specs
)
488 (dolist (db-type +all-db-types
+)
489 (dolist (spec (db-type-spec db-type specs
))
490 (let ((*test-connection-spec
* spec
)
491 (*test-connection-db-type
* db-type
))
492 (do-tests-for-backend db-type spec
)))))
493 (zerop *error-count
*))
495 (defun load-necessary-systems (specs)
496 (dolist (db-type +all-db-types
+)
497 (when (db-type-spec db-type specs
)
498 (clsql-sys:initialize-database-type
:database-type db-type
))))
500 (defun write-report-banner (report-type db-type stream
)
503 ******************************************************************************
504 *** CLSQL ~A begun at ~A
507 *** Database ~:@(~A~) backend~A.
508 ******************************************************************************
513 (clsql:utime-
>time
(get-universal-time)))
514 (lisp-implementation-type)
515 (lisp-implementation-version)
518 (if (not (eq db-type
*test-database-underlying-type
*))
519 (format nil
" with underlying type ~:@(~A~)"
520 *test-database-underlying-type
*)
524 (defun do-tests-for-backend (db-type spec
)
525 (test-connect-to-database db-type spec
)
528 (multiple-value-bind (test-forms skip-tests
)
529 (compute-tests-for-backend db-type
*test-database-underlying-type
*)
531 (write-report-banner "Test Suite" db-type
*report-stream
*)
533 (test-initialise-database)
535 (regression-test:rem-all-tests
)
536 (dolist (test-form test-forms
)
539 (let ((remaining (regression-test:do-tests
*report-stream
*)))
540 (when (regression-test:pending-tests
)
541 (incf *error-count
* (length remaining
))))
543 (let ((sexp-error (list db-type
544 *test-database-underlying-type
*
547 (regression-test:pending-tests
)
548 (lisp-implementation-type)
549 (lisp-implementation-version)
551 (when *sexp-report-stream
*
552 (write sexp-error
:stream
*sexp-report-stream
* :readably t
))
553 (push sexp-error
*error-list
*))
555 (format *report-stream
* "~&Tests skipped:")
557 (dolist (skipped skip-tests
)
558 (format *report-stream
*
559 "~& ~20A ~A~%" (car skipped
) (cdr skipped
)))
560 (format *report-stream
* " None~%")))
564 (defun compute-tests-for-backend (db-type db-underlying-type
)
565 (let ((test-forms '())
567 (dolist (test-form (append *rt-internal
* *rt-connection
* *rt-basic
* *rt-fddl
* *rt-fdml
*
568 *rt-ooddl
* *rt-oodml
* *rt-syntax
*))
569 (let ((test (second test-form
)))
571 ((and (null (clsql-sys:db-type-has-views? db-underlying-type
))
572 (clsql-sys:in test
:fddl
/view
/1 :fddl
/view
/2 :fddl
/view
/3 :fddl
/view
/4))
573 (push (cons test
"views not supported") skip-tests
))
574 ((and (null (clsql-sys:db-type-has-boolean-where? db-underlying-type
))
575 (clsql-sys:in test
:fdml
/select
/11 :oodml
/select
/5))
576 (push (cons test
"boolean where not supported") skip-tests
))
577 ((and (null (clsql-sys:db-type-has-subqueries? db-underlying-type
))
578 (clsql-sys:in test
:fdml
/select
/5 :fdml
/select
/10
579 :fdml
/select
/32 :fdml
/select
/33))
580 (push (cons test
"subqueries not supported") skip-tests
))
581 ((and (null (clsql-sys:db-type-transaction-capable? db-underlying-type
583 (clsql-sys:in test
:fdml
/transaction
/1 :fdml
/transaction
/2 :fdml
/transaction
/3 :fdml
/transaction
/4))
584 (push (cons test
"transactions not supported") skip-tests
))
585 ((and (null (clsql-sys:db-type-has-fancy-math? db-underlying-type
))
586 (clsql-sys:in test
:fdml
/select
/1))
587 (push (cons test
"fancy math not supported") skip-tests
))
588 ((and (eql *test-database-type
* :sqlite
)
589 (clsql-sys:in test
:fddl
/view
/4 :fdml
/select
/10
590 :fdml
/select
/21 :fdml
/select
/32
592 (push (cons test
"not supported by sqlite") skip-tests
))
593 ((and (eql *test-database-type
* :sqlite3
)
594 (clsql-sys:in test
:fddl
/view
/4 :fdml
/select
/10
595 :fdml
/select
/21 :fdml
/select
/32
597 (push (cons test
"not supported by sqlite3") skip-tests
))
598 ((and (not (clsql-sys:db-type-has-bigint? db-type
))
599 (clsql-sys:in test
:basic
/bigint
/1))
600 (push (cons test
"bigint not supported") skip-tests
))
601 ((and (eql *test-database-underlying-type
* :mysql
)
602 (clsql-sys:in test
:fdml
/select
/26))
603 (push (cons test
"string table aliases not supported on all mysql versions") skip-tests
))
604 ((and (eql *test-database-underlying-type
* :mysql
)
605 (clsql-sys:in test
:fdml
/select
/22 :fdml
/query
/5
606 :fdml
/query
/7 :fdml
/query
/8))
607 (push (cons test
"not supported by mysql") skip-tests
))
608 ((and (null (clsql-sys:db-type-has-union? db-underlying-type
))
609 (clsql-sys:in test
:fdml
/query
/6 :fdml
/select
/31))
610 (push (cons test
"union not supported") skip-tests
))
611 ((and (eq *test-database-type
* :oracle
)
612 (clsql-sys:in test
:fdml
/query
/8 :fdml
/select
/21
614 (push (cons test
"syntax not supported") skip-tests
))
615 ((and (eq *test-database-type
* :odbc
)
616 (eq *test-database-underlying-type
* :postgresql
)
617 (clsql-sys:in test
:fddl
/owner
/1))
618 (push (cons test
"table ownership not supported by postgresql odbc driver") skip-tests
))
619 ((and (not (member *test-database-underlying-type
*
620 '(:postgresql
:oracle
)))
621 (clsql-sys:in test
:fddl
/owner
/1))
622 (push (cons test
"table ownership not supported") skip-tests
))
623 ((and (null (clsql-sys:db-type-has-intersect? db-underlying-type
))
624 (clsql-sys:in test
:fdml
/query
/7))
625 (push (cons test
"intersect not supported") skip-tests
))
626 ((and (null (clsql-sys:db-type-has-except? db-underlying-type
))
627 (clsql-sys:in test
:fdml
/query
/8))
628 (push (cons test
"except not supported") skip-tests
))
629 ((and (eq *test-database-underlying-type
* :mssql
)
630 (clsql-sys:in test
:fdml
/select
/9))
631 (push (cons test
"mssql uses integer math for AVG") skip-tests
))
632 ((and (not (member *test-database-underlying-type
*
633 '(:postgresql
:mysql
:sqlite3
)))
634 (clsql-sys:in test
:fdml
/select
/37 :fdml
/select
/38))
635 (push (cons test
"LIMIT keyword not supported in SELECT") skip-tests
))
637 (push test-form test-forms
)))))
638 (values (nreverse test-forms
) (nreverse skip-tests
))))
640 (defun rapid-load (type &optional
(position 0))
641 "Rapid load for interactive testing."
642 (when *default-database
*
643 (disconnect :database
*default-database
*))
644 (test-connect-to-database type
(nth position
(db-type-spec type
(read-specs))))
645 (test-initialise-database)
649 (rapid-load :postgresql
))
655 (rapid-load :oracle
))