Source File testing/testing.ils
1 2
3 4
5 6
7 8 9 10 11 12 13 14
15 16
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
35 36
37 38 39 40 41 (defclass VedaTestingContext ()
42 ( 43 44 45 (name @initarg name
46 @reader GetName)
47 48 49 (packagePath @initarg packagePath
50 @reader GetPackagePath)
51 52 53 (output @initform nil)
54 55 56 57 58 (failed @initform nil
59 @reader GetFailed
60 @writer SetFailed)
61 62 63 (start @initform nil)
64 65 66 67 68 (duration @initform nil
69 @reader GetDuration)))
70
71 72 73 (defclass VedaTestingOptions ()
74 ( 75 76 77 (verbose @initarg verbose
78 @initform 0
79 @reader GetVerboseLevel
80 @writer SetVerboseLevel)
81 82 83 84 85 (reportFormat @initarg reportFormat
86 @initform 'text
87 @reader GetReportFormat
88 @writer SetReportFormat)
89 90 91 92 93 (reportFilename @initarg reportFilename
94 @initform nil
95 @reader GetReportFilename
96 @writer SetReportFilename)))
97
98 99 100 101
102 103 104 105 106 (defclass VedaTestingPackageAbstractInfo ()
107 ( 108 109 110 111 (name
112 @initarg name
113 @reader GetName)
114 115 116 117 (description
118 @initarg description
119 @reader GetDescription)
120 121 122 123 124 125 126 (authors
127 @initarg authors
128 @reader GetAuthors)))
129
130 131 132 133 134 (defclass VedaTestingPackageFilesystemInfo (VedaTestingPackageAbstractInfo)
135 ( 136 137 138 139 (base
140 @initarg base
141 @reader GetBaseDir)
142 143 144 145 (header
146 @initarg header
147 @reader GetHeaderFilename)
148 149 150 151 152 153 154 (requires
155 @initarg requires
156 @reader GetPackageRequires)
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 (sources
174 @initarg sources
175 @reader GetSourceFilenames)
176 177 178 179 (tests
180 @initarg tests
181 @reader GetTestFilenames)))
182
183 184 185 186 187 (defmethod printObject ((pi VedaTestingPackageFilesystemInfo)
188 @optional (port poport))
189 (fprintf port "#<%s %L; base: %L>"
190 (className (classOf pi)) pi->name pi->base))
191
192 193 194 195 196 (defclass VedaTestingPackageSearchOptions ()
197 ( 198 199 200 201 202 203 204 (searchPath
205 @initarg searchPath
206 @initform '("pkg/" "../")
207 @reader GetSearchPath
208 @writer SetSearchPath)
209 210 211 212 213 214 (asyncHandler
215 @initarg asyncHandler
216 @initform nil
217 @reader GetAsyncHandler
218 @writer SetAsyncHandler)))
219
220 221 222 223 224 (defclass VedaTestingPackageLoadOptions (VedaTestingPackageSearchOptions)
225 ( 226 227 228 229 230 231 232 (requires
233 @initarg requires
234 @initform t
235 @reader GetRequires
236 @writer SetRequires)
237 238 239 240 241 242 243 244 245 (forceReload
246 @initarg forceReload
247 @initform nil
248 @reader GetForceReload
249 @writer SetForceReload)))
250
251 252
253 (define VedaTestingRunTests nil)
254 (define VedaTestingRunTestsFromFiles nil)
255 (define VedaTestingPackageInfo nil)
256 (define VedaTestingPackageLoad nil)
257 (define VedaTestingRunTestsOnPackage nil)
258
259 260 (let ()
261
262 263 264 (define defaultTestingOptions
265 (makeInstance 'VedaTestingOptions))
266
267 268 (defmethod VedaTestingFail ((context VedaTestingContext))
269 (SetFailed context t)
270 t)
271
272 273 274 (defmethod VedaTestingFailNow ((context VedaTestingContext))
275 (VedaTestingFail context)
276 (err nil))
277
278 279 (defun Log (context s)
280 (let ((lines (parseString s "\n"))
281 (output (slotValue context 'output)))
282 (foreach line lines
283 (push line output))
284 (setSlotValue context 'output output))
285 t)
286
287 288 289 290 291 292 293 (defun Format (format @rest args)
294 (cond
295 296 ((isCallable 'XtsSprintf)
297 (apply XtsSprintf format args))
298 (t
299 (let ((code (nconc (list 'sprintf nil format)
300 (foreach mapcar arg args
301 (list 'quote arg)))))
302 (eval code)))))
303
304 305 306 (defmethod VedaTestingLog ((context VedaTestingContext) @rest args)
307 (let ((pieces nil))
308 (foreach arg args
309 (when pieces
310 (push " " pieces))
311 (cond
312 ((stringp arg)
313 (push arg pieces))
314 (t
315 (push (sprintf nil "%L" arg) pieces))))
316 (Log context (buildString (reverse pieces) ""))))
317
318 319 320 (defmethod VedaTestingLogf ((context VedaTestingContext) format @rest args)
321 (Log context (apply Format format args)))
322
323 324 (defmethod VedaTestingError ((context VedaTestingContext) @rest args)
325 (apply VedaTestingLog context args)
326 (VedaTestingFail context))
327
328 329 (defmethod VedaTestingErrorf ((context VedaTestingContext) format @rest args)
330 (apply VedaTestingLogf context format args)
331 (VedaTestingFail context))
332
333 334 (defmethod VedaTestingFatal ((context VedaTestingContext) @rest args)
335 (apply VedaTestingLog context args)
336 (VedaTestingFailNow context))
337
338 339 (defmethod VedaTestingFatalf ((context VedaTestingContext) format @rest args)
340 (apply VedaTestingLogf context format args)
341 (VedaTestingFailNow context))
342
343 344 (defun TRunner (context f traceOnErrorP)
345 (setSlotValue context 'start (getCurrentTime))
346 (let (measured)
347 (letseq ((wf (if (isCallable 'measureTime)
348 (lambda (context)
349 (setq measured
350 (caddr (measureTime (funcall f context)))))
351 f))
352 (r (errset (funcall wf context) traceOnErrorP)))
353 (unless r
354 (SetFailed context t)))
355 (let ((duration (cond
356 ((numberp measured)
357 ((fix (measured * 100.0)) / 100.0))
358 (t
359 (compareTime (slotValue context 'start)
360 (getCurrentTime))))))
361 (setSlotValue context 'duration duration))))
362
363 364 (defun BuildOutput (context)
365 (let ((output (slotValue context 'output)))
366 (if output
367 (strcat "\t" (buildString (reverse output) "\n\t") "\n")
368 "")))
369
370 371 (defun Chattyp (options)
372 ((GetVerboseLevel options) > 4))
373
374 375 376 (defun TextReporter (options)
377 (let ((chattyp (Chattyp options)))
378 (lambda (event @rest args)
379 (case event
380 (startTest
381 (let ((context (car args)))
382 (when chattyp
383 (printf "=== RUN %s\n" (GetName context)))))
384 (endTest
385 (let ((context (car args)))
386 (when (or (GetFailed context) chattyp)
387 (let ((duration (GetDuration context)))
388 (let ((tstr (if (plusp duration)
389 (sprintf nil " (%L seconds)" duration)
390 ""))
391 (head (if (GetFailed context) "FAIL" "PASS"))
392 (format "--- %s: %s%s\n%s"))
393 (printf format head (GetName context)
394 tstr (BuildOutput context)))))))))))
395
396 (defun GenTimestamp ()
397 398 "2012-09-25T18:48:46")
399
400 401 (define xmlMustEscapes
402 '(("&" "&")
403 ("<" "<")))
404
405 406 407 (define xmlAposEscapes
408 (append
409 '(("'" "&apos"))
410 xmlMustEscapes))
411
412 413 (defun XmlEscape (string escapes)
414 (cond
415 ((exists entry escapes
416 (nindex string (car entry)))
417 (letseq ((parts (parseString string ""))
418 (safe (foreach mapcar part parts
419 (let ((it (assoc part escapes)))
420 (if it (cadr it) part)))))
421 (buildString safe "")))
422 (t
423 string)))
424
425 426 427 428 (defun XmlEscaper (escapes)
429 (lambda (string)
430 (XmlEscape string escapes)))
431
432 433 434 435 (defun WriteXml (stream contexts pkg)
436 (let ((EA (XmlEscaper xmlAposEscapes))
437 (EB (XmlEscaper xmlMustEscapes))
438 (pkg (or pkg "<None>"))
439 (timestamp (GenTimestamp))
440 (testCount 0)
441 (failureCount 0)
442 (totalTime 0)
443 (otherOutputs nil))
444 (foreach context contexts
445 (setq testCount (testCount + 1))
446 (when (GetFailed context)
447 (setq failureCount (failureCount + 1)))
448 (setq totalTime (totalTime + (GetDuration context))))
449 (fprintf stream "<?xml version='1.0' encoding='ISO-8859-1'?>\n")
450 (fprintf stream "<testsuites>\n")
451 (fprintf stream " <testsuite package='%s' id='0' name='Unknown'\n"
452 (EA pkg))
453 (fprintf stream " timestamp='%s' hostname='localhost'\n"
454 (EA timestamp))
455 (fprintf stream " tests='%d' failures='%d' errors='0'\n"
456 testCount failureCount)
457 (fprintf stream " time='%L'>\n"
458 totalTime)
459 (fprintf stream " <properties/>\n")
460 (foreach context contexts
461 (fprintf stream " <testcase name='%s' classname='t' time='%L'>\n"
462 (EA (GetName context))
463 (GetDuration context))
464 (let ((lines (reverse (slotValue context 'output))))
465 (cond
466 ((GetFailed context)
467 (let ((messageMea (when lines
468 (sprintf nil " message='%s'"
469 (EA (car lines))))))
470 (fprintf stream " <failure type='Fail'%s>"
471 (or messageMea ""))
472 (foreach line lines
473 (fprintf stream "%s\n" (EB line)))
474 (fprintf stream "</failure>\n")))
475 (t
476 (push lines otherOutputs))))
477 (fprintf stream " </testcase>\n"))
478 (cond
479 ((null otherOutputs)
480 (fprintf stream " <system-out/>\n"))
481 (t
482 (fprintf stream " <system-out>")
483 (foreach lines (reverse otherOutputs)
484 (foreach line lines
485 (fprintf stream "%s\n" (EB line))))
486 (fprintf stream "</system-out>\n")))
487 (fprintf stream " <system-err/>\n")
488 (fprintf stream " </testsuite>\n")
489 (fprintf stream "</testsuites>\n")))
490
491 492 493 (defun XmlReporter (options pkg)
494 (let ((chattyp (Chattyp options))
495 (filename (or (GetReportFilename options)
496 "./result.xml"))
497 (contexts nil))
498 (lambda (event @rest args)
499 (case event
500 (startTest
501 (let ((context (car args)))
502 (when chattyp
503 (printf "=== RUN %s\n" (GetName context)))))
504 (endTest
505 (let ((context (car args)))
506 (push context contexts)))
507 (endTestsuite
508 (when contexts
509 (let ((stream (or (outfile filename)
510 (error "Cannot open %L for writing." filename))))
511 (WriteXml stream (reverse contexts) pkg)
512 (close stream))))))))
513
514 515 (defun DevNull (@rest _args)
516 nil)
517
518 519 520 (defun Reporter (options pkg)
521 (let ((reportFormat (GetReportFormat options)))
522 (case reportFormat
523 (text (TextReporter options))
524 (xml (XmlReporter options pkg))
525 526 527 (void DevNull)
528 (t (error "Unsupported report format %L" reportFormat)))))
529
530 531 (defun RunTestsInternal (tests options @key pkg packagePath)
532 (let ((reporter (Reporter options pkg))
533 (ok (when tests t)))
534 (let ((traceOnErrorP (neq reporter DevNull)))
535 (funcall reporter 'startTestsuite)
536 (foreach test tests
537 (letseq ((name (strcat test))
538 (context (makeInstance 'VedaTestingContext
539 ?name name
540 ?packagePath packagePath)))
541 (funcall reporter 'startTest context)
542 (TRunner context test traceOnErrorP)
543 (funcall reporter 'endTest context)
544 (setq ok (and ok (not (GetFailed context))))))
545 (funcall reporter 'endTestsuite ok))
546 ok))
547
548 549 550 551 552 553 554 555 556 557 558 (defun RunTests (tests @key options)
559 (RunTestsInternal tests (or options defaultTestingOptions)))
560
561 562 563 (defun FindTestMethods ()
564 (let ((syms (rexMatchList "^Test_" (symeval 'oblist))))
565 (setof sym syms
566 (and (isCallable sym)
567 568 569 t))))
570
571 572 573 (defun MaskExistingTests ()
574 (let ((pre (makeTable 'pre nil))
575 (old (FindTestMethods)))
576 (foreach sym old
577 (setarray pre sym (getd sym))
578 (putd sym nil))
579 pre))
580
581 582 583 584 (defun RestoreAndFindNewTests (pre)
585 (let ((now (FindTestMethods)))
586 (foreach sym now
587 (remove sym pre))
588 (foreach sym pre
589 590 (putd sym (arrayref pre sym)))
591 now))
592
593 594 595 596 597 598 599 600 601 (defun LoadTestsUsingLoader (loader)
602 (let ((pre (MaskExistingTests)))
603 (funcall loader ?subset 'tests)
604 (RestoreAndFindNewTests pre)))
605
606 607 608 609 610 611 612 613 614 615 616 617 618 (defun RunTestsFromFiles (files @key options)
619 (let ((tests (LoadTestsUsingLoader (FilesLoader files))))
620 (RunTests tests ?options options)))
621
622 623 (define defaultOptions
624 (makeInstance 'VedaTestingPackageLoadOptions))
625
626 627
628 629
630 631 632 633 634 635 636 637 638 (defun TrimLeftIndex (characterBag string from to)
639 (while (and (from <= to)
640 (nindex characterBag (getchar string from)))
641 (setq from (from + 1)))
642 from)
643
644 645 646 647 648 (defun TrimLeft (characterBag string)
649 (let ((a (TrimLeftIndex characterBag string 1 (strlen string))))
650 (cond
651 ((onep a)
652 string)
653 ((a > (strlen string))
654 "")
655 (t
656 (substring string a)))))
657
658 659 660 661 662 663 664 665 666 (defun TrimRightIndex (characterBag string from to)
667 (while (and (from <= to)
668 (nindex characterBag (getchar string to)))
669 (setq to (to - 1)))
670 to)
671
672 673 674 675 676 (defun TrimRight (characterBag string)
677 (letseq ((n (strlen string))
678 (b (TrimRightIndex characterBag string 1 n)))
679 (if (equal b n)
680 string
681 (substring string 1 b))))
682
683 684 685 (defun PrefixP (needle haystack)
686 (let ((n (strlen needle)))
687 (when (n <= (strlen haystack))
688 (let ((i 1))
689 (while (and i (i <= n))
690 (setq i (when (eq (getchar needle i) (getchar haystack i))
691 (i + 1))))
692 i))))
693
694 695 696 697 (defun SuffixP (needle haystack)
698 (let ((n (strlen needle))
699 (m (strlen haystack)))
700 (when (n <= m)
701 (let ((i 1)
702 (offset (m - n)))
703 (while (and i (i <= n))
704 (setq i (when (eq (getchar needle i)
705 (getchar haystack (offset + i)))
706 (i + 1))))
707 (when i
708 offset)))))
709
710 711
712 713 (define minAtTagLength
714 4)
715
716 717 718 (define knownTags
719 '((nil header)
720 ("@description" description)
721 ("@language" language)
722 ("@load" load)
723 ("@requires" requires)
724 ("@author" authors)
725 (nil tags)))
726
727 728 (define whitespaceBag
729 " \t")
730
731 732 733 (define trailingWhitespaceBag
734 (strcat "\n" whitespaceBag))
735
736 737 738 739 (defun TrimWs1 (line from n)
740 (letseq ((a (TrimLeftIndex whitespaceBag line from (min (from + 1) n)))
741 (b (TrimRightIndex trailingWhitespaceBag line a n)))
742 (cond
743 ((a > b)
744 "")
745 (t
746 (substring line a (b - a + 1))))))
747
748 749 750 751 752 753 (defun TrimLineComment (line)
754 (letseq ((n (strlen line))
755 (ic (TrimLeftIndex ";" line 1 n)))
756 (when (ic > 1)
757 (TrimWs1 line ic n))))
758
759 760 761 762 763 764 765 (defun TrimBlockComment (line)
766 (letseq ((n (strlen line))
767 (iw (TrimLeftIndex whitespaceBag line 1 n))
768 (is (TrimLeftIndex "*" line iw (min (iw + 1) n))))
769 (if (equal iw is)
770 (TrimRight trailingWhitespaceBag line)
771 (TrimWs1 line is n))))
772
773 774 775 (define trimmers
776 `((";" ,TrimLineComment)
777 ("/*" ,TrimBlockComment)))
778
779 780 781 (defun SelectTrimmer (line)
782 (cadar (exists trimmer trimmers
783 (PrefixP (car trimmer) line))))
784
785 786 787 788 789 790 791 792 793 794 795 (defun ParseDescription (line filename)
796 797 (let ((pieces (parseString line " \t\n")))
798 (when (and (member (car pieces) '(";;;" "/*"))
799 (equal (cadr pieces) filename)
800 (equal (caddr pieces) "---"))
801 (buildString (nthcdr 3 pieces)))))
802
803 804 805 806 807 808 (defun SplitTag (rawtag)
809 (let ((i minAtTagLength)
810 (n (strlen rawtag)))
811 812 (while (and (i <= n)
813 (not (nindex whitespaceBag (getchar rawtag i))))
814 (setq i (i + 1)))
815 (if (i > n)
816 (list rawtag "")
817 (list (substring rawtag 1 (i - 1))
818 (substring rawtag (i + 1))))))
819
820 821 822 (defun MakeHeaderDpl (header description languagep tags)
823 (let ((kvs (makeTable 'knownTags nil)))
824 (setarray kvs 'header header)
825 (setarray kvs 'description description)
826 (foreach tag tags
827 (let ((key (cadr (assoc (car tag) knownTags)))
828 (raw (cadr tag)))
829 (cond
830 ((and (eq key 'language) languagep)
831 (setarray kvs key (concat (TrimLeft whitespaceBag raw))))
832 ((memq key '(load requires))
833 (setarray kvs key (parseString raw ", \t")))
834 ((eq key 'authors)
835 (setarray kvs key (cons (TrimLeft whitespaceBag raw)
836 (arrayref kvs key))))
837 ((eq key 'description)
838 (setarray kvs key (TrimLeft whitespaceBag raw)))
839 (t
840 (let ((key 'tags))
841 (setarray kvs key (cons tag (arrayref kvs key))))))))
842 (cons nil
843 (foreach mapcan entry knownTags
844 (letseq ((key (cadr entry))
845 (cooked (arrayref kvs key)))
846 (when cooked
847 (list key cooked)))))))
848
849 850 851 852 853 854 855 856 (defun ParseHeader (liner @key mustDescribe allowShebangP languagep)
857 (let ((line nil)
858 (lineno 0)
859 (trimmer nil)
860 (trimmed nil)
861 (description nil)
862 (tags nil)
863 (tag nil)
864 (it nil)
865 (donep nil))
866 (while (and (not donep) (setq line (funcall liner)))
867 (setq lineno (lineno + 1))
868 869 870 (when (and (eq trimmer TrimBlockComment)
871 (setq it (nindex line "*/")))
872 (setq line (substring line 1 (it - 1)))
873 (setq donep t))
874 (cond
875 ((equal line "\n")
876 877 (setq tag nil))
878 ((and allowShebangP (onep lineno) (PrefixP "#!" line))
879 880 )
881 ((null (or trimmer (setq trimmer (SelectTrimmer line))))
882 883 (setq donep 'fail))
884 ((or (null (setq trimmed (funcall trimmer line)))
885 (member trimmed '("Commentary:" "Code:")))
886 887 888 (setq donep t))
889 ((and mustDescribe (null description)
890 (not (setq description (ParseDescription line mustDescribe))))
891 892 893 (setq donep 'fail))
894 ((and (PrefixP "@" trimmed)
895 ((strlen trimmed) >= minAtTagLength))
896 (setq tag (SplitTag trimmed))
897 (push tag tags))
898 ((and tag (nequal trimmed ""))
899 900 (let ((v (strcat (cadr tag) " " (TrimLeft " \t" trimmed))))
901 (rplaca (cdr tag) v)))
902 (t
903 (setq tag nil))))
904 (unless (eq donep 'fail)
905 (MakeHeaderDpl mustDescribe description languagep tags))))
906
907 908
909 910 911 912 913 914 915 (defun BasicParseHeader (liner filename)
916 (when liner
917 (prog1
918 (ParseHeader liner ?mustDescribe filename)
919 920 (funcall liner t))))
921
922 923 924 925 (defun MaybeFileLiner (path)
926 (let ((port (infile path))
927 (line nil))
928 (when port
929 (lambda (@optional closep)
930 (cond
931 ((not closep)
932 (gets line port))
933 (t
934 (close port)
935 nil))))))
936
937 938 939 940 (defun FilesLoader (filenames @key dir)
941 (lambda (@rest _ignored)
942 (foreach filename filenames
943 (when dir
944 (setq filename (strcat dir filename)))
945 (load filename))
946 t))
947
948 949 950 (define ellipsis
951 "...")
952
953 954 955 956 957 (defun ProcessLoadDirective (directive files)
958 (let ((files (copy files))
959 (ellipsisp nil))
960 (let ((loads (foreach maplist dtail directive
961 (let ((file (car dtail)))
962 (cond
963 ((let ((ftail (member file files)))
964 (when ftail
965 (rplaca ftail nil)
966 file)))
967 ((and (equal file ellipsis)
968 (null (cdr dtail)))
969 (setq ellipsisp t)
970 nil)
971 (t
972 (error "No such file: %L; Load header: %L"
973 file directive)))))))
974 (list (remove nil loads) ellipsisp (remove nil files)))))
975
976 977 978 979 980 (defun ClassifyFiles (files subsets)
981 (let ((dpl (list nil)))
982 (foreach file files
983 (exists subset subsets
984 (let ((suffixes (car subset)))
985 (exists suffix suffixes
986 (when (SuffixP suffix file)
987 (letseq ((key (cadr subset))
988 (accum (get dpl key)))
989 (putprop dpl (cons file accum) key)))))))
990 dpl))
991
992 993 (define fileSubsets
994 '( 995 (("-test.ils" "-test.il") tests)
996 997 998 ((".ils" ".il") sources)))
999
1000 1001 (defun FinalizePackageInfo (base pkg headerInfo loads ellipsisp files)
1002 (letseq ((filesInfo (ClassifyFiles files fileSubsets))
1003 (sources (cond
1004 ((and loads ellipsisp)
1005 (append loads filesInfo->sources))
1006 (loads)
1007 (t
1008 filesInfo->sources))))
1009 (makeInstance 'VedaTestingPackageFilesystemInfo
1010 ?name pkg
1011 ?description headerInfo->description
1012 ?authors headerInfo->authors
1013 ?base base
1014 ?header headerInfo->header
1015 ?requires headerInfo->requires
1016 ?sources sources
1017 ?tests filesInfo->tests)))
1018
1019 1020 1021 1022 1023 (defun PackageInfoFromBase (base pkg headerInfo files)
1024 (apply FinalizePackageInfo base pkg headerInfo
1025 (if headerInfo->load
1026 (ProcessLoadDirective headerInfo->load files)
1027 (list nil t files))))
1028
1029 1030 1031 (defun PackageInfoHelper (pkg bases suffixes)
1032 (let (info)
1033 (exists base bases
1034 (let ((dir (strcat base pkg)))
1035 (exists suffix suffixes
1036 (letseq ((filename (strcat pkg suffix))
1037 (liner (MaybeFileLiner (strcat dir "/" filename)))
1038 (headerInfo (BasicParseHeader liner filename)))
1039 (when headerInfo
1040 (let ((files (getDirFiles dir)))
1041 (setq info (PackageInfoFromBase base pkg headerInfo
1042 files))))))))
1043 info))
1044
1045 1046 1047 1048 1049 1050 (defun PackageInfoAsyncHelper (handler pkg bases suffixes k)
1051
1052 (defun IterSuffixes (base suffixes k)
1053 (if (null suffixes)
1054 (k nil)
1055 (letseq ((filename (strcat pkg (car suffixes)))
1056 (path (strcat base pkg "/" filename)))
1057 (VedaPackageAsyncMakeLiner handler path
1058 (lambda (liner)
1059 (let ((info (BasicParseHeader liner filename)))
1060 (if info
1061 (k info)
1062 (IterSuffixes base (cdr suffixes) k))))))))
1063
1064 (defun IterBases (bases k)
1065 (if (null bases)
1066 (k nil nil)
1067 (IterSuffixes (car bases) suffixes
1068 (lambda (info)
1069 (if info
1070 (k (car bases) info)
1071 (IterBases (cdr bases) k))))))
1072
1073 (defun Files (base info files)
1074 (let ((packageInfo (when files
1075 (PackageInfoFromBase base pkg info files))))
1076 (k handler packageInfo)))
1077
1078 (defun HeaderInfo (base info)
1079 (cond
1080 ((null info)
1081 (k handler nil))
1082 (t
1083 (let ((dir (strcat base pkg "/")))
1084 (VedaPackageAsyncGetDirFiles handler dir
1085 (lambda (files)
1086 (Files base info files)))))))
1087
1088 (IterBases bases HeaderInfo))
1089
1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 (defun PackageInfoFromFilesystem (pkg bases asyncHandler)
1102 (let ((suffixes '(".il" ".ils")))
1103 (cond
1104 ((null asyncHandler)
1105 (PackageInfoHelper pkg bases suffixes))
1106 (t
1107 (PackageInfoAsyncHelper asyncHandler
1108 pkg bases suffixes
1109 VedaPackageAsyncResult)
1110 nil))))
1111
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 (defun PackageInfo (pkg @key options)
1134 (cond
1135 ((isCallable 'VedaPackageInfoHook)
1136 (funcall 'VedaPackageInfoHook pkg))
1137 (t
1138 (let ((options (or options defaultOptions)))
1139 (PackageInfoFromFilesystem pkg
1140 (GetSearchPath options)
1141 (GetAsyncHandler options))))))
1142
1143 1144 1145 1146 (defun LoaderFromPackageInfo (info subset)
1147 (let (files)
1148 (when (setq files (get info subset))
1149 (let ((dir (strcat info->base info->name "/")))
1150 (FilesLoader files ?dir dir)))))
1151
1152 1153 1154 (defun LoadSubsetFromPackageInfo (info subset)
1155 (let ((loader (LoaderFromPackageInfo info subset)))
1156 (if loader
1157 (funcall loader ?subset subset)
1158 (error "Don't know how to load %s from package %L."
1159 subset info)))
1160 t)
1161
1162 1163 (defun RequirePackageRec (pkg loaded options noPackageRequiresP)
1164 (let (info)
1165 (cond
1166 ((arrayref loaded pkg))
1167 ((setq info (PackageInfo pkg ?options options))
1168 (unless noPackageRequiresP
1169 (foreach require info->requires
1170 (RequirePackageRec require loaded options noPackageRequiresP)))
1171 (LoadSubsetFromPackageInfo info 'sources)
1172 (setarray loaded pkg info))
1173 (t
1174 (error "Package %L not found." pkg)))))
1175
1176 1177 1178 1179 1180 1181 (defun RequirePackage (pkg loaded options)
1182 (letseq ((requires (GetRequires options))
1183 (noPackageRequiresP (neq requires t)))
1184 (when (pairp requires)
1185 (foreach require requires
1186 (RequirePackageRec require loaded options noPackageRequiresP)))
1187 (RequirePackageRec pkg loaded options noPackageRequiresP)))
1188
1189 1190 1191 1192 (defun BootstrapPackageInfo (filename)
1193 (when (SuffixP ".ils" filename)
1194 (let ((n (strlen filename))
1195 (suffix (rindex filename "/")))
1196 (cond
1197 (suffix
1198 (let ((slen (strlen suffix)))
1199 (let ((pkg (substring suffix 2 (slen - 5)))
1200 (dir (strcat (substring filename 1 (n - slen + 1)) "../")))
1201 (PackageInfoFromFilesystem pkg (list dir) nil))))
1202 (t
1203 (let ((pkg (substring filename 1 (n - 4))))
1204 (PackageInfoFromFilesystem pkg '("../") nil)))))))
1205
1206 1207 1208 1209 (defun BootstrapMarkLoaded (table filename)
1210 (when filename
1211 (let ((info (BootstrapPackageInfo filename)))
1212 (when info
1213 (setarray table (GetName info) info)))))
1214
1215 1216 (define loadedTable nil)
1217
1218 (let ((queryFn 'VedaPackage__getLoadedTable))
1219 (cond
1220 ((isCallable queryFn)
1221 (setq loadedTable (funcall queryFn)))
1222 (t
1223 (setq loadedTable (makeTable 'loaded nil))
1224 (putd queryFn (lambda () loadedTable)))))
1225
1226 1227 (when (isCallable 'get_filename)
1228 (BootstrapMarkLoaded loadedTable (get_filename piport)))
1229
1230 1231 1232 1233 (defun CleanupLoadedTable (forceReload)
1234 (let ((pkgs (cond
1235 ((eq forceReload t)
1236 loadedTable->?)
1237 ((pairp forceReload)
1238 forceReload)
1239 (t
1240 (error "Unrecognized force reload option %L."
1241 forceReload)))))
1242 (foreach pkg pkgs
1243 (remove pkg loadedTable))))
1244
1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 (defun PackageLoad (pkg @key options)
1264 (letseq ((options (or options defaultOptions))
1265 (forceReload (GetForceReload options)))
1266 (when forceReload
1267 (CleanupLoadedTable forceReload))
1268 (RequirePackage pkg loadedTable options)))
1269
1270 1271 1272 1273 (defun MakeLoadedTable ()
1274 (let ((table (makeTable 'loaded nil)))
1275 (foreach key loadedTable
1276 (setarray table key (arrayref loadedTable key)))
1277 (foreach def '(("run"
1278 defines Xts__sprintf
1279 tentativep t)
1280 ("testing"))
1281 (let ((pkg (car def)))
1282 (when (and (not (arrayref table pkg))
1283 (or (null def->defines) (isCallable def->defines)))
1284 (let ((info (PackageInfo pkg)))
1285 (cond
1286 (info
1287 (setarray table pkg info))
1288 ((not def->tentativep)
1289 (error "Cannot mark %L as preloaded." pkg)))))))
1290 table))
1291
1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 (defun RunTestsOnPackage (pkg @key options)
1302 (letseq ((loaded (MakeLoadedTable))
1303 (info (RequirePackage pkg loaded defaultOptions))
1304 (packagePath (when (classp info 'VedaTestingPackageFilesystemInfo)
1305 (let ((baseDir (GetBaseDir info)))
1306 (when baseDir
1307 (list baseDir)))))
1308 (loader (LoaderFromPackageInfo info 'tests))
1309 (tests (when loader
1310 (LoadTestsUsingLoader loader))))
1311 (RunTestsInternal tests (or options defaultTestingOptions)
1312 ?pkg pkg
1313 ?packagePath packagePath)))
1314
1315 (setq VedaTestingRunTests RunTests)
1316 (setq VedaTestingRunTestsFromFiles RunTestsFromFiles)
1317 (setq VedaTestingPackageInfo PackageInfo)
1318 (setq VedaTestingPackageLoad PackageLoad)
1319 (setq VedaTestingRunTestsOnPackage RunTestsOnPackage))
1320
1321