This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/maven.git
The following commit(s) were added to refs/heads/master by this push: new 1fd7f879a4 [MNG-5729] Fix transfer rate computation (#1969) 1fd7f879a4 is described below commit 1fd7f879a4d4253b6b1eb655b5b7b666c5274d00 Author: Guillaume Nodet <gno...@gmail.com> AuthorDate: Sat Dec 14 00:03:27 2024 +0100 [MNG-5729] Fix transfer rate computation (#1969) --- impl/maven-cli/pom.xml | 5 + .../transfer/AbstractMavenTransferListener.java | 9 +- .../maven/cling/transfer/FileSizeFormat.java | 16 + .../transfer/ConsoleMavenTransferListenerTest.java | 139 ++++++++ .../maven/cling/transfer/FileSizeFormatTest.java | 356 +++++++++++++++++++++ .../transfer/SimplexTransferListenerTest.java | 112 +++++++ .../maven/internal/impl/AbstractSession.java | 6 + 7 files changed, 637 insertions(+), 6 deletions(-) diff --git a/impl/maven-cli/pom.xml b/impl/maven-cli/pom.xml index bd8c0b61de..97f35179fe 100644 --- a/impl/maven-cli/pom.xml +++ b/impl/maven-cli/pom.xml @@ -89,6 +89,11 @@ under the License. <artifactId>junit-jupiter-api</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>com.google.jimfs</groupId> <artifactId>jimfs</artifactId> diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/AbstractMavenTransferListener.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/AbstractMavenTransferListener.java index 4cba621f55..fa4341b8f1 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/AbstractMavenTransferListener.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/AbstractMavenTransferListener.java @@ -20,7 +20,6 @@ package org.apache.maven.cling.transfer; import java.io.PrintWriter; import java.time.Duration; -import java.time.Instant; import org.apache.maven.api.MonotonicClock; import org.apache.maven.api.services.MessageBuilder; @@ -83,13 +82,11 @@ public abstract class AbstractMavenTransferListener extends AbstractTransferList message.resetStyle().append(resource.getResourceName()); message.style(STYLE).append(" (").append(format.format(contentLength)); - Duration duration = - Duration.between(Instant.ofEpochMilli(resource.getTransferStartTime()), MonotonicClock.now()); + Duration duration = Duration.between(resource.getStartTime(), MonotonicClock.now()); if ((duration.getSeconds() | duration.getNano()) > 0) { // duration.isPositive() - long bytesPerSecond = Math.round(contentLength / (double) duration.toSeconds()); + double bytesPerSecond = contentLength / (double) duration.toSeconds(); message.append(" at "); - format.format(message, bytesPerSecond); - message.append("/s"); + format.formatRate(message, bytesPerSecond); } message.append(')').resetStyle(); diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/FileSizeFormat.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/FileSizeFormat.java index a46e0b035b..d29eb01540 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/FileSizeFormat.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/FileSizeFormat.java @@ -152,6 +152,22 @@ public class FileSizeFormat { format(builder, size, unit, false); } + public void formatRate(MessageBuilder builder, double rate) { + ScaleUnit unit = ScaleUnit.getScaleUnit(Math.round(rate)); + double scaledRate = rate / unit.bytes(); + if (unit == ScaleUnit.BYTE || scaledRate < 0.05d || scaledRate >= 10.0d) { + builder.append(Long.toString(Math.round(scaledRate))); + } else { + builder.append(Double.toString(Math.round(scaledRate * 10d) / 10d)); + } + if (unit == ScaleUnit.BYTE) { + builder.append(" B"); + } else { + builder.append(" ").append(unit.symbol()); + } + builder.append("/s"); + } + private void format(MessageBuilder builder, long size, ScaleUnit unit, boolean omitSymbol) { if (size < 0L) { throw new IllegalArgumentException("file size cannot be negative: " + size); diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/ConsoleMavenTransferListenerTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/ConsoleMavenTransferListenerTest.java new file mode 100644 index 0000000000..d06c4d18e3 --- /dev/null +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/ConsoleMavenTransferListenerTest.java @@ -0,0 +1,139 @@ +/* + * 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. + */ +package org.apache.maven.cling.transfer; + +import java.io.File; +import java.io.PrintWriter; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.apache.maven.jline.JLineMessageBuilderFactory; +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.transfer.TransferCancelledException; +import org.eclipse.aether.transfer.TransferEvent; +import org.eclipse.aether.transfer.TransferListener; +import org.eclipse.aether.transfer.TransferResource; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ConsoleMavenTransferListenerTest { + + private CountDownLatch startLatch; + private CountDownLatch endLatch; + + @Test + void testTransferProgressedWithPrintResourceNames() throws Exception { + int size = 1000; + ExecutorService service = Executors.newFixedThreadPool(size * 2); + startLatch = new CountDownLatch(size); + endLatch = new CountDownLatch(size); + Map<String, String> output = new ConcurrentHashMap<String, String>(); + + TransferListener listener = new SimplexTransferListener(new ConsoleMavenTransferListener( + new JLineMessageBuilderFactory(), + new PrintWriter(System.out) { + + @Override + public void print(Object o) { + + String string = o.toString(); + int i = string.length() - 1; + while (i >= 0) { + char c = string.charAt(i); + if (c == '\n' || c == '\r' || c == ' ') i--; + else break; + } + + string = string.substring(0, i + 1).trim(); + output.put(string, string); + System.out.print(o); + } + }, + true)); + TransferResource resource = + new TransferResource(null, null, "http://maven.org/test/test-resource", new File(""), null); + resource.setContentLength(size - 1); + + DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(h -> false); // no close handle + + // warm up + test(listener, session, resource, 0); + + for (int i = 1; i < size; i++) { + final int bytes = i; + + service.execute(() -> { + test(listener, session, resource, bytes); + }); + } + + // start all threads at once + try { + startLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + // wait for all thread to end + try { + endLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + // despite all are back, we need to make sure all the events are processed (are async) + // this one should block until all processed + listener.transferSucceeded(new TransferEvent.Builder(session, resource) + .setType(TransferEvent.EventType.SUCCEEDED) + .build()); + + StringBuilder message = new StringBuilder("Messages ["); + boolean test = true; + for (int i = 0; i < 999; i++) { + boolean ok = output.containsKey("Progress (1): test-resource (" + i + "/999 B)"); + if (!ok) { + System.out.println("false : " + i); + message.append(i + ","); + } + test = test & ok; + } + assertTrue(test, message + "] are missing in " + output); + } + + private void test( + TransferListener listener, + DefaultRepositorySystemSession session, + TransferResource resource, + final int bytes) { + TransferEvent event = new TransferEvent.Builder(session, resource) + .setType(TransferEvent.EventType.PROGRESSED) + .setTransferredBytes(bytes) + .build(); + startLatch.countDown(); + try { + listener.transferProgressed(event); + } catch (TransferCancelledException e) { + } + endLatch.countDown(); + } +} diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/FileSizeFormatTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/FileSizeFormatTest.java new file mode 100644 index 0000000000..9a969f0359 --- /dev/null +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/FileSizeFormatTest.java @@ -0,0 +1,356 @@ +/* + * 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. + */ +package org.apache.maven.cling.transfer; + +import org.apache.maven.api.services.MessageBuilder; +import org.apache.maven.cling.transfer.FileSizeFormat.ScaleUnit; +import org.apache.maven.internal.impl.DefaultMessageBuilder; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class FileSizeFormatTest { + + @Test + void testNegativeSize() { + FileSizeFormat format = new FileSizeFormat(); + + long negativeSize = -100L; + assertThrows(IllegalArgumentException.class, () -> format.format(negativeSize)); + } + + @Test + void testSize() { + FileSizeFormat format = new FileSizeFormat(); + + long _0_bytes = 0L; + assertEquals("0 B", format.format(_0_bytes)); + + long _5_bytes = 5L; + assertEquals("5 B", format.format(_5_bytes)); + + long _10_bytes = 10L; + assertEquals("10 B", format.format(_10_bytes)); + + long _15_bytes = 15L; + assertEquals("15 B", format.format(_15_bytes)); + + long _999_bytes = 999L; + assertEquals("999 B", format.format(_999_bytes)); + + long _1000_bytes = 1000L; + assertEquals("1.0 kB", format.format(_1000_bytes)); + + long _5500_bytes = 5500L; + assertEquals("5.5 kB", format.format(_5500_bytes)); + + long _10_kilobytes = 10L * 1000L; + assertEquals("10 kB", format.format(_10_kilobytes)); + + long _15_kilobytes = 15L * 1000L; + assertEquals("15 kB", format.format(_15_kilobytes)); + + long _999_kilobytes = 999L * 1000L; + assertEquals("999 kB", format.format(_999_kilobytes)); + + long _1000_kilobytes = 1000L * 1000L; + assertEquals("1.0 MB", format.format(_1000_kilobytes)); + + long _5500_kilobytes = 5500L * 1000L; + assertEquals("5.5 MB", format.format(_5500_kilobytes)); + + long _10_megabytes = 10L * 1000L * 1000L; + assertEquals("10 MB", format.format(_10_megabytes)); + + long _15_megabytes = 15L * 1000L * 1000L; + assertEquals("15 MB", format.format(_15_megabytes)); + + long _999_megabytes = 999L * 1000L * 1000L; + assertEquals("999 MB", format.format(_999_megabytes)); + + long _1000_megabytes = 1000L * 1000L * 1000L; + assertEquals("1.0 GB", format.format(_1000_megabytes)); + + long _5500_megabytes = 5500L * 1000L * 1000L; + assertEquals("5.5 GB", format.format(_5500_megabytes)); + + long _10_gigabytes = 10L * 1000L * 1000L * 1000L; + assertEquals("10 GB", format.format(_10_gigabytes)); + + long _15_gigabytes = 15L * 1000L * 1000L * 1000L; + assertEquals("15 GB", format.format(_15_gigabytes)); + + long _1000_gigabytes = 1000L * 1000L * 1000L * 1000L; + assertEquals("1000 GB", format.format(_1000_gigabytes)); + } + + @Test + void testSizeWithSelectedScaleUnit() { + FileSizeFormat format = new FileSizeFormat(); + + long _0_bytes = 0L; + assertEquals("0 B", format.format(_0_bytes)); + assertEquals("0 B", format.format(_0_bytes, ScaleUnit.BYTE)); + assertEquals("0 kB", format.format(_0_bytes, ScaleUnit.KILOBYTE)); + assertEquals("0 MB", format.format(_0_bytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_0_bytes, ScaleUnit.GIGABYTE)); + + long _5_bytes = 5L; + assertEquals("5 B", format.format(_5_bytes)); + assertEquals("5 B", format.format(_5_bytes, ScaleUnit.BYTE)); + assertEquals("0 kB", format.format(_5_bytes, ScaleUnit.KILOBYTE)); + assertEquals("0 MB", format.format(_5_bytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_5_bytes, ScaleUnit.GIGABYTE)); + + long _49_bytes = 49L; + assertEquals("49 B", format.format(_49_bytes)); + assertEquals("49 B", format.format(_49_bytes, ScaleUnit.BYTE)); + assertEquals("0 kB", format.format(_49_bytes, ScaleUnit.KILOBYTE)); + assertEquals("0 MB", format.format(_49_bytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_49_bytes, ScaleUnit.GIGABYTE)); + + long _50_bytes = 50L; + assertEquals("50 B", format.format(_50_bytes)); + assertEquals("50 B", format.format(_50_bytes, ScaleUnit.BYTE)); + assertEquals("0.1 kB", format.format(_50_bytes, ScaleUnit.KILOBYTE)); + assertEquals("0 MB", format.format(_50_bytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_50_bytes, ScaleUnit.GIGABYTE)); + + long _999_bytes = 999L; + assertEquals("999 B", format.format(_999_bytes)); + assertEquals("999 B", format.format(_999_bytes, ScaleUnit.BYTE)); + assertEquals("1.0 kB", format.format(_999_bytes, ScaleUnit.KILOBYTE)); + assertEquals("0 MB", format.format(_999_bytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_999_bytes, ScaleUnit.GIGABYTE)); + + long _1000_bytes = 1000L; + assertEquals("1.0 kB", format.format(_1000_bytes)); + assertEquals("1000 B", format.format(_1000_bytes, ScaleUnit.BYTE)); + assertEquals("1.0 kB", format.format(_1000_bytes, ScaleUnit.KILOBYTE)); + assertEquals("0 MB", format.format(_1000_bytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_1000_bytes, ScaleUnit.GIGABYTE)); + + long _49_kilobytes = 49L * 1000L; + assertEquals("49 kB", format.format(_49_kilobytes)); + assertEquals("49000 B", format.format(_49_kilobytes, ScaleUnit.BYTE)); + assertEquals("49 kB", format.format(_49_kilobytes, ScaleUnit.KILOBYTE)); + assertEquals("0 MB", format.format(_49_kilobytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_49_kilobytes, ScaleUnit.GIGABYTE)); + + long _50_kilobytes = 50L * 1000L; + assertEquals("50 kB", format.format(_50_kilobytes)); + assertEquals("50000 B", format.format(_50_kilobytes, ScaleUnit.BYTE)); + assertEquals("50 kB", format.format(_50_kilobytes, ScaleUnit.KILOBYTE)); + assertEquals("0.1 MB", format.format(_50_kilobytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_50_kilobytes, ScaleUnit.GIGABYTE)); + + long _999_kilobytes = 999L * 1000L; + assertEquals("999 kB", format.format(_999_kilobytes)); + assertEquals("999000 B", format.format(_999_kilobytes, ScaleUnit.BYTE)); + assertEquals("999 kB", format.format(_999_kilobytes, ScaleUnit.KILOBYTE)); + assertEquals("1.0 MB", format.format(_999_kilobytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_999_kilobytes, ScaleUnit.GIGABYTE)); + + long _1000_kilobytes = 1000L * 1000L; + assertEquals("1.0 MB", format.format(_1000_kilobytes)); + assertEquals("1000000 B", format.format(_1000_kilobytes, ScaleUnit.BYTE)); + assertEquals("1000 kB", format.format(_1000_kilobytes, ScaleUnit.KILOBYTE)); + assertEquals("1.0 MB", format.format(_1000_kilobytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_1000_kilobytes, ScaleUnit.GIGABYTE)); + + long _49_megabytes = 49L * 1000L * 1000L; + assertEquals("49 MB", format.format(_49_megabytes)); + assertEquals("49000000 B", format.format(_49_megabytes, ScaleUnit.BYTE)); + assertEquals("49000 kB", format.format(_49_megabytes, ScaleUnit.KILOBYTE)); + assertEquals("49 MB", format.format(_49_megabytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_49_megabytes, ScaleUnit.GIGABYTE)); + + long _50_megabytes = 50L * 1000L * 1000L; + assertEquals("50 MB", format.format(_50_megabytes)); + assertEquals("50000000 B", format.format(_50_megabytes, ScaleUnit.BYTE)); + assertEquals("50000 kB", format.format(_50_megabytes, ScaleUnit.KILOBYTE)); + assertEquals("50 MB", format.format(_50_megabytes, ScaleUnit.MEGABYTE)); + assertEquals("0.1 GB", format.format(_50_megabytes, ScaleUnit.GIGABYTE)); + + long _999_megabytes = 999L * 1000L * 1000L; + assertEquals("999 MB", format.format(_999_megabytes)); + assertEquals("999000000 B", format.format(_999_megabytes, ScaleUnit.BYTE)); + assertEquals("999000 kB", format.format(_999_megabytes, ScaleUnit.KILOBYTE)); + assertEquals("999 MB", format.format(_999_megabytes, ScaleUnit.MEGABYTE)); + assertEquals("1.0 GB", format.format(_999_megabytes, ScaleUnit.GIGABYTE)); + + long _1000_megabytes = 1000L * 1000L * 1000L; + assertEquals("1.0 GB", format.format(_1000_megabytes)); + assertEquals("1000000000 B", format.format(_1000_megabytes, ScaleUnit.BYTE)); + assertEquals("1000000 kB", format.format(_1000_megabytes, ScaleUnit.KILOBYTE)); + assertEquals("1000 MB", format.format(_1000_megabytes, ScaleUnit.MEGABYTE)); + assertEquals("1.0 GB", format.format(_1000_megabytes, ScaleUnit.GIGABYTE)); + } + + @Test + void testNegativeProgressedSize() { + FileSizeFormat format = new FileSizeFormat(); + + long negativeProgressedSize = -100L; + assertThrows(IllegalArgumentException.class, () -> format.formatProgress(negativeProgressedSize, 10L)); + } + + @Test + void testNegativeProgressedSizeBiggerThanSize() { + FileSizeFormat format = new FileSizeFormat(); + + assertThrows(IllegalArgumentException.class, () -> format.formatProgress(100L, 10L)); + } + + @Test + void testProgressedSizeWithoutSize() { + FileSizeFormat format = new FileSizeFormat(); + + long _0_bytes = 0L; + assertEquals("0 B", format.formatProgress(_0_bytes, -1L)); + + long _1000_bytes = 1000L; + assertEquals("1.0 kB", format.formatProgress(_1000_bytes, -1L)); + + long _1000_kilobytes = 1000L * 1000L; + assertEquals("1.0 MB", format.formatProgress(_1000_kilobytes, -1L)); + + long _1000_megabytes = 1000L * 1000L * 1000L; + assertEquals("1.0 GB", format.formatProgress(_1000_megabytes, -1L)); + } + + @Test + void testProgressedBothZero() { + FileSizeFormat format = new FileSizeFormat(); + + long _0_bytes = 0L; + assertEquals("0 B", format.formatProgress(_0_bytes, _0_bytes)); + } + + @Test + void testProgressedSizeWithSize() { + FileSizeFormat format = new FileSizeFormat(); + + long _0_bytes = 0L; + long _400_bytes = 400L; + long _800_bytes = 2L * _400_bytes; + assertEquals("0/800 B", format.formatProgress(_0_bytes, _800_bytes)); + assertEquals("400/800 B", format.formatProgress(_400_bytes, _800_bytes)); + assertEquals("800 B", format.formatProgress(_800_bytes, _800_bytes)); + + long _4000_bytes = 4000L; + long _8000_bytes = 2L * _4000_bytes; + long _50_kilobytes = 50000L; + assertEquals("0/8.0 kB", format.formatProgress(_0_bytes, _8000_bytes)); + assertEquals("0.4/8.0 kB", format.formatProgress(_400_bytes, _8000_bytes)); + assertEquals("4.0/8.0 kB", format.formatProgress(_4000_bytes, _8000_bytes)); + assertEquals("8.0 kB", format.formatProgress(_8000_bytes, _8000_bytes)); + assertEquals("8.0/50 kB", format.formatProgress(_8000_bytes, _50_kilobytes)); + assertEquals("16/50 kB", format.formatProgress(2L * _8000_bytes, _50_kilobytes)); + assertEquals("50 kB", format.formatProgress(_50_kilobytes, _50_kilobytes)); + + long _500_kilobytes = 500000L; + long _1000_kilobytes = 2L * _500_kilobytes; + ; + long _5000_kilobytes = 5L * _1000_kilobytes; + long _15_megabytes = 3L * _5000_kilobytes; + assertEquals("0/5.0 MB", format.formatProgress(_0_bytes, _5000_kilobytes)); + assertEquals("0.5/5.0 MB", format.formatProgress(_500_kilobytes, _5000_kilobytes)); + assertEquals("1.0/5.0 MB", format.formatProgress(_1000_kilobytes, _5000_kilobytes)); + assertEquals("5.0 MB", format.formatProgress(_5000_kilobytes, _5000_kilobytes)); + assertEquals("5.0/15 MB", format.formatProgress(_5000_kilobytes, _15_megabytes)); + assertEquals("15 MB", format.formatProgress(_15_megabytes, _15_megabytes)); + + long _500_megabytes = 500000000L; + long _1000_megabytes = 2L * _500_megabytes; + long _5000_megabytes = 5L * _1000_megabytes; + long _15_gigabytes = 3L * _5000_megabytes; + assertEquals("0/500 MB", format.formatProgress(_0_bytes, _500_megabytes)); + assertEquals("1.0/5.0 GB", format.formatProgress(_1000_megabytes, _5000_megabytes)); + assertEquals("5.0 GB", format.formatProgress(_5000_megabytes, _5000_megabytes)); + assertEquals("5.0/15 GB", format.formatProgress(_5000_megabytes, _15_gigabytes)); + assertEquals("15 GB", format.formatProgress(_15_gigabytes, _15_gigabytes)); + } + + @Test + void testFormatRate() { + FileSizeFormat format = new FileSizeFormat(); + + // Test bytes per second + MessageBuilder builder = new DefaultMessageBuilder(); + format.formatRate(builder, 5.0); + assertEquals("5 B/s", builder.build()); + + // Test kilobytes per second + builder = new DefaultMessageBuilder(); + format.formatRate(builder, 5500.0); + assertEquals("5.5 kB/s", builder.build()); + + // Test megabytes per second + builder = new DefaultMessageBuilder(); + format.formatRate(builder, 5500000.0); + assertEquals("5.5 MB/s", builder.build()); + + // Test gigabytes per second + builder = new DefaultMessageBuilder(); + format.formatRate(builder, 5500000000.0); + assertEquals("5.5 GB/s", builder.build()); + } + + @Test + void testFormatRateThresholds() { + FileSizeFormat format = new FileSizeFormat(); + + // Test value less than 0.05 + MessageBuilder builder = new DefaultMessageBuilder(); + format.formatRate(builder, 45.0); // 45 B/s + assertEquals("45 B/s", builder.build()); + + // Test value greater than or equal to 10 + builder = new DefaultMessageBuilder(); + format.formatRate(builder, 15000.0); // 15 kB/s + assertEquals("15 kB/s", builder.build()); + + // Test value between 0.05 and 10 + builder = new DefaultMessageBuilder(); + format.formatRate(builder, 5500.0); // 5.5 kB/s + assertEquals("5.5 kB/s", builder.build()); + } + + @Test + void testFormatRateEdgeCases() { + FileSizeFormat format = new FileSizeFormat(); + + // Test zero rate + MessageBuilder builder = new DefaultMessageBuilder(); + format.formatRate(builder, 0.0); + assertEquals("0 B/s", builder.build()); + + // Test rate at exactly 1000 (1 kB/s) + builder = new DefaultMessageBuilder(); + format.formatRate(builder, 1000.0); + assertEquals("1.0 kB/s", builder.build()); + + // Test rate at exactly 1000000 (1 MB/s) + builder = new DefaultMessageBuilder(); + format.formatRate(builder, 1000000.0); + assertEquals("1.0 MB/s", builder.build()); + } +} diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/SimplexTransferListenerTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/SimplexTransferListenerTest.java new file mode 100644 index 0000000000..36abafaf5b --- /dev/null +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/SimplexTransferListenerTest.java @@ -0,0 +1,112 @@ +/* + * 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. + */ +package org.apache.maven.cling.transfer; + +import java.io.File; + +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.transfer.TransferCancelledException; +import org.eclipse.aether.transfer.TransferEvent; +import org.eclipse.aether.transfer.TransferListener; +import org.eclipse.aether.transfer.TransferResource; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +class SimplexTransferListenerTest { + @Test + void cancellation() throws InterruptedException { + TransferListener delegate = new TransferListener() { + @Override + public void transferInitiated(TransferEvent event) throws TransferCancelledException { + throw new TransferCancelledException(); + } + + @Override + public void transferStarted(TransferEvent event) throws TransferCancelledException { + throw new TransferCancelledException(); + } + + @Override + public void transferProgressed(TransferEvent event) throws TransferCancelledException { + throw new TransferCancelledException(); + } + + @Override + public void transferCorrupted(TransferEvent event) throws TransferCancelledException { + throw new TransferCancelledException(); + } + + @Override + public void transferSucceeded(TransferEvent event) {} + + @Override + public void transferFailed(TransferEvent event) {} + }; + + SimplexTransferListener listener = new SimplexTransferListener(delegate); + + TransferResource resource = + new TransferResource(null, null, "http://maven.org/test/test-resource", new File("file"), null); + DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(h -> false); // no close handle + + // for technical reasons we cannot throw here, even if delegate does cancel transfer + listener.transferInitiated(event(session, resource, TransferEvent.EventType.INITIATED)); + + Thread.sleep(500); // to make sure queue is processed, cancellation applied + + // subsequent call will cancel + assertThrows( + TransferCancelledException.class, + () -> listener.transferStarted(event(session, resource, TransferEvent.EventType.STARTED))); + } + + @Test + void handlesAbsentTransferSource() throws InterruptedException, TransferCancelledException { + TransferResource resource = new TransferResource(null, null, "http://maven.org/test/test-resource", null, null); + + RepositorySystemSession session = Mockito.mock(RepositorySystemSession.class); + TransferListener delegate = Mockito.mock(TransferListener.class); + SimplexTransferListener listener = new SimplexTransferListener(delegate); + + TransferEvent transferInitiatedEvent = event(session, resource, TransferEvent.EventType.INITIATED); + TransferEvent transferStartedEvent = event(session, resource, TransferEvent.EventType.STARTED); + TransferEvent transferProgressedEvent = event(session, resource, TransferEvent.EventType.PROGRESSED); + TransferEvent transferSucceededEvent = event(session, resource, TransferEvent.EventType.SUCCEEDED); + + listener.transferInitiated(transferInitiatedEvent); + listener.transferStarted(transferStartedEvent); + listener.transferProgressed(transferProgressedEvent); + listener.transferSucceeded(transferSucceededEvent); + + Thread.sleep(500); // to make sure queue is processed, cancellation applied + + Mockito.verify(delegate).transferInitiated(transferInitiatedEvent); + Mockito.verify(delegate).transferStarted(transferStartedEvent); + Mockito.verify(delegate).transferProgressed(transferProgressedEvent); + Mockito.verify(delegate).transferSucceeded(transferSucceededEvent); + } + + private static TransferEvent event( + RepositorySystemSession session, TransferResource resource, TransferEvent.EventType type) { + return new TransferEvent.Builder(session, resource).setType(type).build(); + } +} diff --git a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/AbstractSession.java b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/AbstractSession.java index e0a59e07ea..a224563131 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/AbstractSession.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/AbstractSession.java @@ -44,6 +44,7 @@ import org.apache.maven.api.Exclusion; import org.apache.maven.api.Language; import org.apache.maven.api.Listener; import org.apache.maven.api.LocalRepository; +import org.apache.maven.api.MonotonicClock; import org.apache.maven.api.Node; import org.apache.maven.api.Packaging; import org.apache.maven.api.PathScope; @@ -92,6 +93,7 @@ import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.ArtifactType; +import org.eclipse.aether.transfer.TransferResource; import static org.apache.maven.internal.impl.Utils.map; import static org.apache.maven.internal.impl.Utils.nonNull; @@ -113,6 +115,10 @@ public abstract class AbstractSession implements InternalSession { private final Map<org.eclipse.aether.graph.Dependency, Dependency> allDependencies = Collections.synchronizedMap(new WeakHashMap<>()); + static { + TransferResource.setClock(MonotonicClock.get()); + } + public AbstractSession( RepositorySystemSession session, RepositorySystem repositorySystem,