/** * Licensed to Cloudera, Inc. under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. Cloudera, Inc. 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 com.cloudera.flume.conf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import org.antlr.runtime.RecognitionException; import org.antlr.runtime.tree.CommonTree; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.junit.Test; import com.cloudera.flume.ExampleData; /** * This tests a swath of the language features for the flume configuration * language. All should throw exceptions on parser or lexer failures. */ public class TestParser implements ExampleData { static final Logger LOG = LoggerFactory.getLogger(TestParser.class); /** * Test parsing of literals. */ @Test public void testLiteralParser() throws RecognitionException { LOG.info("== literals =="); String s = "1234"; Object o = FlumeBuilder.parseLiteral(s); LOG.info(toTree(o)); assertEquals("(DEC 1234)", toTree(o)); String s1 = "1234.234"; Object o1 = FlumeBuilder.parseLiteral(s1); LOG.info(toTree(o1)); assertEquals("(FLOAT 1234.234)", toTree(o1)); String s2 = "\"string\""; Object o2 = FlumeBuilder.parseLiteral(s2); LOG.info(toTree(o2)); assertEquals("(STRING \"string\")", toTree(o2)); String s3 = "true"; Object o3 = FlumeBuilder.parseLiteral(s3); LOG.info(toTree(o3)); assertEquals("(BOOL true)", toTree(o3)); } /** * Strings are allowed to have some java escape sequences, make sure they are * unescaped when values are instantiated. */ @Test public void testJavaStringEscape() throws RecognitionException, FlumeSpecException { String s2x = "\"\\\"string\\\"\""; CommonTree o2x = FlumeBuilder.parseLiteral(s2x); LOG.info(toTree(o2x)); // assertEquals("\"string\"", FlumeBuilder.buildArg(o2x)); assertEquals("\"string\"", FlumeBuilder.buildArg(o2x)); } String toTree(Object o) { return ((CommonTree) o).toStringTree(); } @Test public void testSinkParser() throws RecognitionException { LOG.info("== sinks =="); String s = "text"; Object o = FlumeBuilder.parseSink(s); LOG.info(s); LOG.info(toTree(o)); assertEquals("(SINK text)", toTree(o)); String s2 = "text(\"bogusdata\")"; Object o2 = FlumeBuilder.parseSink(s2); LOG.info(s2); LOG.info(toTree(o2)); assertEquals("(SINK text (STRING \"bogusdata\"))", toTree(o2)); String s3 = "[text(true)]"; Object o3 = FlumeBuilder.parseSink(s3); LOG.info(s3); LOG.info(toTree(o3)); assertEquals("(MULTI (SINK text (BOOL true)))", toTree(o3)); String s4 = "[text(true) , tail(\"somthingelse\") ]"; Object o4 = FlumeBuilder.parseSink(s4); LOG.info(s4); LOG.info(toTree(o4)); assertEquals( "(MULTI (SINK text (BOOL true)) (SINK tail (STRING \"somthingelse\")))", toTree(o4)); } @Test public void testBuilder() throws RecognitionException { LOG.info("== nodes =="); // String s = "source : tail(\"/var/log/httpd/access_log\") | thrift;"; String s = "nodename : nodesource | nodesink;"; Object o = FlumeBuilder.parse(s); LOG.info(toTree(o)); assertEquals("(NODE nodename (SOURCE nodesource) (SINK nodesink)) null", toTree(o)); } @Test public void testDecorator() throws RecognitionException { LOG.info("== Decorators =="); String s = "{ deco => nodesink } "; Object o = FlumeBuilder.parseSink(s); LOG.info(toTree(o)); assertEquals("(DECO (SINK deco) (SINK nodesink))", toTree(o)); String s3 = "{ deco1 => [ sink1, sink2] } "; Object o3 = FlumeBuilder.parseSink(s3); LOG.info(toTree(o3)); assertEquals("(DECO (SINK deco1) (MULTI (SINK sink1) (SINK sink2)))", toTree(o3)); // Test a "tight decorator" -- no extra spaces String s4 = "{deco1=>sink1} "; Object o4 = FlumeBuilder.parseSink(s4); LOG.info(toTree(o4)); assertEquals("(DECO (SINK deco1) (SINK sink1))", toTree(o4)); } @Test(expected = RuntimeRecognitionException.class) public void testBadDecorator() throws RecognitionException { // deco on left side of deco is not legal. String s2 = "{{ deco1 => deco22 } => nodesink } "; Object o2 = FlumeBuilder.parseSink(s2); LOG.info(toTree(o2)); assertEquals("(DECO (DECO (SINK deco1) (SINK deco22)) (SINK nodesink))", toTree(o2)); } public void testFailover() throws RecognitionException { LOG.info("== Failover =="); String s = "< deco ? nodesink > "; Object o = FlumeBuilder.parseSink(s); LOG.info(toTree(o)); assertEquals("(BACKUP (SINK deco) (SINK nodesink))", toTree(o)); String s2 = "<< deco1 ? deco22 > ? nodesink > "; Object o2 = FlumeBuilder.parseSink(s2); LOG.info(toTree(o2)); assertEquals( "(BACKUP (BACKUP (SINK deco1) (SINK deco22)) (SINK nodesink))", toTree(o2)); String s3 = "< deco1 ? [ sink1, sink2] > "; Object o3 = FlumeBuilder.parseSink(s3); LOG.info(toTree(o3)); assertEquals("(BACKUP (SINK deco1) (MULTI (SINK sink1) (SINK sink2)))", toTree(o3)); } /** * These parse to make sure it works as a root, complex sub sinks work and * that it works as a subsink, and can compose other lets (in the arg and body * slots). */ public void testLet() throws RecognitionException { LOG.info("== Let =="); String s = "let foo := console in foo "; Object o = FlumeBuilder.parseSink(s); LOG.info(toTree(o)); assertEquals("(LET foo (SINK console) (SINK foo))", toTree(o)); String s2 = "let foo := console in < foo ? [ console, foo] >"; Object o2 = FlumeBuilder.parseSink(s2); LOG.info(toTree(o2)); assertEquals( "(LET foo (SINK console) (BACKUP (SINK foo) (MULTI (SINK console) (SINK foo))))", toTree(o2)); String s3 = "[ let foo := console in foo, let bar := console in bar ]"; Object o3 = FlumeBuilder.parseSink(s3); LOG.info(toTree(o3)); assertEquals( "(MULTI (LET foo (SINK console) (SINK foo)) (LET bar (SINK console) (SINK bar)))", toTree(o3)); String s4 = "let foo := console in let bar := console in [ foo, bar ]"; Object o4 = FlumeBuilder.parseSink(s4); LOG.info(toTree(o4)); assertEquals( "(LET foo (SINK console) (LET bar (SINK console) (MULTI (SINK foo) (SINK bar))))", toTree(o4)); String s5 = "let foo := let bar := console in bar in foo"; Object o5 = FlumeBuilder.parseSink(s5); LOG.info(toTree(o5)); assertEquals("(LET foo (LET bar (SINK console) (SINK bar)) (SINK foo))", toTree(o5)); } public void testCombo() throws RecognitionException { LOG.info("== Combo =="); String s = "< deco ? [nodesink, nodesink2] > "; Object o = FlumeBuilder.parseSink(s); LOG.info(toTree(o)); assertEquals( "(BACKUP (SINK deco) (MULTI (SINK nodesink) (SINK nodesink2)))", toTree(o)); String s2 = "{ deco1 => << deco1 ? deco22 > ? nodesink > }"; Object o2 = FlumeBuilder.parseSink(s2); LOG.info(toTree(o2)); assertEquals( "(DECO (SINK deco1) (BACKUP (BACKUP (SINK deco1) (SINK deco22)) (SINK nodesink)))", toTree(o2)); String s3 = "< deco1 ? [ {sampler => sink1 } , sink2] > "; Object o3 = FlumeBuilder.parseSink(s3); LOG.info(toTree(o3)); assertEquals( "(BACKUP (SINK deco1) (MULTI (DECO (SINK sampler) (SINK sink1)) (SINK sink2)))", toTree(o3)); } @Test(expected = RuntimeRecognitionException.class) public void testLexFails() throws RecognitionException { String s = "12345.123423412.123.41.3."; Object o = FlumeBuilder.parseSink(s); LOG.info(toTree(o)); } @Test(expected = RuntimeRecognitionException.class) public void testParseFails() throws RecognitionException { String s = "< deoc asdf fial blah } >"; Object o = FlumeBuilder.parseSink(s); LOG.info(toTree(o)); } @Test public void testNode() throws RecognitionException { LOG.info("== Node =="); String s = "host: source | < deco ? [nodesink, nodesink2] > ; "; Object o = FlumeBuilder.parse(s); LOG.info(toTree(o)); // names with numbers s = "host123: source | < deco ? [nodesink, nodesink2] > ; "; o = FlumeBuilder.parse(s); LOG.info(toTree(o)); // with dashes, underscore s = "host-with_dashes: source | < deco ? [nodesink, nodesink2] > ; "; o = FlumeBuilder.parse(s); LOG.info(toTree(o)); // dns name s = "name.host.com: source | < deco ? [nodesink, nodesink2] > ; "; o = FlumeBuilder.parse(s); LOG.info(toTree(o)); // ip address s = "1.2.3.4: source | < deco ? [nodesink, nodesink2] > ; "; o = FlumeBuilder.parse(s); LOG.info(toTree(o)); // wrong ip address try { s = "1.2.3.4.324: source | < deco ? [nodesink, nodesink2] > ; "; o = FlumeBuilder.parse(s); LOG.info(toTree(o)); fail("This should throw exception"); } catch (RuntimeException e) { // we are ok. } // wrong ip address try { s = "-blah: source | < deco ? [nodesink, nodesink2] > ; "; o = FlumeBuilder.parse(s); LOG.info(toTree(o)); fail("This should throw exception"); } catch (RuntimeException e) { // we are ok. } // wrong ip address try { s = "1234.-blah: source | < deco ? [nodesink, nodesink2] > ; "; o = FlumeBuilder.parse(s); LOG.info(toTree(o)); fail("This should throw exception"); } catch (RuntimeException e) { // we are ok. } } }