Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/concurrency tests #6

Merged
merged 19 commits into from
Jan 13, 2017
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/main/java/io/datanerds/avropatch/Patch.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ public class Patch {
private final List<Operation> operations;

public Patch() {
operations = new ArrayList<>();
headers = new HashMap<>();
this(new ArrayList<>(), new HashMap<>());
}

public Patch(List<Operation> operations) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always a good idea to declare method arguments as final (especially in case of collections) which makes it explicit to the calling function that the list would not be modified in the called method.

Copy link
Member

@dhiller dhiller Dec 19, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disagree. The final only states that the reference can't be reassigned within the block. The list is still mutable.

Agree though on parameter declarations being implicitly final would be a good thing :) Though I think that the boilerplate doesn't satisfy the improvement, would support a source code check rather for a variable being reassigned, as i.e. Findbugs supports AFAIR.

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/io/datanerds/avropatch/operation/Add.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ public final class Add<T> implements Operation {
@AvroSchema(DefaultSchema.VALUE)
public final T value;

@SuppressWarnings("unused") // no arg constructor needed by Avro
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

org.apache.avro looks like fun

private Add() {
this.path = null;
this.value = null;
this(null, null);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/io/datanerds/avropatch/operation/Copy.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ public final class Copy implements Operation {
public final Path from;
public final Path path;

@SuppressWarnings("unused") // no arg constructor needed by Avro

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-arg

private Copy() {
from = null;
path = null;
this(null, null);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/io/datanerds/avropatch/operation/Move.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ public final class Move implements Operation {
public final Path from;
public final Path path;

@SuppressWarnings("unused") // no arg constructor needed by Avro
private Move() {
this.from = null;
this.path = null;
this(null, null);
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/io/datanerds/avropatch/operation/Remove.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ public final class Remove implements Operation {
public static final String op = "remove";
public final Path path;

@SuppressWarnings("unused") // no arg constructor needed by Avro
private Remove() {
this.path = null;
this(null);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/io/datanerds/avropatch/operation/Replace.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ public final class Replace<T> implements Operation {
@AvroSchema(DefaultSchema.VALUE)
public final T value;

@SuppressWarnings("unused") // no arg constructor needed by Avro
private Replace() {
this.path = null;
this.value = null;
this(null, null);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/io/datanerds/avropatch/operation/Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ public final class Test<T> implements Operation {
@AvroSchema(DefaultSchema.VALUE)
public final T value;

@SuppressWarnings("unused") // no arg constructor needed by Avro
private Test() {
this.path = null;
this.value = null;
this(null, null);
}

/**
Expand Down
4 changes: 1 addition & 3 deletions src/main/java/io/datanerds/avropatch/schema/CustomTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@ public interface CustomTypes {

interface PatchType {
String NAME = Patch.class.getSimpleName();
String DOC = "This record represents a JSON PATCH operation holding a sequence of operations to apply to a"
+ " given object.";

String DOC = "This record represents a PATCH holding a sequence of operations to apply to a given object.";
static Schema create(Schema valueSchema) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Defining an interface method. This is new to me. Would love to understand more.

Schema.Field operations = new Schema.Field("operations",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to move the literal ("operations") to a constant before using it here and other places.

Schema.createArray(OperationTypes.Operation.create(valueSchema)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ public final class CustomTypeSerializer {

private CustomTypeSerializer(Schema schema) {
Objects.nonNull(schema);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the schema even be null?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope :-)

writer = AvroData.get().createDatumWriter(schema);
reader = AvroData.get().createDatumReader(schema);
this.writer = AvroData.get().createDatumWriter(schema);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ugh... org.apache.avro does not do generics properly? @SuppressWarnings("unchecked") for the constructor and some comment?

this.reader = AvroData.get().createDatumReader(schema);
}

public byte[] toBytes(Patch value) throws IOException {
Expand All @@ -49,10 +49,10 @@ public Patch toObject(byte[] bytes) throws IOException {
}

public static class Builder {
final List<Schema> types = new ArrayList<>();
protected final List<Schema> types;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering that both a method for adding a type and a schema exist, the combination of declaration and variable name is a little confusing.


public Builder() {
AvroData.get();
this.types = new ArrayList<>();
}

public Builder nullable() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@

public final class AvroData extends ReflectData {

public static List<Conversion<?>> CONVERTERS =
ImmutableList.of(new DateConversion(), new BigIntegerConversion(), new BigDecimalConversion(),
new UUIDConversion());

public static final List<Conversion<?>> CONVERTERS = ImmutableList.of(
new DateConversion(),
new BigIntegerConversion(),
new BigDecimalConversion(),
new UUIDConversion());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say according to K&R style we would need to insert a line break before the last closing brace...

private static final AvroData INSTANCE = new AvroData();

private AvroData() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import java.math.BigInteger;

/**
* This class converts an {@link BigDecimal} value into am Avro {@link IndexedRecord} and back. It depends on
* This class converts a {@link BigDecimal} value into an Avro {@link IndexedRecord} and back. It depends on
* {@link BigIntegerConversion} since it serializes the {@link BigDecimal}s unscaled value into a {@link BigInteger} and
* its scale into an {@link Integer}.
* Also, its logical type is statically registered in {@link LogicalTypes}, because it introduces a new custom type name
Expand Down
35 changes: 23 additions & 12 deletions src/test/java/io/datanerds/avropatch/PatchTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ public void serializesTest() throws IOException {

@Test
public void serializesBunchOfOperations() throws IOException {
Patch patch = new Patch(ImmutableList.of(new Add<>(Path.of("person", "name"), "John Doe"),
Patch patch = new Patch(ImmutableList.of(
new Add<>(Path.of("person", "name"), "John Doe"),
new Copy(Path.parse("/person/firstName"), Path.parse("/person/lastName")),
new Move(Path.parse("/person/firstName"), Path.parse("/person/lastName")),
new Remove(Path.parse("/person/name")),
Expand All @@ -103,7 +104,8 @@ public void serializesBunchOfOperations() throws IOException {
public void serializesDefaultValueTypes() throws IOException {
Date date = new Date();
UUID uuid = UUID.randomUUID();
Patch patch = new Patch(ImmutableList.of(new Add<>(Path.of("some", "value"), "John Doe"),
Patch patch = new Patch(ImmutableList.of(
new Add<>(Path.of("some", "value"), "John Doe"),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API wise this seems to be strange and very cumbersome. Path.of seems to be a looooot of boilerplate. Especially when it is not possible (maybe I'm missing something?) to create a base path and then just add a child to it.

IMO Path either needs a possibility to have a base path and then add children or otherwise people will likely start using it like this which does not seem to be right:

String base = "/some/namespace/here/";
[...]
new Add<>(Path.parse(base+"foobar"), "Knackwurst");

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could do new Add<>(Path.of(base, "foobar"), but it needs documentation. A method append(...) makes sense either way :-)

new Add<>(Path.of("some", "value"), 42),
new Add<>(Path.of("some", "value"), 42L),
new Add<>(Path.of("some", "value"), uuid),
Expand All @@ -118,19 +120,28 @@ public void serializesDefaultValueTypes() throws IOException {
}

@Test
public void serializesArbitraryHeaders() throws IOException {
Patch patch = new Patch(Collections.EMPTY_LIST, ImmutableMap.of("header 1", UUID.randomUUID(),
"header 2", new Date(), "header 3", 1234L, "header 4", new BigDecimal("3214123453.123512345")));

public void serializesArbitraryHeadersWithoutOperations() throws IOException {
Patch patch = new Patch(Collections.EMPTY_LIST,
ImmutableMap.of(
"header 1", UUID.randomUUID(),
"header 2", new Date(),
"header 3", 1234L,
"header 4", new BigDecimal("3214123453.123512345")));
byte[] bytes = patch.toBytes();
assertThat(patch, is(equalTo(Patch.of(bytes))));
}

patch = new Patch(ImmutableList.of(new Copy(Path.of("from", "here"), Path.of("to", "there")),
new Move(Path.of("from", "here"), Path.of("to", "there"))),
ImmutableMap.of("header 1", UUID.randomUUID(),
"header 2", new Date(), "header 3", 1234L, "header 4", new BigDecimal("3214123453.123512345")));

bytes = patch.toBytes();
@Test
public void serializesArbitraryHeadersWithOperations() throws IOException {
Patch patch = new Patch(ImmutableList.of(
new Copy(Path.of("from", "here"), Path.of("to", "there")),
new Move(Path.of("from", "here"), Path.of("to", "there"))),
ImmutableMap.of(
"header 1", UUID.randomUUID(),
"header 2", new Date(),
"header 3", 1234L,
"header 4", new BigDecimal("3214123453.123512345")));
byte[] bytes = patch.toBytes();;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove ;

assertThat(patch, is(equalTo(Patch.of(bytes))));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.hamcrest.core.AllOf.allOf;

Expand Down Expand Up @@ -45,12 +46,9 @@ public static <T extends Operation> Matcher<Iterable<T>> hasItems(T... items) {
}

public static <T extends Operation> Matcher<Iterable<T>> hasItems(List<T> items) {
List<Matcher<? super Iterable<T>>> all = new ArrayList<Matcher<? super Iterable<T>>>(items.size());
for (T element : items) {
all.add(hasItem(element));
}

return allOf(all);
return allOf(items.stream()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Insert line break before items.stream()

.map(item -> hasItem(item))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.map(this::hasItem)

.collect(Collectors.toList()));
}

public static <T extends Operation> Matcher<List<T>> hasItemsOrdered(List<T> expected) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,11 @@
package io.datanerds.avropatch.operation.matcher;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.datanerds.avropatch.Patch;
import io.datanerds.avropatch.operation.Copy;
import io.datanerds.avropatch.operation.Move;
import io.datanerds.avropatch.operation.Operation;
import io.datanerds.avropatch.operation.Path;
import org.hamcrest.CustomTypeSafeMatcher;
import org.hamcrest.Matcher;
import org.junit.Test;

import java.io.IOException;
import java.util.Collections;
import java.util.UUID;

import static io.datanerds.avropatch.operation.matcher.OperationMatchers.hasItemsOrdered;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.AllOf.allOf;

public class PatchMatcher {
Expand Down Expand Up @@ -55,33 +41,4 @@ protected boolean matchesSafely(Patch item) {
}
};
}

@Test
public void headers() throws IOException {
UUID uuid = UUID.randomUUID();
Patch patch = new Patch(Collections.EMPTY_LIST, ImmutableMap.of("header 1", uuid));
Patch patch2 = new Patch(Collections.EMPTY_LIST, ImmutableMap.of("header 1", uuid));
Patch patch3 = new Patch(Collections.EMPTY_LIST, ImmutableMap.of("header 2", uuid));
Patch patch4 = new Patch(Collections.EMPTY_LIST, ImmutableMap.of("header 1", UUID.randomUUID()));

assertThat(patch, is(equalTo(patch2)));
assertThat(patch2, is(equalTo(patch)));
assertThat(patch, is(not(equalTo(patch3))));
assertThat(patch, is(not(equalTo(patch4))));
}

@Test
public void operations() throws IOException {
Operation operation1 = new Copy(Path.of("from", "here"), Path.of("to", "there"));
Operation operation2 = new Move(Path.of("from", "here"), Path.of("to", "there"));
Patch patch1 = new Patch(ImmutableList.of(operation1, operation2), Collections.EMPTY_MAP);
Patch patch2 = new Patch(ImmutableList.of(operation1, operation2), Collections.EMPTY_MAP);
Patch patch3 = new Patch(ImmutableList.of(operation1), Collections.EMPTY_MAP);
Patch patch4 = new Patch(ImmutableList.of(operation2, operation1), Collections.EMPTY_MAP);

assertThat(patch1, is(equalTo(patch2)));
assertThat(patch2, is(equalTo(patch1)));
assertThat(patch1, is(not(equalTo(patch3))));
assertThat(patch1, is(not(equalTo(patch4))));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package io.datanerds.avropatch.operation.matcher;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.datanerds.avropatch.Patch;
import io.datanerds.avropatch.operation.Copy;
import io.datanerds.avropatch.operation.Move;
import io.datanerds.avropatch.operation.Operation;
import io.datanerds.avropatch.operation.Path;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;

import java.io.IOException;
import java.util.Collections;
import java.util.UUID;

import static io.datanerds.avropatch.operation.matcher.PatchMatcher.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;

@RunWith(Enclosed.class)
public class PatchMatcherTest {

public static class Headers {
private final UUID uuid = UUID.randomUUID();
private final Patch patchWithHeader = new Patch(Collections.EMPTY_LIST, ImmutableMap.of("header", uuid));
private final Patch patchWithHeaderSibling = new Patch(Collections.EMPTY_LIST, ImmutableMap.of("header", uuid));
private final Patch differentHeader = new Patch(Collections.EMPTY_LIST, ImmutableMap.of("header 2", uuid));
private final Patch differentHeaderValue = new Patch(Collections.EMPTY_LIST,
ImmutableMap.of("header", UUID.randomUUID()));

@Test
public void equality() throws IOException {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Split the two asserts into two tests. Each assertion should be in a separate test to avoid failing assertions being hidden by assertions already failing before.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best rule would be one assertion per test method.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps you could elaborate a bit on the test methods by naming them so they explain what is tested exactly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're considering to get rid of these classes (see #7), I'll ignore it for now.

assertThat(patchWithHeader, is(equalTo(patchWithHeaderSibling)));
assertThat(patchWithHeaderSibling, is(equalTo(patchWithHeader)));
}

@Test
public void inequality() throws IOException {
assertThat(patchWithHeader, is(not(equalTo(differentHeader))));
assertThat(patchWithHeader, is(not(equalTo(differentHeaderValue))));
}
}

public static class Operations {
private final Operation copy = new Copy(Path.of("from", "here"), Path.of("to", "there"));
private final Operation move = new Move(Path.of("from", "here"), Path.of("to", "there"));
private final Patch copyMovePatch = new Patch(ImmutableList.of(copy, move), Collections.EMPTY_MAP);
private final Patch copyMovePatchSibling = new Patch(ImmutableList.of(copy, move), Collections.EMPTY_MAP);
private final Patch withoutMoveOperation = new Patch(ImmutableList.of(copy), Collections.EMPTY_MAP);
private final Patch reverseOperationOrder = new Patch(ImmutableList.of(move, copy), Collections.EMPTY_MAP);

@Test
public void equality() throws IOException {
assertThat(copyMovePatch, is(equalTo(copyMovePatchSibling)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW: this equalTo() is an own matcher that would have been unnecessary if equals had been overridden in the first place. Then you would have been able to just use is as that is equivalent to is(equalTo(...) using the default hamcrest matcher

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to add a card to the backlog in order to remove the boilerplate test code

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created issue #7 for future discussions and reference on this topic

assertThat(copyMovePatchSibling, is(equalTo(copyMovePatch)));
}

@Test
public void inequality() throws IOException {
assertThat(copyMovePatch, is(not(equalTo(withoutMoveOperation))));
assertThat(copyMovePatch, is(not(equalTo(reverseOperationOrder))));
}
}
}
Loading