;;; basic-test.el --- Test Suite for basic.el
;;
;;; Copyright (C) 2008-2014 Kyle W T Sherman
;;
;; Author:   Kyle W T Sherman <kylewsherman at gmail dot com>
;; Created:  2008-03-29
;; Version:  0.1
;; Keywords: basic compiler interpreter mode
;;
;; This file is not part of GNU Emacs.
;;
;; This 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 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:
;;
;; Tests `basic.el' functionality.
;;
;;; Installation:
;;
;; Load `basic-test.el':
;;
;;   (load "basic-test.el")
;;
;;; Usage:
;;
;; Run tests:
;;
;;   (eval-buffer)

;;; Code:

;; basic
(require 'basic)

;; assert
(defun basic-test (program output &rest input)
  "Run PROGRAM through the BASIC compiler and compare its output to OUTPUT.
\nAny optional INPUT parameters will be used to fill input prompts."
  (let ((basic-debug-log t))
    (save-excursion
      (with-temp-buffer
        (insert program)
        (basic-run-test input))
      (set-buffer "*basic-output*")
      (let ((buffer-str (buffer-substring-no-properties (point-min) (point-max))))
        (when (> (length buffer-str) (length output))
          (setq output (concat output "\n")))
        (assert (string= buffer-str output)))
      )))
    ;; (switch-to-buffer "*basic-debug*")
    ;; (goto-char (point-min))))

;;; Unit Tests: Statements

