From 7997a9906fdce2f83489dc6063041e47bd6aecd4 Mon Sep 17 00:00:00 2001 From: Voidbert Date: Mon, 2 Jun 2025 11:46:12 +0100 Subject: [PATCH 01/15] Setup Haskell project --- .gitignore | 1 + src/testgen/testgen.cabal | 22 ++++++++++++++++++++++ src/testgen/testgen/Main.hs | 4 ++++ 3 files changed, 27 insertions(+) create mode 100644 src/testgen/testgen.cabal create mode 100644 src/testgen/testgen/Main.hs diff --git a/.gitignore b/.gitignore index 4790b95..0ed073a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ # Build artifacts .gradle build +src/testgen/dist-* diff --git a/src/testgen/testgen.cabal b/src/testgen/testgen.cabal new file mode 100644 index 0000000..5eedbdb --- /dev/null +++ b/src/testgen/testgen.cabal @@ -0,0 +1,22 @@ +cabal-version: 2.4 + +name: testgen +version: 0.1 +synopsis: Business layer facade test generator +license: Apache-2.0 + +author: Humberto Gomes + José Lopes + Mariana Rocha + +executable testgen + main-is: Main.hs + + -- Modules included in this executable, other than Main. + -- other-modules: + + -- LANGUAGE extensions used by modules in this package. + -- other-extensions: + build-depends: base ^>=4.15.1.0 && < 5 + hs-source-dirs: testgen + default-language: Haskell2010 diff --git a/src/testgen/testgen/Main.hs b/src/testgen/testgen/Main.hs new file mode 100644 index 0000000..65ae4a0 --- /dev/null +++ b/src/testgen/testgen/Main.hs @@ -0,0 +1,4 @@ +module Main where + +main :: IO () +main = putStrLn "Hello, Haskell!" From 3f66e5b03385d89645b749ad63d2088e5d2435e7 Mon Sep 17 00:00:00 2001 From: Voidbert Date: Mon, 2 Jun 2025 13:17:06 +0100 Subject: [PATCH 02/15] Add Gradle and Cabal integration --- build.gradle.kts | 17 ++++++++++++++++- src/testgen/testgen.cabal | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 2786744..ddef284 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -109,6 +109,13 @@ pitest { timestampedReports.set(false) } +// Test generation + +tasks.register("generateQuickCheckTests") { + workingDir = file("src/testgen") + commandLine = listOf("cabal", "run") +} + tasks.register("generateEvoSuiteTests") { classpath = evoSuiteDownload mainClass = "org.evosuite.EvoSuite" @@ -136,9 +143,17 @@ tasks.named("run") { standardInput = System.`in` } +tasks.register("repl") { + workingDir = file("src/testgen") + commandLine = listOf("cabal", "repl") + standardInput = System.`in` +} + tasks.register("format") { workingDir = file(rootDir) commandLine = listOf("sh", "-c", - "find src -type f | xargs -n1 sh -c 'clang-format -i $0; sed -i s/\\\\r//g $0'" + "(find src/ -type f -not -path 'src/testgen/*' | xargs -n1 sh -c 'clang-format -i $0; sed -i s/\\\\r//g $0')" + + "&&" + + "(cd src/testgen && find testgen -type f | xargs -n1 hindent)" ) } diff --git a/src/testgen/testgen.cabal b/src/testgen/testgen.cabal index 5eedbdb..7a72b1a 100644 --- a/src/testgen/testgen.cabal +++ b/src/testgen/testgen.cabal @@ -1,7 +1,7 @@ cabal-version: 2.4 name: testgen -version: 0.1 +version: 0.0.0.1 synopsis: Business layer facade test generator license: Apache-2.0 From 23f40e4a0c2f70705f129e81477448fea39b43a7 Mon Sep 17 00:00:00 2001 From: Voidbert Date: Mon, 2 Jun 2025 21:12:30 +0100 Subject: [PATCH 03/15] Add Haskell formatter config --- src/testgen/.hindent.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/testgen/.hindent.yaml diff --git a/src/testgen/.hindent.yaml b/src/testgen/.hindent.yaml new file mode 100644 index 0000000..6acfc74 --- /dev/null +++ b/src/testgen/.hindent.yaml @@ -0,0 +1,6 @@ +indent-size: 2 +line-length: 100 +force-trailing-newline: true +sort-imports: true +line-breaks: [] +extensions: [] From e06a7eb6cf1f913fa1a51d849ee3b71e6893b37f Mon Sep 17 00:00:00 2001 From: Voidbert Date: Mon, 2 Jun 2025 21:13:01 +0100 Subject: [PATCH 04/15] Add codegen for basic Java Types --- src/testgen/testgen.cabal | 8 +-- src/testgen/testgen/JavaData.hs | 89 +++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 src/testgen/testgen/JavaData.hs diff --git a/src/testgen/testgen.cabal b/src/testgen/testgen.cabal index 7a72b1a..4b0aea0 100644 --- a/src/testgen/testgen.cabal +++ b/src/testgen/testgen.cabal @@ -11,12 +11,8 @@ author: Humberto Gomes executable testgen main-is: Main.hs + other-modules: JavaData - -- Modules included in this executable, other than Main. - -- other-modules: - - -- LANGUAGE extensions used by modules in this package. - -- other-extensions: - build-depends: base ^>=4.15.1.0 && < 5 + build-depends: base >= 4.15.1.0 && < 5, QuickCheck >= 2.15.0.1 hs-source-dirs: testgen default-language: Haskell2010 diff --git a/src/testgen/testgen/JavaData.hs b/src/testgen/testgen/JavaData.hs new file mode 100644 index 0000000..a9bc684 --- /dev/null +++ b/src/testgen/testgen/JavaData.hs @@ -0,0 +1,89 @@ +{-# LANGUAGE FlexibleInstances #-} + +module JavaData + ( JavaData(..) + ) where + +import Data.Char (isAscii, ord) +import Data.List (intercalate) +import Numeric (showHex) + +-- JavaData definition +class JavaData a where + javaTypeName :: a -> String + toJavaExpression :: a -> String + toJavaVariable :: String -> a -> String + toJavaVariable name obj = javaTypeName obj ++ " " ++ name ++ " = " ++ toJavaExpression obj ++ ";" + +-- Basic scalar types +instance JavaData Bool where + javaTypeName = const "bool" + toJavaExpression False = "false" + toJavaExpression True = "true" + +instance JavaData Int where + javaTypeName = const "int" + toJavaExpression = show + +instance JavaData Integer where + javaTypeName = const "int" + toJavaExpression = show + +instance JavaData Float where + javaTypeName = const "float" + toJavaExpression = show + +instance JavaData Double where + javaTypeName = const "double" + toJavaExpression = show + +pad :: Int -> a -> [a] -> [a] +pad n f xs = replicate (n - length xs) f ++ xs + +toJavaCharacter '"' = "\\\"" +toJavaCharacter '\'' = "\\'" +toJavaCharacter '\\' = "\\\\" +toJavaCharacter '\t' = "\\t" +toJavaCharacter '\b' = "\\b" +toJavaCharacter '\n' = "\\n" +toJavaCharacter '\r' = "\\r" +toJavaCharacter '\f' = "\\f" +toJavaCharacter c + | isAscii c = [c] + | otherwise = "\\u" ++ pad 4 '0' (showHex (ord c) "") + +instance JavaData Char where + javaTypeName = const "char" + toJavaExpression c = "\'" ++ toJavaCharacter c ++ "\'" + +instance JavaData String where + javaTypeName = const "String" + toJavaExpression s = "\"" ++ concat (map toJavaCharacter s) ++ "\"" + +-- Lists +toJavaExpressionList :: JavaData a => [a] -> String +toJavaExpressionList xs = "Arrays.asList(" ++ intercalate ", " (map toJavaExpression xs) ++ ")" + +instance JavaData [Bool] where + javaTypeName = const "List" + toJavaExpression = toJavaExpressionList + +instance JavaData [Int] where + javaTypeName = const "List" + toJavaExpression = toJavaExpressionList + +instance JavaData [Integer] where + javaTypeName = const "List" + toJavaExpression = toJavaExpressionList + +instance JavaData [Float] where + javaTypeName = const "List" + toJavaExpression = toJavaExpressionList + +instance JavaData [Double] where + javaTypeName = const "List" + toJavaExpression = toJavaExpressionList + +instance JavaData [String] where + javaTypeName = const "List" + toJavaExpression = toJavaExpressionList From c37cb9a94de344a7f778fced3c6ab6d73143eade Mon Sep 17 00:00:00 2001 From: Voidbert Date: Mon, 2 Jun 2025 23:34:02 +0100 Subject: [PATCH 05/15] Add Haddock support hindent had to be removed because it formatted Haddock's comments incorrectly --- build.gradle.kts | 10 +++++++--- src/testgen/.hindent.yaml | 6 ------ src/testgen/format.sh | 28 ++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 9 deletions(-) delete mode 100644 src/testgen/.hindent.yaml create mode 100755 src/testgen/format.sh diff --git a/build.gradle.kts b/build.gradle.kts index ddef284..b926389 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -149,11 +149,15 @@ tasks.register("repl") { standardInput = System.`in` } +tasks.register("haddock") { + workingDir = file("src/testgen") + commandLine = listOf("cabal", "haddock", "--haddock-all") +} + tasks.register("format") { workingDir = file(rootDir) commandLine = listOf("sh", "-c", - "(find src/ -type f -not -path 'src/testgen/*' | xargs -n1 sh -c 'clang-format -i $0; sed -i s/\\\\r//g $0')" + - "&&" + - "(cd src/testgen && find testgen -type f | xargs -n1 hindent)" + "(find src/ -type f -not -path 'src/testgen/*' | xargs -n1 sh -c 'clang-format -i $0; sed -i s/\\\\r//g $0') &&" + + "src/testgen/format.sh" ) } diff --git a/src/testgen/.hindent.yaml b/src/testgen/.hindent.yaml deleted file mode 100644 index 6acfc74..0000000 --- a/src/testgen/.hindent.yaml +++ /dev/null @@ -1,6 +0,0 @@ -indent-size: 2 -line-length: 100 -force-trailing-newline: true -sort-imports: true -line-breaks: [] -extensions: [] diff --git a/src/testgen/format.sh b/src/testgen/format.sh new file mode 100755 index 0000000..bec7e60 --- /dev/null +++ b/src/testgen/format.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +# This script checks for formatting errors in the Haskell code. + +# Copyright 2025 Humberto Gomes, José Lopes, Mariana Rocha +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +grep_error_message() { + sed -r "s/^([^:]*):([^:]*):.*$/\1:\2: $1/g" +} + +# shellcheck disable=SC2266 +find . -type f -name "*.hs" | while IFS="" read -r file; do + grep -PHn '.{101,}$' "$file" | grep_error_message "Column exceeds 100 characters" + grep -PHn '\t$' "$file" | grep_error_message "Use of tabs" + grep -PHn '\s+$' "$file" | grep_error_message "Trailing whitespace" +done | sort -V | tee /dev/stderr | test "$(wc -l)" = 0 From 5b8136d054ebf77a2e0738e47799654ca539aa03 Mon Sep 17 00:00:00 2001 From: Voidbert Date: Mon, 2 Jun 2025 23:34:56 +0100 Subject: [PATCH 06/15] Add documentation and more Java utilities --- src/testgen/testgen/{JavaData.hs => Java.hs} | 50 +++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) rename src/testgen/testgen/{JavaData.hs => Java.hs} (63%) diff --git a/src/testgen/testgen/JavaData.hs b/src/testgen/testgen/Java.hs similarity index 63% rename from src/testgen/testgen/JavaData.hs rename to src/testgen/testgen/Java.hs index a9bc684..db33e21 100644 --- a/src/testgen/testgen/JavaData.hs +++ b/src/testgen/testgen/Java.hs @@ -1,8 +1,7 @@ {-# LANGUAGE FlexibleInstances #-} -module JavaData - ( JavaData(..) - ) where +-- | Java code generation utilities +module Java (JavaData(..), indent, decorateTest) where import Data.Char (isAscii, ord) import Data.List (intercalate) @@ -10,12 +9,28 @@ import Numeric (showHex) -- JavaData definition class JavaData a where - javaTypeName :: a -> String - toJavaExpression :: a -> String - toJavaVariable :: String -> a -> String + -- | Gets the equivalent Java name for this Haskell type + javaTypeName :: a -- ^ Input value + -> String -- ^ Java type name + + -- | + -- Creates a Java expression that evaluates to the given value. + -- + -- Example output: @Arrays.asList(1, 2, 3)@ + toJavaExpression :: a -- ^ Input value + -> String -- ^ Java expression + + -- | + -- Creates a Java variable declaration, where the variable is initialized to the given value + -- + -- Example output: @List list = Arrays.asList(1, 2, 3)@ + toJavaVariable :: String -- ^ Variable name + -> a -- ^ Input value + -> String -- ^ Java variable declation toJavaVariable name obj = javaTypeName obj ++ " " ++ name ++ " = " ++ toJavaExpression obj ++ ";" -- Basic scalar types + instance JavaData Bool where javaTypeName = const "bool" toJavaExpression False = "false" @@ -61,6 +76,7 @@ instance JavaData String where toJavaExpression s = "\"" ++ concat (map toJavaCharacter s) ++ "\"" -- Lists + toJavaExpressionList :: JavaData a => [a] -> String toJavaExpressionList xs = "Arrays.asList(" ++ intercalate ", " (map toJavaExpression xs) ++ ")" @@ -87,3 +103,25 @@ instance JavaData [Double] where instance JavaData [String] where javaTypeName = const "List" toJavaExpression = toJavaExpressionList + +-- Utilities + +-- | Indents a sequence of lines with 4 spaces +indent :: [String] -> [String] +indent = map (" " ++) + +-- | +-- Puts the body of a test in a method declaration +-- +-- Example output: +-- +-- @ +-- \@Test +-- void testName() { +-- testContents +-- } +-- @ +decorateTest :: String -- ^ Name of the test method + -> [String] -- ^ Lines constituting the body of the test method + -> [String] -- ^ Lines of the test method +decorateTest name test = concat [["@Test", "void " ++ name ++ "() {"], indent test, ["}"]] From f071e12d3e40a5a1449f1d0b09b88421c507c837 Mon Sep 17 00:00:00 2001 From: Voidbert Date: Mon, 2 Jun 2025 23:50:53 +0100 Subject: [PATCH 07/15] Add test template and test generation --- src/testgen/testgen/TestTemplate.hs | 40 +++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/testgen/testgen/TestTemplate.hs diff --git a/src/testgen/testgen/TestTemplate.hs b/src/testgen/testgen/TestTemplate.hs new file mode 100644 index 0000000..910239b --- /dev/null +++ b/src/testgen/testgen/TestTemplate.hs @@ -0,0 +1,40 @@ +-- | Test template definition +module TestTemplate (TestTemplate(..), genToTestTemplate, generateTestsFromTemplate) where + +import Data.List (intercalate) +import Java (decorateTest) +import Test.QuickCheck.Gen (Gen, generate) + +-- Test template data structure and helpers + +-- | A template used for test generation +data TestTemplate = + TestTemplate + String -- ^ Test name + (IO [String]) -- ^ Generator function of the lines in the test's body + Int -- ^ Number of tests of this type to generate + +-- | Creates a 'TestTemplate' from a QuickCheck generator +genToTestTemplate :: String -- ^ Test name + -> Gen [String] -- ^ QuickCheck generator of the lines of the test's body + -> Int -- ^ Number of tests + -> TestTemplate -- ^ Output test template +genToTestTemplate name gen n = TestTemplate name (generate gen) n + +-- Test generation + +getTestNumber :: Int -> Int -> String +getTestNumber 1 _ = "" +getTestNumber _ i = show i + +generateTestFromTemplate :: TestTemplate -> Int -> IO [String] +generateTestFromTemplate (TestTemplate name sampler n) i = do + body <- sampler + return $ decorateTest (name ++ getTestNumber n i) body + +-- | Generates all tests from a template +generateTestsFromTemplate :: TestTemplate -- ^ Test template to use for generation + -> IO [String] -- ^ Lines of the generated tests +generateTestsFromTemplate t@(TestTemplate _ _ n) = do + tests <- sequence $ zipWith generateTestFromTemplate (repeat t) [1 .. n] + return $ intercalate [""] tests From 5b112a713ddd3190ab38304847fcd875784ef22f Mon Sep 17 00:00:00 2001 From: Voidbert Date: Tue, 3 Jun 2025 00:00:16 +0100 Subject: [PATCH 08/15] Add test class generation --- src/testgen/testgen.cabal | 2 +- src/testgen/testgen/TestClass.hs | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/testgen/testgen/TestClass.hs diff --git a/src/testgen/testgen.cabal b/src/testgen/testgen.cabal index 4b0aea0..3f4f979 100644 --- a/src/testgen/testgen.cabal +++ b/src/testgen/testgen.cabal @@ -11,7 +11,7 @@ author: Humberto Gomes executable testgen main-is: Main.hs - other-modules: JavaData + other-modules: Java, TestClass, TestTemplate build-depends: base >= 4.15.1.0 && < 5, QuickCheck >= 2.15.0.1 hs-source-dirs: testgen diff --git a/src/testgen/testgen/TestClass.hs b/src/testgen/testgen/TestClass.hs new file mode 100644 index 0000000..c758e24 --- /dev/null +++ b/src/testgen/testgen/TestClass.hs @@ -0,0 +1,38 @@ +module TestClass (generateTestClass) where + +import Data.List (intercalate) +import Java (indent) +import TestTemplate (TestTemplate(..), generateTestsFromTemplate) + +templates = + [ + TestTemplate "BooleanTest" (return ["assertTrue(true)"]) 5 + , TestTemplate "IntegerTest" (return ["assertEquals(5, 5)"]) 5 + ] + +generateTests :: IO [String] +generateTests = do + tests <- sequence $ map generateTestsFromTemplate templates + return $ intercalate [""] tests + +generateUnformattedTestClass :: IO [String] +generateUnformattedTestClass = do + tests <- generateTests + return + $ concat + [ [ "import java.util.Arrays;" + , "import java.util.List;" + , "" + , "import MakeItFit.users.User;" + , "" + , "public class MakeItFitTest {" + ] + , indent tests + , ["}"] + ] + +-- | Generates the test class for the applications facade +generateTestClass :: IO String +generateTestClass = do + lines <- generateUnformattedTestClass + return $ unlines lines From 74a1fad6d953e36527e31362e72250481ffd6d16 Mon Sep 17 00:00:00 2001 From: Voidbert Date: Tue, 3 Jun 2025 00:44:50 +0100 Subject: [PATCH 09/15] Add automatic formatting of output --- src/testgen/testgen.cabal | 4 +++- src/testgen/testgen/TestClass.hs | 30 +++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/testgen/testgen.cabal b/src/testgen/testgen.cabal index 3f4f979..eea7aa9 100644 --- a/src/testgen/testgen.cabal +++ b/src/testgen/testgen.cabal @@ -13,6 +13,8 @@ executable testgen main-is: Main.hs other-modules: Java, TestClass, TestTemplate - build-depends: base >= 4.15.1.0 && < 5, QuickCheck >= 2.15.0.1 + build-depends: base >= 4.15.1.0 && < 5, + QuickCheck ^>= 2.15.0.1, + process ^>= 1.6.26.1, hs-source-dirs: testgen default-language: Haskell2010 diff --git a/src/testgen/testgen/TestClass.hs b/src/testgen/testgen/TestClass.hs index c758e24..11191f7 100644 --- a/src/testgen/testgen/TestClass.hs +++ b/src/testgen/testgen/TestClass.hs @@ -4,10 +4,14 @@ import Data.List (intercalate) import Java (indent) import TestTemplate (TestTemplate(..), generateTestsFromTemplate) +import System.Process (StdStream(CreatePipe), createProcess, proc, std_in, std_out, waitForProcess) +import System.IO (hClose, hGetContents, hPutStr) +import Control.Exception (bracket, evaluate) + templates = [ - TestTemplate "BooleanTest" (return ["assertTrue(true)"]) 5 - , TestTemplate "IntegerTest" (return ["assertEquals(5, 5)"]) 5 + TestTemplate "BooleanTest" (return ["assertTrue(true);"]) 5 + , TestTemplate "IntegerTest" (return ["assertEquals(5, 5);"]) 5 ] generateTests :: IO [String] @@ -31,8 +35,28 @@ generateUnformattedTestClass = do , ["}"] ] +format :: String -> IO String +format source = do + (Just stdin, Just stdout, _, process) <- createProcess + (proc "clang-format" ["--assume-filename=MakeItFitTest.java"]) + { std_in = CreatePipe, std_out = CreatePipe } + + bracket + (return ()) + (\_ -> hClose stdin >> hClose stdout) + (\_ -> do + hPutStr stdin source + hClose stdin + + output <- hGetContents stdout + evaluate (length output) -- Force deep strict evaluation + + waitForProcess process + return output + ) + -- | Generates the test class for the applications facade generateTestClass :: IO String generateTestClass = do lines <- generateUnformattedTestClass - return $ unlines lines + format $ unlines lines From 606f2888e79b331d35ff7fa054370a82d4daa243 Mon Sep 17 00:00:00 2001 From: Voidbert Date: Tue, 3 Jun 2025 01:18:40 +0100 Subject: [PATCH 10/15] Add assertion code generation helpers --- src/testgen/testgen/Java.hs | 44 ++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/testgen/testgen/Java.hs b/src/testgen/testgen/Java.hs index db33e21..a1ee995 100644 --- a/src/testgen/testgen/Java.hs +++ b/src/testgen/testgen/Java.hs @@ -1,7 +1,15 @@ {-# LANGUAGE FlexibleInstances #-} -- | Java code generation utilities -module Java (JavaData(..), indent, decorateTest) where +module Java + ( + JavaData(..) + , indent + , decorateTest + , assertTrue + , assertEquals + , assertSame + , assertThrows) where import Data.Char (isAscii, ord) import Data.List (intercalate) @@ -125,3 +133,37 @@ decorateTest :: String -- ^ Name of the test method -> [String] -- ^ Lines constituting the body of the test method -> [String] -- ^ Lines of the test method decorateTest name test = concat [["@Test", "void " ++ name ++ "() {"], indent test, ["}"]] + +-- Assertions + +-- | @assertTrue@ statement generator +assertTrue :: String -- ^ A Java boolean expression + -> String -- ^ A Java assertion statement +assertTrue c = "assertTrue(" ++ c ++ ");" + +-- | @assertEquals@ statement generator +assertEquals :: String -- ^ A Java expression for the expected value + -> String -- ^ A Java expression for the obtained value + -> String -- ^ A Java assertion statement +assertEquals e g = "assertEquals(" ++ e ++ ", " ++ g ++ ");" + +-- | @assertSame@ statement generator +assertSame :: String -- ^ A Java expression for the expected value + -> String -- ^ A Java expression for the obtained value + -> String -- ^ A Java assertion statement +assertSame e g = "assertSame(" ++ e ++ ", " ++ g ++ ");" + +-- | @assertThrows@ statement generator +assertThrows :: String -- ^ Name of the expected exception class + -> [String] -- ^ Lines where the exception should occur + -> [String] -- ^ A Java assertion statement +assertThrows e b = + [ + "assertThrows(" + , (" " ++ e ++ ".class,") + , " () -> {" + ] ++ (indent (indent b)) ++ + [ + " }" + , ");" + ] From dfb0e73c4cadb6a8037c487b2fcc35b016ba09aa Mon Sep 17 00:00:00 2001 From: Voidbert Date: Tue, 3 Jun 2025 01:21:26 +0100 Subject: [PATCH 11/15] Add example test template --- src/testgen/testgen.cabal | 33 +++++++++++++++----------- src/testgen/testgen/FacadeTemplates.hs | 20 ++++++++++++++++ src/testgen/testgen/TestClass.hs | 7 +++--- 3 files changed, 43 insertions(+), 17 deletions(-) create mode 100644 src/testgen/testgen/FacadeTemplates.hs diff --git a/src/testgen/testgen.cabal b/src/testgen/testgen.cabal index eea7aa9..1dfe2ec 100644 --- a/src/testgen/testgen.cabal +++ b/src/testgen/testgen.cabal @@ -1,20 +1,25 @@ cabal-version: 2.4 +name: testgen +version: 0.0.0.1 +license: Apache-2.0 +author: + Humberto Gomes + José Lopes + Mariana Rocha -name: testgen -version: 0.0.0.1 -synopsis: Business layer facade test generator -license: Apache-2.0 - -author: Humberto Gomes - José Lopes - Mariana Rocha +synopsis: Business layer facade test generator executable testgen - main-is: Main.hs - other-modules: Java, TestClass, TestTemplate - - build-depends: base >= 4.15.1.0 && < 5, - QuickCheck ^>= 2.15.0.1, - process ^>= 1.6.26.1, + main-is: Main.hs hs-source-dirs: testgen + other-modules: + FacadeTemplates + Java + TestClass + TestTemplate + default-language: Haskell2010 + build-depends: + base >=4.15.1.0 && <5, + QuickCheck ^>=2.15.0.1, + process ^>=1.6.26.1 diff --git a/src/testgen/testgen/FacadeTemplates.hs b/src/testgen/testgen/FacadeTemplates.hs new file mode 100644 index 0000000..97b33e9 --- /dev/null +++ b/src/testgen/testgen/FacadeTemplates.hs @@ -0,0 +1,20 @@ +module FacadeTemplates ( + equalityTemplate + ) where + +import Java (assertEquals, toJavaExpression) +import TestTemplate (TestTemplate, genToTestTemplate) +import Test.QuickCheck (Gen, arbitrary) + +equalityTestGenerator :: Gen [String] +equalityTestGenerator = do + elem <- arbitrary :: Gen Int + return [assertEquals (toJavaExpression elem) (toJavaExpression elem)] + + -- Other usage examples + -- return [assertTrue (toJavaExpression elem ++ " == " ++ toJavaExpression elem)] + -- return [assertSame (toJavaExpression elem) (toJavaExpression elem)] + -- return $ assertThrows "Exception" [assertEquals "1" "1"] + +equalityTemplate :: TestTemplate +equalityTemplate = genToTestTemplate "equals" equalityTestGenerator 3 diff --git a/src/testgen/testgen/TestClass.hs b/src/testgen/testgen/TestClass.hs index 11191f7..173a779 100644 --- a/src/testgen/testgen/TestClass.hs +++ b/src/testgen/testgen/TestClass.hs @@ -2,16 +2,17 @@ module TestClass (generateTestClass) where import Data.List (intercalate) import Java (indent) -import TestTemplate (TestTemplate(..), generateTestsFromTemplate) +import TestTemplate (generateTestsFromTemplate) import System.Process (StdStream(CreatePipe), createProcess, proc, std_in, std_out, waitForProcess) import System.IO (hClose, hGetContents, hPutStr) import Control.Exception (bracket, evaluate) +import FacadeTemplates + templates = [ - TestTemplate "BooleanTest" (return ["assertTrue(true);"]) 5 - , TestTemplate "IntegerTest" (return ["assertEquals(5, 5);"]) 5 + equalityTemplate -- TODO: remove this simple template only to show the desired architecture ] generateTests :: IO [String] From a107325f3ee5f04fc952ab55f8651d2fbfdfe01b Mon Sep 17 00:00:00 2001 From: Voidbert Date: Tue, 3 Jun 2025 01:24:07 +0100 Subject: [PATCH 12/15] Add formatting of cabal project file --- build.gradle.kts | 2 +- src/testgen/format.sh | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index b926389..4e3304a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -158,6 +158,6 @@ tasks.register("format") { workingDir = file(rootDir) commandLine = listOf("sh", "-c", "(find src/ -type f -not -path 'src/testgen/*' | xargs -n1 sh -c 'clang-format -i $0; sed -i s/\\\\r//g $0') &&" + - "src/testgen/format.sh" + "(cd src/testgen && ./format.sh)" ) } diff --git a/src/testgen/format.sh b/src/testgen/format.sh index bec7e60..6ad56de 100755 --- a/src/testgen/format.sh +++ b/src/testgen/format.sh @@ -20,6 +20,8 @@ grep_error_message() { sed -r "s/^([^:]*):([^:]*):.*$/\1:\2: $1/g" } +cabal format || exit 1 + # shellcheck disable=SC2266 find . -type f -name "*.hs" | while IFS="" read -r file; do grep -PHn '.{101,}$' "$file" | grep_error_message "Column exceeds 100 characters" From 1ae10fa0de07ba67d456f6183ea2b381fc355cbd Mon Sep 17 00:00:00 2001 From: Voidbert Date: Tue, 3 Jun 2025 01:27:26 +0100 Subject: [PATCH 13/15] Add license headers --- src/testgen/testgen/FacadeTemplates.hs | 14 ++++++++++++++ src/testgen/testgen/Java.hs | 14 ++++++++++++++ src/testgen/testgen/Main.hs | 14 ++++++++++++++ src/testgen/testgen/TestClass.hs | 14 ++++++++++++++ src/testgen/testgen/TestTemplate.hs | 14 ++++++++++++++ 5 files changed, 70 insertions(+) diff --git a/src/testgen/testgen/FacadeTemplates.hs b/src/testgen/testgen/FacadeTemplates.hs index 97b33e9..f34954e 100644 --- a/src/testgen/testgen/FacadeTemplates.hs +++ b/src/testgen/testgen/FacadeTemplates.hs @@ -1,3 +1,17 @@ +-- Copyright 2025 Humberto Gomes, José Lopes, Mariana Rocha +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + module FacadeTemplates ( equalityTemplate ) where diff --git a/src/testgen/testgen/Java.hs b/src/testgen/testgen/Java.hs index a1ee995..9fbd4a9 100644 --- a/src/testgen/testgen/Java.hs +++ b/src/testgen/testgen/Java.hs @@ -1,3 +1,17 @@ +-- Copyright 2025 Humberto Gomes, José Lopes, Mariana Rocha +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + {-# LANGUAGE FlexibleInstances #-} -- | Java code generation utilities diff --git a/src/testgen/testgen/Main.hs b/src/testgen/testgen/Main.hs index 65ae4a0..cfd672e 100644 --- a/src/testgen/testgen/Main.hs +++ b/src/testgen/testgen/Main.hs @@ -1,3 +1,17 @@ +-- Copyright 2025 Humberto Gomes, José Lopes, Mariana Rocha +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + module Main where main :: IO () diff --git a/src/testgen/testgen/TestClass.hs b/src/testgen/testgen/TestClass.hs index 173a779..97d0668 100644 --- a/src/testgen/testgen/TestClass.hs +++ b/src/testgen/testgen/TestClass.hs @@ -1,3 +1,17 @@ +-- Copyright 2025 Humberto Gomes, José Lopes, Mariana Rocha +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + module TestClass (generateTestClass) where import Data.List (intercalate) diff --git a/src/testgen/testgen/TestTemplate.hs b/src/testgen/testgen/TestTemplate.hs index 910239b..0afe3f9 100644 --- a/src/testgen/testgen/TestTemplate.hs +++ b/src/testgen/testgen/TestTemplate.hs @@ -1,3 +1,17 @@ +-- Copyright 2025 Humberto Gomes, José Lopes, Mariana Rocha +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + -- | Test template definition module TestTemplate (TestTemplate(..), genToTestTemplate, generateTestsFromTemplate) where From 787d4383e6f99e817351aa19b5388e0aa806d4b1 Mon Sep 17 00:00:00 2001 From: Voidbert Date: Tue, 3 Jun 2025 01:36:25 +0100 Subject: [PATCH 14/15] Add example architecture for generators --- src/testgen/testgen.cabal | 1 + src/testgen/testgen/Generators.hs | 32 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/testgen/testgen/Generators.hs diff --git a/src/testgen/testgen.cabal b/src/testgen/testgen.cabal index 1dfe2ec..caa9267 100644 --- a/src/testgen/testgen.cabal +++ b/src/testgen/testgen.cabal @@ -14,6 +14,7 @@ executable testgen hs-source-dirs: testgen other-modules: FacadeTemplates + Generators Java TestClass TestTemplate diff --git a/src/testgen/testgen/Generators.hs b/src/testgen/testgen/Generators.hs new file mode 100644 index 0000000..5aba521 --- /dev/null +++ b/src/testgen/testgen/Generators.hs @@ -0,0 +1,32 @@ +-- Copyright 2025 Humberto Gomes, José Lopes, Mariana Rocha +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +module Generators (User(..)) where + +import Test.QuickCheck (Arbitrary(..), elements, listOf) + +-- | MakeItFit user +data User = + Beginner + String -- ^ User's name + | + Intermediate + String -- ^ User's name + deriving (Show, Eq) + +instance Arbitrary User where + arbitrary = do + constructor <- elements [Beginner, Intermediate] + name <- listOf $ elements (['A'..'Z'] ++ ['a'..'z'] ++ [' ']) + return $ constructor name From daba7a77adccd932a261f5fc9089b0e1c1555596 Mon Sep 17 00:00:00 2001 From: Voidbert Date: Tue, 3 Jun 2025 01:42:32 +0100 Subject: [PATCH 15/15] Hopefully fixed actions --- .github/workflows/maintenance.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maintenance.yaml b/.github/workflows/maintenance.yaml index edea791..26023ef 100644 --- a/.github/workflows/maintenance.yaml +++ b/.github/workflows/maintenance.yaml @@ -10,6 +10,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' - run: >- sudo apt update && sudo apt install wget && @@ -17,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: find src -type f | xargs -n1 clang-format-19 --dry-run -Werror + - run: gradle format build: timeout-minutes: 10