blob: 49307f91e0a3fc49c25bf76f02cda5a1f3a6b4ef [file] [log] [blame]
/*
* 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 org.apache.druid.data.input.influx;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.apache.druid.java.util.common.Pair;
import org.apache.druid.java.util.common.parsers.ParseException;
import org.apache.druid.java.util.common.parsers.Parser;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.HashMap;
import java.util.Map;
@RunWith(JUnitParamsRunner.class)
public class InfluxParserTest
{
@SuppressWarnings("unused")
private String name;
@SuppressWarnings("unused")
private String input;
@SuppressWarnings("unused")
private Map<String, Object> expected;
private static Object[] testCase(String name, String input, Parsed expected)
{
return Lists.newArrayList(name, input, expected).toArray();
}
public Object[] testData()
{
return Lists.newArrayList(
testCase(
"real sample",
"cpu,host=foo.bar.baz,region=us-east-1,application=echo pct_idle=99.3,pct_user=88.8,m1_load=2i 1465839830100400200",
Parsed.row("cpu", 1465839830100L)
.with("host", "foo.bar.baz")
.with("region", "us-east-1")
.with("application", "echo")
.with("pct_idle", 99.3)
.with("pct_user", 88.8)
.with("m1_load", 2L)
),
testCase(
"negative timestamp",
"foo,region=us-east-1,host=127.0.0.1 m=1.0,n=3.0,o=500i -123456789",
Parsed.row("foo", -123L)
.with("region", "us-east-1")
.with("host", "127.0.0.1")
.with("m", 1.0)
.with("n", 3.0)
.with("o", 500L)
),
testCase(
"truncated timestamp",
"foo,region=us-east-1,host=127.0.0.1 m=1.0,n=3.0,o=500i 123",
Parsed.row("foo", 0L)
.with("region", "us-east-1")
.with("host", "127.0.0.1")
.with("m", 1.0)
.with("n", 3.0)
.with("o", 500L)
),
testCase(
"special characters",
"!@#$%^&*()_-\\=+,+++\\ +++=--\\ --- __**__=\"ü\" 123456789",
Parsed.row("!@#$%^&*()_-=+", 123L)
.with("+++ +++", "-- ---")
.with("__**__", "127.0.0.1")
.with("__**__", "ü")
),
testCase(
"unicode characters",
"\uD83D\uDE00,\uD83D\uDE05=\uD83D\uDE06 \uD83D\uDE0B=100i,b=\"\uD83D\uDE42\" 123456789",
Parsed.row("\uD83D\uDE00", 123L)
.with("\uD83D\uDE05", "\uD83D\uDE06")
.with("\uD83D\uDE0B", 100L)
.with("b", "\uD83D\uDE42")
),
testCase(
"quoted string measurement value",
"foo,region=us-east-1,host=127.0.0.1 m=1.0,n=3.0,o=\"something \\\"cool\\\" \" 123456789",
Parsed.row("foo", 123L)
.with("region", "us-east-1")
.with("host", "127.0.0.1")
.with("m", 1.0)
.with("n", 3.0)
.with("o", "something \"cool\" ")
),
testCase(
"no tags",
"foo m=1.0,n=3.0 123456789",
Parsed.row("foo", 123L)
.with("m", 1.0)
.with("n", 3.0)
),
testCase(
"Escaped characters in identifiers",
"f\\,oo\\ \\=,bar=baz m=1.0,n=3.0 123456789",
Parsed.row("f,oo =", 123L)
.with("bar", "baz")
.with("m", 1.0)
.with("n", 3.0)
),
testCase(
"Escaped characters in identifiers",
"foo\\ \\=,bar=baz m=1.0,n=3.0 123456789",
Parsed.row("foo =", 123L)
.with("bar", "baz")
.with("m", 1.0)
.with("n", 3.0)
)
).toArray();
}
@Test
@Parameters(method = "testData")
public void testParse(String name, String input, Parsed expected)
{
Parser<String, Object> parser = new InfluxParser(null);
Map<String, Object> parsed = parser.parseToMap(input);
MatcherAssert.assertThat(
"correct measurement name",
parsed.get("measurement"),
Matchers.equalTo(expected.measurement)
);
MatcherAssert.assertThat(
"correct timestamp",
parsed.get(InfluxParser.TIMESTAMP_KEY),
Matchers.equalTo(expected.timestamp)
);
expected.kv.forEach((k, v) -> MatcherAssert.assertThat("correct field " + k, parsed.get(k), Matchers.equalTo(v)));
parsed.remove("measurement");
parsed.remove(InfluxParser.TIMESTAMP_KEY);
MatcherAssert.assertThat("No extra keys in parsed data", parsed.keySet(), Matchers.equalTo(expected.kv.keySet()));
}
@Test
public void testParseWhitelistPass()
{
Parser<String, Object> parser = new InfluxParser(Sets.newHashSet("cpu"));
String input = "cpu,host=foo.bar.baz,region=us-east,application=echo pct_idle=99.3,pct_user=88.8,m1_load=2 1465839830100400200";
Map<String, Object> parsed = parser.parseToMap(input);
MatcherAssert.assertThat(parsed.get("measurement"), Matchers.equalTo("cpu"));
}
@Test
public void testParseWhitelistFail()
{
Parser<String, Object> parser = new InfluxParser(Sets.newHashSet("mem"));
String input = "cpu,host=foo.bar.baz,region=us-east,application=echo pct_idle=99.3,pct_user=88.8,m1_load=2 1465839830100400200";
try {
parser.parseToMap(input);
}
catch (ParseException t) {
MatcherAssert.assertThat(t, Matchers.isA(ParseException.class));
return;
}
Assert.fail("Exception not thrown");
}
public Object[] failureTestData()
{
return Lists.newArrayList(
Pair.of("Empty line", ""),
Pair.of("Invalid measurement", "invalid measurement"),
Pair.of("Invalid timestamp", "foo i=123 123x")
).toArray();
}
@Test
@Parameters(method = "failureTestData")
public void testParseFailures(Pair<String, String> testCase)
{
Parser<String, Object> parser = new InfluxParser(null);
try {
parser.parseToMap(testCase.rhs);
}
catch (ParseException t) {
MatcherAssert.assertThat(t, Matchers.isA(ParseException.class));
return;
}
Assert.fail(testCase.rhs + ": exception not thrown");
}
private static class Parsed
{
private String measurement;
private Long timestamp;
private final Map<String, Object> kv = new HashMap<>();
static Parsed row(String measurement, Long timestamp)
{
Parsed e = new Parsed();
e.measurement = measurement;
e.timestamp = timestamp;
return e;
}
Parsed with(String k, Object v)
{
kv.put(k, v);
return this;
}
}
}