Skip to content
This repository was archived by the owner on Jun 17, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/maintenance.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@ 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 &&
wget https://apt.llvm.org/llvm.sh &&
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
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
# Build artifacts
.gradle
build
src/testgen/dist-*
21 changes: 20 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ pitest {
timestampedReports.set(false)
}

// Test generation

tasks.register<Exec>("generateQuickCheckTests") {
workingDir = file("src/testgen")
commandLine = listOf("cabal", "run")
}

tasks.register<JavaExec>("generateEvoSuiteTests") {
classpath = evoSuiteDownload
mainClass = "org.evosuite.EvoSuite"
Expand Down Expand Up @@ -136,9 +143,21 @@ tasks.named<JavaExec>("run") {
standardInput = System.`in`
}

tasks.register<Exec>("repl") {
workingDir = file("src/testgen")
commandLine = listOf("cabal", "repl")
standardInput = System.`in`
}

tasks.register<Exec>("haddock") {
workingDir = file("src/testgen")
commandLine = listOf("cabal", "haddock", "--haddock-all")
}

tasks.register<Exec>("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 && ./format.sh)"
)
}
30 changes: 30 additions & 0 deletions src/testgen/format.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/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"
}

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"
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
26 changes: 26 additions & 0 deletions src/testgen/testgen.cabal
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
cabal-version: 2.4
name: testgen
version: 0.0.0.1
license: Apache-2.0
author:
Humberto Gomes <a104348@alunos.uminho.pt>
José Lopes <a104541@alunos.uminho.pt>
Mariana Rocha <a90187@alunos.uminho.pt>

synopsis: Business layer facade test generator

executable testgen
main-is: Main.hs
hs-source-dirs: testgen
other-modules:
FacadeTemplates
Generators
Java
TestClass
TestTemplate

default-language: Haskell2010
build-depends:
base >=4.15.1.0 && <5,
QuickCheck ^>=2.15.0.1,
process ^>=1.6.26.1
34 changes: 34 additions & 0 deletions src/testgen/testgen/FacadeTemplates.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
-- 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

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
32 changes: 32 additions & 0 deletions src/testgen/testgen/Generators.hs
Original file line number Diff line number Diff line change
@@ -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
183 changes: 183 additions & 0 deletions src/testgen/testgen/Java.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
-- 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
module Java
(
JavaData(..)
, indent
, decorateTest
, assertTrue
, assertEquals
, assertSame
, assertThrows) where

import Data.Char (isAscii, ord)
import Data.List (intercalate)
import Numeric (showHex)

-- JavaData definition
class JavaData a where
-- | 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<Integer> 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"
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<Boolean>"
toJavaExpression = toJavaExpressionList

instance JavaData [Int] where
javaTypeName = const "List<Integer>"
toJavaExpression = toJavaExpressionList

instance JavaData [Integer] where
javaTypeName = const "List<Integer>"
toJavaExpression = toJavaExpressionList

instance JavaData [Float] where
javaTypeName = const "List<Float>"
toJavaExpression = toJavaExpressionList

instance JavaData [Double] where
javaTypeName = const "List<Double>"
toJavaExpression = toJavaExpressionList

instance JavaData [String] where
javaTypeName = const "List<String>"
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, ["}"]]

-- 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)) ++
[
" }"
, ");"
]
18 changes: 18 additions & 0 deletions src/testgen/testgen/Main.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-- 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 ()
main = putStrLn "Hello, Haskell!"
Loading