diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 7ef52520..64f27af2 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -9,18 +9,24 @@ on: workflow_dispatch: jobs: build: - runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 4 + matrix: + os: [ubuntu-latest, windows-latest] + java_version: [17, 21] + runs-on: ${{ matrix.os }} concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.os }}-${{ matrix.java_version }} steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: distribution: 'adopt' - java-version: 17 + java-version: ${{ matrix.java_version }} cache: 'maven' - name: Build with Maven # Maven deploy on changes (or manual run) to master, otherwise just package it - run: mvn -s "${GITHUB_WORKSPACE}/.github/workflows/maven-settings.xml" -B ${{ (((github.event_name == 'push') || (github.event_name == 'workflow_dispatch')) && (github.ref == 'refs/heads/master')) && 'deploy' || 'package' }} --file pom.xml -Prelease,release-snapshot + run: mvn -s ".github/workflows/maven-settings.xml" -B ${{ (((github.event_name == 'push') || (github.event_name == 'workflow_dispatch')) && (github.ref == 'refs/heads/master') && (matrix.os == 'ubuntu-latest') && (matrix.java_version == '17')) && 'deploy' || 'package' }} --file pom.xml "-Prelease,release-snapshot" env: GITHUB_TOKEN: ${{ github.token }} diff --git a/ax-compiler/src/test/java/com/g2forge/alexandria/compiler/TestDynamicJavaCompiler.java b/ax-compiler/src/test/java/com/g2forge/alexandria/compiler/TestDynamicJavaCompiler.java index af287f2e..8b295062 100644 --- a/ax-compiler/src/test/java/com/g2forge/alexandria/compiler/TestDynamicJavaCompiler.java +++ b/ax-compiler/src/test/java/com/g2forge/alexandria/compiler/TestDynamicJavaCompiler.java @@ -8,12 +8,12 @@ import javax.tools.Diagnostic; import javax.tools.JavaFileObject; +import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; import com.g2forge.alexandria.compiler.diagnostic.DataDiagnostic; import com.g2forge.alexandria.compiler.diagnostic.DiagnosticMatcher; -import com.g2forge.alexandria.java.core.helpers.HCollection; import com.g2forge.alexandria.test.HAssert; public class TestDynamicJavaCompiler { @@ -34,7 +34,7 @@ public void error() throws ClassNotFoundException, URISyntaxException { new DynamicJavaCompiler().compile("foo.bar.MyClass", text); } catch (DynamicJavaCompilerException exception) { final List> diagnostics = exception.getDiagnostics().stream().filter(d -> !d.getCode().startsWith("compiler.warn.proc.")).collect(Collectors.toList()); - HAssert.assertThat(HCollection.getOne(diagnostics), new DiagnosticMatcher<>(new DataDiagnostic<>(Diagnostic.Kind.ERROR, null, 1l, text.indexOf("String"), "compiler.err.not.within.bounds", null))); + HAssert.assertThat(diagnostics, Matchers.hasItem(new DiagnosticMatcher<>(new DataDiagnostic<>(Diagnostic.Kind.ERROR, null, 1l, text.indexOf("String"), "compiler.err.not.within.bounds", null)))); } } diff --git a/ax-java/src/test/java/com/g2forge/alexandria/java/io/watch/TestFileWatcher.java b/ax-java/src/test/java/com/g2forge/alexandria/java/io/watch/TestFileWatcher.java index c91e336e..0c6023eb 100644 --- a/ax-java/src/test/java/com/g2forge/alexandria/java/io/watch/TestFileWatcher.java +++ b/ax-java/src/test/java/com/g2forge/alexandria/java/io/watch/TestFileWatcher.java @@ -1,5 +1,6 @@ package com.g2forge.alexandria.java.io.watch; +import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; @@ -10,6 +11,7 @@ import java.nio.file.WatchEvent; import java.nio.file.WatchEvent.Kind; import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -44,12 +46,12 @@ public SimpleWatchEvent(WatchEvent watchEvent) { @Test public void create() throws IOException, InterruptedException { - test(null, (temp, file) -> writeFile(file, new byte[] { 0 }, StandardOpenOption.CREATE_NEW), StandardWatchEventKinds.ENTRY_CREATE); + test(null, (temp, file) -> writeFile(file, new byte[] { 0, 1, 2, 3 }, StandardOpenOption.CREATE_NEW), StandardWatchEventKinds.ENTRY_CREATE); } @Test public void delete() throws IOException, InterruptedException { - test((temp, file) -> writeFile(file, new byte[] { 0 }, StandardOpenOption.CREATE_NEW), (temp, file) -> { + test((temp, file) -> writeFile(file, new byte[] { 0, 1, 2, 3 }, StandardOpenOption.CREATE_NEW), (temp, file) -> { try { Files.delete(file); } catch (IOException exception) { @@ -60,13 +62,15 @@ public void delete() throws IOException, InterruptedException { @Test public void modify() throws IOException, InterruptedException { - test((temp, file) -> writeFile(file, new byte[] { 0 }, StandardOpenOption.CREATE_NEW), (temp, file) -> writeFile(file, new byte[] { 1 }, StandardOpenOption.TRUNCATE_EXISTING), StandardWatchEventKinds.ENTRY_MODIFY); + test((temp, file) -> writeFile(file, new byte[] { 0, 1, 2, 3 }, StandardOpenOption.CREATE_NEW), (temp, file) -> writeFile(file, new byte[] { 1 }, StandardOpenOption.TRUNCATE_EXISTING), StandardWatchEventKinds.ENTRY_MODIFY); } @SafeVarargs protected final void test(final IConsumer2 prep, final IConsumer2 modify, final Kind... kinds) throws InterruptedException { - try (final ICloseableSupplier temp = new TempDirectory(); final FileWatcher watcher = new FileWatcher()) { + try (final ICloseableSupplier temp = new TempDirectory(); + final FileWatcher watcher = new FileWatcher()) { final Path file = temp.get().resolve("file"); + final Object delay = new Object(); // Prepare for testing if (prep != null) prep.accept(temp.get(), file); @@ -79,36 +83,51 @@ protected final void test(final IConsumer2 prep, final IConsumer2 expected = Stream.of(kinds).map(k -> new SimpleWatchEvent(k, file.getFileName())).collect(Collectors.toSet()); - synchronized (events) { - // Test for expected events - events.wait(1000); - - final Set actual = events.stream().map(SimpleWatchEvent::new).collect(Collectors.toSet()); - events.clear(); - Assert.assertEquals(expected, actual); - - // Make sure no more came in - events.wait(100); - Assert.assertEquals(HCollection.emptyList(), events.stream().map(SimpleWatchEvent::new).collect(Collectors.toList())); + final Set expected = Stream.of(kinds).map(k -> new SimpleWatchEvent(k, file.getFileName())).collect(Collectors.toCollection(LinkedHashSet::new)); + int i = 0; + while (!expected.isEmpty() && (i < 20)) { + final Set actual; + synchronized (events) { + actual = events.stream().map(SimpleWatchEvent::new).collect(Collectors.toCollection(LinkedHashSet::new)); + events.clear(); + } + // Mark everything we found + expected.removeAll(actual); + + // Wait a little while before we bother checking again + synchronized (delay) { + delay.wait(100); + } + + i++; } + + // Make sure no more came in + Assert.assertEquals(new LinkedHashSet<>(), expected); } } protected void writeFile(final Path file, byte[] bytes, OpenOption... options) { try (final OutputStream output = Files.newOutputStream(file, options)) { output.write(bytes); + output.flush(); + + if (output instanceof FileOutputStream) { + @SuppressWarnings("resource") + final FileOutputStream fileOutputStream = (FileOutputStream) output; + fileOutputStream.getChannel().force(true); + fileOutputStream.getFD().sync(); + } } catch (IOException exception) { throw new RuntimeIOException(exception); }