@@ -1,11 +1,362 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
3+{-# LANGUAGE TypeApplications #-}
4+{-# OPTIONS_GHC -O0 #-}
5+{-# OPTIONS_GHC -Wno-incomplete-patterns #-}
6+{-# OPTIONS_GHC -Wno-orphans #-}
7+{-# OPTIONS_GHC -fno-enable-rewrite-rules #-}
8+
19module Tests
210 ( tests,
311 )
412where
513
14+import Control.Arrow
15+import Data.List (sort)
16+import Data.Maybe
617import Task
7−import Test.Tasty.Hspec
18+import Test.QuickCheck
19+import Test.Hspec
820
921tests :: Spec
10−tests = do
11− return ()
22+tests = parallel $ do
23+ describe "ErrorState" $ do
24+ let eqState ::
25+ (Arbitrary s, Show s, Eq e, Eq a, Eq s, Show e, Show a) =>
26+ ErrorState e s a ->
27+ ErrorState e s a ->
28+ Property
29+ eqState lhs rhs = property $ \s -> runErrorState lhs s === runErrorState rhs s
30+ describe "Functor" $ do
31+ specify "fmap id = id" $ do
32+ let testProperty ::
33+ forall e s a.
34+ (Arbitrary s, Show s, Eq e, Eq a, Eq s, Show e, Show a) =>
35+ Blind (ErrorState e s a) ->
36+ Property
37+ testProperty (Blind s) = fmap id s `eqState` s
38+ conjoin
39+ [ property $ testProperty @() @Bool @Char,
40+ property $ testProperty @Int @Char @Bool,
41+ property $ testProperty @[Int] @[Bool] @Char,
42+ property $ testProperty @String @Bool @String
43+ ]
44+ specify "fmap (g . f) = fmap g . fmap f" $ do
45+ let testProperty ::
46+ forall e s a b c.
47+ (Arbitrary s, Show s, Eq e, Eq s, Show e, Eq c, Show c) =>
48+ Blind (ErrorState e s a) ->
49+ Fun a b ->
50+ Fun b c ->
51+ Property
52+ testProperty (Blind s) f' g' = fmap (g . f) s `eqState` (fmap g . fmap f) s
53+ where
54+ f = applyFun f'
55+ g = applyFun g'
56+ conjoin
57+ [ property $ testProperty @() @Bool @Char @Int @Char,
58+ property $ testProperty @Int @Char @Bool @() @Char,
59+ property $ testProperty @[Int] @[Bool] @Char @Int @Bool,
60+ property $ testProperty @String @Bool @String @Bool @Char
61+ ]
62+ describe "Applicative" $ do
63+ specify "pure id <*> v = v" $ do
64+ let testProperty ::
65+ forall e s a.
66+ (Arbitrary s, Show s, Eq e, Eq a, Eq s, Show e, Show a) =>
67+ Blind (ErrorState e s a) ->
68+ Property
69+ testProperty (Blind s) = (pure id <*> s) `eqState` s
70+ conjoin
71+ [ property $ testProperty @() @Bool @Char,
72+ property $ testProperty @Int @Char @Bool,
73+ property $ testProperty @[Int] @[Bool] @Char,
74+ property $ testProperty @String @Bool @String
75+ ]
76+ specify "pure f <*> pure x = pure (f x)" $ do
77+ let testProperty ::
78+ forall e s a b.
79+ (Arbitrary s, Show s, Eq e, Eq s, Show e, Eq b, Show b) =>
80+ a ->
81+ Fun a b ->
82+ Property
83+ testProperty x f' = (pure f <*> pure x) `eqState` (pure (f x) :: ErrorState e s b)
84+ where
85+ f = applyFun f'
86+ conjoin
87+ [ property $ testProperty @() @Bool @Char @Int,
88+ property $ testProperty @Int @Char @Bool @(),
89+ property $ testProperty @[Int] @[Bool] @Char @Bool,
90+ property $ testProperty @String @Bool @String @Char
91+ ]
92+ describe "Monad" $ do
93+ specify "m >>= return = m" $ do
94+ let testProperty ::
95+ forall e s a.
96+ (Arbitrary s, Show s, Eq e, Eq a, Eq s, Show e, Show a) =>
97+ Blind (ErrorState e s a) ->
98+ Property
99+ testProperty (Blind s) = (s >>= return) `eqState` s
100+ conjoin
101+ [ property $ testProperty @() @Bool @Char,
102+ property $ testProperty @Int @Char @Bool,
103+ property $ testProperty @[Int] @[Bool] @Char,
104+ property $ testProperty @String @Bool @String
105+ ]
106+ specify "return a >>= f = f a" $ do
107+ let testProperty ::
108+ forall e s a b.
109+ (Arbitrary s, Show s, Eq e, Eq s, Show e, Eq b, Show b) =>
110+ a ->
111+ Blind (Fun a (ErrorState e s b)) ->
112+ Property
113+ testProperty x (Blind f') = (return x >>= f) `eqState` f x
114+ where
115+ f = applyFun f'
116+ conjoin
117+ [ property $ testProperty @() @Bool @Char @Int,
118+ property $ testProperty @Int @Char @Bool @(),
119+ property $ testProperty @[Int] @[Bool] @Char @Bool,
120+ property $ testProperty @String @Bool @String @Char
121+ ]
122+ specify "m >>= (\\x -> f x >>= g) = (m >>= f) >>= g" $ do
123+ let testProperty ::
124+ forall e s a b c.
125+ (Arbitrary s, Show s, Eq e, Eq s, Show e, Eq c, Show c) =>
126+ Blind (ErrorState e s a) ->
127+ Blind (Fun a (ErrorState e s b)) ->
128+ Blind (Fun b (ErrorState e s c)) ->
129+ Property
130+ testProperty (Blind m) (Blind f') (Blind g') = (m >>= (\x -> f x >>= g)) `eqState` ((m >>= f) >>= g)
131+ where
132+ f = applyFun f'
133+ g = applyFun g'
134+ conjoin
135+ [ property $ testProperty @() @Bool @Char @Int @Char,
136+ property $ testProperty @Int @Char @Bool @() @Char,
137+ property $ testProperty @[Int] @[Bool] @Char @Int @Bool,
138+ property $ testProperty @String @Bool @String @Bool @Char
139+ ]
140+ specify "get" $ do
141+ let testGet :: forall b. (Show b, Eq b) => b -> Property
142+ testGet s = executeErrorState s get === (Right s :: Either () b)
143+ conjoin
144+ [ property $ testGet @(),
145+ property $ testGet @Int,
146+ property $ testGet @[Int],
147+ property $ testGet @String
148+ ]
149+ specify "put" $ do
150+ let testPut :: forall b. (Show b, Eq b) => b -> b -> Property
151+ testPut s s' = runErrorState (put s') s === (Right ((), s') :: Either () ((), b))
152+ conjoin
153+ [ property $ testPut @(),
154+ property $ testPut @Int,
155+ property $ testPut @[Int],
156+ property $ testPut @String
157+ ]
158+ specify "throwError" $ do
159+ let testThrowError :: forall e. (Show e, Eq e) => e -> Property
160+ testThrowError e = runErrorState (throwError e) undefined === (Left e :: Either e ((), ()))
161+ conjoin
162+ [ property $ testThrowError @(),
163+ property $ testThrowError @Int,
164+ property $ testThrowError @[Int],
165+ property $ testThrowError @String
166+ ]
167+ specify "modify" $ do
168+ let testModify :: forall b. (Show b, Eq b) => Fun b b -> b -> Property
169+ testModify f b = runErrorState (modify $ applyFun f) b === (Right ((), applyFun f b) :: Either () ((), b))
170+ conjoin
171+ [ property $ testModify @(),
172+ property $ testModify @Int,
173+ property $ testModify @[Int],
174+ property $ testModify @String
175+ ]
176+ describe "BFMonad" $ do
177+ describe "readInput" $ do
178+ let state i =
179+ BFState
180+ { bfDataTape = undefined,
181+ bfInput = i,
182+ bfOutput = undefined
183+ }
184+ specify "non-empty" $ do
185+ let testReadInput :: NonEmptyList Int -> Property
186+ testReadInput (NonEmpty l@(x : xs)) =
187+ ((fst &&& bfInput . snd) <$> runErrorState readInput (state l)) === Right (x, xs)
188+ property testReadInput
189+ specify "empty" $ do
190+ executeErrorState (state []) readInput
191+ `shouldBe` Left NotEnoughInput
192+ specify "writeOutput" $ do
193+ let state =
194+ BFState
195+ { bfDataTape = undefined,
196+ bfInput = undefined,
197+ bfOutput = []
198+ }
199+ testWriteOutput :: [Int] -> Property
200+ testWriteOutput l =
201+ (bfOutput . snd <$> runErrorState (mapM_ writeOutput l) state) === Right (reverse l)
202+ property testWriteOutput
203+ specify "shiftDataR" $ do
204+ let state t =
205+ BFState
206+ { bfDataTape = t,
207+ bfInput = undefined,
208+ bfOutput = undefined
209+ }
210+ testShiftDataR :: Tape Int -> Property
211+ testShiftDataR t = case shiftTapeR t of
212+ Nothing -> runErrorState shiftDataR (state t) === Left DataTapeExhausted
213+ Just t' -> (bfDataTape . snd <$> runErrorState shiftDataR (state t)) === Right t'
214+ property testShiftDataR
215+ specify "shiftDataL" $ do
216+ let state t =
217+ BFState
218+ { bfDataTape = t,
219+ bfInput = undefined,
220+ bfOutput = undefined
221+ }
222+ testShiftDataL :: Tape Int -> Property
223+ testShiftDataL t = case shiftTapeL t of
224+ Nothing -> runErrorState shiftDataL (state t) === Left DataTapeExhausted
225+ Just t' -> (bfDataTape . snd <$> runErrorState shiftDataL (state t)) === Right t'
226+ property testShiftDataL
227+ specify "readData" $ do
228+ let state t =
229+ BFState
230+ { bfDataTape = t,
231+ bfInput = undefined,
232+ bfOutput = undefined
233+ }
234+ testReadData :: Tape Int -> Property
235+ testReadData t =
236+ executeErrorState (state t) readData === Right (tapeValue t)
237+ property testReadData
238+ specify "writeData" $ do
239+ let state t =
240+ BFState
241+ { bfDataTape = t,
242+ bfInput = undefined,
243+ bfOutput = undefined
244+ }
245+ testWriteData :: Tape Int -> Int -> Property
246+ testWriteData t x =
247+ (bfDataTape . snd <$> runErrorState (writeData x) (state t)) === Right t {tapeValue = x}
248+ property testWriteData
249+ describe "BF" $ do
250+ describe "executeCommand" $ do
251+ specify "ShiftRight" $ do
252+ let state t =
253+ BFState
254+ { bfDataTape = t,
255+ bfInput = undefined,
256+ bfOutput = undefined
257+ }
258+ testShiftRight :: Tape Int -> Property
259+ testShiftRight t = case shiftTapeR t of
260+ Nothing -> runErrorState (executeCommand ShiftRight) (state t) === Left DataTapeExhausted
261+ Just t' -> (bfDataTape . snd <$> runErrorState (executeCommand ShiftRight) (state t)) === Right (t')
262+ property testShiftRight
263+ specify "ShiftLeft" $ do
264+ let state t =
265+ BFState
266+ { bfDataTape = t,
267+ bfInput = undefined,
268+ bfOutput = undefined
269+ }
270+ testShiftLeft :: Tape Int -> Property
271+ testShiftLeft t = case shiftTapeL t of
272+ Nothing -> runErrorState (executeCommand ShiftLeft) (state t) === Left DataTapeExhausted
273+ Just t' -> (bfDataTape . snd <$> runErrorState (executeCommand ShiftLeft) (state t)) === Right (t')
274+ property testShiftLeft
275+ specify "Increment" $ do
276+ let state t =
277+ BFState
278+ { bfDataTape = t,
279+ bfInput = undefined,
280+ bfOutput = undefined
281+ }
282+ testProperty :: Tape Int -> Property
283+ testProperty t =
284+ (bfDataTape . snd <$> runErrorState (executeCommand Increment) (state t)) === Right (touchTape (+ 1) t)
285+ property testProperty
286+ specify "Decrement" $ do
287+ let state t =
288+ BFState
289+ { bfDataTape = t,
290+ bfInput = undefined,
291+ bfOutput = undefined
292+ }
293+ testProperty :: Tape Int -> Property
294+ testProperty t =
295+ (bfDataTape . snd <$> runErrorState (executeCommand Decrement) (state t)) === Right (touchTape pred t)
296+ property testProperty
297+ specify "WriteOutput" $ do
298+ let state t o =
299+ BFState
300+ { bfDataTape = t,
301+ bfInput = undefined,
302+ bfOutput = o
303+ }
304+ testProperty :: Tape Int -> [Int] -> Property
305+ testProperty t l =
306+ ((bfDataTape &&& bfOutput) . snd <$> runErrorState (executeCommand WriteOutput) (state t l))
307+ === Right (t, tapeValue t : l)
308+ property testProperty
309+ specify "ReadInput" $ do
310+ let state t i =
311+ BFState
312+ { bfDataTape = t,
313+ bfInput = i,
314+ bfOutput = undefined
315+ }
316+ testProperty :: Tape Int -> [Int] -> Property
317+ testProperty t l = case l of
318+ [] -> runErrorState (executeCommand ReadInput) (state t l) === Left NotEnoughInput
319+ (x : xx) -> ((bfDataTape &&& bfInput) . snd <$> runErrorState (executeCommand ReadInput) (state t l)) === Right ((t {tapeValue = x}, xx))
320+ property testProperty
321+ specify "Loop" $ do
322+ let toList :: Tape a -> [a]
323+ toList t = reverse (leftTape t) <> [tapeValue t] <> rightTape t
324+ testLoop i o l =
325+ (toList . bfDataTape . snd <$> runErrorState (executeCommand (Loop l)) (state . fromJust . initializeTape $ i))
326+ `shouldBe` Right o
327+ state t =
328+ BFState
329+ { bfDataTape = t,
330+ bfInput = undefined,
331+ bfOutput = undefined
332+ }
333+ testLoop [0] [0] []
334+ testLoop [0] [0] [Loop []]
335+ testLoop [5] [0] [Loop [Decrement]]
336+ testLoop [5, 5, 1, 2, 0] [6, 6, 2, 3, 0] [Increment, ShiftRight]
337+ describe "executeProgram" $ do
338+ specify "Hello World" $ do
339+ executeProgram "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++." "" `shouldBe` Just "Hello World!\n"
340+ specify "Bubble Sort" $ do
341+ let sort' x = executeProgram ">>>>>,[>>>,]<<<[<<<[>>>[-<<<-<+>[>]>>]<<<[<]>>[>>>+<<<-]<[>+>>>+<<<<-]<<]>>>[.[-]]>>>[>>>]<<<]" (x <> "\NUL")
342+ property . withMaxSuccess 10 $ \(l :: String) -> let l' = filter (/= '\NUL') l in sort' l' === Just (sort l')
343+ specify "wc" $ do
344+ let wc x = executeProgram ">>>+>>>>>+>>+>>+[<<],[-[-[-[-[-[-[-[-[<+>-[>+<-[>-<-[-[-[<++[<++++++>-]<[>>[-<]<[>]<-]>>[<+>-[<->[-]]]]]]]]]]]]]]]]<[-<<[-]+>]<<[>>>>>>+<<<<<<-]>[>]>>>>>>>+>[<+[>+++++++++<-[>-<-]++>[<+++++++>-[<->-]+[+>>>>>>]]<[>+<-]>[>>>>>++>[-]]+<]>[-<<<<<<]>>>>],]+<++>>>[[+++++>>>>>>]<+>+[[<++++++++>-]<.<<<<<]>>>>>>>>]" (x <> "\NUL")
345+ wc "Hello.\nThis is a thing.\nYay!" `shouldBe` Just "\t2\t6\t28\n"
346+ wc "Hello.\n This is text\nThis is a thing.\nYay!" `shouldBe` Just "\t3\t9\t42\n"
347+ wc "Hello.\n This is \n djnjd \n text\nThis is a thing.\nYay!" `shouldBe` Just "\t5\t10\t52\n"
348+ specify "Squares" $ do
349+ executeProgram "++++[>+++++<-]>[<+++++>-]+<+[>[>+>+<<-]++>>[<<+>>-]>>>[-]++>[-]+>>>+[[-]++++++>>>]<<<[[<++++++++<++>>-]+<.<[>----<-]<]<<[>>>>>[>>>[-]+++++++++<[>-<-]+++++++++>[-[<->-]+[<<<]]<[>+<-]>]<<-]<<-]" ""
350+ `shouldBe` Just (unlines . fmap (show @Int) . takeWhile (<= 10000) . fmap (\x -> x * x) $ [0 ..])
351+ specify "Insertion Sort" $ do
352+ let sort' x = executeProgram ">>+>,[<[[>>+<<-]>[<<+<[->>+[<]]>>>[>]<<-]<<<]>>[<<+>>-]<[>+<-]>[>>]<,]<<<[<+<]>[>.>]" (x <> "\NUL")
353+ property . withMaxSuccess 10 $ \(l :: String) -> let l' = filter (/= '\NUL') l in sort' l' === Just (sort l')
354+
355+instance Arbitrary a => Arbitrary (Tape a) where
356+ arbitrary = applyArbitrary3 Tape
357+
358+instance
359+ (Arbitrary e, Arbitrary a, Function s, CoArbitrary s, Arbitrary s) =>
360+ Arbitrary (ErrorState e s a)
361+ where
362+ arbitrary = ErrorState . applyFun <$> arbitrary