Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 3c3781b9 authored by Marten Gajda's avatar Marten Gajda Committed by GitHub
Browse files

Fix instance insert handling. Implements #623 (#625)

This commit fixes inserting instances to comply with the JavaDoc. In order to achieve that this improves DateTimeArrayFieldAdapter by returning and taking Iterables instead of arrays.
parent 76dbaa7a
Loading
Loading
Loading
Loading
+346 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 dmfs GmbH
 *
 * 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.dmfs.provider.tasks;

import android.content.ContentProviderClient;
import android.content.Context;
import android.content.OperationApplicationException;
import android.os.Build;
import android.os.RemoteException;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;

import org.dmfs.android.contentpal.Operation;
import org.dmfs.android.contentpal.OperationsQueue;
import org.dmfs.android.contentpal.RowSnapshot;
import org.dmfs.android.contentpal.operations.Assert;
import org.dmfs.android.contentpal.operations.BulkAssert;
import org.dmfs.android.contentpal.operations.BulkDelete;
import org.dmfs.android.contentpal.operations.Counted;
import org.dmfs.android.contentpal.operations.Delete;
import org.dmfs.android.contentpal.operations.Put;
import org.dmfs.android.contentpal.queues.BasicOperationsQueue;
import org.dmfs.android.contentpal.rowdata.CharSequenceRowData;
import org.dmfs.android.contentpal.rowdata.Composite;
import org.dmfs.android.contentpal.rowdata.Referring;
import org.dmfs.android.contentpal.rowsnapshots.VirtualRowSnapshot;
import org.dmfs.android.contentpal.transactions.BaseTransaction;
import org.dmfs.android.contenttestpal.operations.AssertEmptyTable;
import org.dmfs.android.contenttestpal.operations.AssertRelated;
import org.dmfs.iterables.SingletonIterable;
import org.dmfs.iterables.elementary.Seq;
import org.dmfs.opentaskspal.tables.InstanceTable;
import org.dmfs.opentaskspal.tables.LocalTaskListsTable;
import org.dmfs.opentaskspal.tables.TaskListsTable;
import org.dmfs.opentaskspal.tables.TasksTable;
import org.dmfs.opentaskspal.tasklists.NameData;
import org.dmfs.opentaskspal.tasks.TimeData;
import org.dmfs.opentaskspal.tasks.TitleData;
import org.dmfs.opentaskstestpal.InstanceTestData;
import org.dmfs.rfc5545.DateTime;
import org.dmfs.tasks.contract.TaskContract.Instances;
import org.dmfs.tasks.contract.TaskContract.TaskLists;
import org.dmfs.tasks.contract.TaskContract.Tasks;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import static org.dmfs.android.contenttestpal.ContentMatcher.resultsIn;
import static org.junit.Assert.assertThat;


/**
 * Tests for {@link TaskProvider}. These tests check various operations on the instances table.
 *
 * @author Gabor Keszthelyi
 * @author Marten Gajda
 */
@RunWith(AndroidJUnit4.class)
public class TaskProviderInstancesTest
{
    private String mAuthority;
    private Context mContext;
    private ContentProviderClient mClient;


    @Before
    public void setUp() throws Exception
    {
        mContext = InstrumentationRegistry.getTargetContext();
        mAuthority = AuthorityUtil.taskAuthority(mContext);
        mClient = mContext.getContentResolver().acquireContentProviderClient(mAuthority);

        // Assert that tables are empty:
        OperationsQueue queue = new BasicOperationsQueue(mClient);
        queue.enqueue(new Seq<Operation<?>>(
                new AssertEmptyTable<>(new TasksTable(mAuthority)),
                new AssertEmptyTable<>(new TaskListsTable(mAuthority)),
                new AssertEmptyTable<>(new InstanceTable(mAuthority))));
        queue.flush();
    }


    @After
    public void tearDown() throws Exception
    {
        /*
        TODO When Test Orchestration is available, there will be no need for clean up here and check in setUp(), every test method will run in separate instrumentation
        https://android-developers.googleblog.com/2017/07/android-testing-support-library-10-is.html
        https://developer.android.com/training/testing/junit-runner.html#using-android-test-orchestrator
        */

        // Clear the DB:
        BasicOperationsQueue queue = new BasicOperationsQueue(mClient);
        queue.enqueue(new SingletonIterable<Operation<?>>(new BulkDelete<>(new LocalTaskListsTable(mAuthority))));
        queue.flush();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
        {
            mClient.close();
        }
        else
        {
            mClient.release();
        }
    }


