/*
* Copyright 2013 Cloudera 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 org.kitesdk.cli;
import com.google.common.base.Preconditions;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.avro.Schema;
import org.apache.hadoop.conf.Configuration;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.junit.Assert;
import org.junit.internal.matchers.TypeSafeMatcher;
import org.kitesdk.data.Dataset;
import org.kitesdk.data.DatasetDescriptor;
import org.kitesdk.data.spi.DefaultConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestUtil {
private static Logger newConsole() {
return LoggerFactory.getLogger(Main.class);
}
public static int run(String... args) throws Exception {
return run(newConsole(), args);
}
public static int run(Logger console, String... args) throws Exception {
return run(console, new Configuration(), args);
}
public static int run(Configuration conf, String... args) throws Exception {
return run(newConsole(), conf, args);
}
public static int run(Logger console, Configuration conf, String... args)
throws Exception {
// ensure the default config is not changed by calling Main
Configuration original = DefaultConfiguration.get();
Main main = new Main(console);
main.setConf(conf);
int rc = main.run(args);
DefaultConfiguration.set(original);
return rc;
}
public static Matcher<DatasetDescriptor> matches(DatasetDescriptor desc) {
return new DescriptorMatcher(desc);
}
public static Matcher<String> matchesMinimizedSchema(Schema schema) {
return new SchemaMatcher(schema, true);
}
public static Matcher<String> matchesSchema(Schema schema) {
return new SchemaMatcher(schema, false);
}
public static Matcher<String> matchesPattern(String pattern) {
return new PatternMatcher(pattern);
}
public static void assertMatches(String pattern, String actual) {
Assert.assertTrue("Expected:\n\n" + pattern + "\n\n" + "\tbut was:\n" +
actual + "\n", matchesPattern(pattern).matches(actual));
}
private static class SchemaMatcher extends TypeSafeMatcher<String> {
private final Schema expected;
private final boolean minimized;
public SchemaMatcher(Schema expected, boolean minimized) {
this.expected = expected;
this.minimized = minimized;
}
@Override
public boolean matchesSafely(String s) {
if (minimized && s.contains("\n")) {
return false;
} else if (!minimized && !s.contains("\n")) {
return false;
}
Schema schema = new Schema.Parser().parse(s);
return (new SchemaComparator().compare(expected, schema) == 0);
}
@Override
public void describeTo(Description description) {
description.appendText(minimized ? "Minimized" : "Full")
.appendText(" schema equivalent to: ")
.appendText(expected.toString(!minimized));
}
}
public static class SchemaComparator implements Comparator<Schema> {
private final ListComparator<Schema.Field> fields =
new ListComparator<Schema.Field>(new Comparator<Schema.Field>() {
@Override
public int compare(Schema.Field f1, Schema.Field f2) {
return SchemaComparator.this.compare(f1.schema(), f2.schema());
}
});
private final ListComparator<Schema> schemas =
new ListComparator<Schema>(this);
private final ListComparator<String> strings =
new ListComparator<String>(String.class);
@Override
public int compare(Schema s1, Schema s2) {
// compare types
int cmp = s1.getType().compareTo(s2.getType());
if (cmp != 0) {
return cmp;
}
// compare names
if (s1.getName() != null) {
if (s2.getName() == null) {
return -1;
}
cmp = s1.getName().compareTo(s2.getName());
if (cmp != 0) {
return cmp;
}
} else if (s2.getName() != null) {
return 1;
}
// compare child types
switch (s1.getType()) {
case RECORD:
return fields.compare(s1.getFields(), s2.getFields());
case UNION:
return schemas.compare(s1.getTypes(), s2.getTypes());
case ENUM:
return strings.compare(s1.getEnumSymbols(), s2.getEnumSymbols());
case ARRAY:
return compare(s1.getElementType(), s2.getElementType());
case MAP:
return compare(s1.getValueType(), s2.getValueType());
}
return 0;
}
}
public static class ListComparator<T> implements Comparator<List<T>> {
private final Comparator<T> itemComparator;
@SuppressWarnings("unchecked")
public ListComparator(Class<T> itemClass) {
Preconditions.checkArgument(Comparable.class.isAssignableFrom(itemClass),
"Items must be Comparable");
itemComparator = (Comparator<T>) new Comparator<Comparable>() {
@Override
public int compare(Comparable o1, Comparable o2) {
return o1.compareTo(o2);
}
};
}
public ListComparator(Comparator<T> itemComparator) {
this.itemComparator = itemComparator;
}
@Override
public int compare(List<T> l1, List<T> l2) {
int s1 = l1.size();
int s2 = l2.size();
int cmp = (s1 < s2) ? -1 : ((s1 == s2) ? 0 : 1);
if (cmp != 0) {
return cmp;
}
for (int i = 0; i < l1.size(); i += 1) {
cmp = itemComparator.compare(l1.get(i), l2.get(i));
if (cmp != 0) {
return cmp;
}
}
return 0;
}
}
private static class DescriptorMatcher extends TypeSafeMatcher<DatasetDescriptor> {
private final DatasetDescriptor descriptor;
public DescriptorMatcher(DatasetDescriptor descriptor) {
this.descriptor = descriptor;
}
@Override
public boolean matchesSafely(DatasetDescriptor actual) {
return descriptor.equals(actual);
}
@Override
public void describeTo(Description description) {
description.appendText("DatasetDescriptor equivalent to ")
.appendText(descriptor.toString());
}
}
private static class PatternMatcher extends TypeSafeMatcher<String> {
private final Pattern pattern;
public PatternMatcher(String pattern) {
this.pattern = Pattern.compile(pattern);
}
@Override
public boolean matchesSafely(String item) {
return pattern.matcher(item).matches();
}
@Override
public void describeTo(Description description) {
description.appendText("String matches ").appendText(pattern.toString());
}
}
}