-
Notifications
You must be signed in to change notification settings - Fork 5
/
qbase64-test.lisp
188 lines (167 loc) · 8.26 KB
/
qbase64-test.lisp
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
(in-package #:cl-user)
(defpackage #:qbase64-test
(:use #:cl #:fiveam #:cl-fad)
(:import-from #:qbase64 #:bytes #:make-byte-vector))
(in-package #:qbase64-test)
;;; utils
(defun external-encode (bytes &key (linebreak 0))
(if (zerop (length bytes))
""
(with-open-temporary-file (tmp :direction :output :element-type '(unsigned-byte 8))
(write-sequence bytes tmp)
(force-output tmp)
(let* ((encoded (uiop:run-program `("base64" "-b" ,(format nil "~A" linebreak) "-i" ,(namestring tmp)) :output (if (zerop linebreak) '(:string :stripped t) :string)))
(length (length encoded)))
(cond ((and (> length 1)
(string= (subseq encoded (- length 2))
(format nil "~A~A" #\Newline #\Newline)))
(subseq encoded 0 (1- length)))
(t encoded))))))
(let ((gen (gen-integer :min 0 :max 255)))
(defun random-byte ()
(funcall gen)))
(defun random-bytes (size)
(let ((bytes (make-byte-vector size)))
(dotimes (i size bytes)
(setf (aref bytes i) (random-byte)))))
(defun gen-random-encoding-set ()
(let ((cache-table (make-hash-table :test #'equal)))
(lambda (size linebreak)
(let* ((key (cons size linebreak))
(cached (gethash key cache-table)))
(when (null cached)
(let* ((bytes (random-bytes size))
(encoded (external-encode bytes :linebreak linebreak)))
(setf cached (list bytes encoded)
(gethash key cache-table) cached)))
cached))))
(let ((gen (gen-random-encoding-set)))
(defun random-encoding-set (size linebreak)
(funcall gen size linebreak)))
(defmacro with-encoding-set ((bytes-var encoded-var) size linebreak &body body)
`(destructuring-bind (,bytes-var ,encoded-var)
(random-encoding-set ,size ,linebreak)
,@body))
;;; encoder tests
(def-suite encoder)
(test (encode-bytes :suite encoder)
(dolist (size (list 0 1 2 3 6 10 30 100 1024))
(dolist (linebreak (list 0 1 2 3 5 10 30 100))
(with-encoding-set (bytes encoded)
size linebreak
(let ((generated (qbase64:encode-bytes bytes :linebreak linebreak)))
(is (string= generated encoded)
"Failed for size ~A, linebreak ~A: Expected ~S for ~S, but got ~S"
size linebreak encoded bytes generated))))))
(test (encode-stream-states :suite encoder)
(with-output-to-string (s)
(let ((out (make-instance 'qbase64:encode-stream
:underlying-stream s)))
(is (open-stream-p out))
(is (equalp (stream-element-type out) '(unsigned-byte 8)))
(is (output-stream-p out))
(is (not (input-stream-p out)))
(close out)
(is (not (open-stream-p out)))
;; underlying stream is not closed automatically
(is (open-stream-p s)))))
(test (encode-stream-single-write :suite encoder)
(dolist (size (list 0 1 2 3 6 10 30 100 1024))
(dolist (linebreak (remove-if (lambda (l) (> l size)) (list 0 1 2 3 5 10 30 100)))
(with-encoding-set (bytes encoded)
size linebreak
(let ((generated
(with-output-to-string (str-out)
(with-open-stream (out (make-instance 'qbase64:encode-stream
:underlying-stream str-out
:linebreak linebreak))
(write-sequence bytes out)))))
(is (string= generated encoded)
"Failed for size ~A, linebreak ~A: Expected ~S for ~S, but got ~S"
size linebreak encoded bytes generated))))))
(test (encode-stream-multi-write :suite encoder)
(dolist (size (list 0 1 2 3 6 10 30 100 1024))
(dolist (linebreak (remove-if (lambda (l) (> l size))
(list 0 1 2 3 5 10 30 100)))
(with-encoding-set (bytes encoded)
size linebreak
(dolist (seq-size (remove-if (lambda (s) (> s size))
(list 1 2 3 5 10 30 100)))
(let ((generated
(with-output-to-string (str-out)
(with-open-stream (out (make-instance 'qbase64:encode-stream
:underlying-stream str-out
:linebreak linebreak))
(loop
for start = 0 then end
for end = (min seq-size (length bytes)) then (min (+ start seq-size) (length bytes))
do (write-sequence bytes out :start start :end end)
until (= end (length bytes)))))))
(is (string= generated encoded)
"Failed for size ~A, linebreak ~A, seq-size ~A: Expected ~S for ~S, but got ~S"
size linebreak seq-size encoded bytes generated)))))))
;;; decoder tests
(def-suite decoder)
(test (decode-string :suite decoder)
(dolist (size (list 0 1 2 3 6 10 30 100 1024))
(dolist (linebreak (list 0 1 2 3 5 10 30 100))
(with-encoding-set (bytes encoded)
size linebreak
(let ((generated (qbase64:decode-string encoded)))
(is (equalp generated bytes)
"Failed for size ~A, linebreak ~A: Expected ~S for ~S, but got ~S"
size linebreak bytes encoded generated))))))
(test (decode-stream-states :suite decoder)
(with-input-from-string (s "AQID")
(let ((in (make-instance 'qbase64:decode-stream
:underlying-stream s)))
(is (open-stream-p in))
(is (equalp (stream-element-type in) '(unsigned-byte 8)))
(is (input-stream-p in))
(is (not (output-stream-p in)))
(close in)
(is (not (open-stream-p in)))
;; underlying stream is not closed automatically
(is (open-stream-p s)))))
(test (decode-stream-single-read :suite decoder)
(dolist (size (list 0 1 2 3 6 10 30 100 1024))
(dolist (linebreak (remove-if (lambda (l) (> l size)) (list 0 1 2 3 5 10 30 100)))
(with-encoding-set (bytes encoded)
size linebreak
(with-input-from-string (str-in encoded)
(with-open-stream (in (make-instance 'qbase64:decode-stream
:underlying-stream str-in))
(let* ((in-seq (make-array (1+ size) :element-type '(unsigned-byte 8)))
(position (read-sequence in-seq in))
(generated (subseq in-seq 0 position)))
(is (= position size)
"Failed for size ~A, linebreak ~A: Expected position ~A for ~S, but got ~A"
size linebreak size encoded position)
(is (equalp generated bytes)
"Failed for size ~A, linebreak ~A: Expected ~S for ~S, but got ~S"
size linebreak bytes encoded generated))))))))
(test (decode-stream-multi-read :suite decoder)
(dolist (size (list 0 1 2 3 6 10 30 100 1024))
(dolist (linebreak (remove-if (lambda (l) (> l size))
(list 0 1 2 3 5 10 30 100)))
(with-encoding-set (bytes encoded)
size linebreak
(dolist (seq-size (remove-if (lambda (s) (> s size))
(list 1 2 3 5 10 30 100)))
(with-input-from-string (str-in encoded)
(with-open-stream (in (make-instance 'qbase64:decode-stream
:underlying-stream str-in))
(let* ((in-seq (make-array (1+ size) :element-type '(unsigned-byte 8)))
(generated))
(loop
for start = 0 then position
for end = (min (+ start seq-size) (length in-seq))
for position = (read-sequence in-seq in :start start :end end)
while (= position end)
finally (setf generated (subseq in-seq 0 position)))
(is (= (length generated) size)
"Failed for size ~A, linebreak ~A, seq-size ~A: Expected ~A for ~S, but got ~A"
size linebreak seq-size size encoded (length generated))
(is (equalp generated bytes)
"Failed for size ~A, linebreak ~A, seq-size ~A: Expected ~S for ~S, but got ~S"
size linebreak seq-size bytes encoded generated)))))))))