    /**
     * Create a single instance.
     */
    @Test
    public void testInsertSingleInstance()
    {
        RowSnapshot<TaskLists> taskList = new VirtualRowSnapshot<>(new LocalTaskListsTable(mAuthority));
        RowSnapshot<Instances> instance = new VirtualRowSnapshot<>(new InstanceTable(mAuthority));

        assertThat(new Seq<>(
                // create a local list
                new Put<>(taskList, new NameData("list1")),
                // insert a new task straight into the instances table
                new Put<>(instance, new Referring<>(Tasks.LIST_ID, taskList, new CharSequenceRowData<>(Tasks.TITLE, "task1")))

        ), resultsIn(mClient,
                new Assert<>(taskList, new NameData("list1")),
                // the task list contains exactly one task with the title "task1"
                new Counted<>(1, new BulkAssert<>(new TasksTable(mAuthority))),
                new AssertRelated<>(new TasksTable(mAuthority), Tasks.LIST_ID, taskList,
                        new Composite<>(
                                new CharSequenceRowData<>(Tasks.TITLE, "task1"))),
                // the instances table contains one instance
                new Counted<>(1, new BulkAssert<>(new InstanceTable(mAuthority))),
                // the instances table contains the given instance
                new Assert<>(instance, new Composite<>(
                        new InstanceTestData(0),
                        new CharSequenceRowData<>(Tasks.TITLE, "task1")))));
    }


    /**
     * Create a single instance and update it.
     */
    @Test
    public void testUpdateSingleInstance()
    {
        RowSnapshot<TaskLists> taskList = new VirtualRowSnapshot<>(new LocalTaskListsTable(mAuthority));
        RowSnapshot<Instances> instance = new VirtualRowSnapshot<>(new InstanceTable(mAuthority));

        assertThat(new Seq<>(
                // create a local list
                new Put<>(taskList, new NameData("list1")),
                // insert a new task straight into the instances table
                new Put<>(instance, new Referring<>(Tasks.LIST_ID, taskList, new CharSequenceRowData<>(Tasks.TITLE, "task1"))),
                // update the instance
                new Put<>(instance, new CharSequenceRowData<>(Tasks.TITLE, "Updated"))
        ), resultsIn(mClient,
                new Assert<>(taskList, new NameData("list1")),
                // the task list contains exactly one task with the title "Updated"
                new Counted<>(1, new BulkAssert<>(new TasksTable(mAuthority))),
                new AssertRelated<>(new TasksTable(mAuthority), Tasks.LIST_ID, taskList,
                        new Composite<>(
                                new CharSequenceRowData<>(Tasks.TITLE, "Updated"))),
                // the instances table contains one instance
                new Counted<>(1, new BulkAssert<>(new InstanceTable(mAuthority))),
                // the instances table contains the given instance
                new Assert<>(instance, new Composite<>(
                        new InstanceTestData(0),
                        new CharSequenceRowData<>(Tasks.TITLE, "Updated")))));
    }


    /**
     * Create a single instance and complete it.
     */
    @Test
    public void testCompleteSingleInstance()
    {
        RowSnapshot<TaskLists> taskList = new VirtualRowSnapshot<>(new LocalTaskListsTable(mAuthority));
        RowSnapshot<Instances> instance = new VirtualRowSnapshot<>(new InstanceTable(mAuthority));

        assertThat(new Seq<>(
                // create a local list
                new Put<>(taskList, new NameData("list1")),
                // insert a new task straight into the instances table
                new Put<>(instance, new Referring<>(Tasks.LIST_ID, taskList, new CharSequenceRowData<>(Tasks.TITLE, "task1"))),
                // update the instance status
                new Put<>(instance, (transactionContext, builder) -> builder.withValue(Tasks.STATUS, Tasks.STATUS_COMPLETED))
        ), resultsIn(mClient,
                new Assert<>(taskList, new NameData("list1")),
                // the task list contains exactly one task with the title "Updated"
                new Counted<>(1, new BulkAssert<>(new TasksTable(mAuthority))),
                new AssertRelated<>(new TasksTable(mAuthority), Tasks.LIST_ID, taskList,
                        new Composite<>(
                                new CharSequenceRowData<>(Tasks.TITLE, "task1"))),
                // the instances table contains one instance
                new Counted<>(1, new BulkAssert<>(new InstanceTable(mAuthority))),
                // the instances table contains the given instance
                new Assert<>(instance, new Composite<>(
                        new InstanceTestData(-1),
                        new CharSequenceRowData<>(Tasks.TITLE, "task1")))));
    }


