diff options
Diffstat (limited to 'core/src/test/java')
8 files changed, 221 insertions, 52 deletions
diff --git a/core/src/test/java/coffee/liz/abstractionengine/app/ui/editor/vim/VimEngineTest.java b/core/src/test/java/coffee/liz/abstractionengine/app/ui/editor/vim/VimEngineTest.java index d87204d..ca1ee9c 100644 --- a/core/src/test/java/coffee/liz/abstractionengine/app/ui/editor/vim/VimEngineTest.java +++ b/core/src/test/java/coffee/liz/abstractionengine/app/ui/editor/vim/VimEngineTest.java @@ -351,6 +351,17 @@ public class VimEngineTest { } @Test + public void characterPasteOnEmptyLineInsertsIntoThatLine() { + final FakeBuffer buffer = new FakeBuffer("cat\n\ndog", 0); + final VimEngine controller = new VimEngine(buffer, status -> { + }); + + type(controller, 'y', 'w', 'j', 'p'); + + assertEquals("cat\ncat\ndog", buffer.getText()); + } + + @Test public void dwDeletesWord() { final FakeBuffer buffer = new FakeBuffer("one two", 0); final VimEngine controller = new VimEngine(buffer, status -> { diff --git a/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridCollisionPropagatationSystemTest.java b/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridCollisionPropagatationSystemTest.java index 9f1ff41..ffee701 100644 --- a/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridCollisionPropagatationSystemTest.java +++ b/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridCollisionPropagatationSystemTest.java @@ -15,6 +15,7 @@ import coffee.liz.abstractionengine.grid.component.GridPosition; import coffee.liz.ecs.math.Vec2i; import coffee.liz.ecs.model.Entity; import coffee.liz.ecs.model.World; +import coffee.liz.ecs.model.Query; import lombok.Getter; import org.junit.jupiter.api.Test; @@ -59,7 +60,7 @@ final class GridCollisionPropagatationSystemTest { final Entity wall = Entity.builder().id(3).build().add(WALL_COLLIDABLE).add(new GridPosition(Vec2i.EAST)); // Propagation takes priority because priority(0) is lower - when(world.query(Set.of(GridMomentum.class, GridPosition.class, GridCollidable.class))) + when(world.resolve(Query.allOf(GridMomentum.class, GridPosition.class, GridCollidable.class))) .thenReturn(Set.of(pusher)); when(indexSystem.entitiesAt(Vec2i.ZERO)).thenReturn(List.of(pusher)); @@ -86,7 +87,7 @@ final class GridCollisionPropagatationSystemTest { final Entity wall = Entity.builder().id(3).build().add(WALL_COLLIDABLE) .add(new GridPosition(Vec2i.EAST.scale(2, 0))); - when(world.query(Set.of(GridMomentum.class, GridPosition.class, GridCollidable.class))) + when(world.resolve(Query.allOf(GridMomentum.class, GridPosition.class, GridCollidable.class))) .thenReturn(Set.of(pusher, toPropagate)); when(indexSystem.entitiesAt(Vec2i.ZERO)).thenReturn(List.of(pusher)); @@ -111,7 +112,7 @@ final class GridCollisionPropagatationSystemTest { final Entity pusher = Entity.builder().id(1).build().add(PROPAGATE_COLLIDABLE).add(new GridMomentum(Vec2i.EAST)) .add(new GridPosition(Vec2i.ZERO)); - when(world.query(Set.of(GridMomentum.class, GridPosition.class, GridCollidable.class))) + when(world.resolve(Query.allOf(GridMomentum.class, GridPosition.class, GridCollidable.class))) .thenReturn(Set.of(pusher)); when(indexSystem.entitiesAt(Vec2i.ZERO)).thenReturn(List.of(pusher)); @@ -133,7 +134,7 @@ final class GridCollisionPropagatationSystemTest { final Entity pusher = Entity.builder().id(1).build().add(PROPAGATE_COLLIDABLE).add(new GridMomentum(Vec2i.ZERO)) .add(new GridPosition(Vec2i.ZERO)); - when(world.query(Set.of(GridMomentum.class, GridPosition.class, GridCollidable.class))) + when(world.resolve(Query.allOf(GridMomentum.class, GridPosition.class, GridCollidable.class))) .thenReturn(Set.of(pusher)); when(indexSystem.entitiesAt(Vec2i.ZERO)).thenReturn(List.of(pusher)); @@ -160,7 +161,7 @@ final class GridCollisionPropagatationSystemTest { final Entity swallower = Entity.builder().id(2).build().add(swallowCollidable) .add(new GridPosition(Vec2i.EAST)); - when(world.query(Set.of(GridMomentum.class, GridPosition.class, GridCollidable.class))) + when(world.resolve(Query.allOf(GridMomentum.class, GridPosition.class, GridCollidable.class))) .thenReturn(Set.of(pusher)); when(indexSystem.entitiesAt(Vec2i.ZERO)).thenReturn(List.of(pusher)); diff --git a/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridIndexSystemTest.java b/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridIndexSystemTest.java index f751927..9fd1a7c 100644 --- a/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridIndexSystemTest.java +++ b/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridIndexSystemTest.java @@ -9,6 +9,7 @@ import coffee.liz.abstractionengine.grid.component.GridPosition; import coffee.liz.ecs.math.Vec2i; import coffee.liz.ecs.model.Entity; import coffee.liz.ecs.model.World; +import coffee.liz.ecs.model.Query; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -25,7 +26,7 @@ class GridIndexSystemTest { alpha.add(new GridPosition(new Vec2i(1, 2))); final Entity beta = Entity.builder().id(12).build(); beta.add(new GridPosition(new Vec2i(0, 0))); - when(world.query(Set.of(GridPosition.class))).thenReturn(Set.of(alpha, beta)); + when(world.resolve(Query.allOf(GridPosition.class))).thenReturn(Set.of(alpha, beta)); system.update(world, GridInputState.builder().movement(Vec2i.NORTH).build(), Duration.ZERO); @@ -40,7 +41,7 @@ class GridIndexSystemTest { final World<GridInputState> world = mockWorld(); final Entity moving = Entity.builder().id(77).build(); moving.add(new GridPosition(Vec2i.ZERO)); - when(world.query(Set.of(GridPosition.class))).thenReturn(Set.of(moving)).thenReturn(Set.of()); + when(world.resolve(Query.allOf(GridPosition.class))).thenReturn(Set.of(moving)).thenReturn(Set.of()); system.update(world, GridInputState.builder().movement(Vec2i.EAST).build(), Duration.ZERO); assertTrue(system.entitiesAt(Vec2i.ZERO).contains(moving)); diff --git a/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridMovementSystemTest.java b/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridMovementSystemTest.java index 2731a7b..0c4ad80 100644 --- a/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridMovementSystemTest.java +++ b/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridMovementSystemTest.java @@ -10,6 +10,7 @@ import coffee.liz.abstractionengine.grid.component.GridPosition; import coffee.liz.ecs.math.Vec2i; import coffee.liz.ecs.model.Entity; import coffee.liz.ecs.model.World; +import coffee.liz.ecs.model.Query; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -25,7 +26,7 @@ final class GridMovementSystemTest { subject.add(new GridControllable()); subject.add(new GridPosition(Vec2i.ZERO)); final Set<Entity> controllableEntities = Set.of(subject); - when(world.query(Set.of(GridControllable.class, GridPosition.class))).thenReturn(controllableEntities); + when(world.resolve(Query.allOf(GridControllable.class, GridPosition.class))).thenReturn(controllableEntities); final GridInputState inputState = GridInputState.builder().movement(Vec2i.SOUTH).build(); final GridMovementSystem system = new GridMovementSystem(); diff --git a/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridPhysicsSystemTest.java b/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridPhysicsSystemTest.java index c6a0cfe..b8b2d1c 100644 --- a/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridPhysicsSystemTest.java +++ b/core/src/test/java/coffee/liz/abstractionengine/grid/system/GridPhysicsSystemTest.java @@ -10,6 +10,7 @@ import coffee.liz.ecs.math.Vec2; import coffee.liz.ecs.math.Vec2i; import coffee.liz.ecs.model.Entity; import coffee.liz.ecs.model.World; +import coffee.liz.ecs.model.Query; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -24,7 +25,7 @@ final class GridPhysicsSystemTest { final Entity body = Entity.builder().id(3).build(); body.add(new GridPosition(Vec2i.ZERO)); body.add(new GridMomentum(Vec2i.EAST)); - when(world.query(Set.of(GridMomentum.class, GridPosition.class))).thenReturn(Set.of(body)); + when(world.resolve(Query.allOf(GridMomentum.class, GridPosition.class))).thenReturn(Set.of(body)); final GridPhysicsSystem system = new GridPhysicsSystem(); system.update(world, GridInputState.builder().movement(Vec2i.NORTH).build(), Duration.ZERO); diff --git a/core/src/test/java/coffee/liz/abstractionengine/grid/system/LambdaEvaluatorSystemTest.java b/core/src/test/java/coffee/liz/abstractionengine/grid/system/LambdaEvaluatorSystemTest.java new file mode 100644 index 0000000..6829417 --- /dev/null +++ b/core/src/test/java/coffee/liz/abstractionengine/grid/system/LambdaEvaluatorSystemTest.java @@ -0,0 +1,97 @@ +package coffee.liz.abstractionengine.grid.system; + +import coffee.liz.abstractionengine.grid.component.GridInputState; +import coffee.liz.abstractionengine.grid.component.GridMomentum; +import coffee.liz.abstractionengine.grid.component.GridPosition; +import coffee.liz.abstractionengine.grid.component.LambdaTerm; +import coffee.liz.abstractionengine.grid.component.Substitution; +import coffee.liz.ecs.DAGWorld; +import coffee.liz.ecs.math.Vec2; +import coffee.liz.ecs.math.Vec2i; +import coffee.liz.ecs.model.Entity; +import coffee.liz.ecs.model.World; +import coffee.liz.ecs.model.Query; +import coffee.liz.lambda.ast.SourceCode; +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +public class LambdaEvaluatorSystemTest { + private static final Vec2<Integer> GRID_DIMENSIONS = Vec2i.builder().x(3).y(3).build(); + private static final SourceCode IDENTITY = SourceCode.ofArrow("(x -> x)(y)"); + private static final SourceCode FREE = SourceCode.ofArrow("z"); + + @Test + public void testDependenciesSatisfied() { + final World<GridInputState> world = new DAGWorld<>(new GridIndexSystem(GRID_DIMENSIONS), + new LambdaEvaluatorSystem(SourceCode.Syntax.ARROW)); + + world.createEntity().add(new GridPosition(Vec2i.builder().x(1).y(1).build())).add(new LambdaTerm(IDENTITY)) + .add(new Substitution(Vec2i.EAST, Map.of("y", Vec2i.builder().x(0).y(0).build()))); + world.createEntity().add(new GridPosition(Vec2i.builder().x(0).y(0).build())).add(new LambdaTerm(FREE)); + + world.update(GridInputState.builder().build(), Duration.ZERO); + + assertEquals(3, world.resolve(Query.allOf()).size()); + final Set<Entity> emittedTerms = world.resolve(Query.allOf(GridMomentum.class, LambdaTerm.class)); + assertEquals(1, emittedTerms.size()); + final SourceCode emitted = emittedTerms.iterator().next().get(LambdaTerm.class).getTerm(); + final SourceCode.Arrow arrow = assertInstanceOf(SourceCode.Arrow.class, emitted); + assertEquals("z", arrow.source()); + } + + @Test + public void testEmitsLambdaSyntax() { + final World<GridInputState> world = new DAGWorld<>(new GridIndexSystem(GRID_DIMENSIONS), + new LambdaEvaluatorSystem(SourceCode.Syntax.LAMBDA)); + + world.createEntity().add(new GridPosition(Vec2i.builder().x(1).y(1).build())).add(new LambdaTerm(IDENTITY)) + .add(new Substitution(Vec2i.EAST, Map.of("y", Vec2i.builder().x(0).y(0).build()))); + world.createEntity().add(new GridPosition(Vec2i.builder().x(0).y(0).build())).add(new LambdaTerm(FREE)); + + world.update(GridInputState.builder().build(), Duration.ZERO); + + final Set<Entity> emittedTerms = world.resolve(Query.allOf(GridMomentum.class, LambdaTerm.class)); + assertEquals(1, emittedTerms.size()); + final SourceCode emitted = emittedTerms.iterator().next().get(LambdaTerm.class).getTerm(); + final SourceCode.Lambda lambda = assertInstanceOf(SourceCode.Lambda.class, emitted); + assertEquals("z", lambda.source()); + } + + @Test + public void testDependenciesUnsatisfied() { + final World<GridInputState> world = new DAGWorld<>(new GridIndexSystem(GRID_DIMENSIONS), + new LambdaEvaluatorSystem(SourceCode.Syntax.ARROW)); + + world.createEntity().add(new GridPosition(Vec2i.builder().x(1).y(1).build())).add(new LambdaTerm(IDENTITY)) + .add(new Substitution(Vec2i.EAST, Map.of("y", Vec2i.builder().x(0).y(0).build()))); + + world.update(GridInputState.builder().build(), Duration.ZERO); + + assertEquals(1, world.resolve(Query.allOf()).size()); + assertEquals(0, world.resolve(Query.allOf(GridMomentum.class, LambdaTerm.class)).size()); + } + + @Test + public void testSpawnsInSubstitutionDirection() { + final Vec2<Integer> spawnMomentum = Vec2i.builder().x(0).y(-1).build(); + final World<GridInputState> world = new DAGWorld<>(new GridIndexSystem(GRID_DIMENSIONS), + new LambdaEvaluatorSystem(SourceCode.Syntax.ARROW)); + + world.createEntity().add(new GridPosition(Vec2i.builder().x(1).y(1).build())).add(new LambdaTerm(IDENTITY)) + .add(new Substitution(spawnMomentum, Map.of("y", Vec2i.builder().x(0).y(0).build()))); + world.createEntity().add(new GridPosition(Vec2i.builder().x(0).y(0).build())).add(new LambdaTerm(FREE)); + + world.update(GridInputState.builder().build(), Duration.ZERO); + + final Set<Entity> emittedTerms = world.resolve(Query.allOf(GridMomentum.class, LambdaTerm.class)); + assertEquals(1, emittedTerms.size()); + final GridMomentum momentum = emittedTerms.iterator().next().get(GridMomentum.class); + assertEquals(new GridMomentum(spawnMomentum), momentum); + } +} diff --git a/core/src/test/java/coffee/liz/ecs/DAGWorldTest.java b/core/src/test/java/coffee/liz/ecs/DAGWorldTest.java index 648745b..b596876 100644 --- a/core/src/test/java/coffee/liz/ecs/DAGWorldTest.java +++ b/core/src/test/java/coffee/liz/ecs/DAGWorldTest.java @@ -8,55 +8,60 @@ import coffee.liz.ecs.model.Component; import coffee.liz.ecs.model.Entity; import coffee.liz.ecs.model.System; import coffee.liz.ecs.model.World; +import coffee.liz.ecs.model.Query; import lombok.RequiredArgsConstructor; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.time.Duration; import java.util.Collection; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.stream.Collectors; +import java.util.stream.Stream; -final class DAGWorldTest { - @Test - public void queryReturnsEntitiesMatchingAllRequestedComponents() { - final DAGWorld<String> world = new DAGWorld<>(Map.of()); - final Entity matching = world.createEntity(); - matching.add(new PositionComponent()); - matching.add(new VelocityComponent()); - final Entity partial = world.createEntity(); - partial.add(new PositionComponent()); - final Entity nonMatching = world.createEntity(); - nonMatching.add(new VelocityComponent()); +public class DAGWorldTest { + @ParameterizedTest + @MethodSource("queryScenarios") + public void queryResolvesExpectedEntities(final Query query, final Set<String> expectedEntityKeys) { + final World<String> world = new DAGWorld<>(); + final Entity both = world.createEntity(); + both.add(new PositionComponent()); + both.add(new VelocityComponent()); + final Entity positionOnly = world.createEntity(); + positionOnly.add(new PositionComponent()); + final Entity velocityOnly = world.createEntity(); + velocityOnly.add(new VelocityComponent()); + final Entity neither = world.createEntity(); world.update("state", Duration.ZERO); - final Set<Entity> results = world.query(Set.of(PositionComponent.class, VelocityComponent.class)); - - assertEquals(Set.of(matching), results); + final Map<String, Entity> entities = Map.of("both", both, "positionOnly", positionOnly, "velocityOnly", + velocityOnly, "neither", neither); + final Set<Entity> expectedEntities = expectedEntityKeys.stream().map(entities::get).collect(Collectors.toSet()); + assertEquals(expectedEntities, world.resolve(query)); } - @Test - public void queryWithoutComponentsReturnsAllEntities() { - final DAGWorld<String> world = new DAGWorld<>(Map.of()); - final Entity entityOne = world.createEntity(); - final Entity entityTwo = world.createEntity(); - - final Set<Entity> results = world.query(List.<Class<? extends Component>>of()); - - assertEquals(Set.of(entityOne, entityTwo), results); + private static Stream<Arguments> queryScenarios() { + return Stream.of(Arguments.of(Query.allOf(PositionComponent.class, VelocityComponent.class), Set.of("both")), + Arguments.of(Query.allOf(), Set.of("both", "positionOnly", "velocityOnly", "neither")), + Arguments.of(Query.anyOf(PositionComponent.class, VelocityComponent.class), + Set.of("both", "positionOnly", "velocityOnly")), + Arguments.of(Query.noneOf(PositionComponent.class, VelocityComponent.class), Set.of("neither"))); } @Test public void updateExecutesSystemsInTopologicalOrder() { final CopyOnWriteArrayList<String> executionLog = new CopyOnWriteArrayList<>(); - final DAGWorld<String> world = new DAGWorld<>(Map.of(SystemC.class, new SystemC(executionLog), SystemA.class, - new SystemA(executionLog), SystemB.class, new SystemB(executionLog))); + final DAGWorld<String> world = new DAGWorld<>(new SystemC(executionLog), new SystemA(executionLog), + new SystemB(executionLog)); world.update("state", Duration.ZERO); assertEquals(List.of("A", "B", "C"), executionLog); @@ -64,29 +69,27 @@ final class DAGWorldTest { @Test public void updateRefreshesComponentCacheAfterEntityMutations() { - final DAGWorld<String> world = new DAGWorld<>(Map.of()); + final DAGWorld<String> world = new DAGWorld<>(); final Entity subject = world.createEntity(); world.update("state", Duration.ZERO); - assertTrue(world.query(Set.of(PositionComponent.class)).isEmpty()); + assertTrue(world.resolve(Query.allOf(PositionComponent.class)).isEmpty()); subject.add(new PositionComponent()); world.update("state", Duration.ZERO); - assertEquals(1, world.query(Set.of(PositionComponent.class)).size()); + assertEquals(1, world.resolve(Query.allOf(PositionComponent.class)).size()); subject.remove(PositionComponent.class); world.update("state", Duration.ZERO); - assertTrue(world.query(Set.of(PositionComponent.class)).isEmpty()); + assertTrue(world.resolve(Query.allOf(PositionComponent.class)).isEmpty()); } @Test public void queryFindsComponentsAddedByEarlierSystemInSameTick() { final List<Set<Entity>> queryResults = new CopyOnWriteArrayList<>(); - final Map<Class<? extends System<String>>, System<String>> systems = new LinkedHashMap<>(); - systems.put(ComponentAdderSystem.class, new ComponentAdderSystem()); - systems.put(ComponentReaderSystem.class, new ComponentReaderSystem(queryResults)); - final DAGWorld<String> world = new DAGWorld<>(systems); + final DAGWorld<String> world = new DAGWorld<>(new ComponentAdderSystem(), + new ComponentReaderSystem(queryResults)); final Entity entity = world.createEntity(); entity.add(new PositionComponent()); @@ -99,13 +102,7 @@ final class DAGWorldTest { @Test public void circularDependencyDetectionThrowsIllegalStateException() { - final Map<Class<? extends System<String>>, System<String>> systems = new LinkedHashMap<>(); - final SystemCycleA systemA = new SystemCycleA(); - final SystemCycleB systemB = new SystemCycleB(); - systems.put(SystemCycleA.class, systemA); - systems.put(SystemCycleB.class, systemB); - - assertThrows(IllegalStateException.class, () -> new DAGWorld<>(systems)); + assertThrows(IllegalStateException.class, () -> new DAGWorld<>(new SystemCycleA(), new SystemCycleB())); } private static final class PositionComponent implements Component { @@ -188,7 +185,7 @@ final class DAGWorldTest { @Override public void update(final World<String> world, final String state, final Duration duration) { - world.query(Set.of(PositionComponent.class)).forEach(e -> e.add(new VelocityComponent())); + world.resolve(Query.allOf(PositionComponent.class)).forEach(e -> e.add(new VelocityComponent())); } } @@ -203,7 +200,7 @@ final class DAGWorldTest { @Override public void update(final World<String> world, final String state, final Duration duration) { - queryResults.add(world.query(Set.of(VelocityComponent.class, PositionComponent.class))); + queryResults.add(world.resolve(Query.allOf(VelocityComponent.class, PositionComponent.class))); } } } diff --git a/core/src/test/java/coffee/liz/lambda/format/ValueEmitterTest.java b/core/src/test/java/coffee/liz/lambda/format/ValueEmitterTest.java new file mode 100644 index 0000000..c1ab704 --- /dev/null +++ b/core/src/test/java/coffee/liz/lambda/format/ValueEmitterTest.java @@ -0,0 +1,60 @@ +package coffee.liz.lambda.format; + +import coffee.liz.lambda.LambdaDriver; +import coffee.liz.lambda.ast.SourceCode; +import coffee.liz.lambda.eval.Value; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ValueEmitterTest { + @Test + void emitsFreeInBothSyntaxes() { + final Value value = LambdaDriver.interpret(SourceCode.ofArrow("z")); + + final SourceCode arrow = ValueEmitter.emit(value, SourceCode.Syntax.ARROW); + final SourceCode lambda = ValueEmitter.emit(value, SourceCode.Syntax.LAMBDA); + + assertEquals("z", assertInstanceOf(SourceCode.Arrow.class, arrow).source()); + assertEquals("z", assertInstanceOf(SourceCode.Lambda.class, lambda).source()); + } + + @Test + void emitsApplicationInBothSyntaxes() { + final Value value = LambdaDriver.interpret(SourceCode.ofArrow("f(x)")); + + final SourceCode arrow = ValueEmitter.emit(value, SourceCode.Syntax.ARROW); + final SourceCode lambda = ValueEmitter.emit(value, SourceCode.Syntax.LAMBDA); + + assertEquals("f(x)", assertInstanceOf(SourceCode.Arrow.class, arrow).source()); + assertEquals("f x", assertInstanceOf(SourceCode.Lambda.class, lambda).source()); + } + + @Test + void emitsClosureWithCaptureMacros() { + final Value value = LambdaDriver.interpret(SourceCode.ofArrow("(x -> y -> x)(z)")); + final SourceCode emitted = ValueEmitter.emit(value, SourceCode.Syntax.ARROW); + final String source = assertInstanceOf(SourceCode.Arrow.class, emitted).source(); + + assertTrue(source.contains("let _cap")); + assertTrue(source.contains("y -> _cap")); + + // Validate emitted program is parsable and evaluates. + final Value roundTrip = LambdaDriver.interpret(emitted); + assertInstanceOf(Value.Closure.class, roundTrip); + } + + @Test + void emitsHygienicCapturesWhenNamesShadow() { + final Value value = LambdaDriver.interpret(SourceCode.ofArrow("(x -> y -> x)(y)")); + final SourceCode emitted = ValueEmitter.emit(value, SourceCode.Syntax.ARROW); + final String source = assertInstanceOf(SourceCode.Arrow.class, emitted).source(); + + assertTrue(source.contains("let _cap")); + assertTrue(source.contains("y -> _cap")); + assertFalse(source.contains("let y =")); + } +} |
