diff --git a/app/src/test/java/com/annimon/hotarufx/bundles/AssertionsBundle.java b/app/src/test/java/com/annimon/hotarufx/bundles/AssertionsBundle.java new file mode 100644 index 0000000..0124611 --- /dev/null +++ b/app/src/test/java/com/annimon/hotarufx/bundles/AssertionsBundle.java @@ -0,0 +1,65 @@ +package com.annimon.hotarufx.bundles; + +import com.annimon.hotarufx.lib.Context; +import com.annimon.hotarufx.lib.Function; +import com.annimon.hotarufx.lib.NumberValue; +import com.annimon.hotarufx.lib.Validator; +import java.util.HashMap; +import java.util.Map; +import lombok.val; +import org.junit.jupiter.api.Assertions; +import static com.annimon.hotarufx.bundles.FunctionInfo.of; +import static com.annimon.hotarufx.bundles.FunctionType.COMMON; + +public class AssertionsBundle implements Bundle { + + private static final Map FUNCTIONS; + static { + FUNCTIONS = new HashMap<>(); + FUNCTIONS.put("assertHasVariable", of(COMMON, AssertionsBundle::assertHasVariable)); + FUNCTIONS.put("assertHasFunction", of(COMMON, AssertionsBundle::assertHasFunction)); + FUNCTIONS.put("assertTrue", of(COMMON, AssertionsBundle::assertTrue)); + FUNCTIONS.put("assertEquals", of(COMMON, AssertionsBundle::assertEquals)); + } + + @Override + public Map functionsInfo() { + return FUNCTIONS; + } + + private static Function assertHasVariable(Context context) { + return args -> { + Validator.with(args).check(1); + val name = args[0].asString(); + Assertions.assertTrue(context.variables().containsKey(name)); + return NumberValue.ZERO; + }; + } + + private static Function assertHasFunction(Context context) { + return args -> { + Validator.with(args).check(1); + val name = args[0].asString(); + Assertions.assertTrue(context.functions().containsKey(name)); + return NumberValue.ZERO; + }; + } + + private static Function assertTrue(Context context) { + return args -> { + Validator.with(args).check(1); + Assertions.assertTrue(args[0].asBoolean()); + return NumberValue.ZERO; + }; + } + + private static Function assertEquals(Context context) { + return args -> { + Validator.with(args).check(2); + val expectedValue = args[0]; + val actualValue = args[1]; + Assertions.assertEquals(expectedValue, actualValue); + return NumberValue.ZERO; + }; + } +} diff --git a/app/src/test/java/com/annimon/hotarufx/bundles/PrintBundle.java b/app/src/test/java/com/annimon/hotarufx/bundles/PrintBundle.java new file mode 100644 index 0000000..a78d77b --- /dev/null +++ b/app/src/test/java/com/annimon/hotarufx/bundles/PrintBundle.java @@ -0,0 +1,65 @@ +package com.annimon.hotarufx.bundles; + +import com.annimon.hotarufx.lib.Context; +import com.annimon.hotarufx.lib.Function; +import com.annimon.hotarufx.lib.NumberValue; +import java.util.HashMap; +import java.util.Map; +import lombok.val; +import static com.annimon.hotarufx.bundles.FunctionInfo.of; +import static com.annimon.hotarufx.bundles.FunctionType.COMMON; + +public class PrintBundle implements Bundle { + + private static final Map FUNCTIONS; + static { + FUNCTIONS = new HashMap<>(); + FUNCTIONS.put("print", of(COMMON, PrintBundle::print)); + FUNCTIONS.put("println", of(COMMON, PrintBundle::println)); + FUNCTIONS.put("dump", of(COMMON, PrintBundle::dump)); + } + + @Override + public Map functionsInfo() { + return FUNCTIONS; + } + + private static Function print(Context context) { + return args -> { + if (args.length > 0) { + System.out.print(args[0]); + } + return NumberValue.ZERO; + }; + } + + private static Function println(Context context) { + return args -> { + if (args.length > 0) { + System.out.println(args[0]); + } else { + System.out.println(); + } + return NumberValue.ZERO; + }; + } + + private static Function dump(Context context) { + return args -> { + val maxVariableLength = context.variables() + .keySet().stream() + .mapToInt(String::length) + .max() + .orElse(20); + System.out.println("\n\tVARIABLES"); + val format = "%"+maxVariableLength+"s %s%n"; + context.variables().forEach((k, v) -> { + System.out.printf(format, k, v); + }); + + System.out.println("\n\tFUNCTIONS"); + context.functions().keySet().forEach(System.out::println); + return NumberValue.ZERO; + }; + } +} diff --git a/app/src/test/java/com/annimon/hotarufx/bundles/ProgramTest.java b/app/src/test/java/com/annimon/hotarufx/bundles/ProgramTest.java new file mode 100644 index 0000000..726e023 --- /dev/null +++ b/app/src/test/java/com/annimon/hotarufx/bundles/ProgramTest.java @@ -0,0 +1,56 @@ +package com.annimon.hotarufx.bundles; + +import com.annimon.hotarufx.io.IOStream; +import com.annimon.hotarufx.lexer.HotaruLexer; +import com.annimon.hotarufx.lib.Context; +import com.annimon.hotarufx.lib.Value; +import com.annimon.hotarufx.parser.HotaruParser; +import com.annimon.hotarufx.parser.visitors.InterpreterVisitor; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.stream.Stream; +import lombok.val; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class ProgramTest { + + private static final String RES_DIR = "src/test/resources"; + + static Stream programPathProvider() throws IOException { + return Files.list(Paths.get(RES_DIR)) + .filter(p -> p.toString().endsWith(".hfx")) + .map(Arguments::of); + } + + static Value run(Path path, Context context) { + final String input; + try (InputStream is = Files.newInputStream(path, StandardOpenOption.READ)) { + input = IOStream.readContent(is); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + return HotaruParser.parse(HotaruLexer.tokenize(input)) + .accept(new InterpreterVisitor(), context); + } + + @DisplayName("Test programs") + @MethodSource("programPathProvider") + @ParameterizedTest + void testProgram(Path path) { + val context = new Context(); + val bundles = new ArrayList>(); + bundles.addAll(BundleLoader.runtimeBundles()); + bundles.add(AssertionsBundle.class); + bundles.add(PrintBundle.class); + BundleLoader.load(context, bundles); + run(path, context); + } +} \ No newline at end of file diff --git a/app/src/test/resources/test.hfx b/app/src/test/resources/test.hfx new file mode 100644 index 0000000..b8740fd --- /dev/null +++ b/app/src/test/resources/test.hfx @@ -0,0 +1,24 @@ +composition(1280, 720, 30) + +A = circle({ + radius: 100, + fill: '#9bc747' +}) + +assertEquals(A.cx, A.cy) +assertEquals(0, A.cy) +assertEquals(100, A.radius) +A@radius + .add(300 ms, 200) + .add(1 sec, 50) + +assertHasVariable("A") +assertHasVariable("Width") +assertHasVariable("HalfWidth") + +println(Width) +println(HalfWidth) + +dump() + +render(A)