    /**
     * Create a single instance and delete it.
     */
    @Test
    public void testDeleteSingleInstance()
    {
        RowSnapshot<TaskLists> taskList = new VirtualRowSnapshot<>(new LocalTaskListsTable(mAuthority));
        RowSnapshot<Instances> instance = new VirtualRowSnapshot<>(new InstanceTable(mAuthority));

        assertThat(new Seq<>(
                // create a local list
                new Put<>(taskList, new NameData("list1")),
                // insert a new task straight into the instances table
                new Put<>(instance, new Referring<>(Tasks.LIST_ID, taskList, new CharSequenceRowData<>(Tasks.TITLE, "task1"))),
                // delete the instance
                new Delete<>(instance)
        ), resultsIn(mClient,
                new Assert<>(taskList, new NameData("list1")),
                // the list does not contain a single task
                new AssertEmptyTable<>(new TasksTable(mAuthority)),
                new AssertEmptyTable<>(new InstanceTable(mAuthority))));

    }


    /**
     * Create a single instance and insert an override for exactly the same instance.
     */
    @Test(expected = IllegalArgumentException.class)
    public void testInsertSingleInstanceTwice() throws RemoteException, OperationApplicationException
    {
        RowSnapshot<TaskLists> taskList = new VirtualRowSnapshot<>(new LocalTaskListsTable(mAuthority));
        RowSnapshot<Tasks> task = new VirtualRowSnapshot<>(new TasksTable(mAuthority));
        RowSnapshot<Instances> instance = new VirtualRowSnapshot<>(new InstanceTable(mAuthority));

        DateTime dateTime = DateTime.parse("20180110T224500Z");
        String dtstart = Long.toString(dateTime.getTimestamp());

        new BaseTransaction().with(new Seq<>(
                // create a local list
                new Put<>(taskList, new NameData("list1")),
                // insert a new task into the tasks table (we insert a task to get a RowReference to the new row)
                new Put<>(task,
                        new Composite<>(
                                new Referring<>(Tasks.LIST_ID, taskList),
                                new TimeData(dateTime),
                                new TitleData("task1"))),
                new Put<>(instance,
                        new Composite<>(
                                new Referring<>(Tasks.LIST_ID, taskList),
                                new CharSequenceRowData<>(Tasks.DTSTART, "1234"),
                                // insert an instance which would override the original instance, which already exists
                                new Referring<>(Tasks.ORIGINAL_INSTANCE_ID, task),
                                new CharSequenceRowData<>(Tasks.ORIGINAL_INSTANCE_TIME, dtstart),
                                new CharSequenceRowData<>(Tasks.TZ, "UTC"),
                                new CharSequenceRowData<>(Tasks.TITLE, "task1")))

        )).commit(mClient);
    }


