From 0375c91027cfa293b2c2f2010ff5f4ed1399fa0b Mon Sep 17 00:00:00 2001 From: Christian Gram Kalhauge <chrg@dtu.dk> Date: Tue, 9 Apr 2024 11:18:58 +0200 Subject: [PATCH] Before refactor --- src/main/java/jpamb/Runtime.java | 85 ++++++++++++++-------- src/main/java/jpamb/cases/Loops.java | 41 +++++++++++ src/main/java/jpamb/cases/Simple.java | 9 +-- src/main/java/jpamb/utils/Case.java | 1 + src/main/java/jpamb/utils/CaseContent.java | 12 ++- src/main/java/jpamb/utils/Cases.java | 9 +++ src/main/java/jpamb/utils/Tag.java | 13 ++-- 7 files changed, 126 insertions(+), 44 deletions(-) create mode 100644 src/main/java/jpamb/cases/Loops.java create mode 100644 src/main/java/jpamb/utils/Cases.java diff --git a/src/main/java/jpamb/Runtime.java b/src/main/java/jpamb/Runtime.java index 130f566..d895fa9 100644 --- a/src/main/java/jpamb/Runtime.java +++ b/src/main/java/jpamb/Runtime.java @@ -1,49 +1,72 @@ package jpamb; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; +import java.lang.reflect.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; -import jpamb.utils.Case; -import jpamb.utils.CaseContent; +import jpamb.utils.*; +import jpamb.cases.*; public class Runtime { - public static void main(String[] args) throws ClassNotFoundException { - var cls = Class.forName("jpamb.cases.Simple"); - for (Method m : cls.getMethods()) { + public static Case[] cases(Method m) { + var cases = m.getAnnotation(Cases.class); + if (cases == null) { var c = m.getAnnotation(Case.class); if (c == null) - continue; + return new Case[] {}; + return new Case[] { c }; + } else { + return cases.value(); + } + } - CaseContent content; - content = CaseContent.parse(c.value()); + public static void main(String[] args) throws ClassNotFoundException, InterruptedException { + var mths = Stream.of(Simple.class, Loops.class).flatMap(c -> Stream.of(c.getMethods())).toList(); + for (Method m : mths) { + for (Case c : cases(m)) { + CaseContent content = CaseContent.parse(c.value()); - if (!Modifier.isStatic(m.getModifiers())) { - System.out.println("Method is not static"); - continue; - } + if (!Modifier.isStatic(m.getModifiers())) { + System.out.println("Method is not static"); + continue; + } + + String id = m.getDeclaringClass().getName() + "." + m.getName() + content + ":"; + System.out.printf("%-80s", id); + System.out.flush(); + final AtomicReference<Throwable> atom = new AtomicReference<>(); + Thread t = new Thread(() -> { + try { + m.invoke(null, content.params()); + } catch (InvocationTargetException e) { + atom.set(e.getCause()); + } catch (IllegalAccessException e) { + atom.set(e); + } + }); + t.start(); + t.join(100); + + if (t.isAlive()) + t.stop(); - String message = null; - try { - m.invoke(null, content.params()); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - Class<? extends Throwable> clazz = e.getCause().getClass(); - if (content.result().expectThrows(clazz)) { + Throwable error = atom.get(); + if (t.isAlive() && error == null) { + error = new TimeoutException(); + } + + String message; + if (error == null) { + message = "did not produce error"; + } else if (content.result().expectThrows(error.getClass())) { message = "success"; } else { - message = e.getCause().toString(); + message = error.toString(); } + System.out.printf("%s%n", message); } - - if (message == null) { - message = "did not produce error"; - } - - System.out.println(cls.getName() + "." + m.getName() + content + ": " + message); - } } } diff --git a/src/main/java/jpamb/cases/Loops.java b/src/main/java/jpamb/cases/Loops.java new file mode 100644 index 0000000..423d026 --- /dev/null +++ b/src/main/java/jpamb/cases/Loops.java @@ -0,0 +1,41 @@ +package jpamb.cases; + +import jpamb.utils.*; +import static jpamb.utils.Tag.TagType.*; + +public class Loops { + + @Case("() -> *") + @Tag({ LOOP }) + public static void forever() { + while (true) { + } + } + + @Case("() -> *") + @Tag({ LOOP }) + public static void neverAsserts() { + int i = 1; + while (i > 0) { + } + assert false; + } + + @Case("() -> *") + @Tag({ LOOP }) + public static int neverDivides() { + int i = 1; + while (i > 0) { + } + return 0 / 0; + } + + @Case("() -> assertion error") + @Tag({ INTEGER_OVERFLOW }) + public static void terminates() { + short i = 0; + while (i++ != 0) { + } + assert false; + } +} diff --git a/src/main/java/jpamb/cases/Simple.java b/src/main/java/jpamb/cases/Simple.java index f0742ca..fe2fb8c 100644 --- a/src/main/java/jpamb/cases/Simple.java +++ b/src/main/java/jpamb/cases/Simple.java @@ -39,12 +39,11 @@ public class Simple { return a / b; } + @Case("(false) -> assertion error") @Case("(true) -> divide by zero") - public static int divideByZeroIf(boolean b) { - if (b) { - return 1 / 0; - } - return 0; + public static int multiError(boolean b) { + assert b; + return 1 / 0; } } diff --git a/src/main/java/jpamb/utils/Case.java b/src/main/java/jpamb/utils/Case.java index e610bad..d834145 100644 --- a/src/main/java/jpamb/utils/Case.java +++ b/src/main/java/jpamb/utils/Case.java @@ -2,6 +2,7 @@ package jpamb.utils; import java.lang.annotation.*; +@Repeatable(Cases.class) @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Case { diff --git a/src/main/java/jpamb/utils/CaseContent.java b/src/main/java/jpamb/utils/CaseContent.java index 4a59beb..39b5299 100644 --- a/src/main/java/jpamb/utils/CaseContent.java +++ b/src/main/java/jpamb/utils/CaseContent.java @@ -5,6 +5,7 @@ import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Scanner; +import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -51,10 +52,13 @@ public record CaseContent( public static enum ResultType { DIVIDE_BY_ZERO, - ASSERTION_ERROR; + ASSERTION_ERROR, + NON_TERMINATION; public static ResultType parse(String string) { - if (string.equals("assertion error")) { + if (string.equals("*")) { + return NON_TERMINATION; + } else if (string.equals("assertion error")) { return ASSERTION_ERROR; } else if (string.equals("divide by zero")) { return DIVIDE_BY_ZERO; @@ -69,6 +73,8 @@ public record CaseContent( return "divide by zero"; case ASSERTION_ERROR: return "assertion error"; + case NON_TERMINATION: + return "*"; default: throw new RuntimeException("Unexpected"); } @@ -80,6 +86,8 @@ public record CaseContent( return clazz.equals(ArithmeticException.class); case ASSERTION_ERROR: return clazz.equals(AssertionError.class); + case NON_TERMINATION: + return clazz.equals(TimeoutException.class); default: throw new RuntimeException("Unexpected"); } diff --git a/src/main/java/jpamb/utils/Cases.java b/src/main/java/jpamb/utils/Cases.java new file mode 100644 index 0000000..7b895e3 --- /dev/null +++ b/src/main/java/jpamb/utils/Cases.java @@ -0,0 +1,9 @@ +package jpamb.utils; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Cases { + Case[] value(); +} diff --git a/src/main/java/jpamb/utils/Tag.java b/src/main/java/jpamb/utils/Tag.java index 9c0e093..139449f 100644 --- a/src/main/java/jpamb/utils/Tag.java +++ b/src/main/java/jpamb/utils/Tag.java @@ -6,11 +6,12 @@ import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Tag { - TagType[] value(); + TagType[] value(); - public static enum TagType { - CONDITIONAL, - LOOP, - CALL - } + public static enum TagType { + CONDITIONAL, + LOOP, + INTEGER_OVERFLOW, + CALL + } } -- GitLab