Java tutorial
/** * Copyright (c) 2015 SQUARESPACE, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.squarespace.template; import static com.squarespace.template.ExecuteErrorType.CODE_LIMIT_REACHED; import static com.squarespace.template.ExecuteErrorType.UNEXPECTED_ERROR; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import java.util.concurrent.atomic.AtomicInteger; import org.testng.annotations.Test; import com.fasterxml.jackson.databind.JsonNode; import com.squarespace.template.HardSoftCodeLimiter.Limit; import com.squarespace.template.Instructions.RootInst; import com.squarespace.template.plugins.CoreFormatters.ApplyFormatter; import com.squarespace.template.plugins.CoreFormatters.TruncateFormatter; public class HardSoftCodeLimiterTest extends UnitTestBase { private static final Formatter APPLY = new ApplyFormatter(); private static final Formatter TRUNCATE = new TruncateFormatter(); @Test public void testNoLimit() throws CodeException { JsonNode node = JsonUtils.decode("[0,1,2,3,4,5,6,7,8,9]"); Context ctx = new Context(node); RootInst root = builder().repeated("@").var("@").end().eof().build(); ctx.execute(root); assertEquals(ctx.buffer().toString(), "0123456789"); } @Test public void testSoftLimit() throws CodeException { TestHandler handler = new TestHandler(Limit.SOFT, 6); CodeLimiter limiter = HardSoftCodeLimiter.builder().setSoftLimit(5).setResolution(1).setHandler(handler) .build(); JsonNode node = JsonUtils.decode("[0,1,2,3,4,5,6,7,8,9]"); Context ctx = new Context(node); ctx.setCodeLimiter(limiter); RootInst root = builder().repeated("@").var("@").end().eof().build(); ctx.execute(root); assertTrue(handler.wasCalled()); } @Test public void testFormatter() throws CodeException { TestHandler handler = new TestHandler(Limit.SOFT, 6); CodeLimiter limiter = HardSoftCodeLimiter.builder().setSoftLimit(5).setResolution(1).setHandler(handler) .build(); JsonNode node = JsonUtils.decode("[\"abc\",\"def\",\"ghi\"]"); Context ctx = new Context(node); ctx.setCodeLimiter(limiter); CodeMaker mk = maker(); // Instruction count: root repeated[N] { var truncate } RootInst root = builder().repeated("@").var("@", mk.fmt(TRUNCATE, mk.args(" 2"))).end().eof().build(); ctx.execute(root); assertTrue(handler.wasCalled()); assertEquals(ctx.buffer().toString(), "ab...de...gh..."); assertEquals(limiter.instructionCount(), 8); } @Test public void testApplyPartialSubcontext() throws CodeException { TestHandler handler = new TestHandler(Limit.SOFT, 6); CodeLimiter limiter = HardSoftCodeLimiter.builder().setSoftLimit(5).setResolution(1).setHandler(handler) .build(); JsonNode node = JsonUtils.decode("[0,1,2]"); JsonNode partials = JsonUtils.decode("{\"foo\":\"{@}\"}"); Context ctx = new Context(node); ctx.setPartials(partials); ctx.setCompiler(compiler()); ctx.setCodeLimiter(limiter); CodeMaker mk = maker(); // Instruction count: root repeated[N] { var apply { root var } } RootInst root = builder().repeated("@").var("@", mk.fmt(APPLY, mk.args(" foo"))).end().eof().build(); ctx.execute(root); assertTrue(handler.wasCalled()); assertEquals(ctx.buffer().toString(), "012"); assertEquals(limiter.instructionCount(), 14); } @Test public void testHardLimit() throws CodeException { TestHandler handler = new TestHandler(Limit.HARD, 6); CodeLimiter limiter = HardSoftCodeLimiter.builder().setHardLimit(5).setResolution(1).setHandler(handler) .build(); JsonNode node = JsonUtils.decode("[0,1,2,3,4,5,6,7,8,9]"); Context ctx = new Context(node); ctx.setCodeLimiter(limiter); RootInst root = builder().repeated("@").var("@").end().eof().build(); try { ctx.execute(root); fail("Expected CODE_LIMIT_REACHED exception"); } catch (CodeExecuteException e) { assertTrue(handler.wasCalled()); assertEquals(handler.limitType(), Limit.HARD); assertEquals(e.getErrorInfo().getType(), CODE_LIMIT_REACHED); assertEquals(limiter.instructionCount(), 6); } } @Test public void testBothLimits() throws CodeException { final AtomicInteger softCount = new AtomicInteger(); final AtomicInteger hardCount = new AtomicInteger(); HardSoftCodeLimiter.Handler handler = new HardSoftCodeLimiter.Handler() { @Override public void onLimit(Limit limit, HardSoftCodeLimiter limiter) throws CodeExecuteException { if (limit.equals(Limit.SOFT)) { softCount.incrementAndGet(); } if (limit.equals(Limit.HARD)) { hardCount.incrementAndGet(); } } }; CodeLimiter codeLimiter = HardSoftCodeLimiter.builder().setSoftLimit(5).setHardLimit(10).setResolution(1) .setHandler(handler).build(); compiler().newExecutor().template("{.repeated section @}{.even?}{@}{.or}#{.end}{.end}") .json("[0,1,2,3,4,5,6,7,8,9]").codeLimiter(codeLimiter).execute(); assertEquals(softCount.get(), 1); assertEquals(hardCount.get(), 1); } @Test public void testResolution() throws CodeException { TestHandler handler = new TestHandler(Limit.HARD, 6); CodeLimiter limiter = HardSoftCodeLimiter.builder().setHardLimit(4).setResolution(3).setHandler(handler) .build(); JsonNode node = JsonUtils.decode("[0,1,2,3,4,5,6,7,8,9]"); Context ctx = new Context(node); ctx.setCodeLimiter(limiter); RootInst root = builder().repeated("@").var("@").end().eof().build(); try { ctx.execute(root); fail("Expected CODE_LIMIT_REACHED exception"); } catch (CodeExecuteException e) { assertTrue(handler.wasCalled()); assertEquals(handler.limitType(), Limit.HARD); assertEquals(e.getErrorInfo().getType(), CODE_LIMIT_REACHED); assertEquals(limiter.instructionCount(), 6); } } private static class TestHandler implements HardSoftCodeLimiter.Handler { private final Limit limitType; private final int assertLimit; private boolean wasCalled; TestHandler(Limit limitType, int assertLimit) { this.limitType = limitType; this.assertLimit = assertLimit; } @Override public void onLimit(Limit limit, HardSoftCodeLimiter limiter) throws CodeExecuteException { if (wasCalled) { throw new CodeExecuteException(new ErrorInfo(UNEXPECTED_ERROR)); } assertEquals(limiter.instructionCount(), assertLimit); this.wasCalled = true; if (limit.equals(Limit.HARD)) { throw new CodeExecuteException(new ErrorInfo(CODE_LIMIT_REACHED)); } } public Limit limitType() { return limitType; } public boolean wasCalled() { return wasCalled; } } }