blob: 35d2c789cc132cea09a5251e1dd1f11f65dc8462 [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.tinkerpop.gremlin.language.grammar;
import org.apache.tinkerpop.gremlin.language.corpus.DocumentationReader;
import org.apache.tinkerpop.gremlin.language.corpus.FeatureReader;
import org.javatuples.Pair;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.hamcrest.Matchers.is;
import static org.junit.Assume.assumeThat;
/**
* Builds a corpus of Gremlin statements from Recipes, "The Traversal" in Reference Documentation and the Gherkin
* test suite and passes them all through the grammar parser.
*/
@RunWith(Parameterized.class)
public class ReferenceGrammarTest extends AbstractGrammarTest {
private static final String featureDir = Paths.get("..", "gremlin-test", "src", "main", "resources", "org", "apache", "tinkerpop", "gremlin", "test", "features").toString();
private static final String docsDir = Paths.get("..", "docs", "src").toString();
private static final Pattern edgePattern = Pattern.compile(".*e\\d.*");
private static final List<Pair<Pattern, BiFunction<String,String,String>>> stringMatcherConverters = new ArrayList<Pair<Pattern, BiFunction<String,String,String>>>() {{
add(Pair.with(Pattern.compile("m\\[(.*)\\]"), (k,v) -> {
// can't handled embedded maps because of the string replace below on the curly braces
final String[] items = v.replace("{", "").replace("}", "").split(",");
final String listItems = Stream.of(items).map(String::trim).map(x -> {
final String[] pair = x.split(":");
final String convertedKey = String.format("%s", pair[0]);
final String convertedValue = String.format("%s", pair[1]);
return String.format("%s:%s", convertedKey, convertedValue);
}).collect(Collectors.joining(","));
return String.format("[%s]", listItems);
}));
add(Pair.with(Pattern.compile("l\\[\\]"), (k,v) -> "[]"));
add(Pair.with(Pattern.compile("l\\[(.*)\\]"), (k,v) -> {
final String[] items = v.split(",");
final String listItems = Stream.of(items).map(String::trim).map(x -> String.format("\"%s\"", x)).collect(Collectors.joining(","));
return String.format("[%s]", listItems);
}));
add(Pair.with(Pattern.compile("v\\[(.+)\\]"), (k,v) -> "\"1\""));
add(Pair.with(Pattern.compile("v(\\d)"), (k,v) -> String.format("new Vertex(%s, \"vertex\")", v)));
add(Pair.with(Pattern.compile("e\\[(.+)\\]"), (k,v) -> "\"1\""));
add(Pair.with(Pattern.compile("d\\[(.*)\\]\\.?.*"), (k,v) -> v));
add(Pair.with(Pattern.compile("m\\[(.*)\\]"), (k,v) -> v.replace('{','[').replace('}', ']')));
add(Pair.with(Pattern.compile("t\\[(.*)\\]"), (k,v) -> String.format("T.%s", v)));
add(Pair.with(Pattern.compile("D\\[(.*)\\]"), (k,v) -> String.format("Direction.%s", v)));
// the grammar doesn't support all the Gremlin we have in the gherkin set, so try to coerce it into
// something that can be parsed so that we get maximum exercise over the parser itself.
add(Pair.with(Pattern.compile("c\\[(.*)\\]"), (k,v) -> k.equals("c1") || k.equals("c2") ? "Order.desc" : "__.identity()")); // closure -> Comparator || Traversal
add(Pair.with(Pattern.compile("s\\[\\]"), (k,v) -> "[]")); // set -> list
add(Pair.with(Pattern.compile("s\\[(.*)\\]"), (k,v) -> "[]")); // set -> list
add(Pair.with(Pattern.compile("(null)"), (k,v) -> "null"));
add(Pair.with(Pattern.compile("(true)"), (k,v) -> "true"));
add(Pair.with(Pattern.compile("(false)"), (k,v) -> "false"));
}};
@Parameterized.Parameters(name = "{0}")
public static Iterable<String> queries() throws IOException {
final Set<String> gremlins = new LinkedHashSet<>(DocumentationReader.parse(docsDir));
gremlins.addAll(FeatureReader.parseGrouped(featureDir, stringMatcherConverters).values().stream().flatMap(Collection::stream).collect(Collectors.toList()));
return gremlins;
}
@Parameterized.Parameter
public String query;
@Test
public void test_parse() {
// can't handle maps with complex embedded types at the moment - basically one Gremlin statement at this point
assumeThat("Complex embedded types are not supported", query.contains("l[\"666\"]"), is(false));
assumeThat("Lambdas are not supported", query.contains("Lambda.function("), is(false));
// start of a closure
assumeThat("Lambdas are not supported", query.contains("{"), is(false));
assumeThat("withComputer() step is not supported", query.startsWith("g.withComputer("), is(false));
assumeThat("Edge instances are not supported", edgePattern.matcher(query).matches(), is(false));
assumeThat("fill() terminator is not supported", query.contains("fill("), is(false));
assumeThat("withoutStrategies() is not supported", query.contains("withoutStrategies("), is(false));
assumeThat("program() is not supported", query.contains("program("), is(false));
assumeThat("Casts are not supported", query.contains("(Map)"), is(false));
parse(query);
}
}