/*
* Copyright 2010 Google 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.google.template.soy.parsepasses.contextautoesc;
import static com.google.common.truth.Truth.assertWithMessage;
import com.google.common.base.Strings;
import com.google.template.soy.base.SourceLocation;
import com.google.template.soy.soytree.RawTextNode;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class RawTextContextUpdaterTest {
// The letter 'M' repeated 1500 times.
private static final String M1500 = Strings.repeat("M", 1500);
private static final int ANY_SLICES = -1;
@Test
public void testPcdata() throws Exception {
assertTransition("HTML_PCDATA", "", "HTML_PCDATA");
assertTransition("HTML_PCDATA", "Hello, World!", "HTML_PCDATA");
assertTransition("HTML_PCDATA", "Jad loves ponies <3 <3 <3 !!!", "HTML_PCDATA");
assertTransition("HTML_PCDATA", "OMG! Ponies, Ponies, Ponies <3", "HTML_PCDATA");
// Entering a tag
assertTransition("HTML_PCDATA", "<", "HTML_BEFORE_OPEN_TAG_NAME");
assertTransition("HTML_PCDATA", "Hello, <", "HTML_BEFORE_OPEN_TAG_NAME");
assertTransition("HTML_PCDATA", "<h1", "HTML_TAG_NAME NORMAL");
// Make sure that encoded HTML doesn't enter TAG.
assertTransition("HTML_PCDATA", "<a", "HTML_PCDATA");
assertTransition("HTML_PCDATA", "<!--", "HTML_COMMENT");
// Test special tags.
assertTransition("HTML_PCDATA", "<script type='text/javascript'", "HTML_TAG SCRIPT");
assertTransition("HTML_PCDATA", "<SCRIPT type='text/javascript'", "HTML_TAG SCRIPT");
assertTransition("HTML_PCDATA", "<style type='text/css'", "HTML_TAG STYLE");
assertTransition("HTML_PCDATA", "<sTyLe type='text/css'", "HTML_TAG STYLE");
assertTransition("HTML_PCDATA", "<textarea name='text'", "HTML_TAG TEXTAREA");
assertTransition("HTML_PCDATA", "<Title lang='en'", "HTML_TAG TITLE");
assertTransition("HTML_PCDATA", "<xmp id='x'", "HTML_TAG XMP");
// Into tag
assertTransition("HTML_PCDATA", "<script>", "JS REGEX");
assertTransition("HTML_PCDATA", "<script >", "JS REGEX");
assertTransition("HTML_PCDATA", "<script type=\"text/javascript\">", "JS REGEX");
assertTransition("HTML_PCDATA", "<a ", "HTML_TAG NORMAL");
assertTransition("HTML_PCDATA", "<a title=foo id='x'", "HTML_TAG NORMAL");
assertTransition("HTML_PCDATA", "<a title=\"foo\"", "HTML_TAG NORMAL");
assertTransition("HTML_PCDATA", "<a title='foo'", "HTML_TAG NORMAL");
assertTransition(
"HTML_PCDATA", "<a title='", "HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT SINGLE_QUOTE");
assertTransition(
"HTML_PCDATA", "<a data-foo='", "HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT SINGLE_QUOTE");
// Into attributes
assertTransition("HTML_PCDATA", "<a onclick=\"", "JS NORMAL SCRIPT DOUBLE_QUOTE REGEX");
assertTransition("HTML_PCDATA", "<a onclick=\'", "JS NORMAL SCRIPT SINGLE_QUOTE REGEX");
assertTransition("HTML_PCDATA", "<a onclick=", "HTML_BEFORE_ATTRIBUTE_VALUE NORMAL SCRIPT");
assertTransition(
"HTML_PCDATA", "<a onclick=\"</script>", "JS_REGEX NORMAL SCRIPT DOUBLE_QUOTE");
assertTransition("HTML_PCDATA", "<xmp style=\"", "CSS XMP STYLE DOUBLE_QUOTE");
assertTransition("HTML_PCDATA", "<xmp style='/*", "CSS_COMMENT XMP STYLE SINGLE_QUOTE");
assertTransition(
"HTML_PCDATA", "<script src=", "HTML_BEFORE_ATTRIBUTE_VALUE SCRIPT URI TRUSTED_RESOURCE");
assertTransition(
"HTML_PCDATA",
"<script src=/search?q=",
"URI SCRIPT URI SPACE_OR_TAG_END QUERY TRUSTED_RESOURCE");
assertTransition(
"HTML_PCDATA",
"<script src=/foo#",
"URI SCRIPT URI SPACE_OR_TAG_END FRAGMENT TRUSTED_RESOURCE");
assertTransition("HTML_PCDATA", "<img src=", "HTML_BEFORE_ATTRIBUTE_VALUE MEDIA URI MEDIA");
assertTransition("HTML_PCDATA", "<img url src=", "HTML_BEFORE_ATTRIBUTE_VALUE MEDIA URI MEDIA");
// Make sure the URI type doesn't carry over if a URI has no attribute value.
assertTransition(
"HTML_PCDATA", "<img src href=", "HTML_BEFORE_ATTRIBUTE_VALUE MEDIA URI NORMAL");
assertTransition(
"HTML_PCDATA", "<img src alt=", "HTML_BEFORE_ATTRIBUTE_VALUE MEDIA PLAIN_TEXT");
// TODO(gboyer): Consider supporting video, audio, and source.
assertTransition("HTML_PCDATA", "<video src=", "HTML_BEFORE_ATTRIBUTE_VALUE NORMAL URI NORMAL");
assertTransition(
"HTML_PCDATA", "<video><source src=", "HTML_BEFORE_ATTRIBUTE_VALUE NORMAL URI NORMAL");
assertTransition("HTML_PCDATA", "<audio src=", "HTML_BEFORE_ATTRIBUTE_VALUE NORMAL URI NORMAL");
assertTransition(
"HTML_PCDATA", "<source src=", "HTML_BEFORE_ATTRIBUTE_VALUE NORMAL URI NORMAL");
assertTransition(
"HTML_PCDATA", "<image xlink:href=", "HTML_BEFORE_ATTRIBUTE_VALUE MEDIA URI MEDIA");
assertTransition(
"HTML_PCDATA",
"<a href=mailto:",
"URI NORMAL URI SPACE_OR_TAG_END AUTHORITY_OR_PATH NORMAL");
assertTransition(
"HTML_PCDATA",
"<input type=button value= onclick=",
"HTML_BEFORE_ATTRIBUTE_VALUE NORMAL SCRIPT");
assertTransition("HTML_PCDATA", "<input type=button value=>", "HTML_PCDATA");
}
@Test
public void testBeforeTagName() throws Exception {
assertTransition("HTML_BEFORE_OPEN_TAG_NAME", "", "HTML_BEFORE_OPEN_TAG_NAME");
assertTransition("HTML_BEFORE_OPEN_TAG_NAME", "/", "HTML_BEFORE_CLOSE_TAG_NAME");
assertTransition("HTML_BEFORE_OPEN_TAG_NAME", "h1", "HTML_TAG_NAME NORMAL");
assertTransition("HTML_BEFORE_OPEN_TAG_NAME", "svg:font-face id='x'", "HTML_TAG NORMAL");
assertTransition("HTML_BEFORE_OPEN_TAG_NAME", ">", "HTML_PCDATA");
assertTransition("HTML_BEFORE_OPEN_TAG_NAME", "><", "HTML_BEFORE_OPEN_TAG_NAME");
// Abort tag name if we see things that aren't really tag names.
assertTransition("HTML_BEFORE_OPEN_TAG_NAME", "3 Kitties!", "HTML_PCDATA");
assertTransition("HTML_BEFORE_OPEN_TAG_NAME", " script", "HTML_PCDATA");
assertTransition("HTML_BEFORE_CLOSE_TAG_NAME", "", "HTML_BEFORE_CLOSE_TAG_NAME");
assertTransition("HTML_BEFORE_CLOSE_TAG_NAME", "9", "ERROR");
assertTransition("HTML_BEFORE_CLOSE_TAG_NAME", "/", "ERROR");
assertTransition("HTML_BEFORE_CLOSE_TAG_NAME", "h1", "HTML_TAG_NAME NORMAL");
assertTransition("HTML_BEFORE_CLOSE_TAG_NAME", "svg:font-face", "HTML_TAG_NAME NORMAL");
assertTransition("HTML_BEFORE_CLOSE_TAG_NAME", "div><", "HTML_BEFORE_OPEN_TAG_NAME");
assertTransition("HTML_BEFORE_CLOSE_TAG_NAME", ">", "ERROR");
assertTransition("HTML_BEFORE_CLOSE_TAG_NAME", "><", "ERROR");
}
@Test
public void testTagName() throws Exception {
assertTransition("HTML_TAG_NAME NORMAL", "", "HTML_TAG_NAME NORMAL");
// Now, it's banned to do something like: <h{if 1}1{/if}>; instead the full tag name must be
// specified.
assertTransition("HTML_TAG_NAME NORMAL", "1", "ERROR");
assertTransition("HTML_TAG_NAME NORMAL", "-foo", "ERROR");
assertTransition("HTML_TAG_NAME NORMAL", " id='x'", "HTML_TAG NORMAL");
assertTransition("HTML_TAG_NAME NORMAL", "\rid='x'", "HTML_TAG NORMAL");
assertTransition("HTML_TAG_NAME NORMAL", "\tid='x'", "HTML_TAG NORMAL");
assertTransition("HTML_TAG_NAME NORMAL", ">", "HTML_PCDATA");
assertTransition("HTML_TAG_NAME NORMAL", "/>", "HTML_PCDATA");
assertTransition(
"HTML_TAG_NAME NORMAL", " href=", "HTML_BEFORE_ATTRIBUTE_VALUE NORMAL URI NORMAL");
assertTransition(
"HTML_TAG_NAME NORMAL", " href=\"", "URI NORMAL URI DOUBLE_QUOTE START NORMAL");
assertTransition("HTML_TAG_NAME NORMAL", " href='", "URI NORMAL URI SINGLE_QUOTE START NORMAL");
assertTransition(
"HTML_TAG_NAME NORMAL", " href=#", "URI NORMAL URI SPACE_OR_TAG_END FRAGMENT NORMAL");
assertTransition("HTML_TAG_NAME NORMAL", " href=>", "HTML_PCDATA");
assertTransition("HTML_TAG_NAME NORMAL", " onclick=\"", "JS NORMAL SCRIPT DOUBLE_QUOTE REGEX");
assertTransition("HTML_TAG_NAME NORMAL", " style=\"", "CSS NORMAL STYLE DOUBLE_QUOTE");
assertTransition(
"HTML_TAG_NAME NORMAL",
" stylez=\"",
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT DOUBLE_QUOTE");
assertTransition(
"HTML_TAG_NAME NORMAL",
" title=\"",
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT DOUBLE_QUOTE");
assertTransition("HTML_TAG_NAME NORMAL", "=foo>", "ERROR");
}
@Test
public void testTag() throws Exception {
assertTransition("HTML_TAG NORMAL", "", "HTML_TAG NORMAL");
assertTransition("HTML_TAG NORMAL", ">", "HTML_PCDATA");
assertTransition("HTML_TAG TEXTAREA", ">", "HTML_RCDATA TEXTAREA");
assertTransition("HTML_TAG TITLE", ">", "HTML_RCDATA TITLE");
assertTransition("HTML_TAG SCRIPT", ">", "JS REGEX");
assertTransition("HTML_TAG STYLE", ">", "CSS");
assertTransition("HTML_TAG NORMAL", "-->", "ERROR");
assertTransition("HTML_TAG NORMAL", " -->", "ERROR");
assertTransition("HTML_TAG NORMAL", "=foo>", "ERROR");
// As in <foo on{$handlerType}="jsHere()">
assertTransition("HTML_TAG NORMAL", " on", "HTML_ATTRIBUTE_NAME NORMAL SCRIPT", 1);
assertTransition("HTML_TAG NORMAL", " ONCLICK", "HTML_ATTRIBUTE_NAME NORMAL SCRIPT", 1);
assertTransition("HTML_TAG NORMAL", " style", "HTML_ATTRIBUTE_NAME NORMAL STYLE", 1);
assertTransition("HTML_TAG NORMAL", " HREF", "HTML_ATTRIBUTE_NAME NORMAL URI NORMAL", 1);
assertTransition("HTML_TAG XMP", " title", "HTML_ATTRIBUTE_NAME XMP PLAIN_TEXT", 1);
assertTransition("HTML_TAG NORMAL", " checked ", "HTML_TAG NORMAL", 3);
assertTransition("HTML_TAG NORMAL", " xlink:href", "HTML_ATTRIBUTE_NAME NORMAL URI NORMAL", 1);
assertTransition("HTML_TAG NORMAL", " g:url", "HTML_ATTRIBUTE_NAME NORMAL URI NORMAL", 1);
assertTransition("HTML_TAG NORMAL", " g:iconUri", "HTML_ATTRIBUTE_NAME NORMAL URI NORMAL", 1);
assertTransition("HTML_TAG NORMAL", " g:urlItem", "HTML_ATTRIBUTE_NAME NORMAL URI NORMAL", 1);
assertTransition("HTML_TAG NORMAL", " g:hourly", "HTML_ATTRIBUTE_NAME NORMAL PLAIN_TEXT", 1);
assertTransition("HTML_TAG NORMAL", " xmlns", "HTML_ATTRIBUTE_NAME NORMAL URI NORMAL", 1);
assertTransition("HTML_TAG NORMAL", " xmlns:foo", "HTML_ATTRIBUTE_NAME NORMAL URI NORMAL", 1);
assertTransition("HTML_TAG NORMAL", " xmlnsxyz", "HTML_ATTRIBUTE_NAME NORMAL PLAIN_TEXT", 1);
assertTransition("HTML_TAG NORMAL", " xmlnsxyz?", "HTML_ATTRIBUTE_NAME NORMAL PLAIN_TEXT", 1);
assertTransition("HTML_TAG NORMAL", " xml?nsxyz", "HTML_ATTRIBUTE_NAME NORMAL PLAIN_TEXT", 1);
assertTransition("HTML_TAG NORMAL", " xmlnsxyz$", "HTML_ATTRIBUTE_NAME NORMAL PLAIN_TEXT", 1);
assertTransition("HTML_TAG NORMAL", " xml$nsxyz", "HTML_ATTRIBUTE_NAME NORMAL PLAIN_TEXT", 1);
assertTransition("HTML_TAG NORMAL", " svg:style='", "CSS NORMAL STYLE SINGLE_QUOTE", 3);
}
@Test
public void testHtmlComment() throws Exception {
assertTransition("HTML_COMMENT", "", "HTML_COMMENT");
assertTransition("HTML_COMMENT", " ", "HTML_COMMENT");
assertTransition("HTML_COMMENT", "\r", "HTML_COMMENT");
assertTransition("HTML_COMMENT", "/", "HTML_COMMENT");
assertTransition("HTML_COMMENT", "x", "HTML_COMMENT");
assertTransition("HTML_COMMENT", ">", "HTML_COMMENT");
assertTransition("HTML_COMMENT", "-", "HTML_COMMENT");
assertTransition("HTML_COMMENT", "-- >", "HTML_COMMENT");
assertTransition("HTML_COMMENT", "->", "HTML_COMMENT");
assertTransition("HTML_COMMENT", "-->", "HTML_PCDATA");
assertTransition("HTML_COMMENT", "--->", "HTML_PCDATA");
assertTransition("HTML_COMMENT", "<!--", "HTML_COMMENT");
}
@Test
public void testAttrName() throws Exception {
assertTransition(
"HTML_ATTRIBUTE_NAME XMP URI NORMAL", "=", "HTML_BEFORE_ATTRIBUTE_VALUE XMP URI NORMAL");
assertTransition(
"HTML_ATTRIBUTE_NAME TEXTAREA PLAIN_TEXT",
"=",
"HTML_BEFORE_ATTRIBUTE_VALUE TEXTAREA PLAIN_TEXT");
assertTransition(
"HTML_ATTRIBUTE_NAME NORMAL PLAIN_TEXT",
" = ",
"HTML_BEFORE_ATTRIBUTE_VALUE NORMAL PLAIN_TEXT");
}
@Test
public void testBeforeAttrValue() throws Exception {
assertTransition(
"HTML_BEFORE_ATTRIBUTE_VALUE NORMAL URI NORMAL",
"\"",
"URI NORMAL URI DOUBLE_QUOTE START NORMAL");
assertTransition(
"HTML_BEFORE_ATTRIBUTE_VALUE NORMAL SCRIPT", "'", "JS NORMAL SCRIPT SINGLE_QUOTE REGEX");
assertTransition(
"HTML_BEFORE_ATTRIBUTE_VALUE NORMAL STYLE", "\"", "CSS NORMAL STYLE DOUBLE_QUOTE");
assertTransition(
"HTML_BEFORE_ATTRIBUTE_VALUE TEXTAREA STYLE",
"color",
"CSS TEXTAREA STYLE SPACE_OR_TAG_END");
assertTransition(
"HTML_BEFORE_ATTRIBUTE_VALUE NORMAL URI NORMAL",
"/",
"URI NORMAL URI SPACE_OR_TAG_END AUTHORITY_OR_PATH NORMAL");
assertTransition(
"HTML_BEFORE_ATTRIBUTE_VALUE TITLE PLAIN_TEXT",
"\"",
"HTML_NORMAL_ATTR_VALUE TITLE PLAIN_TEXT DOUBLE_QUOTE");
assertTransition("HTML_BEFORE_ATTRIBUTE_VALUE NORMAL PLAIN_TEXT", ">", "HTML_PCDATA");
assertTransition("HTML_BEFORE_ATTRIBUTE_VALUE TITLE PLAIN_TEXT", ">", "HTML_RCDATA TITLE");
}
@Test
public void testAttr() throws Exception {
assertTransition(
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT DOUBLE_QUOTE",
"",
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT DOUBLE_QUOTE");
assertTransition(
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT SINGLE_QUOTE",
"",
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT SINGLE_QUOTE");
assertTransition(
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT SPACE_OR_TAG_END",
"",
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT SPACE_OR_TAG_END");
assertTransition(
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT DOUBLE_QUOTE",
"foo",
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT DOUBLE_QUOTE");
assertTransition(
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT SINGLE_QUOTE",
"foo",
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT SINGLE_QUOTE");
assertTransition(
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT SPACE_OR_TAG_END",
"foo",
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT SPACE_OR_TAG_END");
assertTransition(
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT DOUBLE_QUOTE", "\"", "HTML_TAG NORMAL");
assertTransition(
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT SINGLE_QUOTE", "'", "HTML_TAG NORMAL");
assertTransition(
"HTML_NORMAL_ATTR_VALUE SCRIPT PLAIN_TEXT SINGLE_QUOTE", "'", "HTML_TAG SCRIPT");
assertTransition(
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT SPACE_OR_TAG_END",
" x='",
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT SINGLE_QUOTE");
assertTransition(
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT SPACE_OR_TAG_END", " x='y'", "HTML_TAG NORMAL");
assertTransition(
"HTML_NORMAL_ATTR_VALUE NORMAL PLAIN_TEXT SPACE_OR_TAG_END", ">", "HTML_PCDATA");
assertTransition("HTML_NORMAL_ATTR_VALUE SCRIPT PLAIN_TEXT SPACE_OR_TAG_END", ">", "JS REGEX");
}
@Test
public void testCss() throws Exception {
assertTransition("CSS", "", "CSS");
assertTransition("CSS", " p { color: red; }", "CSS");
assertTransition("CSS", "p.clazz#id {\r\n border: 2px;\n}", "CSS");
assertTransition("CSS", "/*\nHello, World! */", "CSS");
assertTransition("CSS", "/*", "CSS_COMMENT");
assertTransition("CSS", "/**", "CSS_COMMENT");
assertTransition("CSS", "/** '", "CSS_COMMENT");
assertTransition("CSS", "/** \"foo", "CSS_COMMENT");
assertTransition("CSS", "'", "CSS_SQ_STRING");
assertTransition("CSS", "\"", "CSS_DQ_STRING");
assertTransition("CSS", "\" /* hello", "CSS_DQ_STRING");
assertTransition("CSS", "</style", "HTML_TAG NORMAL"); // Not a start tag so NORMAL.
assertTransition("CSS", "</Style", "HTML_TAG NORMAL");
// Close style tag in attribute value is not a break. Ok to transition to ERROR.
assertTransition("CSS NORMAL STYLE DOUBLE_QUOTE", "</style", "CSS NORMAL STYLE DOUBLE_QUOTE");
}
@Test
public void testCssComment() throws Exception {
assertTransition("CSS_COMMENT", "", "CSS_COMMENT");
assertTransition("CSS_COMMENT", "\r\n\n\r", "CSS_COMMENT");
assertTransition("CSS_COMMENT", " * /", "CSS_COMMENT");
assertTransition("CSS_COMMENT", " */", "CSS");
assertTransition("CSS_COMMENT", "**/", "CSS");
assertTransition("CSS_COMMENT", "\\*/", "CSS");
assertTransition(
"CSS_COMMENT NORMAL STYLE SPACE_OR_TAG_END", "*/", "CSS NORMAL STYLE SPACE_OR_TAG_END");
}
@Test
public void testCssDqString() throws Exception {
assertTransition("CSS_DQ_STRING", "", "CSS_DQ_STRING");
assertTransition("CSS_DQ_STRING", "Hello, World!", "CSS_DQ_STRING");
assertTransition("CSS_DQ_STRING", "Don't", "CSS_DQ_STRING");
assertTransition("CSS_DQ_STRING", "\"", "CSS");
assertTransition("CSS_DQ_STRING", "\\22", "CSS_DQ_STRING");
assertTransition("CSS_DQ_STRING", "\\22 ", "CSS_DQ_STRING");
assertTransition("CSS_DQ_STRING", "\\27", "CSS_DQ_STRING");
assertTransition("CSS_DQ_STRING", "\r", "ERROR");
assertTransition("CSS_DQ_STRING", "\n", "ERROR");
assertTransition("CSS_DQ_STRING", "</style>", "HTML_PCDATA"); // Or error.
}
@Test
public void testCssSqString() throws Exception {
assertTransition("CSS_SQ_STRING", "", "CSS_SQ_STRING");
assertTransition("CSS_SQ_STRING", "Hello, World!", "CSS_SQ_STRING");
assertTransition("CSS_SQ_STRING", "M. \"The Greatest!\" Ali", "CSS_SQ_STRING");
assertTransition("CSS_SQ_STRING", "'", "CSS");
assertTransition("CSS_SQ_STRING", "\\22", "CSS_SQ_STRING");
assertTransition("CSS_SQ_STRING", "\\22 ", "CSS_SQ_STRING");
assertTransition("CSS_SQ_STRING", "\\27", "CSS_SQ_STRING");
assertTransition("CSS_SQ_STRING", "\r", "ERROR");
assertTransition("CSS_SQ_STRING", "\n", "ERROR");
assertTransition("CSS_SQ_STRING", "</style>", "HTML_PCDATA"); // Or error.
assertTransition(
"CSS_SQ_STRING NORMAL STYLE SPACE_OR_TAG_END", "'", "CSS NORMAL STYLE SPACE_OR_TAG_END");
}
@Test
public void testCssUri() throws Exception {
assertTransition("CSS_URI START NORMAL", "", "CSS_URI START NORMAL");
assertTransition("CSS_URI START NORMAL", "/search?q=cute+bunnies", "CSS_URI QUERY NORMAL");
assertTransition("CSS_URI START NORMAL", "#anchor)", "CSS");
assertTransition("CSS_URI START NORMAL", "#anchor )", "CSS");
assertTransition("CSS_URI START NORMAL", "/do+not+panic", "CSS_URI AUTHORITY_OR_PATH NORMAL");
assertTransition(
"CSS_SQ_URI START NORMAL", "/don%27t+panic", "CSS_SQ_URI AUTHORITY_OR_PATH NORMAL");
assertTransition(
"CSS_SQ_URI START NORMAL",
"Muhammed+\"The+Greatest!\"+Ali",
"CSS_SQ_URI MAYBE_SCHEME NORMAL");
assertTransition(
"CSS_SQ_URI START NORMAL", "(/don%27t+panic)", "CSS_SQ_URI AUTHORITY_OR_PATH NORMAL");
assertTransition(
"CSS_DQ_URI START NORMAL",
"Muhammed+%22The+Greatest!%22+Ali",
"CSS_DQ_URI MAYBE_SCHEME NORMAL");
assertTransition(
"CSS_DQ_URI START NORMAL", "/don't+panic", "CSS_DQ_URI AUTHORITY_OR_PATH NORMAL");
assertTransition("CSS_SQ_URI START NORMAL", "#foo'", "CSS");
assertTransition(
"CSS_URI NORMAL STYLE SPACE_OR_TAG_END START NORMAL",
")",
"CSS NORMAL STYLE SPACE_OR_TAG_END");
assertTransition(
"CSS_DQ_URI NORMAL STYLE SINGLE_QUOTE AUTHORITY_OR_PATH NORMAL",
"\"",
"CSS NORMAL STYLE SINGLE_QUOTE");
assertTransition(
"CSS_SQ_URI NORMAL STYLE DOUBLE_QUOTE FRAGMENT NORMAL",
"x'",
"CSS NORMAL STYLE DOUBLE_QUOTE");
assertTransition(
"CSS_SQ_URI NORMAL STYLE DOUBLE_QUOTE FRAGMENT NORMAL",
"#x'",
"CSS NORMAL STYLE DOUBLE_QUOTE");
assertTransition("CSS", "url(", "CSS_URI START NORMAL");
assertTransition("CSS", "url(/search?q=", "CSS_URI QUERY NORMAL");
assertTransition("CSS", "url( ", "CSS_URI START NORMAL");
assertTransition("CSS", "url('", "CSS_SQ_URI START NORMAL");
assertTransition("CSS", "url('//", "CSS_SQ_URI AUTHORITY_OR_PATH NORMAL");
assertTransition("CSS", "url('/search?q=", "CSS_SQ_URI QUERY NORMAL");
assertTransition("CSS", "url(\"", "CSS_DQ_URI START NORMAL");
assertTransition("CSS", "url(\"/search?q=", "CSS_DQ_URI QUERY NORMAL");
assertTransition("CSS", "url(\"/foo#bar", "CSS_DQ_URI FRAGMENT NORMAL");
// TODO(gboyer): We may want to handle CSS comments, but because Soy already eliminates
// /*...*/, we will assume this will be exceedingly rare, and can only result in getting
// the less-permissive NORMAL state.
assertTransition("CSS", "background-image:url('", "CSS_SQ_URI START MEDIA");
assertTransition("CSS", "background-image:url('/search?q=", "CSS_SQ_URI QUERY MEDIA");
assertTransition("CSS", "content:url('/search?q=", "CSS_SQ_URI QUERY MEDIA");
assertTransition("CSS", "backgrounD-imagE:uRL('/search?q=", "CSS_SQ_URI QUERY MEDIA");
assertTransition("CSS", "{ background:url(\"/search?q=", "CSS_DQ_URI QUERY MEDIA");
assertTransition("CSS", " ;\t \nbackground \t\n : \t \nurl(", "CSS_URI START MEDIA");
assertTransition("CSS", "{cursor:url(/search?q=", "CSS_URI QUERY MEDIA");
assertTransition("CSS", "{list-style:url(/search?q=", "CSS_URI QUERY MEDIA");
assertTransition("CSS", "{list-style-image:url(/search?q=", "CSS_URI QUERY MEDIA");
// These are not image URLs.
assertTransition("CSS", "{;src:url(/search?q=", "CSS_URI QUERY NORMAL");
assertTransition("CSS", "not-background:url(/search?q=", "CSS_URI QUERY NORMAL");
assertTransition("CSS", "{foo;not-background:url(/search?q=", "CSS_URI QUERY NORMAL");
assertTransition("CSS", "{list-style-zmage:url(/search?q=", "CSS_URI QUERY NORMAL");
}
@Test
public void testJsBeforeRegex() throws Exception {
assertTransition("JS REGEX", "", "JS REGEX");
assertTransition("JS REGEX", "/*", "JS_BLOCK_COMMENT REGEX");
assertTransition(
"JS NORMAL SCRIPT SPACE_OR_TAG_END REGEX",
"/*",
"JS_BLOCK_COMMENT NORMAL SCRIPT SPACE_OR_TAG_END REGEX");
assertTransition("JS REGEX", "//", "JS_LINE_COMMENT REGEX");
assertTransition(
"JS NORMAL SCRIPT SPACE_OR_TAG_END REGEX",
"//",
"JS_LINE_COMMENT NORMAL SCRIPT SPACE_OR_TAG_END REGEX");
assertTransition("JS REGEX", "'", "JS_SQ_STRING");
assertTransition(
"JS NORMAL SCRIPT SPACE_OR_TAG_END REGEX",
"'",
"JS_SQ_STRING NORMAL SCRIPT SPACE_OR_TAG_END");
assertTransition("JS REGEX", "\"", "JS_DQ_STRING");
assertTransition(
"JS NORMAL SCRIPT SPACE_OR_TAG_END REGEX",
"\"",
"JS_DQ_STRING NORMAL SCRIPT SPACE_OR_TAG_END");
assertTransition("JS REGEX", "42", "JS DIV_OP");
assertTransition(
"JS NORMAL SCRIPT SPACE_OR_TAG_END REGEX",
"42",
"JS NORMAL SCRIPT SPACE_OR_TAG_END DIV_OP");
assertTransition("JS REGEX", "0.", "JS DIV_OP");
assertTransition("JS REGEX", "x", "JS DIV_OP");
assertTransition("JS REGEX", "-", "JS REGEX");
assertTransition("JS REGEX", "--", "JS DIV_OP");
assertTransition("JS REGEX", " \t \n ", "JS REGEX");
assertTransition("JS REGEX", ")", "JS DIV_OP");
assertTransition("JS REGEX", "/", "JS_REGEX");
assertTransition(
"JS NORMAL SCRIPT SPACE_OR_TAG_END REGEX", "/", "JS_REGEX NORMAL SCRIPT SPACE_OR_TAG_END");
assertTransition("JS REGEX", "/[xy]/", "JS DIV_OP");
assertTransition("JS REGEX", "</script>", "HTML_PCDATA");
}
@Test
public void testJsBeforeDivOp() throws Exception {
assertTransition("JS DIV_OP", "", "JS DIV_OP");
assertTransition("JS DIV_OP", "/*", "JS_BLOCK_COMMENT DIV_OP");
assertTransition(
"JS NORMAL SCRIPT SPACE_OR_TAG_END DIV_OP",
"/*",
"JS_BLOCK_COMMENT NORMAL SCRIPT SPACE_OR_TAG_END DIV_OP");
assertTransition("JS DIV_OP", "//", "JS_LINE_COMMENT DIV_OP");
assertTransition(
"JS NORMAL SCRIPT SPACE_OR_TAG_END DIV_OP",
"//",
"JS_LINE_COMMENT NORMAL SCRIPT SPACE_OR_TAG_END DIV_OP");
assertTransition("JS DIV_OP", "'", "JS_SQ_STRING");
assertTransition(
"JS NORMAL SCRIPT SPACE_OR_TAG_END DIV_OP",
"'",
"JS_SQ_STRING NORMAL SCRIPT SPACE_OR_TAG_END");
assertTransition("JS DIV_OP", "\"", "JS_DQ_STRING");
assertTransition(
"JS NORMAL SCRIPT SPACE_OR_TAG_END DIV_OP",
"\"",
"JS_DQ_STRING NORMAL SCRIPT SPACE_OR_TAG_END");
assertTransition("JS DIV_OP", "42", "JS DIV_OP");
assertTransition(
"JS NORMAL SCRIPT SPACE_OR_TAG_END DIV_OP",
"42",
"JS NORMAL SCRIPT SPACE_OR_TAG_END DIV_OP");
assertTransition("JS DIV_OP", "0.", "JS DIV_OP");
assertTransition("JS DIV_OP", "x", "JS DIV_OP");
assertTransition("JS DIV_OP", "-", "JS REGEX");
assertTransition("JS DIV_OP", "--", "JS DIV_OP");
assertTransition("JS DIV_OP", " \n ", "JS DIV_OP");
assertTransition("JS DIV_OP", ")", "JS DIV_OP");
assertTransition("JS DIV_OP", "/", "JS REGEX");
assertTransition(
"JS NORMAL SCRIPT SPACE_OR_TAG_END DIV_OP", "/", "JS NORMAL SCRIPT SPACE_OR_TAG_END REGEX");
assertTransition("JS DIV_OP", "/[xy]/", "JS REGEX");
assertTransition("JS DIV_OP", "</script>", "HTML_PCDATA");
}
@Test
public void testJsLineComment() throws Exception {
assertTransition("JS_LINE_COMMENT DIV_OP", "", "JS_LINE_COMMENT DIV_OP");
assertTransition("JS_LINE_COMMENT DIV_OP", "*/", "JS_LINE_COMMENT DIV_OP");
assertTransition("JS_LINE_COMMENT DIV_OP", "Hello, World!", "JS_LINE_COMMENT DIV_OP");
assertTransition("JS_LINE_COMMENT DIV_OP", "\"'/", "JS_LINE_COMMENT DIV_OP");
assertTransition("JS_LINE_COMMENT DIV_OP", "\n", "JS DIV_OP");
assertTransition(
"JS_LINE_COMMENT NORMAL SCRIPT DOUBLE_QUOTE DIV_OP",
"\n",
"JS NORMAL SCRIPT DOUBLE_QUOTE DIV_OP");
assertTransition("JS_LINE_COMMENT DIV_OP", "</script>", "HTML_PCDATA");
assertTransition("JS_LINE_COMMENT REGEX", "", "JS_LINE_COMMENT REGEX");
assertTransition("JS_LINE_COMMENT REGEX", "*/", "JS_LINE_COMMENT REGEX");
assertTransition("JS_LINE_COMMENT REGEX", "Hello, World!", "JS_LINE_COMMENT REGEX");
assertTransition("JS_LINE_COMMENT REGEX", "\"'/", "JS_LINE_COMMENT REGEX");
assertTransition("JS_LINE_COMMENT REGEX", "\n", "JS REGEX");
assertTransition("JS_LINE_COMMENT REGEX", "</script>", "HTML_PCDATA");
}
@Test
public void testJsBlockComment() throws Exception {
assertTransition("JS_BLOCK_COMMENT DIV_OP", "", "JS_BLOCK_COMMENT DIV_OP");
assertTransition("JS_BLOCK_COMMENT DIV_OP", "\n", "JS_BLOCK_COMMENT DIV_OP");
assertTransition("JS_BLOCK_COMMENT DIV_OP", "Hello, World!", "JS_BLOCK_COMMENT DIV_OP");
assertTransition("JS_BLOCK_COMMENT DIV_OP", "\"'/", "JS_BLOCK_COMMENT DIV_OP");
assertTransition("JS_BLOCK_COMMENT DIV_OP", "*/", "JS DIV_OP");
assertTransition(
"JS_BLOCK_COMMENT NORMAL SCRIPT DOUBLE_QUOTE DIV_OP",
"*/",
"JS NORMAL SCRIPT DOUBLE_QUOTE DIV_OP");
assertTransition("JS_BLOCK_COMMENT DIV_OP", "</script>", "HTML_PCDATA");
assertTransition("JS_BLOCK_COMMENT REGEX", "", "JS_BLOCK_COMMENT REGEX");
assertTransition("JS_BLOCK_COMMENT REGEX", "\r\n", "JS_BLOCK_COMMENT REGEX");
assertTransition("JS_BLOCK_COMMENT REGEX", "Hello, World!", "JS_BLOCK_COMMENT REGEX");
assertTransition("JS_BLOCK_COMMENT REGEX", "\"'/", "JS_BLOCK_COMMENT REGEX");
assertTransition("JS_BLOCK_COMMENT REGEX", "*/", "JS REGEX");
assertTransition("JS_BLOCK_COMMENT REGEX", "</script>", "HTML_PCDATA"); // Or error.
}
@Test
public void testJsDqString() throws Exception {
assertTransition("JS_DQ_STRING", "", "JS_DQ_STRING");
assertTransition("JS_DQ_STRING", "Hello, World!", "JS_DQ_STRING");
assertTransition("JS_DQ_STRING", M1500, "JS_DQ_STRING"); // Check for stack overflow
assertTransition("JS_DQ_STRING", "\"", "JS DIV_OP");
assertTransition(
"JS_DQ_STRING NORMAL SCRIPT SINGLE_QUOTE",
"Hello, World!",
"JS_DQ_STRING NORMAL SCRIPT SINGLE_QUOTE");
assertTransition(
"JS_DQ_STRING NORMAL SCRIPT SINGLE_QUOTE", "\"", "JS NORMAL SCRIPT SINGLE_QUOTE DIV_OP");
assertTransition("JS_DQ_STRING", "</script>", "HTML_PCDATA"); // Or error.
assertTransition("JS_DQ_STRING", "</p>", "JS_DQ_STRING");
}
@Test
public void testJsSqString() throws Exception {
assertTransition("JS_SQ_STRING", "", "JS_SQ_STRING");
assertTransition("JS_SQ_STRING", "Hello, World!", "JS_SQ_STRING");
assertTransition("JS_SQ_STRING", M1500, "JS_SQ_STRING"); // Check for stack overflow
assertTransition("JS_SQ_STRING", "/*", "JS_SQ_STRING");
assertTransition("JS_SQ_STRING", "\"", "JS_SQ_STRING");
assertTransition("JS_SQ_STRING", "\\x27", "JS_SQ_STRING");
assertTransition("JS_SQ_STRING", "\\'", "JS_SQ_STRING");
assertTransition("JS_SQ_STRING", "\r", "ERROR");
assertTransition("JS_SQ_STRING", "\\\rn", "JS_SQ_STRING");
assertTransition("JS_SQ_STRING", "'", "JS DIV_OP");
assertTransition(
"JS_SQ_STRING NORMAL SCRIPT DOUBLE_QUOTE",
"Hello, World!",
"JS_SQ_STRING NORMAL SCRIPT DOUBLE_QUOTE");
assertTransition(
"JS_SQ_STRING NORMAL SCRIPT DOUBLE_QUOTE", "'", "JS NORMAL SCRIPT DOUBLE_QUOTE DIV_OP");
assertTransition("JS_SQ_STRING", "</script>", "HTML_PCDATA"); // Or error.
assertTransition("JS_SQ_STRING", "</s>", "JS_SQ_STRING");
}
@Test
public void testJsRegex() throws Exception {
assertTransition("JS_REGEX", "", "JS_REGEX");
assertTransition("JS_REGEX", "Hello, World!", "JS_REGEX");
assertTransition("JS_REGEX", "\\/*", "JS_REGEX");
assertTransition("JS_REGEX", "[/*]", "JS_REGEX");
assertTransition("JS_REGEX", "\"", "JS_REGEX");
assertTransition("JS_REGEX", "\\x27", "JS_REGEX");
assertTransition("JS_REGEX", "\\'", "JS_REGEX");
assertTransition("JS_REGEX", "\r", "ERROR");
assertTransition("JS_REGEX", "\\\rn", "ERROR"); // Line continuations not allowed in RegExps.
assertTransition("JS_REGEX", "/", "JS DIV_OP");
assertTransition(
"JS_REGEX NORMAL SCRIPT DOUBLE_QUOTE",
"Hello, World!",
"JS_REGEX NORMAL SCRIPT DOUBLE_QUOTE");
assertTransition(
"JS_REGEX NORMAL SCRIPT DOUBLE_QUOTE", "/", "JS NORMAL SCRIPT DOUBLE_QUOTE DIV_OP");
assertTransition("JS_REGEX", "</script>", "HTML_PCDATA"); // Or error.
}
@Test
public void testUri() throws Exception {
assertTransition("URI START NORMAL", "", "URI START NORMAL");
assertTransition("URI START NORMAL", ".", "URI MAYBE_SCHEME NORMAL");
assertTransition("URI START NORMAL", "/", "URI AUTHORITY_OR_PATH NORMAL");
assertTransition("URI START NORMAL", "#", "URI FRAGMENT NORMAL");
assertTransition("URI START NORMAL", "x", "URI MAYBE_SCHEME NORMAL");
assertTransition("URI START NORMAL", "x:", "URI AUTHORITY_OR_PATH NORMAL");
assertTransition("URI START NORMAL", "?", "URI QUERY NORMAL");
assertTransition("URI START NORMAL", "&", "URI MAYBE_SCHEME NORMAL");
assertTransition("URI START NORMAL", "=", "URI MAYBE_SCHEME NORMAL");
assertTransition("URI START NORMAL", "javascript:", "URI DANGEROUS_SCHEME NORMAL");
assertTransition("URI START NORMAL", "JavaScript:", "URI DANGEROUS_SCHEME NORMAL");
assertTransition("URI START NORMAL", "not-javascript:", "URI AUTHORITY_OR_PATH NORMAL");
assertTransition("URI START NORMAL", "data:", "URI DANGEROUS_SCHEME NORMAL");
// NOTE(gboyer): Perhaps in media URIs we can consider allowing data if followed by an image
// mime type, but it doesn't seem critical and is easy to work around.
assertTransition("URI START MEDIA", "data:", "URI DANGEROUS_SCHEME MEDIA");
assertTransition("URI START NORMAL", "bloB:", "URI DANGEROUS_SCHEME NORMAL");
assertTransition("URI START NORMAL", "FiLeSystem:", "URI DANGEROUS_SCHEME NORMAL");
assertTransition("URI QUERY NORMAL", "", "URI QUERY NORMAL");
assertTransition("URI QUERY NORMAL", ".", "URI QUERY NORMAL");
assertTransition("URI QUERY NORMAL", "/", "URI QUERY NORMAL");
assertTransition("URI QUERY NORMAL", "#", "URI FRAGMENT NORMAL");
assertTransition("URI QUERY NORMAL", "x", "URI QUERY NORMAL");
assertTransition("URI QUERY NORMAL", "&", "URI QUERY NORMAL");
assertTransition("URI QUERY NORMAL", "javascript:", "URI QUERY NORMAL");
assertTransition("URI FRAGMENT NORMAL", "", "URI FRAGMENT NORMAL");
assertTransition("URI FRAGMENT NORMAL", "?", "URI FRAGMENT NORMAL");
assertTransition("URI FRAGMENT NORMAL", "javascript:", "URI FRAGMENT NORMAL");
assertTransition("URI MAYBE_SCHEME NORMAL", ":", "URI AUTHORITY_OR_PATH NORMAL");
// Schemes can have a dot.
assertTransition("URI MAYBE_SCHEME NORMAL", ".", "URI MAYBE_SCHEME NORMAL");
assertTransition("URI MAYBE_SCHEME NORMAL", "foo.bar", "URI MAYBE_SCHEME NORMAL");
assertTransition("URI MAYBE_SCHEME NORMAL", "/", "URI AUTHORITY_OR_PATH NORMAL");
assertTransition("URI MAYBE_SCHEME NORMAL", "/foo", "URI AUTHORITY_OR_PATH NORMAL");
assertTransition("URI MAYBE_SCHEME NORMAL", "?", "URI QUERY NORMAL");
assertTransition("URI MAYBE_SCHEME NORMAL", "blah?blah", "URI QUERY NORMAL");
assertTransition("URI MAYBE_SCHEME NORMAL", "#", "URI FRAGMENT NORMAL");
// If we have a hard-coded prefix, & and = don't do anything.
assertTransition("URI MAYBE_SCHEME NORMAL", "=", "URI MAYBE_SCHEME NORMAL");
assertTransition("URI MAYBE_SCHEME NORMAL", "&", "URI MAYBE_SCHEME NORMAL");
// We don't care about schemes that end with javascript:.
assertTransition("URI MAYBE_SCHEME NORMAL", "javascript:", "URI AUTHORITY_OR_PATH NORMAL");
assertTransition("URI MAYBE_VARIABLE_SCHEME NORMAL", ".", "URI MAYBE_VARIABLE_SCHEME NORMAL");
assertTransition(
"URI MAYBE_VARIABLE_SCHEME NORMAL", "foo.bar", "URI MAYBE_VARIABLE_SCHEME NORMAL");
assertTransition("URI MAYBE_VARIABLE_SCHEME NORMAL", "/", "URI AUTHORITY_OR_PATH NORMAL");
assertTransition("URI MAYBE_VARIABLE_SCHEME NORMAL", "foo/bar", "URI AUTHORITY_OR_PATH NORMAL");
assertTransition("URI MAYBE_VARIABLE_SCHEME NORMAL", "?", "URI QUERY NORMAL");
assertTransition("URI MAYBE_VARIABLE_SCHEME NORMAL", "#", "URI FRAGMENT NORMAL");
// If we have a variable prefix, we use & and = to heuristically transition.
assertTransition("URI MAYBE_VARIABLE_SCHEME NORMAL", "=", "URI QUERY NORMAL");
assertTransition("URI MAYBE_VARIABLE_SCHEME NORMAL", "&", "URI QUERY NORMAL");
assertTransition("URI MAYBE_VARIABLE_SCHEME NORMAL", "bah&foo=", "URI QUERY NORMAL");
assertTransition("URI MAYBE_VARIABLE_SCHEME NORMAL", ":", "ERROR");
assertTransition("URI MAYBE_VARIABLE_SCHEME NORMAL", "javascript:", "ERROR");
}
@Test
public void testRcdata() throws Exception {
assertTransition("HTML_RCDATA XMP", "", "HTML_RCDATA XMP");
assertTransition("HTML_RCDATA XMP", "Hello, World!", "HTML_RCDATA XMP");
assertTransition("HTML_RCDATA XMP", "<p", "HTML_RCDATA XMP");
assertTransition("HTML_RCDATA XMP", "<p ", "HTML_RCDATA XMP");
assertTransition("HTML_RCDATA XMP", "</textarea>", "HTML_RCDATA XMP");
assertTransition("HTML_RCDATA XMP", "</xmp>", "HTML_PCDATA");
assertTransition("HTML_RCDATA XMP", "</xMp>", "HTML_PCDATA");
assertTransition("HTML_RCDATA TEXTAREA", "</xmp>", "HTML_RCDATA TEXTAREA");
assertTransition("HTML_RCDATA TEXTAREA", "</textarea>", "HTML_PCDATA");
}
@Test
public void testText() throws Exception {
// Plain text's only edge should be back to itself.
assertTransition("TEXT", "", "TEXT");
assertTransition("TEXT", "Hello, World!", "TEXT");
assertTransition("TEXT", "<p", "TEXT");
assertTransition("TEXT", "&D*(@*(#*(AW*D(J*#(J*(JS!!!''\"", "TEXT");
assertTransition("TEXT", "<script>var x='", "TEXT");
assertTransition("TEXT", "<a href='", "TEXT");
}
@Test
public void testTemplateElementNesting() throws Exception {
assertTransition("HTML_PCDATA", "<template>", "HTML_PCDATA templateNestDepth=1");
assertTransition("HTML_PCDATA", "<template id='i'>foo", "HTML_PCDATA templateNestDepth=1");
assertTransition("HTML_PCDATA", "<template>foo<template>", "HTML_PCDATA templateNestDepth=2");
assertTransition("HTML_PCDATA", "<template>foo</template>", "HTML_PCDATA");
assertTransition(
"HTML_PCDATA", "<template>foo<template></template>", "HTML_PCDATA templateNestDepth=1");
assertTransition(
"HTML_PCDATA",
"<template>foo<script>//</template></script>",
"HTML_PCDATA templateNestDepth=1");
assertTransition(
"HTML_PCDATA", "<template>foo<!--</template>-->", "HTML_PCDATA templateNestDepth=1");
assertTransition("HTML_PCDATA", "</template>", "ERROR");
assertTransition("HTML_PCDATA", "<template>foo</template></template>", "ERROR");
assertTransition(
"HTML_PCDATA templateNestDepth=4", "</template>", "HTML_PCDATA templateNestDepth=3");
assertTransition(
"HTML_PCDATA templateNestDepth=4", "</TempLate>", "HTML_PCDATA templateNestDepth=3");
}
private static void assertTransition(String from, String rawText, String to) throws Exception {
assertTransition(from, rawText, to, ANY_SLICES);
}
private static void assertTransition(String from, String rawText, String to, int numSlices)
throws Exception {
SlicedRawTextNode node;
try {
node =
RawTextContextUpdater.processRawText(
new RawTextNode(0, rawText, SourceLocation.UNKNOWN), parseContext(from));
} catch (SoyAutoescapeException e) {
if (!to.equals("ERROR")) {
throw new AssertionError("Expected context (" + to + ") but got an exception", e);
} else {
return; // Good!
}
}
// Assert against the toString() for simpler test authoring -- if a developer misspells the
// "to" context, they'll see a useful string-based diff.
assertWithMessage(rawText)
.that(node.getEndContext().toString())
.isEqualTo("(Context " + to + ")");
if (numSlices != ANY_SLICES) {
assertWithMessage(rawText).that(node.getSlices().size()).isEqualTo(numSlices);
}
}
private static Context parseContext(String text) {
return Context.parse(text);
}
}