Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ jobs:
JAVA_TOOL_OPTIONS: >-
-XX:+UseCompactObjectHeaders
-XX:+UseStringDeduplication
--enable-native-access=ALL-UNNAMED
GRADLE_OPTS: -Dorg.gradle.daemon=false
defaults:
run:
Expand Down Expand Up @@ -119,9 +120,9 @@ jobs:
shell: bash
run: |
if [[ "${{ runner.os }}" == "Linux" ]]; then
./gradlew test jacocoTestReport --no-daemon --stacktrace
./gradlew test jacocoTestReport --no-daemon --stacktrace --quiet
else
./gradlew test --no-daemon --stacktrace
./gradlew test --no-daemon --stacktrace --quiet
fi

- name: Upload coverage to Coveralls
Expand Down
6 changes: 6 additions & 0 deletions de.peeeq.wurstscript/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,12 @@ tasks.register('ensureStdLib', JavaExec) {
test {
dependsOn 'ensureStdLib'
useTestNG()
// Keep CI output focused on failures; compiler/runtime tests intentionally exercise native
// print paths and can otherwise flood the log with expected diagnostic output.
testLogging {
showStandardStreams = false
events 'failed', 'skipped'
}

// The suite is a few thousand independent compilations and was running one at a time, so it
// took as long as the sum of them. Forks rather than threads: the harness keeps state in
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/**
* Legacy Jass attribute support.
* @deprecated This internal compatibility package is planned for removal.
*/
@org.eclipse.jdt.annotation.NonNullByDefault
@Deprecated
package de.peeeq.wurstscript.frotty.jassAttributes;

Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/**
* Legacy Jass validation support.
* @deprecated This internal compatibility package is planned for removal.
*/
@org.eclipse.jdt.annotation.NonNullByDefault
@Deprecated
package de.peeeq.wurstscript.frotty.jassValidator;

Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/**
* Legacy Jurst compatibility frontend. Prefer Wurst source for new code.
* @deprecated Jurst is retained only for compatibility and is planned for removal.
*/
@org.eclipse.jdt.annotation.NonNullByDefault
@Deprecated
package de.peeeq.wurstscript.jurst;

Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public V put(K key, V value) {
V oldV = base.put(key, value);
if (oldV != null) {
for (BiConsumer<K, V> f : onDelete) {
f.accept(key, value);
f.accept(key, oldV);
}
}
for (BiConsumer<K, V> f : onInserts) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package de.peeeq.wurstscript.attributes;

import de.peeeq.wurstscript.gui.WurstGuiCliImpl;
import de.peeeq.wurstscript.parser.WPos;
import org.testng.annotations.Test;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertSame;
import static org.testng.Assert.assertTrue;

public class ErrorHandlerTests {
@Test
public void tracksErrorsWarningsAndPerFileBuckets() {
WurstGuiCliImpl gui = new WurstGuiCliImpl();
ErrorHandler handler = new ErrorHandler(gui);
CompileError error = new CompileError(new WPos("one.wurst", null, 1, 1), "bad");
CompileError warning = new CompileError(new WPos("one.wurst", null, 2, 1), "careful",
CompileError.ErrorType.WARNING);

handler.sendError(error);
handler.sendError(warning);

assertEquals(handler.getErrorCount(), 1);
assertEquals(handler.getErrors(), java.util.List.of(error));
assertEquals(handler.getWarnings(), java.util.List.of(warning));
assertEquals(handler.getBucketForFile("one.wurst", CompileError.ErrorType.ERROR), java.util.List.of(error));
assertEquals(handler.getBucketForFile("one.wurst", CompileError.ErrorType.WARNING), java.util.List.of(warning));
assertEquals(gui.getErrorList().size(), 1);

handler.removeFromGlobal(error);
handler.removeFromGlobal(warning);
assertTrue(handler.getErrors().isEmpty());
assertTrue(handler.getWarnings().isEmpty());
assertEquals(handler.getBucketForFile("one.wurst", CompileError.ErrorType.ERROR), null);
assertEquals(handler.getBucketForFile("one.wurst", CompileError.ErrorType.WARNING), null);
}

@Test
public void exposesGuiAndUnitTestMode() {
WurstGuiCliImpl gui = new WurstGuiCliImpl();
ErrorHandler handler = new ErrorHandler(gui);

assertSame(handler.getGui(), gui);
assertFalse(handler.isUnitTestMode());
handler.enableUnitTestMode();
assertTrue(handler.isUnitTestMode());
assertFalse(handler.isOutputTestSource());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package tests.utils;

import de.peeeq.wurstscript.utils.Lazy;
import de.peeeq.wurstscript.utils.NotNullList;
import de.peeeq.wurstscript.utils.Pair;
import org.testng.annotations.Test;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.expectThrows;

public class CoreUtilitiesTests {
@Test
public void lazySupplierRunsExactlyOnceIncludingNullValues() {
AtomicInteger calls = new AtomicInteger();
Lazy<String> lazy = Lazy.create(() -> {
calls.incrementAndGet();
return null;
});

assertEquals(lazy.get(), null);
assertEquals(lazy.get(), null);
assertEquals(calls.get(), 1);
}

@Test
public void notNullListRejectsNullThroughEveryMutationPath() {
NotNullList<String> values = new NotNullList<>();
values.add("a");
values.add(1, "b");
values.addAll(List.of("c", "d"));
values.addAll(1, List.of("x"));
values.set(0, "z");
assertEquals(values, List.of("z", "x", "b", "c", "d"));

expectThrows(IllegalArgumentException.class, () -> values.add(null));
expectThrows(IllegalArgumentException.class, () -> values.add(0, null));
expectThrows(IllegalArgumentException.class, () -> values.addAll(Arrays.asList("ok", null)));
expectThrows(IllegalArgumentException.class, () -> values.addAll(0, Arrays.asList((String) null)));
expectThrows(IllegalArgumentException.class, () -> values.set(0, null));
}

@Test
public void pairProvidesValueEqualityAndAccessors() {
Pair<String, Integer> first = Pair.create("value", 7);
Pair<String, Integer> equal = Pair.create("value", 7);
Pair<String, Integer> different = Pair.create("other", 7);

assertEquals(first.getA(), "value");
assertEquals(first.getB(), 7);
assertEquals(first.toString(), "(value, 7)");
assertEquals(first, equal);
assertEquals(first.hashCode(), equal.hashCode());
assertFalse(first.equals(different));
assertEquals(Pair.create(null, 7), Pair.create(null, 7));
assertFalse(first.equals("value"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package tests.utils;

import de.peeeq.wurstscript.utils.LineOffsets;
import org.testng.annotations.Test;

import static org.testng.Assert.assertEquals;

public class LineOffsetsTests {
@Test
public void resolvesPreviousOffsetsAndColumns() {
LineOffsets offsets = new LineOffsets();
offsets.set(1, 5);
offsets.set(2, 10);

assertEquals(offsets.get(0), -1);
assertEquals(offsets.get(1), 5);
assertEquals(offsets.get(3), 10);
assertEquals(offsets.getLine(7), 2);
assertEquals(offsets.getColumn(7), 2);
}

@Test
public void growsForLargeLineNumbersAndClampsQueries() {
LineOffsets offsets = new LineOffsets();
offsets.set(256, 1000);

assertEquals(offsets.get(256), 1000);
assertEquals(offsets.get(10000), 1000);
assertEquals(offsets.getLine(1000), 256);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package tests.utils;

import de.peeeq.wurstscript.utils.MapWithIndexes;
import org.testng.annotations.Test;

import java.util.List;
import java.util.Set;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;

public class MapWithIndexesTests {
private record Item(String group, Set<String> tags, boolean active) {
}

@Test
public void indexesStayConsistentWhenValuesAreReplaced() {
MapWithIndexes<String, Item> items = new MapWithIndexes<>();
MapWithIndexes.Index<String, String> groups = items.createIndex(Item::group);
MapWithIndexes.PredIndex<String> active = items.createPredicateIndex(Item::active);
MapWithIndexes.Index<String, String> tags = items.createMultiIndex(Item::tags);

items.put("one", new Item("red", Set.of("warm", "bright"), true));
assertEquals(groups.lookup("red"), List.of("one"));
assertEquals(active.lookup(), List.of("one"));
assertEquals(tags.lookup("warm"), List.of("one"));

items.put("one", new Item("blue", Set.of("cold"), false));
assertTrue(groups.lookup("red").isEmpty());
assertEquals(groups.lookup("blue"), List.of("one"));
assertTrue(active.lookup().isEmpty());
assertTrue(tags.lookup("warm").isEmpty());
assertEquals(tags.lookup("cold"), List.of("one"));
}

@Test
public void removeAllAndClearUpdateEveryIndex() {
MapWithIndexes<String, Item> items = new MapWithIndexes<>();
MapWithIndexes.Index<String, String> groups = items.createIndex(Item::group);
items.put("one", new Item("red", Set.of(), true));
items.put("two", new Item("red", Set.of(), true));
items.put("three", new Item("blue", Set.of(), true));

items.removeAll(List.of("one", "three"));
assertEquals(items.keySet(), Set.of("two"));
assertEquals(groups.lookup("red"), List.of("two"));
assertTrue(groups.lookup("blue").isEmpty());

items.clear();
assertTrue(items.isEmpty());
assertTrue(groups.lookup("red").isEmpty());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,29 @@ public void generatedProgramsAreCrashFree(@From(RandomProgram.class) Program pro
Assert.assertNotNull(result.getGui());
}

@Property(maxInvocations = 64)
public void generatedProgramsCompileForBothBackends(@From(RandomProgram.class) Program program) {
assertCompilesForBothBackends(program);
}

@Test
public void generatedCorpusCompilesForBothBackends() {
new RandomProgram().generate(0).forEach(this::assertCompilesForBothBackends);
}

private void assertCompilesForBothBackends(Program program) {
CompilationResult result = test()
.setStopOnFirstError(false)
.executeProg(false)
.testLua(true)
.luaOnly(false)
.compilationUnits(asCompilationUnits(program));

Assert.assertTrue(result.getGui().getErrorList().isEmpty(),
"generated program produced compiler diagnostics: " + result.getGui().getErrorList()
+ "\nsource:\n" + String.join("\n---\n", program.sources));
}

@Property(maxInvocations = 180)
public void mixedNewlineStylesAreCrashFree(@From(RandomProgram.class) Program program) {
String alternateNewline = "\n".equals(program.newline) ? "\r\n" : "\n";
Expand Down Expand Up @@ -173,7 +196,7 @@ private static String buildRandomSingleProgram(int seed, String newline) {

if (includeInterface) {
lines.add("interface IHandler");
lines.add(indent + "function handle(int value) returns int");
lines.add(indent + "function process(int value) returns int");
}

lines.add("class Counter");
Expand All @@ -193,7 +216,7 @@ private static String buildRandomSingleProgram(int seed, String newline) {

if (includeInterface) {
lines.add("class Sink implements IHandler");
lines.add(indent + "function handle(int value) returns int");
lines.add(indent + "function process(int value) returns int");
lines.add(indent + indent + "return value + base");
}

Expand Down Expand Up @@ -222,7 +245,7 @@ private static String buildRandomSingleProgram(int seed, String newline) {

if (includeInterface) {
lines.add(indent + "IHandler handler = new Sink()");
lines.add(indent + "total = handler.handle(total)");
lines.add(indent + "total = handler.process(total)");
}

if (includeTuple) {
Expand Down
Loading
Loading