content: module Platform.Test where
  import Data.Int
  import Data.List
  import qualified Data.Map.Strict as S
  import Control.Monad.State
  import Control.Monad.Trans.Maybe
  import System.Directory
  import System.Exit
  import System.FilePath.Posix
  
  import Platform.Minimal64
  import Spec.Decode
  import Spec.Execute
  import Platform.Run (readProgram)
  import Platform.BufferMMIO
  import Platform.Plic
  import Spec.CSRFile
  import Spec.Machine
  import Spec.Spec
  import Utility.Utility
  import Spec.VirtualMemory
  import Utility.MapMemory
  
  data Test = Test { name :: String, instructionSet :: InstructionSet, input :: String, returnValue :: Int64, output :: String }
  
  runTest :: Test -> IO Bool
  runTest (Test name iset input returnValue output) = do
    result <- runFile iset ("test/build/" ++ name) input
    let isSuccess = (result == (returnValue, output))
    when (not isSuccess) (putStrLn ("Running " ++ name ++ " gave output " ++ show result ++ " but expected " ++ show (returnValue, output)))
    return $ isSuccess
  
  -- TODO: Read this from a file.
  tests :: [Test]
  tests =
    [Test "add64" RV64I ""  11 "",
           Test "ebreak64" RV64IM "" 0 "D\n?\n",
           Test "mul_support64" RV64IM "" 0 "A\n",
           Test "mul_support64" RV64I  "" 0 "cA\n",
           Test "sub64" RV64I ""   7 "",
           Test "mul64" RV64IM ""  42 "",
           Test "and64" RV64I ""  35 "",
           Test "or64"  RV64IM "" 111 "",
           Test "xor64" RV64I ""  76 "",
           Test "csr64" RV64IM ""  29 "",
           Test "hello64" RV64IM "" 0 "Hello, world!\n",
           Test "reverse64" RV64IM "asdf" 0 "fdsa\n",
           Test "thuemorse64" RV64IM "" 0 "01101001100101101001011001101001100101100110100101101001100101101001011001101001011010011001011001101001100101101001011001101001\n",
           Test "illegal64" RV64IM "" 0 "!\n?\n"]
           --Test "time64" RV64IM "" 0 "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\n!\n.\n"]
  
  getRiscvTests :: IO [Test]
  getRiscvTests = do
    ls <- getDirectoryContents "riscv-tests/isa"
    return (map makeTest (sort (filter isEnabled ls)))
    where makeTest f = Test ("../../riscv-tests/isa/" ++ f) RV64IMAF "" 0 ""
          isEnabled f = (isPrefixOf "rv64mi-" f || isPrefixOf "rv64si-" f || isPrefixOf "rv64ui-" f || isPrefixOf "rv64ua-" f
           || isPrefixOf "rv64uf-" f)
                         && not (isSuffixOf ".dump" f)
  
  runProgram :: InstructionSet -> Maybe Int64 -> Minimal64 -> String -> (Int64, String)
  runProgram iset maybeToHostAddress comp input = (returnValue, output)
    where ((returnValue, _), output) = runBufferIO (runStateT (stepHelper iset maybeToHostAddress (return DoNothing) (return (mtimecmp_addr,0)) (\inst -> return (inst /= 0x6f )) (return ()):: BufferState Minimal64 Int64) comp) input
  
  runFile :: InstructionSet -> String -> String -> IO (Int64, String)
  runFile iset f input = do
    putStrLn $ "Run" ++ f
    (maybeToHostAddress, mem) <- readProgram f
    let c = Minimal64 { registers = S.empty,
                        fpregisters = (take 31 $ repeat 0),
                        csrs = (resetCSRFile 64),
                        pc = 0x80000000,
                        nextPC = 0,
                        privMode = Machine,
                        mem = MapMemory { bytes = S.fromList mem, reservation = Nothing } } in
      return $ runProgram iset maybeToHostAddress c input
  
  main :: IO ()
  main = do
    riscvTests <- getRiscvTests
    let allTests = tests ++ riscvTests
    results <- mapM runTest allTests
    mapM_ (putStrLn . show) (zip (map (takeBaseName . name) allTests) results)
    if all id results
      then do
      putStrLn "All tests passed!"
      exitWith ExitSuccess
      else do
      putStrLn $ (show (sum (map fromEnum results))) ++ "/" ++ (show $ length allTests) ++ " tests passed."
      exitWith (ExitFailure 1)
  