diff --git a/.github/workflows/maintenance.yaml b/.github/workflows/maintenance.yaml index 26023ef..5393e4d 100644 --- a/.github/workflows/maintenance.yaml +++ b/.github/workflows/maintenance.yaml @@ -21,7 +21,7 @@ jobs: sudo bash ./llvm.sh 19 && (sudo rm /usr/bin/clang-format || exit 0) && sudo apt -y install clang-format-19 - - run: gradle format + - run: gradle --no-daemon format build: timeout-minutes: 10 @@ -33,4 +33,4 @@ jobs: java-version: '21' distribution: 'temurin' - - run: ./gradlew --no-daemon build + - run: cabal update && ./gradlew --no-daemon build diff --git a/build.gradle.kts b/build.gradle.kts index 4e3304a..fc86786 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -112,8 +112,11 @@ pitest { // Test generation tasks.register("generateQuickCheckTests") { + File("src/facadetests/java/MakeItFit").mkdirs() + dependsOn(tasks.jar) workingDir = file("src/testgen") - commandLine = listOf("cabal", "run") + commandLine = + listOf("cabal", "run", "testgen", "--", "../facadetests/java/MakeItFit/MakeItFitTest.java") } tasks.register("generateEvoSuiteTests") { @@ -139,11 +142,21 @@ tasks.register("generateEvoSuiteTests") { // Other tasks +tasks.named("build") { + finalizedBy(tasks.named("buildHaskell")) +} + +tasks.register("buildHaskell") { + workingDir = file("src/testgen") + commandLine = listOf("cabal", "build") +} + tasks.named("run") { standardInput = System.`in` } tasks.register("repl") { + dependsOn(tasks.jar) workingDir = file("src/testgen") commandLine = listOf("cabal", "repl") standardInput = System.`in` diff --git a/src/testgen/testgen.cabal b/src/testgen/testgen.cabal index caa9267..66138b1 100644 --- a/src/testgen/testgen.cabal +++ b/src/testgen/testgen.cabal @@ -23,4 +23,4 @@ executable testgen build-depends: base >=4.15.1.0 && <5, QuickCheck ^>=2.15.0.1, - process ^>=1.6.26.1 + process ^>=1.6.25.0 diff --git a/src/testgen/testgen/FacadeTemplates.hs b/src/testgen/testgen/FacadeTemplates.hs index f34954e..9fab41b 100644 --- a/src/testgen/testgen/FacadeTemplates.hs +++ b/src/testgen/testgen/FacadeTemplates.hs @@ -13,12 +13,13 @@ -- limitations under the License. module FacadeTemplates ( - equalityTemplate + equalityTemplate, + emailTemplate ) where -import Java (assertEquals, toJavaExpression) -import TestTemplate (TestTemplate, genToTestTemplate) -import Test.QuickCheck (Gen, arbitrary) +import Java (assertEquals, runJava, toJavaExpression) +import TestTemplate (TestTemplate(..), genToTestTemplate) +import Test.QuickCheck (Gen, arbitrary, elements, generate, listOf) equalityTestGenerator :: Gen [String] equalityTestGenerator = do @@ -32,3 +33,25 @@ equalityTestGenerator = do equalityTemplate :: TestTemplate equalityTemplate = genToTestTemplate "equals" equalityTestGenerator 3 + +genEmail :: Gen String +genEmail = do + before <- listOf $ elements (['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9']) + after <- listOf $ elements (['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9']) + return $ before ++ "@" ++ after ++ ".com" + +emailTestGenerator :: IO [String] +emailTestGenerator = do + email <- generate $ genEmail + resultLines <- runJava + [ + "import MakeItFit.utils.EmailValidator;" + , "System.out.println(EmailValidator.isValidEmail(" ++ toJavaExpression email ++ "));" + ] + return + [ + assertEquals (toJavaExpression email) (last resultLines) + ] + +emailTemplate :: TestTemplate +emailTemplate = TestTemplate "validEmail" emailTestGenerator 1 diff --git a/src/testgen/testgen/Java.hs b/src/testgen/testgen/Java.hs index 9fbd4a9..d107c76 100644 --- a/src/testgen/testgen/Java.hs +++ b/src/testgen/testgen/Java.hs @@ -23,12 +23,27 @@ module Java , assertTrue , assertEquals , assertSame - , assertThrows) where + , assertThrows + , runJava + ) where import Data.Char (isAscii, ord) -import Data.List (intercalate) +import Data.List (intercalate, isPrefixOf) import Numeric (showHex) +import System.Process + ( + StdStream(CreatePipe) + , createProcess + , proc + , std_in + , std_out + , std_err + , waitForProcess + ) +import System.IO (Handle, hClose, hGetContents, hPutStr) +import Control.Exception (bracket, evaluate) + -- JavaData definition class JavaData a where -- | Gets the equivalent Java name for this Haskell type @@ -181,3 +196,32 @@ assertThrows e b = " }" , ");" ] + +-- JVM interop + +-- | Runs Java code with classes the project classes loaded +runJava :: [String] -- ^ Java statements + -> IO [String] -- ^ Lines output by the statements +runJava statements = do + (Just stdin, Just stdout, Just stderr, process) <- createProcess + (proc "jshell" ["--class-path=../../build/libs/ATS.jar"]) + { std_in = CreatePipe, std_out = CreatePipe, std_err = CreatePipe } + + bracket + (return ()) + (\_ -> do + hClose stdin + hClose stdout + hClose stderr + ) + (\_ -> do + hPutStr stdin (unlines statements) + hClose stdin + + output <- hGetContents stdout + evaluate (length output) -- Force deep strict evaluation + let outputLines = lines output + + waitForProcess process + return $ filter (not . isPrefixOf "| ") outputLines + ) diff --git a/src/testgen/testgen/Main.hs b/src/testgen/testgen/Main.hs index cfd672e..bf6a6c9 100644 --- a/src/testgen/testgen/Main.hs +++ b/src/testgen/testgen/Main.hs @@ -14,5 +14,19 @@ module Main where +import Java (runJava) +import System.Environment (getArgs) +import System.Exit (ExitCode(ExitFailure), exitWith) +import System.IO (hPutStrLn, stderr) +import TestClass (generateTestClass) + main :: IO () -main = putStrLn "Hello, Haskell!" +main = do + args <- getArgs + case args of + [file] -> do + test <- generateTestClass + writeFile file test + _ -> do + hPutStrLn stderr "Usage: cabal run testgen -- OutputFile.java" + exitWith (ExitFailure 1) diff --git a/src/testgen/testgen/TestClass.hs b/src/testgen/testgen/TestClass.hs index 97d0668..76fe043 100644 --- a/src/testgen/testgen/TestClass.hs +++ b/src/testgen/testgen/TestClass.hs @@ -39,8 +39,12 @@ generateUnformattedTestClass = do tests <- generateTests return $ concat - [ [ "import java.util.Arrays;" + [ [ "package MakeItFit;" + , "" + , "import java.util.Arrays;" , "import java.util.List;" + , "import org.junit.jupiter.api.Test;" + , "import static org.junit.jupiter.api.Assertions.*;" , "" , "import MakeItFit.users.User;" , ""