Refactor ounit functions

This commit is contained in:
aNNiMON 2024-02-10 22:30:10 +02:00
parent 03d890df7c
commit 31945471dd
3 changed files with 130 additions and 139 deletions

View File

@ -5,7 +5,6 @@ import com.annimon.ownlang.lib.*;
import com.annimon.ownlang.modules.Module; import com.annimon.ownlang.modules.Module;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -22,110 +21,91 @@ public final class ounit implements Module {
@Override @Override
public Map<String, Function> functions() { public Map<String, Function> functions() {
final var result = new LinkedHashMap<String, Function>(16); return Map.of(
result.put("assertEquals", new assertEquals()); "assertEquals", this::assertEquals,
result.put("assertNotEquals", new assertNotEquals()); "assertNotEquals", this::assertNotEquals,
result.put("assertSameType", new assertSameType()); "assertSameType", this::assertSameType,
result.put("assertTrue", new assertTrue()); "assertTrue", this::assertTrue,
result.put("assertFalse", new assertFalse()); "assertFalse", this::assertFalse,
result.put("runTests", new runTests()); "runTests", this::runTests
return result; );
} }
private static String microsToSeconds(long micros) { private static String microsToSeconds(long micros) {
return new DecimalFormat("#0.0000").format(micros / 1000d / 1000d) + " sec"; return new DecimalFormat("#0.0000").format(micros / 1000d / 1000d) + " sec";
} }
private static class assertEquals implements Function { private Value assertEquals(Value[] args) {
@Override Arguments.check(2, args.length);
public Value execute(Value[] args) { if (args[0].equals(args[1])) return NumberValue.ONE;
Arguments.check(2, args.length); throw new OUnitAssertionException("Values are not equal: "
if (args[0].equals(args[1])) return NumberValue.ONE; + "1: " + args[0] + ", 2: " + args[1]);
throw new OUnitAssertionException("Values are not equal: "
+ "1: " + args[0] + ", 2: " + args[1]);
}
} }
private static class assertNotEquals implements Function { private Value assertNotEquals(Value[] args) {
@Override Arguments.check(2, args.length);
public Value execute(Value[] args) { if (!args[0].equals(args[1])) return NumberValue.ONE;
Arguments.check(2, args.length); throw new OUnitAssertionException("Values are equal: " + args[0]);
if (!args[0].equals(args[1])) return NumberValue.ONE;
throw new OUnitAssertionException("Values are equal: " + args[0]);
}
} }
private static class assertSameType implements Function { private Value assertSameType(Value[] args) {
@Override Arguments.check(2, args.length);
public Value execute(Value[] args) { if (args[0].type() == args[1].type()) return NumberValue.ONE;
Arguments.check(2, args.length); throw new OUnitAssertionException("Types mismatch. "
if (args[0].type() == args[1].type()) return NumberValue.ONE; + "1: " + Types.typeToString(args[0].type())
throw new OUnitAssertionException("Types mismatch. " + ", 2: " + Types.typeToString(args[1].type()));
+ "1: " + Types.typeToString(args[0].type())
+ ", 2: " + Types.typeToString(args[1].type()));
}
} }
private static class assertTrue implements Function { private Value assertTrue(Value[] args) {
@Override Arguments.check(1, args.length);
public Value execute(Value[] args) { if (args[0].asInt() != 0) return NumberValue.ONE;
Arguments.check(1, args.length); throw new OUnitAssertionException("Expected true, but found false.");
if (args[0].asInt() != 0) return NumberValue.ONE;
throw new OUnitAssertionException("Expected true, but found false.");
}
} }
private static class assertFalse implements Function { private Value assertFalse(Value[] args) {
@Override Arguments.check(1, args.length);
public Value execute(Value[] args) { if (args[0].asInt() == 0) return NumberValue.ONE;
Arguments.check(1, args.length); throw new OUnitAssertionException("Expected false, but found true.");
if (args[0].asInt() == 0) return NumberValue.ONE;
throw new OUnitAssertionException("Expected false, but found true.");
}
} }
private static class runTests implements Function { private Value runTests(Value[] args) {
final var testFunctions = ScopeHandler.functions().entrySet().stream()
.filter(e -> e.getKey().toLowerCase().startsWith("test"))
.toList();
List<TestInfo> tests = testFunctions.stream()
.map(e -> runTest(e.getKey(), e.getValue()))
.toList();
@Override int failures = 0;
public Value execute(Value[] args) { long summaryTime = 0;
final var testFunctions = ScopeHandler.functions().entrySet().stream() final StringBuilder result = new StringBuilder();
.filter(e -> e.getKey().toLowerCase().startsWith("test")) for (TestInfo test : tests) {
.toList(); if (!test.isPassed) failures++;
List<TestInfo> tests = testFunctions.stream() summaryTime += test.elapsedTimeInMicros;
.map(e -> runTest(e.getKey(), e.getValue()))
.toList();
int failures = 0;
long summaryTime = 0;
final StringBuilder result = new StringBuilder();
for (TestInfo test : tests) {
if (!test.isPassed) failures++;
summaryTime += test.elapsedTimeInMicros;
result.append(Console.newline());
result.append(test.info());
}
result.append(Console.newline()); result.append(Console.newline());
result.append(String.format("Tests run: %d, Failures: %d, Time elapsed: %s", result.append(test.info());
tests.size(), failures,
microsToSeconds(summaryTime)));
return new StringValue(result.toString());
} }
result.append(Console.newline());
result.append(String.format("Tests run: %d, Failures: %d, Time elapsed: %s",
tests.size(), failures,
microsToSeconds(summaryTime)));
return new StringValue(result.toString());
}
private TestInfo runTest(String name, Function f) { private TestInfo runTest(String name, Function f) {
final long startTime = System.nanoTime(); final long startTime = System.nanoTime();
boolean isSuccessfull; boolean isSuccessfull;
String failureDescription; String failureDescription;
try { try {
f.execute(); f.execute();
isSuccessfull = true; isSuccessfull = true;
failureDescription = ""; failureDescription = "";
} catch (OUnitAssertionException oae) { } catch (OUnitAssertionException oae) {
isSuccessfull = false; isSuccessfull = false;
failureDescription = oae.getMessage(); failureDescription = oae.getMessage();
}
final long elapsedTime = System.nanoTime() - startTime;
return new TestInfo(name, isSuccessfull, failureDescription, elapsedTime / 1000);
} }
final long elapsedTime = System.nanoTime() - startTime;
return new TestInfo(name, isSuccessfull, failureDescription, elapsedTime / 1000);
} }
private static class OUnitAssertionException extends RuntimeException { private static class OUnitAssertionException extends RuntimeException {
@ -142,7 +122,7 @@ public final class ounit implements Module {
long elapsedTimeInMicros long elapsedTimeInMicros
) { ) {
public String info() { public String info() {
return String.format("%s [%s]\n%sElapsed: %s\n", return "%s [%s]\n%sElapsed: %s\n".formatted(
name, name,
isPassed ? "passed" : "FAILED", isPassed ? "passed" : "FAILED",
isPassed ? "" : (failureDescription + "\n"), isPassed ? "" : (failureDescription + "\n"),

View File

@ -0,0 +1,52 @@
package com.annimon.ownlang.parser;
import com.annimon.ownlang.lib.FunctionValue;
import com.annimon.ownlang.lib.NumberValue;
import com.annimon.ownlang.lib.ScopeHandler;
import com.annimon.ownlang.parser.ast.Node;
import com.annimon.ownlang.stages.Stage;
import com.annimon.ownlang.stages.StagesData;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.fail;
public class MockOUnitStage implements Stage<Node, Node> {
@Override
public Node perform(StagesData stagesData, Node input) {
ScopeHandler.resetScope();
ScopeHandler.setFunction("assertEquals", (args) -> {
assertEquals(args[0], args[1]);
return NumberValue.ONE;
});
ScopeHandler.setFunction("assertNotEquals", (args) -> {
assertNotEquals(args[0], args[1]);
return NumberValue.ONE;
});
ScopeHandler.setFunction("assertSameType", (args) -> {
assertEquals(args[0].type(), args[1].type());
return NumberValue.ONE;
});
ScopeHandler.setFunction("assertTrue", (args) -> {
assertTrue(args[0].asInt() != 0);
return NumberValue.ONE;
});
ScopeHandler.setFunction("assertFalse", (args) -> {
assertFalse(args[0].asInt() != 0);
return NumberValue.ONE;
});
ScopeHandler.setFunction("assertFail", (args) -> {
assertThrows(Throwable.class,
() -> ((FunctionValue) args[0]).getValue().execute());
return NumberValue.ONE;
});
ScopeHandler.setFunction("fail", (args) -> {
if (args.length > 0) {
fail(args[0].asString());
} else {
fail();
}
return NumberValue.ONE;
});
return input;
}
}

View File

@ -2,8 +2,6 @@ package com.annimon.ownlang.parser;
import com.annimon.ownlang.Console; import com.annimon.ownlang.Console;
import com.annimon.ownlang.exceptions.OwnLangParserException; import com.annimon.ownlang.exceptions.OwnLangParserException;
import com.annimon.ownlang.lib.FunctionValue;
import com.annimon.ownlang.lib.NumberValue;
import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.lib.ScopeHandler;
import com.annimon.ownlang.parser.ast.ClassDeclarationStatement; import com.annimon.ownlang.parser.ast.ClassDeclarationStatement;
import com.annimon.ownlang.parser.ast.FunctionDefineStatement; import com.annimon.ownlang.parser.ast.FunctionDefineStatement;
@ -42,7 +40,7 @@ public class ProgramsTest {
.then(new ParserStage()) .then(new ParserStage())
.then(new LinterStage(LinterStage.Mode.SEMANTIC)) .then(new LinterStage(LinterStage.Mode.SEMANTIC))
.thenConditional(true, new OptimizationStage(9)) .thenConditional(true, new OptimizationStage(9))
.then(ProgramsTest::mockOUnit) .then(new MockOUnitStage())
.then(new ExecutionStage()) .then(new ExecutionStage())
.then((stagesData, input) -> { .then((stagesData, input) -> {
input.accept(testFunctionsExecutor); input.accept(testFunctionsExecutor);
@ -50,45 +48,6 @@ public class ProgramsTest {
}); });
} }
private static Node mockOUnit(StagesData stagesData, Node input) {
ScopeHandler.resetScope();
// Let's mock junit methods as ounit functions
ScopeHandler.setFunction("assertEquals", (args) -> {
assertEquals(args[0], args[1]);
return NumberValue.ONE;
});
ScopeHandler.setFunction("assertNotEquals", (args) -> {
assertNotEquals(args[0], args[1]);
return NumberValue.ONE;
});
ScopeHandler.setFunction("assertSameType", (args) -> {
assertEquals(args[0].type(), args[1].type());
return NumberValue.ONE;
});
ScopeHandler.setFunction("assertTrue", (args) -> {
assertTrue(args[0].asInt() != 0);
return NumberValue.ONE;
});
ScopeHandler.setFunction("assertFalse", (args) -> {
assertFalse(args[0].asInt() != 0);
return NumberValue.ONE;
});
ScopeHandler.setFunction("assertFail", (args) -> {
assertThrows(Throwable.class,
() -> ((FunctionValue) args[0]).getValue().execute());
return NumberValue.ONE;
});
ScopeHandler.setFunction("fail", (args) -> {
if (args.length > 0) {
fail(args[0].asString());
} else {
fail();
}
return NumberValue.ONE;
});
return input;
}
@ParameterizedTest @ParameterizedTest
@MethodSource("data") @MethodSource("data")
public void testProgram(InputSource inputSource) { public void testProgram(InputSource inputSource) {
@ -119,7 +78,7 @@ public class ProgramsTest {
@Override @Override
public void visit(ClassDeclarationStatement s) { public void visit(ClassDeclarationStatement s) {
// skip for tests
} }
}; };
} }