From cb4093b68018c45f38d62d8411bf10e2a5df3d9c Mon Sep 17 00:00:00 2001 From: Morten Haraldsen Date: Thu, 5 Dec 2024 11:23:00 +0100 Subject: [PATCH] Version 3.0 (#9) * Default the output to use only ASCII by default * Add support for all functional interfaces in Java. * Cleanup formatting configuration and output formatting * Update test/build dependencies --------- Co-authored-by: Morten Haraldsen --- pom.xml | 38 +- src/main/java/com/ethlo/ascii/TableTheme.java | 88 ++- src/main/java/com/ethlo/time/Chronograph.java | 611 +++++++++++++++++- .../java/com/ethlo/time/OutputConfig.java | 20 - .../java/com/ethlo/time/OutputFormatter.java | 27 + src/main/java/com/ethlo/time/ReportUtil.java | 26 +- ...{Report.java => TableOutputformatter.java} | 174 +++-- .../java/com/ethlo/time/ChronographTest.java | 21 +- .../com/ethlo/time/ChronographTimedTest.java | 378 +++++++++++ .../java/com/ethlo/util/DurationUtilTest.java | 2 +- .../com/ethlo/util/ListPerformanceTest.java | 43 +- .../java/com/ethlo/util/LongListTest.java | 15 - 12 files changed, 1189 insertions(+), 254 deletions(-) create mode 100644 src/main/java/com/ethlo/time/OutputFormatter.java rename src/main/java/com/ethlo/time/{Report.java => TableOutputformatter.java} (66%) create mode 100644 src/test/java/com/ethlo/time/ChronographTimedTest.java diff --git a/pom.xml b/pom.xml index 57c536e..d8002e8 100755 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ chronograph Chronograph Easy-to-use stopwatch for task performance statistics - 2.0.0 + 3.0.0 Apache License, Version 2.0 @@ -26,7 +26,7 @@ maven-compiler-plugin - 3.6.1 + 3.13.0 17 17 @@ -36,7 +36,7 @@ org.jacoco jacoco-maven-plugin - 0.8.11 + 0.8.12 prepare-agent @@ -54,7 +54,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + 3.5.0 **/*BenchmarkTest.java @@ -64,7 +64,7 @@ org.apache.maven.plugins maven-source-plugin - 3.0.1 + 3.3.1 attach-sources @@ -77,7 +77,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.3.1 + 3.6.3 attach-javadocs @@ -93,12 +93,12 @@ org.codehaus.mojo versions-maven-plugin - 2.8.1 + 2.16.2 org.sonatype.plugins nexus-staging-maven-plugin - 1.6.13 + 1.6.14 true sonatype-nexus-staging @@ -109,7 +109,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.2.1 sign-artifacts @@ -126,7 +126,7 @@ org.codehaus.mojo license-maven-plugin - 2.0.0 + 2.2.0 false @@ -165,7 +165,7 @@ org.apache.maven.wagon wagon-ssh - 2.12 + 3.5.3 @@ -177,7 +177,7 @@ - https://github.com:ethlo/chronograph + https://github.com/ethlo/chronograph scm:git:git@github.com:ethlo/chronograph.git scm:git:git@github.com:ethlo/chronograph.git @@ -189,21 +189,25 @@ org.junit.jupiter junit-jupiter-engine - 5.9.0 + 5.11.0 + test + + + org.mockito + mockito-core + 5.14.2 test - org.slf4j slf4j-api - 1.8.0-beta4 + 2.0.12 test - org.assertj assertj-core - 3.23.1 + 3.25.3 test diff --git a/src/main/java/com/ethlo/ascii/TableTheme.java b/src/main/java/com/ethlo/ascii/TableTheme.java index eaf4c62..17de799 100644 --- a/src/main/java/com/ethlo/ascii/TableTheme.java +++ b/src/main/java/com/ethlo/ascii/TableTheme.java @@ -24,20 +24,25 @@ public class TableTheme { // Inspiration: https://www.compart.com/en/unicode/search?q=Box+Drawings#characters - public static final TableTheme DEFAULT = TableTheme.builder().build(); - - public static final TableTheme TSV = TableTheme.builder() - .leftTop("") - .rightTop("") - .bottomCross("") - .topCross("") - .rightBottom("") - .leftBottom("") - .verticalSeparator("") - .horizontalSeparator("\t") + public static final TableTheme ASCII = TableTheme.builder().name("ASCII").build(); + + public static final TableTheme SINGLE = TableTheme.builder() + .name("Single") + .cross("┼") + .rightCross("┤") + .leftCross("├") + .topCross("┬") + .bottomCross("┴") + .leftTop("┌") + .rightTop("┐") + .leftBottom("└") + .rightBottom("┘") + .verticalSeparator("─") + .horizontalSeparator("│") .build(); public static final TableTheme DOUBLE = TableTheme.builder() + .name("Double") .cross("╬") .rightCross("╣") .leftCross("╠") @@ -50,37 +55,35 @@ public class TableTheme .verticalSeparator("═") .horizontalSeparator("║") .build(); - - public static final TableTheme ROUNDED = DEFAULT.begin() - .leftTop("╭") - .rightTop("╮") - .leftBottom("╰") - .rightBottom("╯") - .build(); - public static final TableTheme RED_HERRING = TableTheme.builder() + .name("Red Herring") .stringColor(AnsiColor.BRIGHT_WHITE) .numericColor(AnsiColor.GREEN) .verticalSpacerColor(AnsiColor.RED) .horizontalSpacerColor(AnsiColor.RED) - .cellBackground(AnsiBackgroundColor.BLACK) .build(); - public static final TableTheme MINIMAL = TableTheme.builder() + .name("Minimal") .stringColor(AnsiColor.GRAY) .numericColor(AnsiColor.GREEN) .horizontalSeparator(" ") .verticalSpacerColor(AnsiColor.GRAY) .horizontalSpacerColor(AnsiColor.GRAY) - .cellBackground(AnsiBackgroundColor.BLACK) .build(); - public static final TableTheme COMPACT = TableTheme.builder() + .name("Compact") .verticalSeparator("") .horizontalSeparator("") .padding(" ") .build(); - + public static final TableTheme DEFAULT = ASCII; + public static final TableTheme ROUNDED = SINGLE.begin() + .name("Rounded") + .leftTop("╭") + .rightTop("╮") + .leftBottom("╰") + .rightBottom("╯") + .build(); private final AnsiColor stringColor; private final AnsiColor numericColor; private final AnsiColor horizontalSpacerColor; @@ -99,9 +102,11 @@ public class TableTheme private final String rightBottom; private final String topCross; private final String bottomCross; + private final String name; private TableTheme(Builder builder) { + this.name = builder.name; this.stringColor = builder.stringColor; this.numericColor = builder.numericColor; this.horizontalSpacerColor = builder.horizontalSpacerColor; @@ -129,6 +134,7 @@ public static Builder builder() public Builder begin() { final Builder builder = new Builder(); + builder.name = this.name; builder.stringColor = this.stringColor; builder.numericColor = this.numericColor; builder.horizontalSpacerColor = this.horizontalSpacerColor; @@ -234,25 +240,31 @@ public String getRightBottom() return rightBottom; } + public String getName() + { + return name; + } + public static final class Builder { - public String topCross = "┬"; - public String bottomCross = "┴"; + public String topCross = "+"; + public String bottomCross = "+"; private AnsiColor stringColor = AnsiColor.NONE; private AnsiColor numericColor = AnsiColor.NONE; private AnsiColor horizontalSpacerColor = AnsiColor.NONE; private AnsiColor verticalSpacerColor = AnsiColor.NONE; private AnsiBackgroundColor cellBackground = AnsiBackgroundColor.NONE; - private String horizontalSeparator = "│"; - private String verticalSeparator = "─"; + private String horizontalSeparator = "|"; + private String verticalSeparator = "-"; private String padding = " "; - private String cross = "┼"; - private String leftCross = "├"; - private String rightCross = "┤"; - private String leftTop = "┌"; - private String rightTop = "┐"; - private String leftBottom = "└"; - private String rightBottom = "┘"; + private String cross = "+"; + private String leftCross = "+"; + private String rightCross = "+"; + private String leftTop = "+"; + private String rightTop = "+"; + private String leftBottom = "+"; + private String rightBottom = "+"; + private String name; private Builder() { @@ -364,5 +376,11 @@ public TableTheme build() { return new TableTheme(this); } + + public Builder name(String name) + { + this.name = name; + return this; + } } } diff --git a/src/main/java/com/ethlo/time/Chronograph.java b/src/main/java/com/ethlo/time/Chronograph.java index 28069de..22843ff 100644 --- a/src/main/java/com/ethlo/time/Chronograph.java +++ b/src/main/java/com/ethlo/time/Chronograph.java @@ -27,39 +27,82 @@ import java.util.Optional; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.BinaryOperator; +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; +import java.util.function.DoubleBinaryOperator; +import java.util.function.DoubleConsumer; +import java.util.function.DoubleFunction; +import java.util.function.DoublePredicate; +import java.util.function.DoubleSupplier; +import java.util.function.DoubleToIntFunction; +import java.util.function.DoubleToLongFunction; +import java.util.function.DoubleUnaryOperator; import java.util.function.Function; +import java.util.function.IntBinaryOperator; +import java.util.function.IntConsumer; +import java.util.function.IntFunction; +import java.util.function.IntPredicate; +import java.util.function.IntSupplier; +import java.util.function.IntToDoubleFunction; +import java.util.function.IntToLongFunction; +import java.util.function.IntUnaryOperator; +import java.util.function.LongBinaryOperator; +import java.util.function.LongConsumer; +import java.util.function.LongFunction; +import java.util.function.LongPredicate; +import java.util.function.LongSupplier; +import java.util.function.LongToDoubleFunction; +import java.util.function.LongToIntFunction; +import java.util.function.LongUnaryOperator; +import java.util.function.ObjDoubleConsumer; +import java.util.function.ObjIntConsumer; +import java.util.function.ObjLongConsumer; +import java.util.function.Predicate; import java.util.function.Supplier; +import java.util.function.ToDoubleBiFunction; +import java.util.function.ToDoubleFunction; +import java.util.function.ToIntBiFunction; +import java.util.function.ToIntFunction; +import java.util.function.ToLongBiFunction; +import java.util.function.ToLongFunction; +import java.util.function.UnaryOperator; import com.ethlo.ascii.TableTheme; +/** + * A utility for tracking and timing tasks with high precision. + * Supports capturing execution statistics for various task types. + */ public class Chronograph { - private static TableTheme theme = TableTheme.DEFAULT; - private static OutputConfig outputConfig = OutputConfig.DEFAULT; - private final ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1); private final Map taskInfos; private final CaptureConfig captureConfig; private final String name; - public Chronograph(final String name) - { - this(name, CaptureConfig.DEFAULT); - } - public Chronograph(final CaptureConfig captureConfig) + private Chronograph(final String name) { - this("", captureConfig); + this(name, CaptureConfig.DEFAULT); } - public Chronograph(final String name, final CaptureConfig captureConfig) + private Chronograph(final String name, final CaptureConfig captureConfig) { this.taskInfos = new LinkedHashMap<>(); this.name = name; this.captureConfig = captureConfig; } + /** + * Creates a new Chronograph instance without a name. + * + * @return a new Chronograph instance. + */ public static Chronograph create() { return new Chronograph(""); @@ -80,7 +123,7 @@ public static Chronograph create(String name, final CaptureConfig captureConfig) return new Chronograph(name, captureConfig); } - public void timed(final String taskName, final Runnable task) + public void time(final String taskName, final Runnable task) { try { @@ -92,7 +135,7 @@ public void timed(final String taskName, final Runnable task) } } - public R timedFunction(final String taskName, final Function task, T input) + public R time(final String taskName, final Function task, T input) { try { @@ -104,7 +147,7 @@ public R timedFunction(final String taskName, final Function task, } } - public R timedSupplier(final String taskName, final Supplier task) + public R time(final String taskName, final Supplier task) { try { @@ -116,14 +159,552 @@ public R timedSupplier(final String taskName, final Supplier task) } } + public void time(final String taskName, final BiConsumer task, T t, U u) + { + try + { + start(taskName); + task.accept(t, u); + } finally + { + stop(taskName); + } + } + + public R time(final String taskName, final BiFunction task, T t, U u) + { + try + { + start(taskName); + return task.apply(t, u); + } finally + { + stop(taskName); + } + } + + public T time(final String taskName, final BinaryOperator task, T t1, T t2) + { + try + { + start(taskName); + return task.apply(t1, t2); + } finally + { + stop(taskName); + } + } + + public boolean time(final String taskName, final BiPredicate task, T t, U u) + { + try + { + start(taskName); + return task.test(t, u); + } finally + { + stop(taskName); + } + } + + public boolean time(final String taskName, final BooleanSupplier task) + { + try + { + start(taskName); + return task.getAsBoolean(); + } finally + { + stop(taskName); + } + } + + public void time(final String taskName, final Consumer task, T t) + { + try + { + start(taskName); + task.accept(t); + } finally + { + stop(taskName); + } + } + + public double time(final String taskName, final DoubleBinaryOperator task, double d1, double d2) + { + try + { + start(taskName); + return task.applyAsDouble(d1, d2); + } finally + { + stop(taskName); + } + } + + public void time(final String taskName, final DoubleConsumer task, double d) + { + try + { + start(taskName); + task.accept(d); + } finally + { + stop(taskName); + } + } + + public R time(final String taskName, final DoubleFunction task, double d) + { + try + { + start(taskName); + return task.apply(d); + } finally + { + stop(taskName); + } + } + + public boolean time(final String taskName, final DoublePredicate task, double d) + { + try + { + start(taskName); + return task.test(d); + } finally + { + stop(taskName); + } + } + + public double time(final String taskName, final DoubleSupplier task) + { + try + { + start(taskName); + return task.getAsDouble(); + } finally + { + stop(taskName); + } + } + + public int time(final String taskName, final DoubleToIntFunction task, double d) + { + try + { + start(taskName); + return task.applyAsInt(d); + } finally + { + stop(taskName); + } + } + + public long time(final String taskName, final DoubleToLongFunction task, double d) + { + try + { + start(taskName); + return task.applyAsLong(d); + } finally + { + stop(taskName); + } + } + + public double time(final String taskName, final DoubleUnaryOperator task, double d) + { + try + { + start(taskName); + return task.applyAsDouble(d); + } finally + { + stop(taskName); + } + } + + public int time(final String taskName, final IntBinaryOperator task, int i1, int i2) + { + try + { + start(taskName); + return task.applyAsInt(i1, i2); + } finally + { + stop(taskName); + } + } + + public void time(final String taskName, final IntConsumer task, int i) + { + try + { + start(taskName); + task.accept(i); + } finally + { + stop(taskName); + } + } + + public R time(final String taskName, final IntFunction task, int i) + { + try + { + start(taskName); + return task.apply(i); + } finally + { + stop(taskName); + } + } + + public boolean time(final String taskName, final IntPredicate task, int i) + { + try + { + start(taskName); + return task.test(i); + } finally + { + stop(taskName); + } + } + + public int time(final String taskName, final IntSupplier task) + { + try + { + start(taskName); + return task.getAsInt(); + } finally + { + stop(taskName); + } + } + + public double time(final String taskName, final IntToDoubleFunction task, int i) + { + try + { + start(taskName); + return task.applyAsDouble(i); + } finally + { + stop(taskName); + } + } + + public long time(final String taskName, final IntToLongFunction task, int i) + { + try + { + start(taskName); + return task.applyAsLong(i); + } finally + { + stop(taskName); + } + } + + public int time(final String taskName, final IntUnaryOperator task, int i) + { + try + { + start(taskName); + return task.applyAsInt(i); + } finally + { + stop(taskName); + } + } + + public long time(final String taskName, final LongBinaryOperator task, long l1, long l2) + { + try + { + start(taskName); + return task.applyAsLong(l1, l2); + } finally + { + stop(taskName); + } + } + + public void time(final String taskName, final LongConsumer task, long l) + { + try + { + start(taskName); + task.accept(l); + } finally + { + stop(taskName); + } + } + + public R time(final String taskName, final LongFunction task, long l) + { + try + { + start(taskName); + return task.apply(l); + } finally + { + stop(taskName); + } + } + + public boolean time(final String taskName, final LongPredicate task, long l) + { + try + { + start(taskName); + return task.test(l); + } finally + { + stop(taskName); + } + } + + public long time(final String taskName, final LongSupplier task) + { + try + { + start(taskName); + return task.getAsLong(); + } finally + { + stop(taskName); + } + } + + public double time(final String taskName, final LongToDoubleFunction task, long l) + { + try + { + start(taskName); + return task.applyAsDouble(l); + } finally + { + stop(taskName); + } + } + + public int time(final String taskName, final LongToIntFunction task, long l) + { + try + { + start(taskName); + return task.applyAsInt(l); + } finally + { + stop(taskName); + } + } + + public long time(final String taskName, final LongUnaryOperator task, long l) + { + try + { + start(taskName); + return task.applyAsLong(l); + } finally + { + stop(taskName); + } + } + + public void time(final String taskName, final ObjDoubleConsumer task, T t, double d) + { + try + { + start(taskName); + task.accept(t, d); + } finally + { + stop(taskName); + } + } + + public void time(final String taskName, final ObjIntConsumer task, T t, int i) + { + try + { + start(taskName); + task.accept(t, i); + } finally + { + stop(taskName); + } + } + + public void time(final String taskName, final ObjLongConsumer task, T t, long l) + { + try + { + start(taskName); + task.accept(t, l); + } finally + { + stop(taskName); + } + } + + public double time(final String taskName, final ToDoubleBiFunction task, T t, U u) + { + try + { + start(taskName); + return task.applyAsDouble(t, u); + } finally + { + stop(taskName); + } + } + + public double time(final String taskName, final ToDoubleFunction task, T t) + { + try + { + start(taskName); + return task.applyAsDouble(t); + } finally + { + stop(taskName); + } + } + + public int time(final String taskName, final ToIntBiFunction task, T t, U u) + { + try + { + start(taskName); + return task.applyAsInt(t, u); + } finally + { + stop(taskName); + } + } + + public int time(final String taskName, final ToIntFunction task, T t) + { + try + { + start(taskName); + return task.applyAsInt(t); + } finally + { + stop(taskName); + } + } + + public long time(final String taskName, final ToLongBiFunction task, T t, U u) + { + try + { + start(taskName); + return task.applyAsLong(t, u); + } finally + { + stop(taskName); + } + } + + public long time(final String taskName, final ToLongFunction task, T t) + { + try + { + start(taskName); + return task.applyAsLong(t); + } finally + { + stop(taskName); + } + } + + public T time(final String taskName, final UnaryOperator task, T t) + { + try + { + start(taskName); + return task.apply(t); + } finally + { + stop(taskName); + } + } + + public boolean time(String taskName, Predicate predicate, T t) + { + try + { + start(taskName); + return predicate.test(t); + } finally + { + stop(taskName); + } + } + + /// LEGACY + + /** + * Use {@link #time(String, Supplier)}() + */ + @Deprecated + public T timedSupplier(String taskName, Supplier supplier) + { + return time(taskName, supplier); + } + + /** + * Use {@link #time(String, Runnable)}() + */ + @Deprecated + public void timed(String taskName, Runnable runnable) + { + time(taskName, runnable); + } + + /** + * Use {@link #time(String, Function, Object)} + */ + @Deprecated + public R timedFunction(String taskName, Function runnable, T input) + { + return time(taskName, runnable, input); + } + + /// + public String prettyPrint(final String title) { - return Report.prettyPrint(this.getTaskData(), outputConfig.title(title), theme); + return prettyPrint(OutputConfig.DEFAULT.title(title), TableTheme.DEFAULT); } public String prettyPrint() { - return Report.prettyPrint(this.getTaskData(), outputConfig, theme); + return prettyPrint(OutputConfig.DEFAULT, TableTheme.DEFAULT); + } + + public String prettyPrint(OutputConfig outputConfig, TableTheme tableTheme) + { + return new TableOutputformatter(tableTheme, outputConfig).format(this.getTaskData()); + } + + public String prettyPrint(OutputConfig outputConfig) + { + return prettyPrint(outputConfig, TableTheme.DEFAULT); + } + + public String prettyPrint(TableTheme tableTheme) + { + return prettyPrint(OutputConfig.DEFAULT, tableTheme); } public void start(String task) diff --git a/src/main/java/com/ethlo/time/OutputConfig.java b/src/main/java/com/ethlo/time/OutputConfig.java index ea9e35a..06ea155 100644 --- a/src/main/java/com/ethlo/time/OutputConfig.java +++ b/src/main/java/com/ethlo/time/OutputConfig.java @@ -61,7 +61,6 @@ public class OutputConfig private boolean total; private boolean percentage; private boolean benchmarkMode = false; - private boolean formatting = true; public OutputConfig() { @@ -81,7 +80,6 @@ private OutputConfig(Builder builder) this.total = builder.total; this.percentage = builder.percentage; this.benchmarkMode = builder.benchmarkMode; - this.formatting = builder.formatting; } @@ -140,11 +138,6 @@ public boolean benchmarkMode() return benchmarkMode; } - public boolean formatting() - { - return formatting; - } - public OutputConfig title(final String title) { return new OutputConfig(new Builder(this).title(title)); @@ -200,11 +193,6 @@ public OutputConfig benchmarkMode(final boolean b) return new OutputConfig(new Builder(this).benchmarkMode(b)); } - public OutputConfig formatting(final boolean b) - { - return new OutputConfig(new Builder(this).formatting(b)); - } - public static class Builder { private String title; @@ -218,7 +206,6 @@ public static class Builder private boolean total; private boolean percentage; private boolean benchmarkMode; - private boolean formatting; private Builder(OutputConfig config) { @@ -233,7 +220,6 @@ private Builder(OutputConfig config) this.average = config.average; this.title = config.title; this.benchmarkMode = config.benchmarkMode; - this.formatting = config.formatting; } public Builder title(final String title) @@ -301,11 +287,5 @@ public Builder benchmarkMode(final boolean b) this.benchmarkMode = b; return this; } - - public Builder formatting(final boolean b) - { - this.formatting = b; - return this; - } } } diff --git a/src/main/java/com/ethlo/time/OutputFormatter.java b/src/main/java/com/ethlo/time/OutputFormatter.java new file mode 100644 index 0000000..70e5b99 --- /dev/null +++ b/src/main/java/com/ethlo/time/OutputFormatter.java @@ -0,0 +1,27 @@ +package com.ethlo.time; + +/*- + * #%L + * Chronograph + * %% + * Copyright (C) 2019 - 2024 Morten Haraldsen (ethlo) + * %% + * 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. + * #L% + */ + +@FunctionalInterface +public interface OutputFormatter +{ + String format(ChronographData data); +} diff --git a/src/main/java/com/ethlo/time/ReportUtil.java b/src/main/java/com/ethlo/time/ReportUtil.java index 20aae14..091c7ae 100644 --- a/src/main/java/com/ethlo/time/ReportUtil.java +++ b/src/main/java/com/ethlo/time/ReportUtil.java @@ -30,8 +30,6 @@ public class ReportUtil public static final int SECONDS_PER_MINUTE = 60; public static final int NANOS_PER_MILLI = 1_000_000; private static final int NANOS_PER_MICRO = 1_000; - private static final long MICRO = 1_000_000; - private static final long MILLI = 1_000; public static String humanReadable(Duration duration) { @@ -92,7 +90,7 @@ else if (hasSecondOrMore) if (millis == 0 && micros > 0) { - sb.append(df.format(nanos / (double) NANOS_PER_MICRO)).append(" μs "); + sb.append(df.format(nanos / (double) NANOS_PER_MICRO)).append(" us "); } if (millis == 0 && micros == 0 && nano > 0) @@ -104,28 +102,6 @@ else if (hasSecondOrMore) return sb.toString().trim(); } - public static String humanReadable(final double throughput) - { - final NumberFormat nf = NumberFormat.getNumberInstance(); - nf.setRoundingMode(RoundingMode.HALF_UP); - nf.setGroupingUsed(true); - - if (throughput > MICRO) - { - nf.setMaximumFractionDigits(0); - } - else if (throughput > MILLI) - { - nf.setMaximumFractionDigits(1); - } - else - { - nf.setMaximumFractionDigits(2); - } - - return nf.format(throughput); - } - public static String formatInteger(final long value) { final NumberFormat nf = NumberFormat.getNumberInstance(); diff --git a/src/main/java/com/ethlo/time/Report.java b/src/main/java/com/ethlo/time/TableOutputformatter.java similarity index 66% rename from src/main/java/com/ethlo/time/Report.java rename to src/main/java/com/ethlo/time/TableOutputformatter.java index 25bf095..356709c 100644 --- a/src/main/java/com/ethlo/time/Report.java +++ b/src/main/java/com/ethlo/time/TableOutputformatter.java @@ -2,16 +2,16 @@ /*- * #%L - * chronograph + * Chronograph * %% - * Copyright (C) 2019 Morten Haraldsen (ethlo) + * Copyright (C) 2019 - 2024 Morten Haraldsen (ethlo) * %% * 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. @@ -27,6 +27,7 @@ import java.util.Comparator; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import com.ethlo.ascii.SeparatorRow; import com.ethlo.ascii.Table; @@ -35,53 +36,20 @@ import com.ethlo.ascii.TableTheme; import com.ethlo.time.statistics.PerformanceStatistics; -public class Report +public class TableOutputformatter implements OutputFormatter { + private final TableTheme tableTheme; + private final OutputConfig outputConfig; - public static final double NANOS_PER_SECOND = 1_000_000_000D; - - public static String prettyPrint(ChronographData chronographData, OutputConfig outputConfig, TableTheme theme) + public TableOutputformatter(TableTheme tableTheme, OutputConfig outputConfig) { - if (chronographData.isEmpty()) - { - return "No performance data"; - } - - final List rows = new LinkedList<>(); - - final NumberFormat pf = NumberFormat.getPercentInstance(); - pf.setMinimumFractionDigits(1); - pf.setMaximumFractionDigits(1); - pf.setGroupingUsed(false); - - final NumberFormat nf = NumberFormat.getNumberInstance(); - nf.setRoundingMode(RoundingMode.HALF_UP); - nf.setGroupingUsed(true); - - // Header rows - rows.add(SeparatorRow.getInstance()); - rows.add(getHeaderRow(outputConfig)); - rows.add(SeparatorRow.getInstance()); - - // Task data - final List taskPerformanceStats = new ArrayList<>(chronographData.getTaskStatistics()); - taskPerformanceStats.sort(comparator(outputConfig)); - for (TaskPerformanceStatistics taskStats : taskPerformanceStats) - { - rows.add(getTableRow(outputConfig, chronographData.getTotalTime(), taskStats, pf, nf)); - } - - // Summary row - if (taskPerformanceStats.size() > 1) - { - rows.add(SeparatorRow.getInstance()); - rows.add(totals(outputConfig, chronographData)); - } - - // Bottom - rows.add(SeparatorRow.getInstance()); + this.tableTheme = Objects.requireNonNull(tableTheme, "tableTheme cannot be null"); + this.outputConfig = Objects.requireNonNull(outputConfig, "outputConfig cannot be null"); + } - return new Table(theme, rows).render(outputConfig.title() != null ? outputConfig.title() : chronographData.getName()); + public TableOutputformatter() + { + this(TableTheme.DEFAULT, OutputConfig.DEFAULT); } private static Comparator comparator(final OutputConfig outputConfig) @@ -100,7 +68,7 @@ private static TableRow getTableRow(final OutputConfig outputConfig, Duration to row.append(new TableCell(taskStats.name())); final long invocations = taskStats.performanceStatistics().getTotalInvocations(); - ; + final boolean multipleInvocations = invocations > 1; final PerformanceStatistics performanceStatistics = taskStats.performanceStatistics(); @@ -111,15 +79,15 @@ private static TableRow getTableRow(final OutputConfig outputConfig, Duration to outputPercentage(outputConfig, totalTime, pf, row, performanceStatistics); - conditionalOutput(outputConfig, row, multipleInvocations, outputConfig.median(), performanceStatistics.getMedian()); + conditionalOutput(row, multipleInvocations, outputConfig.median(), performanceStatistics.getMedian()); - conditionalOutput(outputConfig, row, multipleInvocations, outputConfig.standardDeviation(), performanceStatistics.getStandardDeviation()); + conditionalOutput(row, multipleInvocations, outputConfig.standardDeviation(), performanceStatistics.getStandardDeviation()); - conditionalOutput(outputConfig, row, multipleInvocations, outputConfig.mean(), performanceStatistics.getAverage()); + conditionalOutput(row, multipleInvocations, outputConfig.mean(), performanceStatistics.getAverage()); - conditionalOutput(outputConfig, row, multipleInvocations, outputConfig.min(), performanceStatistics.getMin()); + conditionalOutput(row, multipleInvocations, outputConfig.min(), performanceStatistics.getMin()); - conditionalOutput(outputConfig, row, multipleInvocations, outputConfig.max(), performanceStatistics.getMax()); + conditionalOutput(row, multipleInvocations, outputConfig.max(), performanceStatistics.getMax()); outputPercentiles(outputConfig, row, multipleInvocations, performanceStatistics); @@ -132,7 +100,7 @@ private static void outputPercentiles(final OutputConfig outputConfig, final Tab { for (double percentile : outputConfig.percentiles()) { - outputCell(outputConfig, row, multipleInvocations, performanceStatistics.getPercentile(percentile)); + outputCell(row, multipleInvocations, performanceStatistics.getPercentile(percentile)); } } } @@ -141,15 +109,7 @@ private static void outputTotal(final OutputConfig outputConfig, final TableRow { if (outputConfig.total()) { - final String str; - if (outputConfig.formatting()) - { - str = ReportUtil.humanReadable(performanceStatistics.getElapsedTotal()); - } - else - { - str = getRawNumberFormat().format(performanceStatistics.getElapsedTotal().toNanos() / NANOS_PER_SECOND); - } + final String str = ReportUtil.humanReadable(performanceStatistics.getElapsedTotal()); row.append(new TableCell(str, false, true)); } } @@ -159,7 +119,7 @@ private static void outputPercentage(final OutputConfig outputConfig, final Dura if (outputConfig.percentage()) { final double pct = totalTime.isZero() ? 0D : performanceStatistics.getElapsedTotal().toNanos() / (double) totalTime.toNanos(); - row.append(new TableCell(outputConfig.formatting() ? pf.format(pct) : getRawNumberFormat().format(pct), false, true)); + row.append(new TableCell(pf.format(pct), false, true)); } } @@ -168,7 +128,7 @@ private static void addInvocations(final OutputConfig outputConfig, final TaskPe if (outputConfig.invocations()) { String invocationsStr; - if (taskStats.sampleSize() != invocations && outputConfig.formatting()) + if (taskStats.sampleSize() != invocations) { // Reduced sample rate invocationsStr = "(" + nf.format(taskStats.sampleSize()) + ") " + nf.format(invocations); @@ -176,34 +136,25 @@ private static void addInvocations(final OutputConfig outputConfig, final TaskPe else { // Full sampling - invocationsStr = outputConfig.formatting() ? nf.format(invocations) : getRawNumberFormat().format(invocations); + invocationsStr = nf.format(invocations); } row.append(new TableCell(invocationsStr, false, true)); } } - private static void conditionalOutput(final OutputConfig outputConfig, final TableRow row, final boolean multipleInvocations, final boolean shouldShow, final Duration duration) + private static void conditionalOutput(final TableRow row, final boolean multipleInvocations, final boolean shouldShow, final Duration duration) { if (shouldShow) { - outputCell(outputConfig, row, multipleInvocations, duration); + outputCell(row, multipleInvocations, duration); } } - private static void outputCell(final OutputConfig outputConfig, final TableRow row, final boolean multipleInvocations, final Duration duration) + private static void outputCell(final TableRow row, final boolean multipleInvocations, final Duration duration) { if (multipleInvocations) { - final String str; - if (outputConfig.formatting()) - { - str = ReportUtil.humanReadable(duration); - } - else - { - final NumberFormat nf = getRawNumberFormat(); - str = nf.format(duration.toNanos()); - } + final String str = ReportUtil.humanReadable(duration); row.append(new TableCell(str, false, true)); } else @@ -212,16 +163,6 @@ private static void outputCell(final OutputConfig outputConfig, final TableRow r } } - private static NumberFormat getRawNumberFormat() - { - final NumberFormat nf = NumberFormat.getNumberInstance(); - nf.setRoundingMode(RoundingMode.HALF_UP); - nf.setMinimumFractionDigits(6); - nf.setMaximumFractionDigits(6); - nf.setGroupingUsed(false); - return nf; - } - private static TableRow getHeaderRow(final OutputConfig outputConfig) { final TableRow headerRow = new TableRow(); @@ -282,15 +223,58 @@ private static TableRow getHeaderRow(final OutputConfig outputConfig) return headerRow; } - private static TableRow totals(final OutputConfig outputConfig, final ChronographData chronographData) + private static TableRow totals(final ChronographData chronographData) { final long totalInvocations = chronographData.getTaskStatistics().stream().map(t -> t.performanceStatistics().getTotalInvocations()).reduce(0L, Long::sum); return new TableRow() .append(new TableCell("Sum", false, false)) - .append(new TableCell( - outputConfig.formatting() ? ReportUtil.humanReadable(chronographData.getTotalTime()) : getRawNumberFormat().format(chronographData.getTotalTime().toNanos() / NANOS_PER_SECOND), false, true)) - .append(new TableCell( - outputConfig.formatting() ? ReportUtil.formatInteger(totalInvocations) : getRawNumberFormat().format(totalInvocations), false, true)) - .append(new TableCell(outputConfig.formatting() ? "100.0%" : "1.000000", false, true)); + .append(new TableCell(ReportUtil.humanReadable(chronographData.getTotalTime()), false, true)) + .append(new TableCell(ReportUtil.formatInteger(totalInvocations), false, true)) + .append(new TableCell("100.0%", false, true)); + } + + @Override + public String format(final ChronographData chronographData) + { + if (chronographData.isEmpty()) + { + return "No performance data"; + } + + final List rows = new LinkedList<>(); + + final NumberFormat pf = NumberFormat.getPercentInstance(); + pf.setMinimumFractionDigits(1); + pf.setMaximumFractionDigits(1); + pf.setGroupingUsed(false); + + final NumberFormat nf = NumberFormat.getNumberInstance(); + nf.setRoundingMode(RoundingMode.HALF_UP); + nf.setGroupingUsed(true); + + // Header rows + rows.add(SeparatorRow.getInstance()); + rows.add(getHeaderRow(outputConfig)); + rows.add(SeparatorRow.getInstance()); + + // Task data + final List taskPerformanceStats = new ArrayList<>(chronographData.getTaskStatistics()); + taskPerformanceStats.sort(comparator(outputConfig)); + for (TaskPerformanceStatistics taskStats : taskPerformanceStats) + { + rows.add(getTableRow(outputConfig, chronographData.getTotalTime(), taskStats, pf, nf)); + } + + // Summary row + if (taskPerformanceStats.size() > 1) + { + rows.add(SeparatorRow.getInstance()); + rows.add(totals(chronographData)); + } + + // Bottom + rows.add(SeparatorRow.getInstance()); + + return new Table(tableTheme, rows).render(outputConfig.title() != null ? outputConfig.title() : chronographData.getName()); } } diff --git a/src/test/java/com/ethlo/time/ChronographTest.java b/src/test/java/com/ethlo/time/ChronographTest.java index 3ed5ac9..27cdb12 100644 --- a/src/test/java/com/ethlo/time/ChronographTest.java +++ b/src/test/java/com/ethlo/time/ChronographTest.java @@ -61,7 +61,7 @@ public void sequenceMultipleFunctionTasks() for (int i = 1; i <= 100_000; i++) { - assertThat((int) chronograph.timedFunction("foo", this::microsecondTask, 1)).isEqualTo(2); + assertThat((int) chronograph.time("foo", this::microsecondTaskInput, 1)).isEqualTo(2); } } @@ -72,9 +72,10 @@ public void sequenceMultipleRunnableTasks() for (int i = 1; i <= 100_000; i++) { - chronograph.timed("foo", this::microsecondTask); - chronograph.timed("bar", this::microsecondTask); - chronograph.timed("baz baz baz baz baz baz", this::microsecondTask); + chronograph.time("foo", this::microsecondTask); + final int input = i; + chronograph.time("bar", () -> microsecondTaskInput(input)); + chronograph.time("baz baz baz baz baz baz", this::microsecondTask); } assertThat(chronograph.getTasks()).hasSize(3); @@ -105,7 +106,7 @@ private void microsecondTask() } } - private int microsecondTask(int input) + private int microsecondTaskInput(int input) { try { @@ -161,7 +162,7 @@ public void testSimpleSingleInvocations() final Chronograph chronograph = Chronograph.create(); for (int i = 0; i < 10; i++) { - chronograph.timed("Task-" + i, () -> { + chronograph.time("Task-" + i, () -> { }); } logger.info(chronograph.prettyPrint()); @@ -255,12 +256,12 @@ void testMergeResults() chronograph1.stop(); chronograph2.stop(); + + System.out.println(chronograph1.prettyPrint(TableTheme.DOUBLE)); + System.out.println(chronograph2.prettyPrint(OutputConfig.COMPACT)); final ChronographData merged = chronograph2.getTaskData().merge("merged", chronograph1.getTaskData()); - System.out.println(Report.prettyPrint(merged, - OutputConfig.EXTENDED.benchmarkMode(true), - TableTheme.RED_HERRING - )); + System.out.println(new TableOutputformatter().format(merged)); assertThat(true).isTrue(); } diff --git a/src/test/java/com/ethlo/time/ChronographTimedTest.java b/src/test/java/com/ethlo/time/ChronographTimedTest.java new file mode 100644 index 0000000..4d3d9f0 --- /dev/null +++ b/src/test/java/com/ethlo/time/ChronographTimedTest.java @@ -0,0 +1,378 @@ +package com.ethlo.time; + +/*- + * #%L + * Chronograph + * %% + * Copyright (C) 2019 - 2024 Morten Haraldsen (ethlo) + * %% + * 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. + * #L% + */ + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.BinaryOperator; +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; +import java.util.function.DoubleConsumer; +import java.util.function.DoubleFunction; +import java.util.function.DoublePredicate; +import java.util.function.DoubleToIntFunction; +import java.util.function.DoubleToLongFunction; +import java.util.function.DoubleUnaryOperator; +import java.util.function.Function; +import java.util.function.IntBinaryOperator; +import java.util.function.IntUnaryOperator; +import java.util.function.LongUnaryOperator; +import java.util.function.ObjDoubleConsumer; +import java.util.function.ObjIntConsumer; +import java.util.function.ObjLongConsumer; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.ToDoubleFunction; +import java.util.function.ToLongFunction; +import java.util.function.UnaryOperator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ChronographTimedTest +{ + private Chronograph chronograph; + + @BeforeEach + void setUp() + { + chronograph = spy(Chronograph.create()); + } + + @Test + void testTimedSupplier() + { + String taskName = "Supplier Task"; + Supplier supplier = () -> "Hello, World!"; + + String result = chronograph.time(taskName, supplier); + + assertThat(result).isEqualTo("Hello, World!"); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedFunction() + { + String taskName = "Function Task"; + Function function = i -> "Result: " + i; + + String result = chronograph.time(taskName, function, 42); + + assertThat(result).isEqualTo("Result: 42"); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedBiFunction() + { + String taskName = "BiFunction Task"; + BiFunction biFunction = Integer::sum; + + Integer result = chronograph.time(taskName, biFunction, 5, 10); + + assertThat(result).isEqualTo(15); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedPredicate() + { + String taskName = "Predicate Task"; + Predicate predicate = s -> s.length() > 5; + + boolean result = chronograph.time(taskName, predicate, "HelloWorld"); + + assertThat(result).isTrue(); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedConsumer() + { + String taskName = "Consumer Task"; + Consumer consumer = mock(Consumer.class); + + chronograph.time(taskName, consumer, "Hello"); + + verify(consumer).accept("Hello"); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedDoubleUnaryOperator() + { + String taskName = "DoubleUnaryOperator Task"; + DoubleUnaryOperator operator = d -> d * 2; + + double result = chronograph.time(taskName, operator, 3.5); + + assertThat(result).isEqualTo(7.0); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedIntBinaryOperator() + { + String taskName = "IntBinaryOperator Task"; + IntBinaryOperator operator = Integer::sum; + + int result = chronograph.time(taskName, operator, 2, 3); + + assertThat(result).isEqualTo(5); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedBiConsumer() + { + String taskName = "BiConsumer Task"; + BiConsumer biConsumer = mock(BiConsumer.class); + + chronograph.time(taskName, biConsumer, "Test", 10); + + verify(biConsumer).accept("Test", 10); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedBinaryOperator() + { + String taskName = "BinaryOperator Task"; + BinaryOperator operator = Integer::max; + + int result = chronograph.time(taskName, operator, 5, 10); + + assertThat(result).isEqualTo(10); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedBiPredicate() + { + String taskName = "BiPredicate Task"; + BiPredicate biPredicate = String::equals; + + boolean result = chronograph.time(taskName, biPredicate, "test", "test"); + + assertThat(result).isTrue(); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedBooleanSupplier() + { + String taskName = "BooleanSupplier Task"; + BooleanSupplier booleanSupplier = () -> true; + + boolean result = chronograph.time(taskName, booleanSupplier); + + assertThat(result).isTrue(); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedDoubleConsumer() + { + String taskName = "DoubleConsumer Task"; + DoubleConsumer doubleConsumer = mock(DoubleConsumer.class); + + chronograph.time(taskName, doubleConsumer, 5.5); + + verify(doubleConsumer).accept(5.5); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedDoubleFunction() + { + String taskName = "DoubleFunction Task"; + DoubleFunction doubleFunction = d -> "Value: " + d; + + String result = chronograph.time(taskName, doubleFunction, 2.5); + + assertThat(result).isEqualTo("Value: 2.5"); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedDoublePredicate() + { + String taskName = "DoublePredicate Task"; + DoublePredicate predicate = d -> d > 10.0; + + boolean result = chronograph.time(taskName, predicate, 15.0); + + assertThat(result).isTrue(); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedDoubleToIntFunction() + { + String taskName = "DoubleToIntFunction Task"; + DoubleToIntFunction function = d -> (int) Math.floor(d); + + int result = chronograph.time(taskName, function, 3.8); + + assertThat(result).isEqualTo(3); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedDoubleToLongFunction() + { + String taskName = "DoubleToLongFunction Task"; + DoubleToLongFunction function = d -> (long) Math.ceil(d); + + long result = chronograph.time(taskName, function, 7.3); + + assertThat(result).isEqualTo(8L); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedIntUnaryOperator() + { + String taskName = "IntUnaryOperator Task"; + IntUnaryOperator operator = i -> i * i; + + int result = chronograph.time(taskName, operator, 5); + + assertThat(result).isEqualTo(25); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedLongUnaryOperator() + { + String taskName = "LongUnaryOperator Task"; + LongUnaryOperator operator = l -> l * 2; + + long result = chronograph.time(taskName, operator, 6L); + + assertThat(result).isEqualTo(12L); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedObjDoubleConsumer() + { + String taskName = "ObjDoubleConsumer Task"; + ObjDoubleConsumer consumer = mock(ObjDoubleConsumer.class); + + chronograph.time(taskName, consumer, "Value", 3.14); + + verify(consumer).accept("Value", 3.14); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedObjIntConsumer() + { + String taskName = "ObjIntConsumer Task"; + ObjIntConsumer consumer = mock(ObjIntConsumer.class); + + chronograph.time(taskName, consumer, "Value", 42); + + verify(consumer).accept("Value", 42); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedObjLongConsumer() + { + String taskName = "ObjLongConsumer Task"; + ObjLongConsumer consumer = mock(ObjLongConsumer.class); + + chronograph.time(taskName, consumer, "Value", 100L); + + verify(consumer).accept("Value", 100L); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedToDoubleFunction() + { + String taskName = "ToDoubleFunction Task"; + ToDoubleFunction function = Double::parseDouble; + + double result = chronograph.time(taskName, function, "3.14"); + + assertThat(result).isEqualTo(3.14); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedToLongFunction() + { + String taskName = "ToLongFunction Task"; + ToLongFunction function = Long::parseLong; + + long result = chronograph.time(taskName, function, "12345"); + + assertThat(result).isEqualTo(12345L); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + + @Test + void testTimedUnaryOperator() + { + String taskName = "UnaryOperator Task"; + UnaryOperator operator = String::toUpperCase; + + String result = chronograph.time(taskName, operator, "test"); + + assertThat(result).isEqualTo("TEST"); + verify(chronograph).start(taskName); + verify(chronograph).stop(taskName); + } + +} diff --git a/src/test/java/com/ethlo/util/DurationUtilTest.java b/src/test/java/com/ethlo/util/DurationUtilTest.java index e1f3109..e836777 100644 --- a/src/test/java/com/ethlo/util/DurationUtilTest.java +++ b/src/test/java/com/ethlo/util/DurationUtilTest.java @@ -69,7 +69,7 @@ public void humanReadableFormatLessThanSecond() @Test public void humanReadableFormatLessThanMillisecond() { - assertThat(ReportUtil.humanReadable(Duration.ofNanos(456_789))).isEqualTo("456.79 μs"); + assertThat(ReportUtil.humanReadable(Duration.ofNanos(456_789))).isEqualTo("456.79 us"); } @Test diff --git a/src/test/java/com/ethlo/util/ListPerformanceTest.java b/src/test/java/com/ethlo/util/ListPerformanceTest.java index d4aa31e..1fd31cc 100644 --- a/src/test/java/com/ethlo/util/ListPerformanceTest.java +++ b/src/test/java/com/ethlo/util/ListPerformanceTest.java @@ -29,6 +29,8 @@ import java.util.LinkedList; import java.util.List; +import com.ethlo.ascii.AnsiBackgroundColor; + import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,7 +40,7 @@ import com.ethlo.time.Chronograph; import com.ethlo.time.ChronographData; import com.ethlo.time.OutputConfig; -import com.ethlo.time.Report; +import com.ethlo.time.TableOutputformatter; class ListPerformanceTest { @@ -53,8 +55,8 @@ void performanceTestLargeLinkedList() final Chronograph c = Chronograph.create(); for (int i = 0; i < count; i++) { - final List list = c.timedSupplier("add", () -> addLinkedList(size)); - c.timed("sort", () -> list.sort(Comparator.naturalOrder())); + final List list = c.time("add", () -> addLinkedList(size)); + c.time("sort", () -> list.sort(Comparator.naturalOrder())); } logger.info(c.prettyPrint("LinkedList")); @@ -67,8 +69,8 @@ void performanceTestLargeArrayList() final Chronograph c = Chronograph.create(); for (int i = 0; i < count; i++) { - final List list = c.timedSupplier("add", () -> addArrayList(size)); - c.timed("sort", () -> list.sort(Comparator.naturalOrder())); + final List list = c.time("add", () -> addArrayList(size)); + c.time("sort", () -> list.sort(Comparator.naturalOrder())); } logger.info(c.prettyPrint("ArrayList")); @@ -81,8 +83,8 @@ void performanceTestLargeLongList() final Chronograph c = Chronograph.create(); for (int i = 0; i < count; i++) { - final LongList list = c.timedSupplier("add", () -> addLongList(size)); - c.timed("sort", list::sort); + final LongList list = c.time("add", () -> addLongList(size)); + c.time("sort", list::sort); } logger.info(c.prettyPrint("LongList")); @@ -97,11 +99,11 @@ void rateLimitingTest() final IndexedCollection list = new LongList(100_000); for (int i = 0; i < 2_000_000; i++) { - c.timed("Warmup", () -> list.add(randomNano())); + c.time("Warmup", () -> list.add(randomNano())); } for (int i = 0; i < 10_000_000; i++) { - c.timed("Adding", () -> list.add(randomNano())); + c.time("Adding", () -> list.add(randomNano())); } System.out.println(c.prettyPrint()); @@ -123,9 +125,9 @@ private Chronograph performAddBenchmark(final int runs, final int size) for (int i = 0; i < runs; i++) { - c.timedFunction("LinkedList - Add", this::addLinkedList, size); - c.timedFunction("ArrayList - Add", this::addArrayList, size); - c.timedFunction("IndexedCollection - Add", this::addLongList, size); + c.time("LinkedList - Add", this::addLinkedList, size); + c.time("ArrayList - Add", this::addArrayList, size); + c.time("IndexedCollection - Add", this::addLongList, size); } return c; } @@ -135,8 +137,9 @@ void performanceTestMediumSort() { final Chronograph c = performSortBenchmark(10, 500_000); output(c, TableTheme.RED_HERRING); + output(c, TableTheme.SINGLE); output(c, TableTheme.DOUBLE); - output(c, TableTheme.DEFAULT); + output(c, TableTheme.DEFAULT.begin().cellBackground(AnsiBackgroundColor.BRIGHT_CYAN).build()); output(c, TableTheme.ROUNDED); output(c, TableTheme.MINIMAL); output(c, TableTheme.COMPACT); @@ -153,9 +156,9 @@ private Chronograph performSortBenchmark(final int runs, final int size) final List linkedList = addLinkedList(size); final List arrayList = addArrayList(size); - c.timed("LinkedList - Sort", () -> linkedList.sort(Comparator.naturalOrder())); - c.timed("ArrayList - Sort", () -> arrayList.sort(Comparator.naturalOrder())); - c.timed("IndexedCollection - Sort", longList::sort); + c.time("LinkedList - Sort", () -> linkedList.sort(Comparator.naturalOrder())); + c.time("ArrayList - Sort", () -> arrayList.sort(Comparator.naturalOrder())); + c.time("IndexedCollection - Sort", longList::sort); } return c; } @@ -169,10 +172,7 @@ void testCombinedPerformanceTable() final Chronograph d = performSortBenchmark(8, 10_000); final ChronographData combined = ChronographData.combine("Combined", Arrays.asList(a, b, c, d)); - System.out.println(Report.prettyPrint(combined, - OutputConfig.EXTENDED.benchmarkMode(true), - TableTheme.RED_HERRING - )); + System.out.println(new TableOutputformatter().format(combined)); assertThat(true).isTrue(); } @@ -180,7 +180,8 @@ void testCombinedPerformanceTable() private void output(final Chronograph c, TableTheme theme) { - System.out.println(Report.prettyPrint(c.getTaskData(), OutputConfig.EXTENDED.benchmarkMode(true), theme)); + System.out.println(theme.getName()); + System.out.println(c.prettyPrint(OutputConfig.EXTENDED.benchmarkMode(true), theme)); } private LongList addLongList(int count) diff --git a/src/test/java/com/ethlo/util/LongListTest.java b/src/test/java/com/ethlo/util/LongListTest.java index fa32413..b33a4b0 100644 --- a/src/test/java/com/ethlo/util/LongListTest.java +++ b/src/test/java/com/ethlo/util/LongListTest.java @@ -23,7 +23,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; public class LongListTest @@ -162,18 +161,4 @@ void size() final LongList l = createList(10, true); assertThat(l.size()).isEqualTo(10); } - - @Disabled - @Test - void overflow() - { - final LongList l = new LongList(10000); - System.out.println(Runtime.getRuntime().maxMemory() / 1024D / 1024D); - for (long i = 0; i < (long) Integer.MAX_VALUE; i++) - { - l.add(1); - } - - assertThrows(IndexOutOfBoundsException.class, () -> l.add(1)); - } }