This repository has been archived on 2023-06-18. You can view files and clone it, but cannot push or open issues or pull requests.
ima02/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JsMessageVisitorTest.java
github-classroom[bot] e42e547e48
Initial commit
2023-04-25 11:33:41 +00:00

586 lines
21 KiB
Java

/*
* Copyright 2008 The Closure Compiler Authors.
*
* 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.javascript.jscomp;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import static com.google.javascript.jscomp.JsMessage.Style;
import static com.google.javascript.jscomp.JsMessage.Style.CLOSURE;
import static com.google.javascript.jscomp.JsMessage.Style.LEGACY;
import static com.google.javascript.jscomp.JsMessage.Style.RELAX;
import static com.google.javascript.jscomp.JsMessageVisitor.isLowerCamelCaseWithNumericSuffixes;
import static com.google.javascript.jscomp.JsMessageVisitor.toLowerCamelCaseWithNumericSuffixes;
import com.google.javascript.rhino.Node;
import junit.framework.TestCase;
import java.util.List;
/**
* Test for {@link JsMessageVisitor}.
*
* @author anatol@google.com (Anatol Pomazau)
*/
public class JsMessageVisitorTest extends TestCase {
private Compiler compiler;
private List<JsMessage> messages;
private boolean allowLegacyMessages;
@Override
protected void setUp() throws Exception {
messages = Lists.newLinkedList();
allowLegacyMessages = true;
}
public void testJsMessageOnVar() {
extractMessagesSafely(
"/** @desc Hello */ var MSG_HELLO = goog.getMsg('a')");
assertEquals(0, compiler.getWarningCount());
assertEquals(1, messages.size());
JsMessage msg = messages.get(0);
assertEquals("MSG_HELLO", msg.getKey());
assertEquals("Hello", msg.getDesc());
}
public void testJsMessageOnProperty() {
extractMessagesSafely("/** @desc a */ " +
"pint.sub.MSG_MENU_MARK_AS_UNREAD = goog.getMsg('a')");
assertEquals(0, compiler.getWarningCount());
assertEquals(1, messages.size());
JsMessage msg = messages.get(0);
assertEquals("MSG_MENU_MARK_AS_UNREAD", msg.getKey());
assertEquals("a", msg.getDesc());
}
public void testOrphanedJsMessage() {
extractMessagesSafely("goog.getMsg('a')");
assertEquals(1, compiler.getWarningCount());
assertEquals(0, messages.size());
JSError warn = compiler.getWarnings()[0];
assertEquals(JsMessageVisitor.MESSAGE_NODE_IS_ORPHANED, warn.getType());
}
public void testMessageWithoutDescription() {
extractMessagesSafely("var MSG_HELLO = goog.getMsg('a')");
assertEquals(1, compiler.getWarningCount());
assertEquals(1, messages.size());
JsMessage msg = messages.get(0);
assertEquals("MSG_HELLO", msg.getKey());
assertEquals(JsMessageVisitor.MESSAGE_HAS_NO_DESCRIPTION,
compiler.getWarnings()[0].getType());
}
public void testIncorrectMessageReporting() {
extractMessages("var MSG_HELLO = goog.getMsg('a' + + 'b')");
assertEquals(1, compiler.getErrorCount());
assertEquals(0, compiler.getWarningCount());
assertEquals(0, messages.size());
JSError mailformedTreeError = compiler.getErrors()[0];
assertEquals(JsMessageVisitor.MESSAGE_TREE_MALFORMED,
mailformedTreeError.getType());
assertEquals("Message parse tree malformed. "
+ "STRING or ADD node expected; found: POS",
mailformedTreeError.description);
}
public void testEmptyMessage() {
// This is an edge case. Empty messages are useless, but shouldn't fail
extractMessagesSafely("var MSG_EMPTY = '';");
assertEquals(1, messages.size());
JsMessage msg = messages.get(0);
assertEquals("MSG_EMPTY", msg.getKey());
assertEquals("", msg.toString());
}
public void testConcatOfStrings() {
extractMessagesSafely("var MSG_NOTEMPTY = 'aa' + 'bbb' \n + ' ccc';");
assertEquals(1, messages.size());
JsMessage msg = messages.get(0);
assertEquals("MSG_NOTEMPTY", msg.getKey());
assertEquals("aabbb ccc", msg.toString());
}
public void testLegacyFormatDescription() {
extractMessagesSafely("var MSG_SILLY = 'silly test message';\n"
+ "var MSG_SILLY_HELP = 'help text';");
assertEquals(1, messages.size());
JsMessage msg = messages.get(0);
assertEquals("MSG_SILLY", msg.getKey());
assertEquals("help text", msg.getDesc());
assertEquals("silly test message", msg.toString());
}
public void testLegacyFormatParametizedFunction() {
extractMessagesSafely("var MSG_SILLY = function(one, two) {"
+ " return one + ', ' + two + ', buckle my shoe';"
+ "};");
assertEquals(1, messages.size());
JsMessage msg = messages.get(0);
assertEquals("MSG_SILLY", msg.getKey());
assertEquals(null, msg.getDesc());
assertEquals("{$one}, {$two}, buckle my shoe", msg.toString());
}
public void testLegacyMessageWithDescAnnotation() {
// Well, is was better do not allow legacy messages with @desc annotations,
// but people love to mix styles so we need to check @desc also.
extractMessagesSafely(
"/** @desc The description */ var MSG_A = 'The Message';");
assertEquals(1, messages.size());
assertEquals(1, compiler.getWarningCount());
JsMessage msg = messages.get(0);
assertEquals("MSG_A", msg.getKey());
assertEquals("The Message", msg.toString());
assertEquals("The description", msg.getDesc());
}
public void testLegacyMessageWithDescAnnotationAndHelpVar() {
// Well, is was better do not allow legacy messages with @desc annotations,
// but people love to mix styles so we need to check @desc also.
extractMessagesSafely(
"var MSG_A_HELP = 'This is a help var';\n" +
"/** @desc The description in @desc*/ var MSG_A = 'The Message';");
assertEquals(1, messages.size());
assertEquals(1, compiler.getWarningCount());
JsMessage msg = messages.get(0);
assertEquals("MSG_A", msg.getKey());
assertEquals("The Message", msg.toString());
assertEquals("The description in @desc", msg.getDesc());
}
public void testClosureMessageWithHelpPostfix() {
extractMessagesSafely("/** @desc help text */\n"
+ "var MSG_FOO_HELP = goog.getMsg('Help!');");
assertEquals(1, messages.size());
JsMessage msg = messages.get(0);
assertEquals("MSG_FOO_HELP", msg.getKey());
assertEquals("help text", msg.getDesc());
assertEquals("Help!", msg.toString());
}
public void testClosureMessageWithoutGoogGetmsg() {
allowLegacyMessages = false;
extractMessages("var MSG_FOO_HELP = 'I am a bad message';");
assertEquals(1, messages.size());
assertEquals(1, compiler.getErrors().length);
JSError error = compiler.getErrors()[0];
assertEquals(JsMessageVisitor.MESSAGE_NOT_INITIALIZED_USING_NEW_SYNTAX,
error.getType());
}
public void testClosureFormatParametizedFunction() {
extractMessagesSafely("/** @desc help text */"
+ "var MSG_SILLY = goog.getMsg('{$adjective} ' + 'message', "
+ "{'adjective': 'silly'});");
assertEquals(1, messages.size());
JsMessage msg = messages.get(0);
assertEquals("MSG_SILLY", msg.getKey());
assertEquals("help text", msg.getDesc());
assertEquals("{$adjective} message", msg.toString());
}
public void testHugeMessage() {
extractMessagesSafely("/**" +
" * @desc A message with lots of stuff.\n" +
" * @hidden\n" +
" */" +
"var MSG_HUGE = goog.getMsg(" +
" '{$startLink_1}Google{$endLink}' +" +
" '{$startLink_2}blah{$endLink}{$boo}{$foo_001}{$boo}' +" +
" '{$foo_002}{$xxx_001}{$image}{$image_001}{$xxx_002}'," +
" {'startLink_1': '<a href=http://www.google.com/>'," +
" 'endLink': '</a>'," +
" 'startLink_2': '<a href=\"' + opt_data.url + '\">'," +
" 'boo': opt_data.boo," +
" 'foo_001': opt_data.foo," +
" 'foo_002': opt_data.boo.foo," +
" 'xxx_001': opt_data.boo + opt_data.foo," +
" 'image': htmlTag7," +
" 'image_001': opt_data.image," +
" 'xxx_002': foo.callWithOnlyTopLevelKeys(" +
" bogusFn, opt_data, null, 'bogusKey1'," +
" opt_data.moo, 'bogusKey2', param10)});");
assertEquals(1, messages.size());
JsMessage msg = messages.get(0);
assertEquals("MSG_HUGE", msg.getKey());
assertEquals("A message with lots of stuff.", msg.getDesc());
assertTrue(msg.isHidden());
assertEquals("{$startLink_1}Google{$endLink}{$startLink_2}blah{$endLink}" +
"{$boo}{$foo_001}{$boo}{$foo_002}{$xxx_001}{$image}" +
"{$image_001}{$xxx_002}", msg.toString());
}
public void testUnnamedGoogleMessage() {
extractMessagesSafely("var MSG_UNNAMED_2 = goog.getMsg('Hullo');");
assertEquals(1, messages.size());
JsMessage msg = messages.get(0);
assertEquals(null, msg.getDesc());
assertEquals("MSG_16LJMYKCXT84X", msg.getKey());
assertEquals("MSG_16LJMYKCXT84X", msg.getId());
}
public void testEmptyTextMessage() {
extractMessagesSafely("/** @desc text */ var MSG_FOO = goog.getMsg('');");
assertEquals(1, messages.size());
assertEquals(1, compiler.getWarningCount());
assertEquals("Message value of MSG_FOO is just an empty string. "
+ "Empty messages are forbidden.",
compiler.getWarnings()[0].description);
}
public void testEmptyTextComplexMessage() {
extractMessagesSafely("/** @desc text */ var MSG_BAR = goog.getMsg("
+ "'' + '' + '' + ''\n+'');");
assertEquals(1, messages.size());
assertEquals(1, compiler.getWarningCount());
assertEquals("Message value of MSG_BAR is just an empty string. "
+ "Empty messages are forbidden.",
compiler.getWarnings()[0].description);
}
public void testMessageIsNoUnnamed() {
extractMessagesSafely("var MSG_UNNAMED_ITEM = goog.getMsg('Hullo');");
assertEquals(1, messages.size());
JsMessage msg = messages.get(0);
assertEquals("MSG_UNNAMED_ITEM", msg.getKey());
assertFalse(msg.isHidden());
}
public void testMsgVarWithoutAssignment() {
extractMessages("var MSG_SILLY;");
assertEquals(1, compiler.getErrors().length);
JSError error = compiler.getErrors()[0];
assertEquals(JsMessageVisitor.MESSAGE_HAS_NO_VALUE, error.getType());
}
public void testRegularVarWithoutAssignment() {
extractMessagesSafely("var SILLY;");
assertTrue(messages.isEmpty());
}
public void itIsNotImplementedYet_testMsgPropertyWithoutAssignment() {
extractMessages("goog.message.MSG_SILLY_PROP;");
assertEquals(1, compiler.getErrors().length);
JSError error = compiler.getErrors()[0];
assertEquals("Message MSG_SILLY_PROP has no value", error.description);
}
public void testMsgVarWithIncorrectRightSide() {
extractMessages("var MSG_SILLY = 0;");
assertEquals(1, compiler.getErrors().length);
JSError error = compiler.getErrors()[0];
assertEquals("Message parse tree malformed. Cannot parse value of "
+ "message MSG_SILLY", error.description);
}
public void testIncorrectMessage() {
extractMessages("DP_DatePicker.MSG_DATE_SELECTION = {};");
assertEquals(0, messages.size());
assertEquals(1, compiler.getErrors().length);
JSError error = compiler.getErrors()[0];
assertEquals("Message parse tree malformed. "+
"Message must be initialized using goog.getMsg function.",
error.description);
}
public void testUnrecognizedFunction() {
allowLegacyMessages = false;
extractMessages("DP_DatePicker.MSG_DATE_SELECTION = somefunc('a')");
assertEquals(0, messages.size());
assertEquals(1, compiler.getErrors().length);
JSError error = compiler.getErrors()[0];
assertEquals("Message parse tree malformed. "+
"Message initialized using unrecognized function. " +
"Please use goog.getMsg() instead.",
error.description);
}
public void testExtractPropertyMessage() {
extractMessagesSafely("/**"
+ " * @desc A message that demonstrates placeholders\n"
+ " * @hidden\n"
+ " */"
+ "a.b.MSG_SILLY = goog.getMsg(\n"
+ " '{$adjective} ' + '{$someNoun}',\n"
+ " {'adjective': adj, 'someNoun': noun});");
assertEquals(1, messages.size());
JsMessage msg = messages.get(0);
assertEquals("MSG_SILLY", msg.getKey());
assertEquals("{$adjective} {$someNoun}", msg.toString());
assertEquals("A message that demonstrates placeholders", msg.getDesc());
assertTrue(msg.isHidden());
}
public void testAlmostButNotExternalMessage() {
extractMessagesSafely(
"/** @desc External */ var MSG_EXTERNAL = goog.getMsg('External');");
assertEquals(0, compiler.getWarningCount());
assertEquals(1, messages.size());
assertFalse(messages.get(0).isExternal());
assertEquals("MSG_EXTERNAL", messages.get(0).getKey());
}
public void testExternalMessage() {
extractMessagesSafely("var MSG_EXTERNAL_111 = goog.getMsg('Hello World');");
assertEquals(0, compiler.getWarningCount());
assertEquals(1, messages.size());
assertTrue(messages.get(0).isExternal());
assertEquals("111", messages.get(0).getId());
}
public void testIsValidMessageNameStrict() {
JsMessageVisitor visitor = new DummyJsVisitor(CLOSURE);
assertTrue(visitor.isMessageName("MSG_HELLO", true));
assertTrue(visitor.isMessageName("MSG_", true));
assertTrue(visitor.isMessageName("MSG_HELP", true));
assertTrue(visitor.isMessageName("MSG_FOO_HELP", true));
assertFalse(visitor.isMessageName("_FOO_HELP", true));
assertFalse(visitor.isMessageName("MSGFOOP", true));
}
public void testIsValidMessageNameRelax() {
JsMessageVisitor visitor = new DummyJsVisitor(RELAX);
assertFalse(visitor.isMessageName("MSG_HELP", false));
assertFalse(visitor.isMessageName("MSG_FOO_HELP", false));
}
public void testIsValidMessageNameLegacy() {
theseAreLegacyMessageNames(new DummyJsVisitor(RELAX));
theseAreLegacyMessageNames(new DummyJsVisitor(LEGACY));
}
private void theseAreLegacyMessageNames(JsMessageVisitor visitor) {
assertTrue(visitor.isMessageName("MSG_HELLO", false));
assertTrue(visitor.isMessageName("MSG_", false));
assertFalse(visitor.isMessageName("MSG_HELP", false));
assertFalse(visitor.isMessageName("MSG_FOO_HELP", false));
assertFalse(visitor.isMessageName("_FOO_HELP", false));
assertFalse(visitor.isMessageName("MSGFOOP", false));
}
public void testUnexistedPlaceholders() {
extractMessages("var MSG_FOO = goog.getMsg('{$foo}:', {});");
assertEquals(0, messages.size());
JSError[] errors = compiler.getErrors();
assertEquals(1, errors.length);
JSError error = errors[0];
assertEquals(JsMessageVisitor.MESSAGE_TREE_MALFORMED, error.getType());
assertEquals("Message parse tree malformed. Unrecognized message "
+ "placeholder referenced: foo", error.description);
}
public void testUnusedReferenesAreNotOK() {
extractMessages("/** @desc AA */ "
+ "var MSG_FOO = goog.getMsg('lalala:', {foo:1});");
assertEquals(0, messages.size());
JSError[] errors = compiler.getErrors();
assertEquals(1, errors.length);
JSError error = errors[0];
assertEquals(JsMessageVisitor.MESSAGE_TREE_MALFORMED, error.getType());
assertEquals("Message parse tree malformed. Unused message placeholder: "
+ "foo", error.description);
}
public void testDuplicatePlaceHoldersAreBad() {
extractMessages("var MSG_FOO = goog.getMsg("
+ "'{$foo}:', {'foo': 1, 'foo' : 2});");
assertEquals(0, messages.size());
JSError[] errors = compiler.getErrors();
assertEquals(1, errors.length);
JSError error = errors[0];
assertEquals(JsMessageVisitor.MESSAGE_TREE_MALFORMED, error.getType());
assertEquals("Message parse tree malformed. Duplicate placeholder "
+ "name: foo", error.description);
}
public void testDuplicatePlaceholderReferencesAreOk() {
extractMessagesSafely("var MSG_FOO = goog.getMsg("
+ "'{$foo}:, {$foo}', {'foo': 1});");
assertEquals(1, messages.size());
JsMessage msg = messages.get(0);
assertEquals("{$foo}:, {$foo}", msg.toString());
}
public void testCamelcasePlaceholderNamesAreOk() {
extractMessagesSafely("var MSG_WITH_CAMELCASE = goog.getMsg("
+ "'Slide {$slideNumber}:', {'slideNumber': opt_index + 1});");
assertEquals(1, messages.size());
JsMessage msg = messages.get(0);
assertEquals("MSG_WITH_CAMELCASE", msg.getKey());
assertEquals("Slide {$slideNumber}:", msg.toString());
List<CharSequence> parts = msg.parts();
assertEquals(3, parts.size());
assertEquals("slideNumber",
((JsMessage.PlaceholderReference)parts.get(1)).getName());
}
public void testWithNonCamelcasePlaceholderNamesAreNotOk() {
extractMessages("var MSG_WITH_CAMELCASE = goog.getMsg("
+ "'Slide {$slide_number}:', {'slide_number': opt_index + 1});");
assertEquals(0, messages.size());
JSError[] errors = compiler.getErrors();
assertEquals(1, errors.length);
JSError error = errors[0];
assertEquals(JsMessageVisitor.MESSAGE_TREE_MALFORMED, error.getType());
assertEquals("Message parse tree malformed. Placeholder name not in "
+ "lowerCamelCase: slide_number", error.description);
}
public void testUnquotedPlaceholdersAreOk() {
extractMessagesSafely("/** @desc Hello */ "
+ "var MSG_FOO = goog.getMsg('foo {$unquoted}:', {unquoted: 12});");
assertEquals(1, messages.size());
assertEquals(0, compiler.getWarningCount());
}
public void testIsLowerCamelCaseWithNumericSuffixes() {
assertTrue(isLowerCamelCaseWithNumericSuffixes("name"));
assertFalse(isLowerCamelCaseWithNumericSuffixes("NAME"));
assertFalse(isLowerCamelCaseWithNumericSuffixes("Name"));
assertTrue(isLowerCamelCaseWithNumericSuffixes("a4Letter"));
assertFalse(isLowerCamelCaseWithNumericSuffixes("A4_LETTER"));
assertTrue(isLowerCamelCaseWithNumericSuffixes("startSpan_1_23"));
assertFalse(isLowerCamelCaseWithNumericSuffixes("startSpan_1_23b"));
assertFalse(isLowerCamelCaseWithNumericSuffixes("START_SPAN_1_23"));
assertFalse(isLowerCamelCaseWithNumericSuffixes(""));
}
public void testToLowerCamelCaseWithNumericSuffixes() {
assertEquals("name", toLowerCamelCaseWithNumericSuffixes("NAME"));
assertEquals("a4Letter", toLowerCamelCaseWithNumericSuffixes("A4_LETTER"));
assertEquals("startSpan_1_23",
toLowerCamelCaseWithNumericSuffixes("START_SPAN_1_23"));
}
public void testDuplicateMessageError() {
extractMessages(
"(function () {/** @desc Hello */ var MSG_HELLO = goog.getMsg('a')})" +
"(function () {/** @desc Hello2 */ var MSG_HELLO = goog.getMsg('a')})");
assertEquals(0, compiler.getWarningCount());
assertOneError(JsMessageVisitor.MESSAGE_DUPLICATE_KEY);
}
public void testNoDuplicateErrorOnExternMessage() {
extractMessagesSafely(
"(function () {/** @desc Hello */ " +
"var MSG_EXTERNAL_2 = goog.getMsg('a')})" +
"(function () {/** @desc Hello2 */ " +
"var MSG_EXTERNAL_2 = goog.getMsg('a')})");
}
public void testErrorWhenUsingMsgPrefixWithFallback() {
extractMessages(
"/** @desc Hello */ var MSG_HELLO_1 = goog.getMsg('hello');\n" +
"/** @desc Hello */ var MSG_HELLO_2 = goog.getMsg('hello');\n" +
"/** @desc Hello */ " +
"var MSG_HELLO_3 = goog.getMsgWithFallback(MSG_HELLO_1, MSG_HELLO_2);");
assertOneError(JsMessageVisitor.MESSAGE_TREE_MALFORMED);
}
private void assertOneError(DiagnosticType type) {
String errors = Joiner.on("\n").join(compiler.getErrors());
assertEquals("There should be one error. " + errors,
1, compiler.getErrorCount());
JSError error = compiler.getErrors()[0];
assertEquals(type, error.getType());
}
private void extractMessagesSafely(String input) {
extractMessages(input);
JSError[] errors = compiler.getErrors();
assertEquals(
"Unexpected error(s): " + Joiner.on("\n").join(compiler.getErrors()),
0, compiler.getErrorCount());
}
private void extractMessages(String input) {
compiler = new Compiler();
Node root = compiler.parseTestCode(input);
JsMessageVisitor visitor = new CollectMessages(compiler);
visitor.process(null, root);
}
private class CollectMessages extends JsMessageVisitor {
private CollectMessages(Compiler compiler) {
super(compiler, true, Style.getFromParams(true, allowLegacyMessages),
null);
}
@Override
protected void processJsMessage(JsMessage message,
JsMessageDefinition definition) {
messages.add(message);
}
}
private class DummyJsVisitor extends JsMessageVisitor {
private DummyJsVisitor(Style style) {
super(null, true, style, null);
}
@Override
protected void processJsMessage(JsMessage message,
JsMessageDefinition definition) {
// no-op
}
}
}