;; remarks
(basic-test
 "
10 REM Remark Test
20 REM `1234567890-=
30 REM ~!@#$%^&*()_+
40 REM []\\;',./
50 REM {}|:\"<>?
"
 "")

;; math
(basic-test
 "
10 REM BASIC Test: Math
20 PRINT \"BASIC Test: Math\": PRINT
30 A = -1 * 10 + 6 * 10
40 PRINT A
50 A = 10 * -1 + 6 * 10
60 PRINT A
70 A = -5 + 6 * 20 / 2 - 5
80 PRINT A
90 A = ((-1 + 6) * 20) / (5 - 3)
100 PRINT A
"
 "BASIC Test: Math

50
50
50
50
")

;; string concatenation
(basic-test
 "
10 REM BASIC Test: Strings
20 PRINT \"BASIC Test: Strings\": PRINT
30 S$ = \"1\"
40 PRINT S$
50 T$ = \"2\"
60 PRINT S$ + T$
70 S$ = S$ + T$ + \"3\"
80 PRINT S$
"
 "BASIC Test: Strings

1
12
123
")

;; undeclared variables
(basic-test
 "
10 REM BASIC Test: Undeclared Vars
20 PRINT \"BASIC Test: Undeclared Vars\": PRINT
30 A = A + 1
40 PRINT A
50 S$ = S$ + \"1\"
60 PRINT S$
"
 "BASIC Test: Undeclared Vars

1
1
")

;; data/read/restore
(basic-test
 "
10 REM BASIC Test: DATA/READ/RESTORE
20 PRINT \"BASIC Test: DATA/READ/RESTORE\": PRINT
30 DATA 1, 2, 3
40 FOR I = 1 TO 3
50   READ A
60   PRINT \"#\" I \" = \" A
70 NEXT
80 FOR I = 4 TO 6
90    READ A
100   PRINT \"#\" I \" = \" A
110 NEXT
120 RESTORE
130 FOR I = 1 TO 6
140   READ A
150   PRINT \"#\" I \" = \" A
160 NEXT
170 DATA 4, 5, 6
"
 "BASIC Test: DATA/READ/RESTORE

#1 = 1
#2 = 2
#3 = 3
#4 = 4
#5 = 5
#6 = 6
#1 = 1
#2 = 2
#3 = 3
#4 = 4
#5 = 5
#6 = 6
")

;; def
(basic-test
 "
10 REM BASIC Test: DEF
20 PRINT \"BASIC Test: DEF\": PRINT
30 DEF FNA(X)=X+1
40 PRINT STR$(FNA(1)) + \" + \" + STR$(FNA(2)) + \" = \" + STR$(FNA(1)+FNA(2))
50 DEF FNB(Z)=2*Z+1
60 FOR I = 1 TO 5
70   Z=INT(10+FNB(I)-.5*I)
80 PRINT Z;
90 NEXT I
100 PRINT
"
 "BASIC Test: DEF

2 + 3 = 5
1214151718
")

;; dim
(basic-test
 "
10 REM BASIC Test: DIM
20 PRINT \"BASIC Test: DIM\": PRINT
30 DIM A(10), B(10,10), C(10,10,10), S$(10), T$(10,10)
100 FOR I = 1 TO 10
110   A(I) = I
120 NEXT
130 FOR I = 6 TO 10
140   PRINT A(I-5)
150 NEXT
160 A = 100
170 PRINT A
180 PRINT A(10)
200 FOR I = 1 TO 10
210   S$(I) = CHR$(64+I)
220 NEXT
230 FOR I = 6 TO 10
240   PRINT S$(I-5)
250 NEXT
260 S$ = \"Z\"
270 PRINT S$
280 PRINT S$(10)
"
 "BASIC Test: DIM

1
2
3
4
5
100
10
A
B
C
D
E
Z
J
")

;; end
(basic-test
 "
10 REM BASIC Test: END
20 PRINT \"BASIC Test: END\": PRINT
30 END
40 PRINT \"Error\"
"
 "BASIC Test: END

")

;; for/next
(basic-test
 "
10 REM BASIC Test: FOR/NEXT
20 PRINT \"BASIC Test: FOR/NEXT\": PRINT
30 LET C = 0
40 FOR I = 1 TO 100
50   C = C + I
60 NEXT
70 PRINT C
80 FOR I = 0 TO -50 STEP -1
90   C = C + I
100 NEXT I
110 PRINT C
"
 "BASIC Test: FOR/NEXT

5050
3775
")

;; goto
(basic-test
 "
10 REM BASIC Test: GOTO
20 PRINT \"BASIC Test: GOTO\": PRINT
20 GOTO 70
30 PRINT \"Error\"
40 PRINT \"Test 2\"
50 END
60 PRINT \"Error\"
70 PRINT \"Test 1\"
80 GOTO 40
90 PRINT \"Error\"
"
 "BASIC Test: GOTO

Test 1
Test 2")

;; gosub/return
(basic-test
 "
10 REM BASIC Test: GOSUB/RETURN
20 PRINT \"BASIC Test: GOSUB/RETURN\": PRINT
30 PRINT \"Pass 1\"
40 GOSUB 80
50 PRINT \"Pass 2\"
60 GOSUB 140
70 END
80 PRINT \"Test 1\"
90 GOSUB 140
100 RETURN
110 PRINT \"Test 3\"
120 GOSUB 170
130 RETURN
140 PRINT \"Test 2\"
150 GOSUB 110
160 RETURN
170 PRINT \"Test 4\"
180 RETURN
190 END
"
 "BASIC Test: GOSUB/RETURN

Pass 1
Test 1
Test 2
Test 3
Test 4
Pass 2
Test 2
Test 3
Test 4
")

;; input
(basic-test
 "
10 REM BASIC Test: INPUT
20 PRINT \"BASIC Test: INPUT\": PRINT
30 INPUT \"Input number: \", N
40 INPUT \"Input string: \"; S$
50 PRINT \"Number = \", N, \", String = \", \"'\" + S$ + \"'\"
"
 "BASIC Test: INPUT

Input number:   1
Input string: test
Number =        1       , String =      'test'
"
 1 "test")

;; let
(basic-test
 "
10 REM BASIC Test: LET
20 PRINT \"BASIC Test: LET\": PRINT
30 LET A = 1
40 B = 2
50 PRINT \"A = \"; A; \", B = \"; B
"
 "BASIC Test: LET

A = 1, B = 2
")

;; next (see for/next)

;; print
(basic-test
 "
10 REM BASIC Test: PRINT
20 PRINT \"BASIC Test: PRINT\": PRINT
30 LET A = 1
40 LET B = 2
50 LET C$ = \"test\"
60 LET D$ = \"more\"
70 PRINT \"Print test, \"; A; \", \"; B;
80 PRINT \", \"; C$;
90 PRINT \", \"; D$
100 PRINT \"Print test 2, \" + STR$(A) + \", \" + STR$(B);
110 PRINT \", \"+C$;
120 PRINT \", \"+D$
130 PRINT \"Tab test:\"
140 PRINT TAB(5); \"*\"; TAB(10); \"*\"; TAB(15); \"*\"; TAB(20); \"*\"; TAB(25); \"*\"; TAB(30); \"*\"
150 PRINT \"123456789012345678901234567890\"
"
 "BASIC Test: PRINT

Print test, 1, 2, test, more
Print test 2, 1, 2, test, more
Tab test:
    *    *    *    *    *    *
123456789012345678901234567890
")

;; read (see data/read/restore)

;; rem
(basic-test
 "
10 REM BASIC Test: REM
20 PRINT \"BASIC Test: REM\": PRINT
30 REM REM Test
"
 "BASIC Test: REM
")

;; restore (see data/read/restore)

;; return (see gosub/return)

;; stop
(basic-test
 "
10 REM BASIC Test: STOP
20 PRINT \"BASIC Test: STOP\": PRINT
30 STOP
40 PRINT \"Error\"
"
 "BASIC Test: STOP
")

;;; Unit Tests: Reserved Words

;; reserved words
(basic-test
 "
10 REM BASIC Test: Reserved Words
20 PRINT \"BASIC Test: Reserved Words\": PRINT
30 FOR I = 0 TO 100 STEP 2
40   IF I MOD 10 <> 0 THEN 70
50   IF (I <= 20 OR I >= 80) AND I <> 100 THEN 70
60   PRINT I
70 NEXT I
"
 "BASIC Test: Reserved Words

30
40
50
60
70
100
")

;;; Unit Tests: Functions

;; reserved words
(basic-test
 "
10 REM BASIC Test: Functions
20 PRINT \"BASIC Test: Functions\": PRINT
30 PRINT \"ABS(10) = \";  ABS(10)
31 PRINT \"ABS(0) = \";  ABS(0)
32 PRINT \"ABS(-10) = \";  ABS(-10)
40 PRINT \"ASC('A') = \";  ASC(\"A\")
41 PRINT \"ASC('Z') = \";  ASC(\"Z\")
50 PRINT \"ATN(10) = \";  ATN(10)
51 PRINT \"ATN(0) = \";  ATN(0)
52 PRINT \"ATN(-10) = \";  ATN(-10)
60 PRINT \"CHR$(65) = \";  CHR$(65)
61 PRINT \"CHR$(90) = \";  CHR$(90)
70 PRINT \"COS(10) = \";  COS(10)
71 PRINT \"COS(0) = \";  COS(0)
72 PRINT \"COS(-10) = \";  COS(-10)
80 PRINT \"EXP(10) = \";  EXP(10)
81 PRINT \"EXP(0) = \";  EXP(0)
82 PRINT \"EXP(-10) = \";  EXP(-10)
90 PRINT \"INT(10.7) = \";  INT(10.7)
91 PRINT \"INT(0) = \";  INT(0)
92 PRINT \"INT(-10.2) = \";  INT(-10.2)
100 PRINT \"LEFT$('California', 2) = \";  LEFT$(\"California\", 2)
101 PRINT \"LEFT$('Minnesota', 3) = \"; LEFT$(\"Minnesota\", 3)
110 PRINT \"LEN('California') = \"; LEN(\"California\")
111 PRINT \"LEN('Minnesota') = \"; LEN(\"Minnesota\")
120 PRINT \"LOG(10) = \"; LOG(10)
121 PRINT \"LOG(0) = \"; LOG(0)
122 PRINT \"LOG(-10) = \"; LOG(-10)
130 PRINT \"MID$('California', 1) = \"; MID$(\"California\", 1)
130 PRINT \"MID$('California', 2, 3) = \"; MID$(\"California\", 2, 3)
130 PRINT \"MID$('California', 10) = \"; MID$(\"California\", 10)
130 PRINT \"MID$('California', 12) =\"; MID$(\"California\", 12)
131 PRINT \"MID$('Minnesota', 2) = \"; MID$(\"Minnesota\", 2)
131 PRINT \"MID$('Minnesota', 4, 5) = \"; MID$(\"Minnesota\", 4, 5)
131 PRINT \"MID$('Minnesota', 8) = \"; MID$(\"Minnesota\", 8)
131 PRINT \"MID$('Minnesota', 10) =\"; MID$(\"Minnesota\", 10)
140 PRINT \"RND(-10) = \"; RND(-10)
141 PRINT \"RND(0) = \"; RND(0)
150 PRINT \"RIGHT$('California', 2) = \"; RIGHT$(\"California\", 2)
151 PRINT \"RIGHT$('Minnesota', 3) = \"; RIGHT$(\"Minnesota\", 3)
160 PRINT \"SGN(10) = \"; SGN(10)
161 PRINT \"SGN(0) = \"; SGN(0)
162 PRINT \"SGN(-10) = \"; SGN(-10)
170 PRINT \"SIN(10) = \"; SIN(10)
171 PRINT \"SIN(0) = \"; SIN(0)
172 PRINT \"SIN(-10) = \"; SIN(-10)
180 PRINT \"SQR(10) = \"; SQR(10)
181 PRINT \"SQR(0) = \"; SQR(0)
182 PRINT \"SQR(-10) = \"; SQR(-10)
190 PRINT \"STR$(10) = \"; STR$(10)
191 PRINT \"STR$(0) = \"; STR$(0)
192 PRINT \"STR$(-10) = \"; STR$(-10)
200 PRINT \"TAB(1) =\"; TAB(1); \"Tabbed 1\"
201 PRINT \"TAB(5) =\"; TAB(5); \"Tabbed 5\"
202 PRINT \"TAB(10) =\"; TAB(10); \"Tabbed 10\"
203 PRINT \"TAB(20) = \"; TAB(20); \"Tabbed 20\"
210 PRINT \"TAN(10) = \"; TAN(10)
211 PRINT \"TAN(0) = \"; TAN(0)
212 PRINT \"TAN(-10) = \"; TAN(-10)
220 PRINT \"VAL('10') = \"; VAL(\"10\")
221 PRINT \"VAL('0') = \"; VAL(\"0\")
222 PRINT \"VAL('-10') = \"; VAL(\"-10\")
"
 "BASIC Test: Functions

ABS(10) = 10
ABS(0) = 0
ABS(-10) = 10
ASC('A') = 65
ASC('Z') = 90
ATN(10) = 1.4711276743037347
ATN(0) = 0.0
ATN(-10) = -1.4711276743037347
CHR$(65) = A
CHR$(90) = Z
COS(10) = -0.8390715290764524
COS(0) = 1.0
COS(-10) = -0.8390715290764524
EXP(10) = 22026.465794806718
EXP(0) = 1.0
EXP(-10) = 4.5399929762484854e-05
INT(10.7) = 10
INT(0) = 0
INT(-10.2) = -11
LEFT$('California', 2) = Ca
LEFT$('Minnesota', 3) = Min
LEN('California') = 10
LEN('Minnesota') = 9
LOG(10) = 2.302585092994046
LOG(0) = -1.0e+INF
LOG(-10) = 0.0e+NaN
MID$('California', 1) = California
MID$('California', 2, 3) = ali
MID$('California', 10) = a
MID$('California', 12) =
MID$('Minnesota', 2) = innesota
MID$('Minnesota', 4, 5) = nesot
MID$('Minnesota', 8) = ta
MID$('Minnesota', 10) =
RND(-10) = 0.18235988178869306
RND(0) = 0.18235988178869306
RIGHT$('California', 2) = ia
RIGHT$('Minnesota', 3) = ota
SGN(10) = 1
SGN(0) = 0
SGN(-10) = -1
SIN(10) = -0.5440211108893698
SIN(0) = 0.0
SIN(-10) = 0.5440211108893698
SQR(10) = 3.1622776601683795
SQR(0) = 0.0
SQR(-10) = -0.0e+NaN
STR$(10) = 10
STR$(0) = 0
STR$(-10) = -10
TAB(1) =
Tabbed 1
TAB(5) =
    Tabbed 5
TAB(10) =Tabbed 10
TAB(20) =          Tabbed 20
TAN(10) = 0.6483608274590866
TAN(0) = 0.0
TAN(-10) = -0.6483608274590866
VAL('10') = 10
VAL('0') = 0
VAL('-10') = -10
")

;;; Functional Tests

;; test 1
(basic-test
 "
10 REM BASIC Test 1
20 PRINT \"BASIC Test 1\": PRINT
30 PRINT \"Hello World\"
40 END
"
 "BASIC Test 1

Hello World
")

;; test 2
(basic-test
 "
10 REM BASIC Test 2
20 PRINT \"BASIC Test 2\": PRINT
30 PRINT \"Summing the values between 1 and 100\"
40 LET total = 0
50 FOR I = 1 TO 100
60   LET total = total + I
70 NEXT I
80 PRINT \"The total of all digits between 1 and 100 is \" total
90 END
"
 "BASIC Test 2

Summing the values between 1 and 100
The total of all digits between 1 and 100 is 5050
")

;; test 3
(basic-test
 "
10 REM BASIC Test 3
20 PRINT \"BASIC Test 3\": PRINT
30 INPUT \"What's your age? \"; age
40 PRINT \"You are \" age * 7 \" in dog years!\"
50 END
"
 "BASIC Test 3

What's your age? 35
You are 245 in dog years!
"
 35)

;; test 4
(basic-test
 "
10 REM BASIC Test 4
20 PRINT \"BASIC Test 4\": PRINT
30 A = 1: FOR I = A TO A+2 : PRINT I : NEXT
40 END
"
 "BASIC Test 4

1
2
3
")

;; done
(basic-test
 "
10 REM BASIC Tests Completely Successfully
20 PRINT \"BASIC Tests Completely Successfully\"
30 END
"
 "BASIC Tests Completely Successfully
")

;;; basic-test.el ends here