diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..c1f9dfd --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,90 @@ +name: Java + +on: [push] + +env: + time: 3 + time_windows: 5 + +jobs: + format-check: + name: Check Code Formatting + continue-on-error: true + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 1 + - name: Check Format + run: | + mvn --batch-mode -Pverify-format clean compile + shell: 'bash' + + codecov: + name: Codecov + continue-on-error: true + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: 21 + distribution: 'temurin' +# - name: Install dependencies +# run: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V + - name: Run tests and collect coverage + timeout-minutes: ${{ fromJSON(env.time) }} + run: mvn -B test + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v4 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + build: + runs-on: ubuntu-latest + strategy: + matrix: + # run different builds with the listed java versions + java: [ 8, 21 ] + name: "build-ubuntu Java ${{ matrix.java }}" + steps: + - uses: actions/checkout@v4 + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: ${{ matrix.java }} + distribution: 'temurin' + - name: Build with Maven + timeout-minutes: ${{ fromJSON(env.time) }} + run: mvn --batch-mode --update-snapshots install + + + build-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: 8 + distribution: 'temurin' + - name: Build with Maven + timeout-minutes: ${{ fromJSON(env.time_windows) }} + run: mvn --batch-mode --update-snapshots install + + + build-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: 17 + distribution: 'temurin' + - name: Build with Maven + timeout-minutes: ${{ fromJSON(env.time) }} + run: mvn --batch-mode --update-snapshots install \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3126a5c..8390539 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. [#14](https://github.com/netsec-ethz/scion-java-multiping/pull/14) - Added output of median and average values [#15](https://github.com/netsec-ethz/scion-java-multiping/pull/15) +- Simple CI script to test formatting and JUnit + [#15](https://github.com/netsec-ethz/scion-java-multiping/pull/15) ### Fixed diff --git a/src/main/java/org/scion/multiping/PingAll.java b/src/main/java/org/scion/multiping/PingAll.java index d6d6f36..360eb90 100644 --- a/src/main/java/org/scion/multiping/PingAll.java +++ b/src/main/java/org/scion/multiping/PingAll.java @@ -24,8 +24,10 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.ToDoubleFunction; import java.util.stream.Collectors; - import org.scion.jpan.*; import org.scion.jpan.internal.PathRawParser; import org.scion.multiping.util.*; @@ -89,7 +91,7 @@ private enum Policy { } private static final Policy POLICY = Policy.FASTEST_TR_ASYNC; - private static final boolean SHOW_PATH = !true; + private static final boolean SHOW_PATH = false; public static void main(String[] args) throws IOException { PRINT = true; @@ -124,18 +126,12 @@ public static void main(String[] args) throws IOException { Result maxPaths = results.stream().max(Comparator.comparingInt(Result::getPathCount)).get(); // avg/median: - double avgPing = - results.stream().filter(Result::isSuccess).mapToDouble(Result::getPingMs).average().orElse(-1); - double avgHops = - results.stream().filter(r -> r.getHopCount() > 0).mapToInt(Result::getHopCount).average().orElse(-1); - double avgPaths = - results.stream().filter(r -> r.getPathCount() > 0).mapToInt(Result::getPathCount).average().orElse(-1); - List pings = results.stream().filter(Result::isSuccess).map(Result::getPingMs).sorted().collect(Collectors.toList()); - double medianPing = pings.isEmpty() ? -1 : pings.get(pings.size() / 2); - List hops = results.stream().map(Result::getHopCount).filter(hopCount -> hopCount > 0).sorted().collect(Collectors.toList()); - int medianHops = hops.isEmpty() ? -1 : hops.get(hops.size() / 2); - List paths = results.stream().map(Result::getPathCount).filter(pathCount -> pathCount > 0).sorted().collect(Collectors.toList()); - int medianPaths = paths.isEmpty() ? -1 : paths.get(paths.size() / 2); + double avgPing = avg(results, Result::isSuccess, Result::getPingMs); + double avgHops = avg(results, r -> r.getHopCount() > 0, Result::getHopCount); + double avgPaths = avg(results, r -> r.getPathCount() > 0, Result::getPathCount); + double medianPing = median(results, Result::isSuccess, Result::getPingMs).orElse(-1.0); + int medianHops = median(results, r -> r.getHopCount() > 0, Result::getHopCount).orElse(-1); + int medianPaths = median(results, r -> r.getPathCount() > 0, Result::getPathCount).orElse(-1); println(""); println("Max hops = " + maxHops.getHopCount() + ": " + maxHops); @@ -404,15 +400,16 @@ public void onException(Throwable t) { } } - // Wait for all messages to be received - try { - if (!barrier.await(1100, TimeUnit.MILLISECONDS)) { - throw new IllegalStateException("Missing messages: " + barrier.getCount() + "/" + paths.size()); - } - } catch (InterruptedException e) { + // Wait for all messages to be received + try { + if (!barrier.await(1100, TimeUnit.MILLISECONDS)) { + throw new IllegalStateException( + "Missing messages: " + barrier.getCount() + "/" + paths.size()); + } + } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new IllegalStateException(e); - } + } } catch (IOException e) { println("ERROR: " + e.getMessage()); @@ -441,4 +438,15 @@ public void onException(Throwable t) { } return best; } + + private static double avg( + List list, Predicate filter, ToDoubleFunction mapper) { + return list.stream().filter(filter).mapToDouble(mapper).average().orElse(-1); + } + + private static Optional median( + List list, Predicate filter, Function mapper) { + List list2 = list.stream().filter(filter).map(mapper).sorted().collect(Collectors.toList()); + return list2.isEmpty() ? Optional.empty() : Optional.of(list2.get(list2.size() / 2)); + } } diff --git a/src/main/java/org/scion/multiping/util/DownloadAssignmentsFromWeb.java b/src/main/java/org/scion/multiping/util/DownloadAssignmentsFromWeb.java index aad5709..12c2c28 100644 --- a/src/main/java/org/scion/multiping/util/DownloadAssignmentsFromWeb.java +++ b/src/main/java/org/scion/multiping/util/DownloadAssignmentsFromWeb.java @@ -17,7 +17,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; - import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; @@ -25,7 +24,7 @@ public class DownloadAssignmentsFromWeb { private static final String HTTPS_URL = - "https://docs.anapaya.net/en/latest/resources/isd-as-assignments/"; + "https://docs.anapaya.net/en/latest/resources/isd-as-assignments/"; public static void main(String[] args) throws IOException { new DownloadAssignmentsFromWeb().jsoup(); diff --git a/src/test/java/org/scion/multiping/util/DonwloaderTest.java b/src/test/java/org/scion/multiping/util/DonwloaderTest.java new file mode 100644 index 0000000..24d10fd --- /dev/null +++ b/src/test/java/org/scion/multiping/util/DonwloaderTest.java @@ -0,0 +1,39 @@ +// Copyright 2025 ETH Zurich +// +// 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. + +package org.scion.multiping.util; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import org.junit.jupiter.api.Test; +import org.scion.jpan.ScionUtil; + +class DonwloaderTest { + + @Test + void findAssignments() { + List list = DownloadAssignmentsFromWeb.getList(); + assertFalse(list.isEmpty()); + + boolean found = false; + for (ParseAssignments.HostEntry e : list) { + if (e.getIsdAs() == ScionUtil.parseIA("64-2:0:9")) { + found = true; + break; + } + } + assertTrue(found); + } +}