Compare commits
1 Commits
issue-25/m
...
fc0c1b4db9
| Author | SHA1 | Date | |
|---|---|---|---|
| fc0c1b4db9 |
@@ -1,30 +0,0 @@
|
|||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [master]
|
|
||||||
pull_request:
|
|
||||||
branches: [master]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up JDK 16
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
java-version: '16'
|
|
||||||
distribution: 'temurin'
|
|
||||||
|
|
||||||
- name: Build and test with coverage
|
|
||||||
run: ./mvnw verify --batch-mode
|
|
||||||
|
|
||||||
- name: Upload coverage report
|
|
||||||
if: always()
|
|
||||||
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: jacoco-report
|
|
||||||
path: target/site/jacoco/
|
|
||||||
3
.mvn/wrapper/maven-wrapper.properties
vendored
3
.mvn/wrapper/maven-wrapper.properties
vendored
@@ -1,3 +0,0 @@
|
|||||||
wrapperVersion=3.3.4
|
|
||||||
distributionType=only-script
|
|
||||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip
|
|
||||||
295
mvnw
vendored
295
mvnw
vendored
@@ -1,295 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
# Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
# or more contributor license agreements. See the NOTICE file
|
|
||||||
# distributed with this work for additional information
|
|
||||||
# regarding copyright ownership. The ASF licenses this file
|
|
||||||
# to you 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.
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
# Apache Maven Wrapper startup batch script, version 3.3.4
|
|
||||||
#
|
|
||||||
# Optional ENV vars
|
|
||||||
# -----------------
|
|
||||||
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
|
|
||||||
# MVNW_REPOURL - repo url base for downloading maven distribution
|
|
||||||
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
|
||||||
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
set -euf
|
|
||||||
[ "${MVNW_VERBOSE-}" != debug ] || set -x
|
|
||||||
|
|
||||||
# OS specific support.
|
|
||||||
native_path() { printf %s\\n "$1"; }
|
|
||||||
case "$(uname)" in
|
|
||||||
CYGWIN* | MINGW*)
|
|
||||||
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
|
|
||||||
native_path() { cygpath --path --windows "$1"; }
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# set JAVACMD and JAVACCMD
|
|
||||||
set_java_home() {
|
|
||||||
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
|
|
||||||
if [ -n "${JAVA_HOME-}" ]; then
|
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
|
||||||
JAVACCMD="$JAVA_HOME/jre/sh/javac"
|
|
||||||
else
|
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
|
||||||
JAVACCMD="$JAVA_HOME/bin/javac"
|
|
||||||
|
|
||||||
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
|
|
||||||
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
|
|
||||||
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
JAVACMD="$(
|
|
||||||
'set' +e
|
|
||||||
'unset' -f command 2>/dev/null
|
|
||||||
'command' -v java
|
|
||||||
)" || :
|
|
||||||
JAVACCMD="$(
|
|
||||||
'set' +e
|
|
||||||
'unset' -f command 2>/dev/null
|
|
||||||
'command' -v javac
|
|
||||||
)" || :
|
|
||||||
|
|
||||||
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
|
|
||||||
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# hash string like Java String::hashCode
|
|
||||||
hash_string() {
|
|
||||||
str="${1:-}" h=0
|
|
||||||
while [ -n "$str" ]; do
|
|
||||||
char="${str%"${str#?}"}"
|
|
||||||
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
|
|
||||||
str="${str#?}"
|
|
||||||
done
|
|
||||||
printf %x\\n $h
|
|
||||||
}
|
|
||||||
|
|
||||||
verbose() { :; }
|
|
||||||
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
|
|
||||||
|
|
||||||
die() {
|
|
||||||
printf %s\\n "$1" >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
trim() {
|
|
||||||
# MWRAPPER-139:
|
|
||||||
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
|
|
||||||
# Needed for removing poorly interpreted newline sequences when running in more
|
|
||||||
# exotic environments such as mingw bash on Windows.
|
|
||||||
printf "%s" "${1}" | tr -d '[:space:]'
|
|
||||||
}
|
|
||||||
|
|
||||||
scriptDir="$(dirname "$0")"
|
|
||||||
scriptName="$(basename "$0")"
|
|
||||||
|
|
||||||
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
|
|
||||||
while IFS="=" read -r key value; do
|
|
||||||
case "${key-}" in
|
|
||||||
distributionUrl) distributionUrl=$(trim "${value-}") ;;
|
|
||||||
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
|
|
||||||
esac
|
|
||||||
done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
|
||||||
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
|
||||||
|
|
||||||
case "${distributionUrl##*/}" in
|
|
||||||
maven-mvnd-*bin.*)
|
|
||||||
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
|
|
||||||
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
|
|
||||||
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
|
|
||||||
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
|
|
||||||
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
|
|
||||||
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
|
|
||||||
*)
|
|
||||||
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
|
|
||||||
distributionPlatform=linux-amd64
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
|
|
||||||
;;
|
|
||||||
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
|
|
||||||
*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
|
||||||
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
|
||||||
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
|
|
||||||
distributionUrlName="${distributionUrl##*/}"
|
|
||||||
distributionUrlNameMain="${distributionUrlName%.*}"
|
|
||||||
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
|
|
||||||
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
|
|
||||||
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
|
|
||||||
|
|
||||||
exec_maven() {
|
|
||||||
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
|
|
||||||
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ -d "$MAVEN_HOME" ]; then
|
|
||||||
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
|
||||||
exec_maven "$@"
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "${distributionUrl-}" in
|
|
||||||
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
|
|
||||||
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# prepare tmp dir
|
|
||||||
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
|
|
||||||
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
|
|
||||||
trap clean HUP INT TERM EXIT
|
|
||||||
else
|
|
||||||
die "cannot create temp dir"
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p -- "${MAVEN_HOME%/*}"
|
|
||||||
|
|
||||||
# Download and Install Apache Maven
|
|
||||||
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
|
||||||
verbose "Downloading from: $distributionUrl"
|
|
||||||
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
|
||||||
|
|
||||||
# select .zip or .tar.gz
|
|
||||||
if ! command -v unzip >/dev/null; then
|
|
||||||
distributionUrl="${distributionUrl%.zip}.tar.gz"
|
|
||||||
distributionUrlName="${distributionUrl##*/}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# verbose opt
|
|
||||||
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
|
|
||||||
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
|
|
||||||
|
|
||||||
# normalize http auth
|
|
||||||
case "${MVNW_PASSWORD:+has-password}" in
|
|
||||||
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
|
||||||
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
|
|
||||||
verbose "Found wget ... using wget"
|
|
||||||
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
|
|
||||||
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
|
|
||||||
verbose "Found curl ... using curl"
|
|
||||||
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
|
|
||||||
elif set_java_home; then
|
|
||||||
verbose "Falling back to use Java to download"
|
|
||||||
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
|
|
||||||
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
|
|
||||||
cat >"$javaSource" <<-END
|
|
||||||
public class Downloader extends java.net.Authenticator
|
|
||||||
{
|
|
||||||
protected java.net.PasswordAuthentication getPasswordAuthentication()
|
|
||||||
{
|
|
||||||
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
|
|
||||||
}
|
|
||||||
public static void main( String[] args ) throws Exception
|
|
||||||
{
|
|
||||||
setDefault( new Downloader() );
|
|
||||||
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
END
|
|
||||||
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
|
|
||||||
verbose " - Compiling Downloader.java ..."
|
|
||||||
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
|
|
||||||
verbose " - Running Downloader.java ..."
|
|
||||||
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
|
||||||
if [ -n "${distributionSha256Sum-}" ]; then
|
|
||||||
distributionSha256Result=false
|
|
||||||
if [ "$MVN_CMD" = mvnd.sh ]; then
|
|
||||||
echo "Checksum validation is not supported for maven-mvnd." >&2
|
|
||||||
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
|
||||||
exit 1
|
|
||||||
elif command -v sha256sum >/dev/null; then
|
|
||||||
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
|
|
||||||
distributionSha256Result=true
|
|
||||||
fi
|
|
||||||
elif command -v shasum >/dev/null; then
|
|
||||||
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
|
|
||||||
distributionSha256Result=true
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
|
|
||||||
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ $distributionSha256Result = false ]; then
|
|
||||||
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
|
|
||||||
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# unzip and move
|
|
||||||
if command -v unzip >/dev/null; then
|
|
||||||
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
|
|
||||||
else
|
|
||||||
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Find the actual extracted directory name (handles snapshots where filename != directory name)
|
|
||||||
actualDistributionDir=""
|
|
||||||
|
|
||||||
# First try the expected directory name (for regular distributions)
|
|
||||||
if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
|
|
||||||
if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
|
|
||||||
actualDistributionDir="$distributionUrlNameMain"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If not found, search for any directory with the Maven executable (for snapshots)
|
|
||||||
if [ -z "$actualDistributionDir" ]; then
|
|
||||||
# enable globbing to iterate over items
|
|
||||||
set +f
|
|
||||||
for dir in "$TMP_DOWNLOAD_DIR"/*; do
|
|
||||||
if [ -d "$dir" ]; then
|
|
||||||
if [ -f "$dir/bin/$MVN_CMD" ]; then
|
|
||||||
actualDistributionDir="$(basename "$dir")"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
set -f
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$actualDistributionDir" ]; then
|
|
||||||
verbose "Contents of $TMP_DOWNLOAD_DIR:"
|
|
||||||
verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
|
|
||||||
die "Could not find Maven distribution directory in extracted archive"
|
|
||||||
fi
|
|
||||||
|
|
||||||
verbose "Found extracted Maven distribution directory: $actualDistributionDir"
|
|
||||||
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
|
|
||||||
mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
|
|
||||||
|
|
||||||
clean || :
|
|
||||||
exec_maven "$@"
|
|
||||||
34
pom.xml
34
pom.xml
@@ -25,40 +25,6 @@
|
|||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<version>3.2.5</version>
|
<version>3.2.5</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<groupId>org.jacoco</groupId>
|
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
|
||||||
<version>0.8.11</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<goals>
|
|
||||||
<goal>prepare-agent</goal>
|
|
||||||
<goal>report</goal>
|
|
||||||
<goal>check</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
<configuration>
|
|
||||||
<excludes>
|
|
||||||
<!-- UI classes require a display and cannot be unit-tested -->
|
|
||||||
<exclude>ovh/gasser/newshapes/App.class</exclude>
|
|
||||||
<exclude>ovh/gasser/newshapes/ui/**</exclude>
|
|
||||||
<exclude>ovh/gasser/newshapes/Selection.class</exclude>
|
|
||||||
</excludes>
|
|
||||||
<rules>
|
|
||||||
<rule>
|
|
||||||
<element>BUNDLE</element>
|
|
||||||
<limits>
|
|
||||||
<limit>
|
|
||||||
<counter>LINE</counter>
|
|
||||||
<value>COVEREDRATIO</value>
|
|
||||||
<minimum>0.50</minimum>
|
|
||||||
</limit>
|
|
||||||
</limits>
|
|
||||||
</rule>
|
|
||||||
</rules>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
|||||||
@@ -66,11 +66,11 @@ public class HTMLDraftman implements ShapeVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitTriangle(STriangle sTriangle) {
|
public void visitTriangle(STriangle sTriangle) {
|
||||||
htmlOutput.printf("<div class=\"triangle%d\"></div>\n", sTriangle.hashCode());
|
htmlOutput.printf("<div class=\"triangle%d\"></div>\n", this.hashCode());
|
||||||
var bounds = sTriangle.getBounds();
|
var bounds = sTriangle.getBounds();
|
||||||
ColorAttributes colAttrs = (ColorAttributes) sTriangle.getAttributes(ColorAttributes.ID);
|
ColorAttributes colAttrs = (ColorAttributes) sTriangle.getAttributes(ColorAttributes.ID);
|
||||||
String colorString = formatCSSColor(colAttrs.filledColor);
|
String colorString = formatCSSColor(colAttrs.filledColor);
|
||||||
StringJoiner joiner = new StringJoiner("\n", ".triangle%d{\n".formatted(sTriangle.hashCode()), "\n}\n");
|
StringJoiner joiner = new StringJoiner("\n", ".triangle%d{\n".formatted(this.hashCode()), "\n}\n");
|
||||||
joiner.add(" position: absolute;");
|
joiner.add(" position: absolute;");
|
||||||
joiner.add(" top: %dpx;".formatted(bounds.y));
|
joiner.add(" top: %dpx;".formatted(bounds.y));
|
||||||
joiner.add(" left: %dpx;".formatted(bounds.x));
|
joiner.add(" left: %dpx;".formatted(bounds.x));
|
||||||
|
|||||||
@@ -1,144 +0,0 @@
|
|||||||
package ovh.gasser.newshapes;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import ovh.gasser.newshapes.attributes.SelectionAttributes;
|
|
||||||
import ovh.gasser.newshapes.shapes.SCircle;
|
|
||||||
import ovh.gasser.newshapes.shapes.SRectangle;
|
|
||||||
import ovh.gasser.newshapes.shapes.STriangle;
|
|
||||||
import ovh.gasser.newshapes.ui.listeners.SelectionListener;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class SelectionTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testIsEmptyInitially() {
|
|
||||||
Selection selection = new Selection();
|
|
||||||
assertTrue(selection.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testAdd() {
|
|
||||||
Selection selection = new Selection();
|
|
||||||
SCircle circle = SCircle.create(0, 0, 50);
|
|
||||||
|
|
||||||
selection.add(circle);
|
|
||||||
|
|
||||||
assertFalse(selection.isEmpty());
|
|
||||||
assertEquals(1, selection.getSelectedShapes().size());
|
|
||||||
assertTrue(selection.getSelectedShapes().contains(circle));
|
|
||||||
|
|
||||||
// Verify shape is marked as selected
|
|
||||||
SelectionAttributes attrs = (SelectionAttributes) circle.getAttributes(SelectionAttributes.ID);
|
|
||||||
assertNotNull(attrs);
|
|
||||||
assertTrue(attrs.selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testAddAll() {
|
|
||||||
Selection selection = new Selection();
|
|
||||||
SCircle circle1 = SCircle.create(0, 0, 50);
|
|
||||||
SRectangle rect = SRectangle.create(10, 10, 100, 50);
|
|
||||||
STriangle triangle = STriangle.create(50, 50, 30, java.awt.Color.BLACK, java.awt.Color.BLACK);
|
|
||||||
|
|
||||||
List<ovh.gasser.newshapes.shapes.Shape> shapes = Arrays.asList(circle1, rect, triangle);
|
|
||||||
selection.addAll(shapes);
|
|
||||||
|
|
||||||
assertEquals(3, selection.getSelectedShapes().size());
|
|
||||||
assertTrue(selection.getSelectedShapes().contains(circle1));
|
|
||||||
assertTrue(selection.getSelectedShapes().contains(rect));
|
|
||||||
assertTrue(selection.getSelectedShapes().contains(triangle));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testClear() {
|
|
||||||
Selection selection = new Selection();
|
|
||||||
SCircle circle = SCircle.create(0, 0, 50);
|
|
||||||
SRectangle rect = SRectangle.create(10, 10, 100, 50);
|
|
||||||
|
|
||||||
selection.add(circle);
|
|
||||||
selection.add(rect);
|
|
||||||
|
|
||||||
assertFalse(selection.isEmpty());
|
|
||||||
assertEquals(2, selection.getSelectedShapes().size());
|
|
||||||
|
|
||||||
selection.clear();
|
|
||||||
|
|
||||||
assertTrue(selection.isEmpty());
|
|
||||||
assertEquals(0, selection.getSelectedShapes().size());
|
|
||||||
|
|
||||||
// Verify shapes are marked as unselected
|
|
||||||
SelectionAttributes circleAttrs = (SelectionAttributes) circle.getAttributes(SelectionAttributes.ID);
|
|
||||||
SelectionAttributes rectAttrs = (SelectionAttributes) rect.getAttributes(SelectionAttributes.ID);
|
|
||||||
|
|
||||||
assertNotNull(circleAttrs);
|
|
||||||
assertNotNull(rectAttrs);
|
|
||||||
assertFalse(circleAttrs.selected);
|
|
||||||
assertFalse(rectAttrs.selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetSelectedShapesReturnsCopy() {
|
|
||||||
Selection selection = new Selection();
|
|
||||||
SCircle circle = SCircle.create(0, 0, 50);
|
|
||||||
selection.add(circle);
|
|
||||||
|
|
||||||
List<ovh.gasser.newshapes.shapes.Shape> copy = selection.getSelectedShapes();
|
|
||||||
|
|
||||||
// Try to modify the copy
|
|
||||||
assertThrows(UnsupportedOperationException.class, () -> copy.add(SCircle.create(10, 10, 20)));
|
|
||||||
|
|
||||||
// Original should be unchanged
|
|
||||||
assertEquals(1, selection.getSelectedShapes().size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testAddListener() {
|
|
||||||
Selection selection = new Selection();
|
|
||||||
AtomicBoolean listenerNotified = new AtomicBoolean(false);
|
|
||||||
AtomicInteger notificationCount = new AtomicInteger(0);
|
|
||||||
|
|
||||||
SelectionListener listener = (selectedShapes) -> {
|
|
||||||
listenerNotified.set(true);
|
|
||||||
notificationCount.incrementAndGet();
|
|
||||||
};
|
|
||||||
|
|
||||||
selection.addListener(listener);
|
|
||||||
|
|
||||||
// Adding a shape should notify listener
|
|
||||||
SCircle circle = SCircle.create(0, 0, 50);
|
|
||||||
selection.add(circle);
|
|
||||||
|
|
||||||
assertTrue(listenerNotified.get());
|
|
||||||
assertEquals(1, notificationCount.get());
|
|
||||||
|
|
||||||
// Adding another shape should notify listener again
|
|
||||||
SRectangle rect = SRectangle.create(10, 10, 100, 50);
|
|
||||||
selection.add(rect);
|
|
||||||
|
|
||||||
assertEquals(2, notificationCount.get());
|
|
||||||
|
|
||||||
// Clearing should notify listener
|
|
||||||
selection.clear();
|
|
||||||
|
|
||||||
assertEquals(3, notificationCount.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testNullAddAllDoesNothing() {
|
|
||||||
Selection selection = new Selection();
|
|
||||||
|
|
||||||
// Should not throw
|
|
||||||
assertDoesNotThrow(() -> selection.addAll(null));
|
|
||||||
|
|
||||||
// Selection should still be empty
|
|
||||||
assertTrue(selection.isEmpty());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
223
src/test/java/ovh/gasser/newshapes/shapes/EdgeCaseTest.java
Normal file
223
src/test/java/ovh/gasser/newshapes/shapes/EdgeCaseTest.java
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
package ovh.gasser.newshapes.shapes;
|
||||||
|
|
||||||
|
import ovh.gasser.newshapes.App;
|
||||||
|
import ovh.gasser.newshapes.attributes.SelectionAttributes;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class EdgeCaseTest {
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// NegativeCoordinates
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class NegativeCoordinates {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rectangleAtNegativeCoordsShouldPreserveBounds() {
|
||||||
|
SRectangle rect = SRectangle.create(-10, -20, 50, 50);
|
||||||
|
Rectangle bounds = rect.getBounds();
|
||||||
|
assertEquals(-10, bounds.x, "x should be -10");
|
||||||
|
assertEquals(-20, bounds.y, "y should be -20");
|
||||||
|
assertEquals(50, bounds.width, "width should be 50");
|
||||||
|
assertEquals(50, bounds.height, "height should be 50");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void circleAtNegativeCoordsShouldPreserveBounds() {
|
||||||
|
SCircle circle = SCircle.create(-30, -40, 25);
|
||||||
|
Rectangle bounds = circle.getBounds();
|
||||||
|
assertEquals(-30, bounds.x, "x should be -30");
|
||||||
|
assertEquals(-40, bounds.y, "y should be -40");
|
||||||
|
assertEquals(50, bounds.width, "width should be radius*2 = 50");
|
||||||
|
assertEquals(50, bounds.height, "height should be radius*2 = 50");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void triangleAtNegativeCoordsShouldPreserveBounds() {
|
||||||
|
STriangle tri = STriangle.create(-5, -15, 60, Color.RED, Color.BLACK);
|
||||||
|
Rectangle bounds = tri.getBounds();
|
||||||
|
assertEquals(-5, bounds.x, "x should be -5");
|
||||||
|
assertEquals(-15, bounds.y, "y should be -15");
|
||||||
|
assertEquals(60, bounds.width, "width should be 60");
|
||||||
|
assertEquals(60, bounds.height, "height should be 60");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void translateToNegativePositionShouldUpdateBoundsCorrectly() {
|
||||||
|
SRectangle rect = SRectangle.create(10, 20, 80, 40);
|
||||||
|
rect.translate(-50, -70);
|
||||||
|
Rectangle bounds = rect.getBounds();
|
||||||
|
assertEquals(-40, bounds.x, "x after translate should be -40");
|
||||||
|
assertEquals(-50, bounds.y, "y after translate should be -50");
|
||||||
|
// dimensions must be unchanged
|
||||||
|
assertEquals(80, bounds.width, "width must remain 80 after translate");
|
||||||
|
assertEquals(40, bounds.height, "height must remain 40 after translate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// ZeroDimensions
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class ZeroDimensions {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rectangleWithZeroWidthShouldReturnZeroWidthFromFactory() {
|
||||||
|
SRectangle rect = SRectangle.create(0, 0, 0, 50);
|
||||||
|
assertEquals(0, rect.getBounds().width,
|
||||||
|
"factory should allow zero width without clamping");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rectangleWithZeroHeightShouldReturnZeroHeightFromFactory() {
|
||||||
|
SRectangle rect = SRectangle.create(0, 0, 50, 0);
|
||||||
|
assertEquals(0, rect.getBounds().height,
|
||||||
|
"factory should allow zero height without clamping");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void circleWithZeroRadiusShouldHaveZeroBoundsDimensions() {
|
||||||
|
SCircle circle = SCircle.create(5, 10, 0);
|
||||||
|
assertEquals(0, circle.getRadius(), "radius should be 0");
|
||||||
|
assertEquals(0, circle.getBounds().width, "bounds width should be 0 (radius*2)");
|
||||||
|
assertEquals(0, circle.getBounds().height, "bounds height should be 0 (radius*2)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// ResizeClamping
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class ResizeClamping {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rectangleResizedToNegativeShouldClampWidthToOne() {
|
||||||
|
// Start with a 10x10 rect and drag the E handle far to the left
|
||||||
|
SRectangle rect = SRectangle.create(0, 0, 10, 10);
|
||||||
|
rect.resize(ResizeHandle.E, -200, 0);
|
||||||
|
assertTrue(rect.getBounds().width >= 1,
|
||||||
|
"width must be clamped to a minimum of 1 after negative resize");
|
||||||
|
assertEquals(1, rect.getBounds().width,
|
||||||
|
"width should be exactly 1 after extreme negative resize on E handle");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rectangleResizedToNegativeShouldClampHeightToOne() {
|
||||||
|
SRectangle rect = SRectangle.create(0, 0, 10, 10);
|
||||||
|
rect.resize(ResizeHandle.S, 0, -200);
|
||||||
|
assertTrue(rect.getBounds().height >= 1,
|
||||||
|
"height must be clamped to a minimum of 1 after negative resize");
|
||||||
|
assertEquals(1, rect.getBounds().height,
|
||||||
|
"height should be exactly 1 after extreme negative resize on S handle");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void circleResizedToNegativeShouldClampToTwo() {
|
||||||
|
SCircle circle = SCircle.create(0, 0, 50);
|
||||||
|
// Drag E handle far to the left → shrink
|
||||||
|
circle.resize(ResizeHandle.E, -500, 0);
|
||||||
|
assertTrue(circle.getBounds().width >= 2,
|
||||||
|
"circle width must be clamped to a minimum of 2");
|
||||||
|
assertTrue(circle.getBounds().height >= 2,
|
||||||
|
"circle height must be clamped to a minimum of 2");
|
||||||
|
assertEquals(2, circle.getBounds().width,
|
||||||
|
"circle width should be exactly 2 after extreme negative resize");
|
||||||
|
assertEquals(2, circle.getBounds().height,
|
||||||
|
"circle height should be exactly 2 after extreme negative resize");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// NullHandling
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class NullHandling {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void textCreatedWithNullShouldReturnPlaceholder() {
|
||||||
|
SText text = SText.create(0, 0, null);
|
||||||
|
assertEquals(SText.PLACEHOLDER_TEXT, text.getText(),
|
||||||
|
"null input should be normalized to the placeholder text");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void textCreatedWithBlankStringShouldReturnPlaceholder() {
|
||||||
|
SText text = SText.create(0, 0, " ");
|
||||||
|
assertEquals(SText.PLACEHOLDER_TEXT, text.getText(),
|
||||||
|
"blank string input should be normalized to the placeholder text");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getAttributesWithUnknownKeyShouldReturnNull() {
|
||||||
|
SRectangle rect = SRectangle.create(0, 0, 50, 50);
|
||||||
|
assertNull(rect.getAttributes("NONEXISTENT"),
|
||||||
|
"getAttributes() with an unknown key must return null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void emptyCollectionCreationShouldNotThrow() {
|
||||||
|
assertDoesNotThrow(() -> SCollection.of(),
|
||||||
|
"SCollection.of() with no arguments must not throw");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void emptyCollectionGetBoundsShouldReturnWindowSize() {
|
||||||
|
SCollection collection = SCollection.of();
|
||||||
|
Rectangle bounds = collection.getBounds();
|
||||||
|
assertNotNull(bounds, "getBounds() on empty collection must not return null");
|
||||||
|
assertEquals(App.WIN_SIZE.width, bounds.width,
|
||||||
|
"empty collection bounds width should equal WIN_SIZE.width");
|
||||||
|
assertEquals(App.WIN_SIZE.height, bounds.height,
|
||||||
|
"empty collection bounds height should equal WIN_SIZE.height");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// LargeValues
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class LargeValues {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rectangleAtMaxIntHalfCoordsShouldNotThrow() {
|
||||||
|
int halfMax = Integer.MAX_VALUE / 2;
|
||||||
|
assertDoesNotThrow(() -> SRectangle.create(halfMax, halfMax, 100, 100),
|
||||||
|
"SRectangle.create() with very large coordinates must not throw");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rectangleAtMaxIntHalfCoordsPreservesBounds() {
|
||||||
|
int halfMax = Integer.MAX_VALUE / 2;
|
||||||
|
SRectangle rect = SRectangle.create(halfMax, halfMax, 100, 100);
|
||||||
|
assertEquals(halfMax, rect.getBounds().x, "x should be Integer.MAX_VALUE/2");
|
||||||
|
assertEquals(halfMax, rect.getBounds().y, "y should be Integer.MAX_VALUE/2");
|
||||||
|
assertEquals(100, rect.getBounds().width, "width should be 100");
|
||||||
|
assertEquals(100, rect.getBounds().height, "height should be 100");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void translateWithLargeValuesShouldNotThrow() {
|
||||||
|
SRectangle rect = SRectangle.create(0, 0, 50, 50);
|
||||||
|
// Integer overflow is a known platform behaviour; we only assert no exception
|
||||||
|
assertDoesNotThrow(() -> rect.translate(Integer.MAX_VALUE, Integer.MAX_VALUE),
|
||||||
|
"translate() with Integer.MAX_VALUE must not throw (overflow is acceptable)");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void translateWithLargeNegativeValuesShouldNotThrow() {
|
||||||
|
SRectangle rect = SRectangle.create(0, 0, 50, 50);
|
||||||
|
assertDoesNotThrow(() -> rect.translate(Integer.MIN_VALUE, Integer.MIN_VALUE),
|
||||||
|
"translate() with Integer.MIN_VALUE must not throw (overflow is acceptable)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
package ovh.gasser.newshapes.shapes;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import ovh.gasser.newshapes.App;
|
|
||||||
import ovh.gasser.newshapes.attributes.ColorAttributes;
|
|
||||||
import ovh.gasser.newshapes.attributes.SelectionAttributes;
|
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Rectangle;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class SCollectionTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testCreateWithShapes() {
|
|
||||||
SRectangle rect = SRectangle.create(10, 20, 100, 50);
|
|
||||||
SCircle circle = SCircle.create(50, 50, 25);
|
|
||||||
|
|
||||||
SCollection collection = SCollection.of(rect, circle);
|
|
||||||
|
|
||||||
assertNotNull(collection);
|
|
||||||
assertEquals(2, collection.stream().count());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testAdd() {
|
|
||||||
SCollection collection = SCollection.of();
|
|
||||||
SRectangle rect = SRectangle.create(10, 20, 100, 50);
|
|
||||||
|
|
||||||
collection.add(rect);
|
|
||||||
|
|
||||||
assertEquals(1, collection.stream().count());
|
|
||||||
assertSame(rect, collection.iterator().next());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testRemove() {
|
|
||||||
SRectangle rect = SRectangle.create(10, 20, 100, 50);
|
|
||||||
SCollection collection = SCollection.of(rect);
|
|
||||||
|
|
||||||
collection.remove(rect);
|
|
||||||
|
|
||||||
assertEquals(0, collection.stream().count());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testIterator() {
|
|
||||||
SRectangle rect1 = SRectangle.create(10, 20, 100, 50);
|
|
||||||
SRectangle rect2 = SRectangle.create(30, 40, 60, 70);
|
|
||||||
SCollection collection = SCollection.of(rect1, rect2);
|
|
||||||
|
|
||||||
Iterator<Shape> iterator = collection.iterator();
|
|
||||||
|
|
||||||
assertTrue(iterator.hasNext());
|
|
||||||
assertSame(rect1, iterator.next());
|
|
||||||
assertTrue(iterator.hasNext());
|
|
||||||
assertSame(rect2, iterator.next());
|
|
||||||
assertFalse(iterator.hasNext());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testStream() {
|
|
||||||
SRectangle rect1 = SRectangle.create(10, 20, 100, 50);
|
|
||||||
SRectangle rect2 = SRectangle.create(30, 40, 60, 70);
|
|
||||||
SCircle circle = SCircle.create(50, 50, 25);
|
|
||||||
SCollection collection = SCollection.of(rect1, rect2, circle);
|
|
||||||
|
|
||||||
List<Shape> shapes = collection.stream().collect(Collectors.toList());
|
|
||||||
|
|
||||||
assertEquals(3, shapes.size());
|
|
||||||
assertTrue(shapes.contains(rect1));
|
|
||||||
assertTrue(shapes.contains(rect2));
|
|
||||||
assertTrue(shapes.contains(circle));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetBoundsEmptyCollection() {
|
|
||||||
SCollection collection = SCollection.of();
|
|
||||||
|
|
||||||
Rectangle bounds = collection.getBounds();
|
|
||||||
|
|
||||||
assertEquals(App.WIN_SIZE.width, bounds.width);
|
|
||||||
assertEquals(App.WIN_SIZE.height, bounds.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetBoundsWithChildren() {
|
|
||||||
SRectangle rect1 = SRectangle.create(10, 10, 50, 50);
|
|
||||||
SRectangle rect2 = SRectangle.create(100, 100, 80, 40);
|
|
||||||
SCollection collection = SCollection.of(rect1, rect2);
|
|
||||||
|
|
||||||
Rectangle bounds = collection.getBounds();
|
|
||||||
|
|
||||||
// Union should cover from (10,10) to (180,140)
|
|
||||||
assertEquals(10, bounds.x);
|
|
||||||
assertEquals(10, bounds.y);
|
|
||||||
assertEquals(170, bounds.width); // 100+80-10 = 170
|
|
||||||
assertEquals(130, bounds.height); // 100+40-10 = 130
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testTranslate() {
|
|
||||||
SRectangle rect = SRectangle.create(10, 20, 100, 50);
|
|
||||||
SCollection collection = SCollection.of(rect);
|
|
||||||
|
|
||||||
collection.translate(5, 10);
|
|
||||||
|
|
||||||
Rectangle bounds = rect.getBounds();
|
|
||||||
assertEquals(15, bounds.x);
|
|
||||||
assertEquals(30, bounds.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testClone() {
|
|
||||||
SRectangle rect = SRectangle.create(10, 20, 100, 50);
|
|
||||||
SCollection original = SCollection.of(rect);
|
|
||||||
|
|
||||||
SCollection cloned = (SCollection) original.clone();
|
|
||||||
|
|
||||||
assertNotSame(original, cloned);
|
|
||||||
assertEquals(original.stream().count(), cloned.stream().count());
|
|
||||||
|
|
||||||
// Verify SelectionAttributes is added to clone
|
|
||||||
assertNotNull(cloned.getAttributes(SelectionAttributes.ID));
|
|
||||||
|
|
||||||
// Verify deep copy - modifying clone doesn't affect original
|
|
||||||
Iterator<Shape> clonedIterator = cloned.iterator();
|
|
||||||
Shape clonedChild = clonedIterator.next();
|
|
||||||
clonedChild.translate(100, 100);
|
|
||||||
|
|
||||||
Rectangle originalBounds = rect.getBounds();
|
|
||||||
assertEquals(10, originalBounds.x);
|
|
||||||
assertEquals(20, originalBounds.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testToString() {
|
|
||||||
SCollection collection = SCollection.of();
|
|
||||||
|
|
||||||
String str = collection.toString();
|
|
||||||
|
|
||||||
assertTrue(str.contains("SCollection"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testAddAttributesPropagatesToChildren() {
|
|
||||||
SRectangle rect = SRectangle.create(10, 20, 100, 50);
|
|
||||||
SCollection collection = SCollection.of(rect);
|
|
||||||
|
|
||||||
ColorAttributes attrs = new ColorAttributes(true, true, Color.RED, Color.BLUE);
|
|
||||||
collection.addAttributes(attrs);
|
|
||||||
|
|
||||||
ColorAttributes childAttrs = (ColorAttributes) rect.getAttributes(ColorAttributes.ID);
|
|
||||||
|
|
||||||
assertNotNull(childAttrs);
|
|
||||||
assertEquals(Color.RED, childAttrs.filledColor);
|
|
||||||
assertEquals(Color.BLUE, childAttrs.strokedColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetAttributesReturnsChildColor() {
|
|
||||||
SRectangle rect = SRectangle.create(10, 20, 100, 50, Color.GREEN);
|
|
||||||
SCircle circle = SCircle.create(50, 50, 25);
|
|
||||||
SCollection collection = SCollection.of(rect, circle);
|
|
||||||
|
|
||||||
ColorAttributes attrs = (ColorAttributes) collection.getAttributes(ColorAttributes.ID);
|
|
||||||
|
|
||||||
assertNotNull(attrs);
|
|
||||||
// First child's color should be returned (strokedColor for SRectangle)
|
|
||||||
assertEquals(Color.GREEN, attrs.strokedColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package ovh.gasser.newshapes.util;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class StreamableTest {
|
|
||||||
|
|
||||||
private static class TestStreamable implements Streamable<String> {
|
|
||||||
private final List<String> elements;
|
|
||||||
|
|
||||||
TestStreamable(List<String> elements) {
|
|
||||||
this.elements = new ArrayList<>(elements);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public java.util.Iterator<String> iterator() {
|
|
||||||
return elements.iterator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testStreamReturnsStreamOfElements() {
|
|
||||||
List<String> testData = Arrays.asList("a", "b", "c");
|
|
||||||
Streamable<String> streamable = new TestStreamable(testData);
|
|
||||||
|
|
||||||
Stream<String> result = streamable.stream();
|
|
||||||
|
|
||||||
assertNotNull(result);
|
|
||||||
assertEquals(testData, result.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testStreamEmptyCollection() {
|
|
||||||
List<String> emptyData = new ArrayList<>();
|
|
||||||
Streamable<String> streamable = new TestStreamable(emptyData);
|
|
||||||
|
|
||||||
Stream<String> result = streamable.stream();
|
|
||||||
|
|
||||||
assertNotNull(result);
|
|
||||||
assertTrue(result.toList().isEmpty());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user