| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 freemarker.core; |
| |
| import java.io.IOException; |
| |
| import org.junit.Test; |
| |
| import com.google.common.collect.ImmutableMap; |
| |
| import freemarker.template.DefaultObjectWrapper; |
| import freemarker.template.TemplateException; |
| import freemarker.test.TemplateTest; |
| import freemarker.test.templatesuite.models.Listables; |
| |
| public class ListErrorsTest extends TemplateTest { |
| |
| @Test |
| public void testValid() throws IOException, TemplateException { |
| assertOutput("<#list 1..2 as x><#list 3..4>${x}:<#items as x>${x}</#items></#list>;</#list>", "1:34;2:34;"); |
| assertOutput("<#list [] as x>${x}<#else><#list 1..2 as x>${x}<#sep>, </#list></#list>", "1, 2"); |
| assertOutput("<#macro m>[<#nested 3>]</#macro>" |
| + "<#list 1..2 as x>" |
| + "${x}@${x?index}" |
| + "<@m ; x>" |
| + "${x}," |
| + "<#list 4..4 as x>${x}@${x?index}</#list>" |
| + "</@>" |
| + "${x}@${x?index}; " |
| + "</#list>", |
| "1@0[3,4@0]1@0; 2@1[3,4@0]2@1; "); |
| } |
| |
| @Test |
| public void stringInterpolationBugFixTest() throws IOException, TemplateException { |
| assertOutput("<#list 1..3 as x>${'${x?index}'}</#list>", "012"); |
| } |
| |
| @Test |
| public void testInvalidItemsParseTime() throws IOException, TemplateException { |
| assertErrorContains("<#items as x>${x}</#items>", |
| "#items", "must be inside", "#list"); |
| assertErrorContains("<#list xs><#macro m><#items as x></#items></#macro></#list>", |
| "#items", "must be inside", "#list"); |
| assertErrorContains("<#list xs><#forEach x in xs><#items as x></#items></#forEach></#list>", |
| "#forEach", "doesn't support", "#items"); |
| assertErrorContains("<#list xs as x><#items as x>${x}</#items></#list>", |
| "#list", "must not have", "#items", "as loopVar"); |
| assertErrorContains("<#list xs><#list xs as x><#items as x>${x}</#items></#list></#list>", |
| "#list", "must not have", "#items", "as loopVar"); |
| assertErrorContains("<#list xs></#list>", |
| "#list", "must have", "#items", "as loopVar"); |
| assertErrorContains("<#forEach x in xs><#items as x></#items></#forEach>", |
| "#forEach", "doesn't support", "#items"); |
| assertErrorContains("<#list xs><#forEach x in xs><#items as x></#items></#forEach></#list>", |
| "#forEach", "doesn't support", "#items"); |
| } |
| |
| @Test |
| public void testInvalidSepParseTime() throws IOException, TemplateException { |
| assertErrorContains("<#sep>, </#sep>", |
| "#sep", "must be inside", "#list", "#foreach"); |
| assertErrorContains("<#sep>, ", |
| "#sep", "must be inside", "#list", "#foreach"); |
| assertErrorContains("<#list xs as x><#else><#sep>, </#list>", |
| "#sep", "must be inside", "#list", "#foreach"); |
| assertErrorContains("<#list xs as x><#macro m><#sep>, </#macro></#list>", |
| "#sep", "must be inside", "#list", "#foreach"); |
| } |
| |
| @Test |
| public void testInvalidItemsRuntime() throws IOException, TemplateException { |
| assertErrorContains("<#list 1..1><#items as x></#items><#items as x></#items></#list>", |
| "#items", "already entered earlier"); |
| assertErrorContains("<#list 1..1><#items as x><#items as y>${x}/${y}</#items></#items></#list>", |
| "#items", "Can't nest #items into each other"); |
| } |
| |
| @Test |
| public void testInvalidLoopVarBuiltinLHO() { |
| assertErrorContains("<#list foos>${foo?index}</#list>", |
| "?index", "foo", "no loop variable"); |
| assertErrorContains("<#list foos as foo></#list>${foo?index}", |
| "?index", "foo" , "no loop variable"); |
| assertErrorContains("<#list foos as foo><#macro m>${foo?index}</#macro></#list>", |
| "?index", "foo" , "no loop variable"); |
| assertErrorContains("<#list foos as foo><#function f>${foo?index}</#function></#list>", |
| "?index", "foo" , "no loop variable"); |
| assertErrorContains("<#list xs as x>${foo?index}</#list>", |
| "?index", "foo" , "no loop variable"); |
| assertErrorContains("<#list foos as foo><@m; foo>${foo?index}</@></#list>", |
| "?index", "foo" , "user defined directive"); |
| assertErrorContains( |
| "<#list foos as foo><@m; foo><@m; foo>${foo?index}</@></@></#list>", |
| "?index", "foo" , "user defined directive"); |
| assertErrorContains( |
| "<#list foos as foo><@m; foo>" |
| + "<#list foos as foo><@m; foo>${foo?index}</@></#list>" |
| + "</@></#list>", |
| "?index", "foo" , "user defined directive"); |
| } |
| |
| @Test |
| public void testKeyValueSameName() { |
| assertErrorContains("<#list {} as foo, foo></#list>", |
| "key", "value", "both" , "foo"); |
| } |
| |
| @Test |
| public void testCollectionVersusHash() { |
| assertErrorContains("<#list {} as i></#list>", |
| "as k, v"); |
| assertErrorContains("<#list [] as k, v></#list>", |
| "only one loop variable"); |
| } |
| |
| @Test |
| public void testNonEx2NonStringKey() throws IOException, TemplateException { |
| addToDataModel("m", new Listables.NonEx2MapAdapter(ImmutableMap.of("k1", "v1", 2, "v2"), new DefaultObjectWrapper())); |
| assertOutput("<#list m?keys as k>${k};</#list>", "k1;2;"); |
| assertErrorContains("<#list m as k, v></#list>", |
| "string", "number", ".TemplateHashModelEx2"); |
| } |
| |
| } |