/*
 * Copyright 2018, TeamDev. All rights reserved.
 *
 * Redistribution and use in source and/or binary forms, with or without
 * modification, must retain the above copyright notice and the following
 * disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package io.spine.base;

import com.google.common.testing.NullPointerTester;
import com.google.protobuf.Any;
import io.spine.test.base.AnyHolder;
import io.spine.test.base.GenericHolder;
import io.spine.test.base.StringHolder;
import io.spine.test.base.StringHolderHolder;
import io.spine.testing.UtilityClassTest;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static com.google.common.truth.Truth.assertThat;
import static io.spine.base.FieldPaths.fieldAt;
import static io.spine.base.FieldPaths.parse;
import static io.spine.protobuf.AnyPacker.pack;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

@DisplayName("FieldPaths should")
class FieldPathsTest extends UtilityClassTest<FieldPaths> {

    FieldPathsTest() {
        super(FieldPaths.class);
    }

    @Override
    protected void configure(NullPointerTester tester) {
        tester.setDefault(FieldPath.class, FieldPath.getDefaultInstance());
    }

    @Test
    @DisplayName("parse simple path")
    void parseSimple() {
        String path = "val";
        FieldPath parsed = parse(path);
        assertEquals(1 , parsed.getFieldNameCount());
        assertEquals(path , parsed.getFieldName(0));
    }

    @Test
    @DisplayName("parse long field paths")
    void parseLong() {
        String path = "shh.holder.val";
        FieldPath parsed = parse(path);
        assertThat(parsed.getFieldNameList()).containsExactly("shh", "holder", "val");
    }

    @Test
    @DisplayName("not parse empty")
    void failToParseEmpty() {
        assertThrows(IllegalArgumentException.class, () -> parse(""));
    }

    @Test
    @DisplayName("obtain value at a simple path")
    void obtainSimple() {
        StringHolder holder = StringHolder
                .newBuilder()
                .setVal("foobar")
                .build();
        FieldPath path = parse("val");
        Object result = fieldAt(holder, path);
        assertEquals(holder.getVal(), result);
    }

    @Test
    @DisplayName("obtain value at a complex path")
    void obtainComplex() {
        Any value = pack(Time.getCurrentTime());
        AnyHolder anyHolder = AnyHolder
                .newBuilder()
                .setVal(value)
                .build();
        GenericHolder anyHolderHolder = GenericHolder
                .newBuilder()
                .setAny(anyHolder)
                .build();
        FieldPath pathToTypeUrl = parse("any.val.type_url");
        Object actual = fieldAt(anyHolderHolder, pathToTypeUrl);
        assertEquals(value.getTypeUrl(), actual);
    }

    @Test
    @DisplayName("obtain value at a recursive path")
    void obtainRecursive() {
        String value = "42";
        StringHolder holder0 = StringHolder
                .newBuilder()
                .setVal(value)
                .build();
        StringHolderHolder holder1 = StringHolderHolder
                .newBuilder()
                .setHolder(holder0)
                .build();
        GenericHolder holder2 = GenericHolder
                .newBuilder()
                .setShh(holder1)
                .build();
        GenericHolder holder3 = GenericHolder
                .newBuilder()
                .setGeneric(holder2)
                .build();
        FieldPath path = parse("generic.shh.holder.val");
        Object actual = fieldAt(holder3, path);
        assertEquals(value, actual);
    }
}

@@ -4464,11 +4464,854 @@
 );%0A    %7D
+%0A%0A    @Test%0A    @DisplayName(%22fail if the path contains a typo%22)%0A    void failOnTypo() %7B%0A        String value = %22hello%22;%0A        StringHolder holder = StringHolder%0A                .newBuilder()%0A                .setVal(value)%0A                .build();%0A        FieldPath wrongPath = parse(%22wrong_field_name%22);%0A        assertThrows(IllegalArgumentException.class, () -%3E fieldAt(holder, wrongPath));%0A    %7D%0A%0A    @Test%0A    @DisplayName(%22fail if the path reaches over a primitive value%22)%0A    void failOnMissingField() %7B%0A        String value = %22primitive value%22;%0A        StringHolder holder = StringHolder%0A                .newBuilder()%0A                .setVal(value)%0A                .build();%0A        FieldPath wrongPath = parse(%22val.this_field_is_absent%22);%0A        assertThrows(IllegalArgumentException.class, () -%3E fieldAt(holder, wrongPath));%0A    %7D
 %0A%7D%0A

Add some more negative test cases.