    /**
     * Create a single instance and insert an override for a new instance, turning the event into a recurring event.
     */
    @Test
    public void testInsertSingleInstanceAddAnother() throws RemoteException, OperationApplicationException
    {
        RowSnapshot<TaskLists> taskList = new VirtualRowSnapshot<>(new LocalTaskListsTable(mAuthority));
        RowSnapshot<Tasks> task = new VirtualRowSnapshot<>(new TasksTable(mAuthority));
        RowSnapshot<Instances> instance = new VirtualRowSnapshot<>(new InstanceTable(mAuthority));

        DateTime dateTimeOriginal = DateTime.parse("20180110T224500Z");
        // override is one day later
        DateTime dateTimeOverride = DateTime.parse("20180111T224500Z");
        String startOverride = Long.toString(dateTimeOverride.getTimestamp());

        assertThat(new Seq<>(
                // create a local list
                new Put<>(taskList, new NameData("list1")),
                // insert a new task into the tasks table (we insert a task to get a RowReference to the new row)
                new Put<>(task,
                        new Composite<Tasks>(
                                new Referring<>(Tasks.LIST_ID, taskList),
                                new TimeData(dateTimeOriginal),
                                new TitleData("task1"))),
                new Put<>(instance,
                        new Composite<Instances>(
                                new Referring<>(Tasks.LIST_ID, taskList),
                                new CharSequenceRowData<>(Tasks.DTSTART, "1234"),
                                new CharSequenceRowData<>(Tasks.IS_ALLDAY, "0"),
                                // insert an override instance
                                new Referring<>(Tasks.ORIGINAL_INSTANCE_ID, task),
                                new CharSequenceRowData<>(Tasks.ORIGINAL_INSTANCE_TIME, startOverride),
                                new CharSequenceRowData<>(Tasks.TZ, "UTC"),
                                new CharSequenceRowData<>(Tasks.TITLE, "task override")))

        ), resultsIn(mClient,
                new Assert<>(taskList, new NameData("list1")),
                // the task list contains exactly two tasks
                new Counted<>(2, new BulkAssert<>(new TasksTable(mAuthority))),
                // check that the original task has RDATES now, one for the original start and one for the new override
                new Assert<>(task,
                        new Composite<Tasks>(
                                new Referring<>(Tasks.LIST_ID, taskList),
                                new TimeData(dateTimeOriginal),
                                new CharSequenceRowData<>(Tasks.RDATE, "20180110T224500Z,20180111T224500Z"),
                                new TitleData("task1"))),
                // and check there is a task for the override
                new AssertRelated<>(new TasksTable(mAuthority), Tasks.ORIGINAL_INSTANCE_ID, task,
                        new Composite<Tasks>(
                                new Referring<>(Tasks.LIST_ID, taskList),
                                new CharSequenceRowData<>(Tasks.ORIGINAL_INSTANCE_TIME, startOverride),
                                new TimeData(new DateTime(1234)),
                                new TitleData("task override")))
                // TODO: enable tests below once recurrence has been implemented
//                // the instances table contains two instances as well
//                new Counted<>(2, new BulkAssert<>(new InstanceTable(mAuthority))),
//                // one instance is related to the task
//               new AssertRelated<>(new InstanceTable(mAuthority), Instances.TASK_ID, task,
//                        new Composite<>(
//                                new InstanceTestData(new Present<>(dateTimeOriginal), new Absent<>(), new Present<>(dateTimeOriginal), 0),
//                                new CharSequenceRowData<>(Tasks.TITLE, "task1"))),
//                // the other instance is for the override
//                new Assert<>(instance, new Composite<>(
//                        new InstanceTestData(new Present<>(new DateTime(1234)), new Absent<>(), new Present<>(dateTimeOverride), 0),
//                        new CharSequenceRowData<>(Tasks.TITLE, "task override")))
        ));
    }
}
+0 −25
Original line number Diff line number Diff line
@@ -156,31 +156,6 @@ public class TaskProviderTest
    }


    /**
     * Create 1 local task list and 1 task (via the instances table), check values in TaskLists, Tasks, Instances tables.
     */
    @Test
    public void testInsertSingleInstance()
    {
        RowSnapshot<TaskLists> taskList = new VirtualRowSnapshot<>(new LocalTaskListsTable(mAuthority));
        RowSnapshot<Instances> instance = new VirtualRowSnapshot<>(new InstanceTable(mAuthority));

        assertThat(new Seq<>(
                new Put<>(taskList, new NameData("list1")),
                // insert a new task straight into the instances table
                new Referring<>(taskList, Tasks.LIST_ID, new Put<>(instance, new CharSequenceRowData<>(Tasks.TITLE, "task1")))

        ), resultsIn(mClient,
                new Assert<>(taskList, new NameData("list1")),
                new AssertRelated<>(new InstanceTable(mAuthority), Tasks.LIST_ID, taskList,
                        new Composite<>(
                                new InstanceTestData(0),
                                new CharSequenceRowData<>(Tasks.TITLE, "task1"),
                                new CharSequenceRowData<>(Tasks.TZ, null))
                )));
    }


    /**
     * Create 1 local task list and 1 task, update task via instances table and check values in TaskLists, Tasks, Instances tables.
     */
+1 −1
Original line number Diff line number Diff line
@@ -44,7 +44,7 @@ public abstract class AbstractTaskAdapter implements TaskAdapter
    @Override
    public boolean isRecurring()
    {
        return valueOf(RRULE) != null || valueOf(RDATE) != null;
        return valueOf(RRULE) != null || valueOf(RDATE).iterator().hasNext();
    }


+2 −2
Original line number Diff line number Diff line
@@ -70,7 +70,7 @@ public class ContentValuesInstanceAdapter extends AbstractInstanceAdapter


    @Override
    public <T> boolean isUpdated(FieldAdapter<T, InstanceAdapter> fieldAdapter)
    public boolean isUpdated(FieldAdapter<?, InstanceAdapter> fieldAdapter)
    {
        return fieldAdapter.isSetIn(mValues);
    }
@@ -98,7 +98,7 @@ public class ContentValuesInstanceAdapter extends AbstractInstanceAdapter


    @Override
    public <T> void unset(FieldAdapter<T, InstanceAdapter> fieldAdapter) throws IllegalStateException
    public void unset(FieldAdapter<?, InstanceAdapter> fieldAdapter) throws IllegalStateException
    {
        fieldAdapter.removeFrom(mValues);
    }
+2 −2
Original line number Diff line number Diff line
@@ -68,7 +68,7 @@ public class ContentValuesListAdapter extends AbstractListAdapter


    @Override
    public <T> boolean isUpdated(FieldAdapter<T, ListAdapter> fieldAdapter)
    public boolean isUpdated(FieldAdapter<?, ListAdapter> fieldAdapter)
    {
        return fieldAdapter.isSetIn(mValues);
    }
@@ -96,7 +96,7 @@ public class ContentValuesListAdapter extends AbstractListAdapter


    @Override
    public <T> void unset(FieldAdapter<T, ListAdapter> fieldAdapter) throws IllegalStateException
    public void unset(FieldAdapter<?, ListAdapter> fieldAdapter) throws IllegalStateException
    {
        fieldAdapter.removeFrom(mValues);
    }
Loading