packages feed

build-1.0: test/Main.hs

{-# LANGUAGE OverloadedStrings #-}
import Control.Monad
import Data.Bool
import Data.List.Extra
import Data.Maybe
import System.Exit

import qualified Data.Map as Map

import Build
import Build.Store
import Build.System
import Build.Task

import Spreadsheet
import Examples()

-- | A build system that acceptes a list of target keys.
type MultiBuild c i k v = Tasks c k v -> [k] -> Store i k v -> Store i k v

sequentialMultiBuild :: Build Monad i k v -> MultiBuild Monad i k v
sequentialMultiBuild build task outputs store = case outputs of
    []     -> store
    (k:ks) -> sequentialMultiBuild build task ks (build task k store)

sequentialMultiBuildA :: Build Applicative i k v -> MultiBuild Applicative i k v
sequentialMultiBuildA build task outputs store = case outputs of
    []     -> store
    (k:ks) -> sequentialMultiBuildA build task ks (build task k store)

inputCells :: [Cell]
inputCells = [ "A1", "A2", "A3" ]

inputs :: i -> Store i Cell Int
inputs i = initialise i $ \cell -> fromMaybe 0 $ lookup cell $ zip inputCells [1..]

spreadsheet :: Spreadsheet
spreadsheet cell = case name cell of
    "B1"  -> Just $ 1                       --          1
    "B2"  -> Just $ "B1" + 1                -- 1 + 1 == 2
    "B3"  -> Just $ "A3" * abs "B2"         -- 3 * 2 == 6
    "C1"  -> Just $ IfZero "B3" "C2" 1000   --          1000
    "C2"  -> Just $ IfZero "B3" 2000 "C1"   --          1000
    "C3"  -> Just $ Random 1 6              --          1..6
    "F0"  -> Just $ 0                       --          0
    "F1"  -> Just $ 1                       --          1
    'F':_ -> Just $ rel (-1) 0 + rel (-2) 0 --          Fn = F(n - 1) + F(n - 2)
    _     -> Nothing

acyclicSpreadsheet :: Spreadsheet
acyclicSpreadsheet cell = case name cell of
    "B1"  -> Just $ 1                       --          1
    "B2"  -> Just $ "B1" + 1                -- 1 + 1 == 2
    "B3"  -> Just $ "A3" * abs "B2"         -- 3 * 2 == 6
    "C1"  -> Just $ IfZero "B3" "B2" 1000   --          1000
    "C2"  -> Just $ IfZero "B3" 2000 "C1"   --          1000
    "C3"  -> Just $ Random 1 6              --          1..6
    "F0"  -> Just $ 0                       --          0
    "F1"  -> Just $ 1                       --          1
    'F':_ -> Just $ rel (-1) 0 + rel (-2) 0 --          Fn = F(n - 1) + F(n - 2)
    _     -> Nothing

targets :: [Cell]
targets = [ "A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "F0", "F1", "F4" ]

tasks :: Tasks Monad Cell Int
tasks = spreadsheetTask spreadsheet

tasksA :: Tasks Applicative Cell Int
tasksA = spreadsheetTaskA acyclicSpreadsheet

test :: String -> Build Monad i Cell Int -> i -> IO Bool
test name build i = do
    let store   = inputs i
        result  = sequentialMultiBuild build tasks targets store
        correct = all (correctBuild tasks store result) targets
    -- when False $ putStrLn $ "========\n" ++ show (getInfo result) ++ "\n========"
    putStr $ name ++ " is "
    case (trim name, correct) of
        ("dumb", False) -> do putStr "incorrect, which is [OK]\n"; return True
        (_     , False) -> do putStr "incorrect: [FAIL]\n"       ; return False
        (_     , True ) -> do putStr "correct: [OK]\n"           ; return True

testA :: String -> Build Applicative i Cell Int -> i -> IO Bool
testA name build i = do
    let store   = inputs i
        result  = sequentialMultiBuildA build tasksA targets store
        correct = all (correctBuild tasks store result) targets
    -- when False $ putStrLn $ "========\n" ++ show (getInfo result) ++ "\n========"
    putStrLn $ name ++ " is " ++ bool "incorrect: [FAIL]" "correct: [OK]" correct
    return correct

testSuite :: IO Bool
testSuite = and <$> sequence
    [ test  "dumb      " dumb       ()
    , test  "busy      " busy       ()
    , test  "memo      " memo       ()
    , testA "make      " make       (0, Map.empty)
    , testA "ninja     " ninja      mempty
    , testA "cloudBuild" cloudBuild mempty
    , test  "excel     " excel      (const True, mempty)
    , test  "shake     " shake      mempty
    , test  "bazel     " bazel      mempty
    , test  "cloudShake" cloudShake mempty
    , testA "buck      " buck       mempty
    , test  "nix       " nix        mempty ]

main :: IO ()
main = do
    success <- testSuite
    unless success $ die "\n========== At least one test failed! ==========\n"