com.ichi2.anki.tests.ContentProviderTest.java Source code

Java tutorial

Introduction

Here is the source code for com.ichi2.anki.tests.ContentProviderTest.java

Source

/***************************************************************************************
 *                                                                                      *
 * Copyright (c) 2015 Frank Oltmanns <frank.oltmanns@gmail.com>                         *
 * Copyright (c) 2015 Timothy Rae <timothy.rae@gmail.com>                               *
 *                                                                                      *
 * This program is free software; you can redistribute it and/or modify it under        *
 * the terms of the GNU General Public License as published by the Free Software        *
 * Foundation; either version 3 of the License, or (at your option) any later           *
 * version.                                                                             *
 *                                                                                      *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
 *                                                                                      *
 * You should have received a copy of the GNU General Public License along with         *
 * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
 ****************************************************************************************/

package com.ichi2.anki.tests;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.test.AndroidTestCase;
import android.util.Log;

import com.ichi2.anki.AbstractFlashcardViewer;
import com.ichi2.anki.AnkiDroidApp;
import com.ichi2.anki.CollectionHelper;
import com.ichi2.anki.FlashCardsContract;
import com.ichi2.anki.api.AddContentApi;
import com.ichi2.anki.exception.ConfirmModSchemaException;
import com.ichi2.libanki.Card;
import com.ichi2.libanki.Collection;
import com.ichi2.libanki.Decks;
import com.ichi2.libanki.Models;
import com.ichi2.libanki.Note;
import com.ichi2.libanki.Sched;
import com.ichi2.libanki.Utils;

import org.json.JSONArray;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

/**
 * Test cases for {@link com.ichi2.anki.provider.CardContentProvider}.
 * <p/>
 * These tests should cover all supported operations for each URI.
 */
public class ContentProviderTest extends AndroidTestCase {

    private static final String BASIC_MODEL_NAME = "com.ichi2.anki.provider.test.basic.x94oa3F";
    private static final String TEST_FIELD_VALUE = "test field value";
    private static final String TEST_TAG = "aldskfhewjklhfczmxkjshf";
    private static final String[] TEST_DECKS = { "cmxieunwoogyxsctnjmv", "sstuljxgmfdyugiujyhq",
            "pdsqoelhmemmmbwjunnu", "scxipjiyozczaaczoawo" };
    private static final String TEST_MODEL_NAME = "com.ichi2.anki.provider.test.a1x6h9l";
    private static final String[] TEST_MODEL_FIELDS = { "FRONTS", "BACK" };
    private static final String[] TEST_MODEL_CARDS = { "cArD1", "caRD2" };
    private static final String[] TEST_MODEL_QFMT = { "{{FRONTS}}", "{{BACK}}" };
    private static final String[] TEST_MODEL_AFMT = { "{{BACK}}", "{{FRONTS}}" };
    private static final String[] TEST_NOTE_FIELDS = { "dis is za Fr0nt", "Te$t" };
    private static final String TEST_MODEL_CSS = "styleeeee";

    private int mNumDecksBeforeTest;
    private long[] mTestDeckIds = new long[TEST_DECKS.length];
    private ArrayList<Uri> mCreatedNotes;
    private long mModelId = 0;
    private String[] mDummyFields = new String[1];

    /**
     * Initially create one note for each model.
     */
    @Override
    protected void setUp() throws Exception {
        super.setUp();
        Log.i(AnkiDroidApp.TAG, "setUp()");
        mCreatedNotes = new ArrayList<>();
        final Collection col = CollectionHelper.getInstance().getCol(getContext());
        // Add a new basic model that we use for testing purposes (existing models could potentially be corrupted)
        JSONObject model = Models.addBasicModel(col, BASIC_MODEL_NAME);
        mModelId = model.getLong("id");
        ArrayList<String> flds = col.getModels().fieldNames(model);
        // Use the names of the fields as test values for the notes which will be added
        mDummyFields = flds.toArray(new String[flds.size()]);
        // create test decks and add one note for every deck
        final AddContentApi api = new AddContentApi(getContext());
        HashMap<Long, String> deckList = api.getDeckList();
        mNumDecksBeforeTest = deckList.size();
        // TODO: add the notes directly with libanki
        for (int i = 0; i < TEST_DECKS.length; i++) {
            mTestDeckIds[i] = api.addNewDeck(TEST_DECKS[i]);
            Uri newNoteUri = api.addNewNote(mModelId, mTestDeckIds[i], mDummyFields, TEST_TAG);
            assertNotNull(newNoteUri);
            mCreatedNotes.add(newNoteUri);
            // Check that the flds data was set correctly
            long nid = Long.parseLong(newNoteUri.getLastPathSegment());
            Note addedNote = col.getNote(nid);
            assertTrue("Check that the flds data was set correctly",
                    Arrays.equals(addedNote.getFields(), mDummyFields));
            assertTrue("Check that there was at least one card generated", addedNote.cards().size() > 0);
        }
        // Add a note to the default deck as well so that testQueryNextCard() works
        Uri newNoteUri = api.addNewNote(mModelId, 1, mDummyFields, TEST_TAG);
        assertNotNull(newNoteUri);
        mCreatedNotes.add(newNoteUri);
    }

    /**
     * Remove the notes and decks created in setUp().
     */
    @Override
    protected void tearDown() throws Exception {
        Log.i(AnkiDroidApp.TAG, "tearDown()");
        final Collection col = CollectionHelper.getInstance().getCol(getContext());
        // Delete all notes
        List<Long> remnantNotes = col.findNotes("tag:" + TEST_TAG);
        if (remnantNotes.size() > 0) {
            long[] nids = new long[remnantNotes.size()];
            for (int i = 0; i < remnantNotes.size(); i++) {
                nids[i] = remnantNotes.get(i);
            }
            col.remNotes(nids);
            col.save();
            assertEquals("Check that remnant notes have been deleted", 0, col.findNotes("tag:" + TEST_TAG).size());
        }
        // delete test decks
        for (long did : mTestDeckIds) {
            col.getDecks().rem(did, true);
        }
        col.getDecks().flush();
        assertEquals("Check that all created decks have been deleted", mNumDecksBeforeTest, col.getDecks().count());
        // Delete test model
        col.modSchema(false);
        col.getModels().rem(col.getModels().get(mModelId));
        super.tearDown();
    }

    /**
     * Check that inserting and removing a note into default deck works as expected
     */
    public void testInsertAndRemoveNote() throws Exception {
        // Get required objects for test
        final ContentResolver cr = getContext().getContentResolver();
        final Collection col = CollectionHelper.getInstance().getCol(getContext());
        final AddContentApi api = new AddContentApi(getContext());
        // Add the note
        Uri newNoteUri = api.addNewNote(mModelId, 1, TEST_NOTE_FIELDS, TEST_TAG);
        assertNotNull("Check that URI returned from addNewNote is not null", newNoteUri);
        // Check that it looks as expected
        Note addedNote = new Note(col, Long.parseLong(newNoteUri.getLastPathSegment()));
        addedNote.load();
        assertTrue("Check that fields were set correctly", Arrays.equals(addedNote.getFields(), TEST_NOTE_FIELDS));
        assertEquals("Check that tag was set correctly", TEST_TAG, addedNote.getTags().get(0));
        int expectedNumCards = col.getModels().get(mModelId).getJSONArray("tmpls").length();
        assertEquals("Check that correct number of cards generated", expectedNumCards, addedNote.cards().size());
        // Now delete the note
        cr.delete(newNoteUri, null, null);
        try {
            addedNote.load();
            fail("Expected RuntimeException to be thrown when deleting note");
        } catch (RuntimeException e) {
            // Expect RuntimeException to be thrown when loading deleted note
        }
    }

    /**
     * Test that a query for all the notes added in setup() looks correct
     */
    public void testQueryNoteIds() {
        final ContentResolver cr = getContext().getContentResolver();
        // Query all available notes
        final Cursor allNotesCursor = cr.query(FlashCardsContract.Note.CONTENT_URI, null, "tag:" + TEST_TAG, null,
                null);
        assertNotNull(allNotesCursor);
        try {
            assertEquals("Check number of results", mCreatedNotes.size(), allNotesCursor.getCount());
            while (allNotesCursor.moveToNext()) {
                // Check that it's possible to leave out columns from the projection
                for (int i = 0; i < FlashCardsContract.Note.DEFAULT_PROJECTION.length; i++) {
                    String[] projection = removeFromProjection(FlashCardsContract.Note.DEFAULT_PROJECTION, i);
                    String noteId = allNotesCursor
                            .getString(allNotesCursor.getColumnIndex(FlashCardsContract.Note._ID));
                    Uri noteUri = Uri.withAppendedPath(FlashCardsContract.Note.CONTENT_URI, noteId);
                    final Cursor singleNoteCursor = cr.query(noteUri, projection, null, null, null);
                    assertNotNull("Check that there is a valid cursor for detail data", singleNoteCursor);
                    try {
                        assertEquals("Check that there is exactly one result", 1, singleNoteCursor.getCount());
                        assertTrue("Move to beginning of cursor after querying for detail data",
                                singleNoteCursor.moveToFirst());
                        // Check columns
                        assertEquals("Check column count", projection.length, singleNoteCursor.getColumnCount());
                        for (int j = 0; j < projection.length; j++) {
                            assertEquals("Check column name " + j, projection[j],
                                    singleNoteCursor.getColumnName(j));
                        }
                    } finally {
                        singleNoteCursor.close();
                    }
                }
            }
        } finally {
            allNotesCursor.close();
        }
    }

    /**
     * Check that a valid Cursor is returned when querying notes table with non-default projections
     */
    public void testQueryNotesProjection() {
        final ContentResolver cr = getContext().getContentResolver();
        // Query all available notes
        for (int i = 0; i < FlashCardsContract.Note.DEFAULT_PROJECTION.length; i++) {
            String[] projection = removeFromProjection(FlashCardsContract.Note.DEFAULT_PROJECTION, i);
            final Cursor allNotesCursor = cr.query(FlashCardsContract.Note.CONTENT_URI, projection,
                    "tag:" + TEST_TAG, null, null);
            assertNotNull("Check that there is a valid cursor", allNotesCursor);
            try {
                assertEquals("Check number of results", mCreatedNotes.size(), allNotesCursor.getCount());
                // Check columns
                assertEquals("Check column count", projection.length, allNotesCursor.getColumnCount());
                for (int j = 0; j < projection.length; j++) {
                    assertEquals("Check column name " + j, projection[j], allNotesCursor.getColumnName(j));
                }
            } finally {
                allNotesCursor.close();
            }
        }
    }

    private String[] removeFromProjection(String[] inputProjection, int idx) {
        String[] outputProjection = new String[inputProjection.length - 1];
        for (int i = 0; i < idx; i++) {
            outputProjection[i] = inputProjection[i];
        }
        for (int i = idx + 1; i < inputProjection.length; i++) {
            outputProjection[i - 1] = inputProjection[i];
        }
        return outputProjection;
    }

    /**
     * Check that updating the flds column works as expected
     */
    public void testUpdateNoteFields() {
        final ContentResolver cr = getContext().getContentResolver();
        ContentValues cv = new ContentValues();
        // Change the fields so that the first field is now "newTestValue"
        String[] dummyFields2 = mDummyFields.clone();
        dummyFields2[0] = TEST_FIELD_VALUE;
        for (Uri uri : mCreatedNotes) {
            // Update the flds
            cv.put(FlashCardsContract.Note.FLDS, Utils.joinFields(dummyFields2));
            cr.update(uri, cv, null, null);
            // Query the table again
            Cursor noteCursor = cr.query(uri, FlashCardsContract.Note.DEFAULT_PROJECTION, null, null, null);
            try {
                assertNotNull("Check that there is a valid cursor for detail data after update", noteCursor);
                assertEquals("Check that there is one and only one entry after update", 1, noteCursor.getCount());
                assertTrue("Move to first item in cursor", noteCursor.moveToFirst());
                String[] newFlds = Utils
                        .splitFields(noteCursor.getString(noteCursor.getColumnIndex(FlashCardsContract.Note.FLDS)));
                assertTrue("Check that the flds have been updated correctly", Arrays.equals(newFlds, dummyFields2));
            } finally {
                noteCursor.close();
            }
        }
    }

    /**
     * Check that inserting a new model works as expected
     */
    public void testInsertAndUpdateModel() throws Exception {
        final ContentResolver cr = getContext().getContentResolver();
        ContentValues cv = new ContentValues();
        // Insert a new model
        cv.put(FlashCardsContract.Model.NAME, TEST_MODEL_NAME);
        cv.put(FlashCardsContract.Model.FIELD_NAMES, Utils.joinFields(TEST_MODEL_FIELDS));
        cv.put(FlashCardsContract.Model.NUM_CARDS, TEST_MODEL_CARDS.length);
        cv.put(FlashCardsContract.Model.CSS, TEST_MODEL_CSS);
        Uri modelUri = cr.insert(FlashCardsContract.Model.CONTENT_URI, cv);
        assertNotNull("Check inserted model isn't null", modelUri);
        long mid = Long.parseLong(modelUri.getLastPathSegment());
        final Collection col = CollectionHelper.getInstance().getCol(getContext());
        try {
            JSONObject model = col.getModels().get(mid);
            assertEquals("Check model name", TEST_MODEL_NAME, model.getString("name"));
            assertEquals("Check css", TEST_MODEL_CSS, model.getString("css"));
            assertEquals("Check templates length", TEST_MODEL_CARDS.length, model.getJSONArray("tmpls").length());
            assertEquals("Check field length", TEST_MODEL_FIELDS.length, model.getJSONArray("flds").length());
            JSONArray flds = model.getJSONArray("flds");
            for (int i = 0; i < flds.length(); i++) {
                assertEquals("Check name of fields", flds.getJSONObject(i).getString("name"), TEST_MODEL_FIELDS[i]);
            }
            // Update each of the templates in the model
            for (int i = 0; i < TEST_MODEL_CARDS.length; i++) {
                cv = new ContentValues();
                cv.put(FlashCardsContract.CardTemplate.NAME, TEST_MODEL_CARDS[i]);
                cv.put(FlashCardsContract.CardTemplate.QUESTION_FORMAT, TEST_MODEL_QFMT[i]);
                cv.put(FlashCardsContract.CardTemplate.ANSWER_FORMAT, TEST_MODEL_AFMT[i]);
                cv.put(FlashCardsContract.CardTemplate.BROWSER_QUESTION_FORMAT, TEST_MODEL_QFMT[i]);
                cv.put(FlashCardsContract.CardTemplate.BROWSER_ANSWER_FORMAT, TEST_MODEL_AFMT[i]);
                Uri tmplUri = Uri.withAppendedPath(Uri.withAppendedPath(modelUri, "templates"),
                        Integer.toString(i));
                assertTrue("Update rows", cr.update(tmplUri, cv, null, null) > 0);
                JSONObject template = col.getModels().get(mid).getJSONArray("tmpls").getJSONObject(i);
                assertEquals("Check template name", TEST_MODEL_CARDS[i], template.getString("name"));
                assertEquals("Check qfmt", TEST_MODEL_QFMT[i], template.getString("qfmt"));
                assertEquals("Check afmt", TEST_MODEL_AFMT[i], template.getString("afmt"));
                assertEquals("Check bqfmt", TEST_MODEL_QFMT[i], template.getString("bqfmt"));
                assertEquals("Check bafmt", TEST_MODEL_AFMT[i], template.getString("bafmt"));
            }
        } finally {
            // Delete the model (this will force a full-sync)
            try {
                col.modSchema(false);
                col.getModels().rem(col.getModels().get(mid));
            } catch (ConfirmModSchemaException e) {
                // This will never happen
                throw new IllegalStateException("Unexpected ConfirmModSchemaException trying to remove model");
            }
        }
    }

    /**
     * Query .../models URI
     */
    public void testQueryAllModels() {
        final ContentResolver cr = getContext().getContentResolver();
        // Query all available models
        final Cursor allModels = cr.query(FlashCardsContract.Model.CONTENT_URI, null, null, null, null);
        assertNotNull(allModels);
        try {
            assertTrue("Check that there is at least one result", allModels.getCount() > 0);
            while (allModels.moveToNext()) {
                long modelId = allModels.getLong(allModels.getColumnIndex(FlashCardsContract.Model._ID));
                Uri modelUri = Uri.withAppendedPath(FlashCardsContract.Model.CONTENT_URI, Long.toString(modelId));
                final Cursor singleModel = cr.query(modelUri, null, null, null, null);
                assertNotNull(singleModel);
                try {
                    assertEquals("Check that there is exactly one result", 1, singleModel.getCount());
                    assertTrue("Move to beginning of cursor", singleModel.moveToFirst());
                    String nameFromModels = allModels
                            .getString(allModels.getColumnIndex(FlashCardsContract.Model.NAME));
                    String nameFromModel = singleModel
                            .getString(allModels.getColumnIndex(FlashCardsContract.Model.NAME));
                    assertEquals("Check that model names are the same", nameFromModel, nameFromModels);
                    String flds = allModels
                            .getString(allModels.getColumnIndex(FlashCardsContract.Model.FIELD_NAMES));
                    assertTrue("Check that valid number of fields", Utils.splitFields(flds).length >= 1);
                    Integer numCards = allModels
                            .getInt(allModels.getColumnIndex(FlashCardsContract.Model.NUM_CARDS));
                    assertTrue("Check that valid number of cards", numCards >= 1);
                } finally {
                    singleModel.close();
                }
            }
        } finally {
            allModels.close();
        }
    }

    /**
     * Move all the cards from their old decks to the first deck that was added in setup()
     */
    public void testMoveCardsToOtherDeck() {
        final ContentResolver cr = getContext().getContentResolver();
        // Query all available notes
        final Cursor allNotesCursor = cr.query(FlashCardsContract.Note.CONTENT_URI, null, "tag:" + TEST_TAG, null,
                null);
        assertNotNull(allNotesCursor);
        try {
            assertEquals("Check number of results", mCreatedNotes.size(), allNotesCursor.getCount());
            while (allNotesCursor.moveToNext()) {
                // Now iterate over all cursors
                Uri cardsUri = Uri.withAppendedPath(
                        Uri.withAppendedPath(FlashCardsContract.Note.CONTENT_URI,
                                allNotesCursor
                                        .getString(allNotesCursor.getColumnIndex(FlashCardsContract.Note._ID))),
                        "cards");
                final Cursor cardsCursor = cr.query(cardsUri, null, null, null, null);
                assertNotNull("Check that there is a valid cursor after query for cards", cardsCursor);
                try {
                    assertTrue("Check that there is at least one result for cards", cardsCursor.getCount() > 0);
                    while (cardsCursor.moveToNext()) {
                        long targetDid = mTestDeckIds[0];
                        // Move to test deck
                        ContentValues values = new ContentValues();
                        values.put(FlashCardsContract.Card.DECK_ID, targetDid);
                        Uri cardUri = Uri.withAppendedPath(cardsUri, cardsCursor
                                .getString(cardsCursor.getColumnIndex(FlashCardsContract.Card.CARD_ORD)));
                        cr.update(cardUri, values, null, null);
                        Cursor movedCardCur = cr.query(cardUri, null, null, null, null);
                        assertNotNull("Check that there is a valid cursor after moving card", movedCardCur);
                        assertTrue("Move to beginning of cursor after moving card", movedCardCur.moveToFirst());
                        long did = movedCardCur
                                .getLong(movedCardCur.getColumnIndex(FlashCardsContract.Card.DECK_ID));
                        assertEquals("Make sure that card is in new deck", targetDid, did);
                    }
                } finally {
                    cardsCursor.close();
                }
            }
        } finally {
            allNotesCursor.close();
        }
    }

    /**
     * Check that querying the current model gives a valid result
     */
    public void testQueryCurrentModel() {
        final ContentResolver cr = getContext().getContentResolver();
        Uri uri = Uri.withAppendedPath(FlashCardsContract.Model.CONTENT_URI,
                FlashCardsContract.Model.CURRENT_MODEL_ID);
        final Cursor modelCursor = cr.query(uri, null, null, null, null);
        assertNotNull(modelCursor);
        try {
            assertEquals("Check that there is exactly one result", 1, modelCursor.getCount());
            assertTrue("Move to beginning of cursor", modelCursor.moveToFirst());
            assertNotNull("Check non-empty field names",
                    modelCursor.getString(modelCursor.getColumnIndex(FlashCardsContract.Model.FIELD_NAMES)));
            assertTrue("Check at least one template",
                    modelCursor.getInt(modelCursor.getColumnIndex(FlashCardsContract.Model.NUM_CARDS)) > 0);
        } finally {
            modelCursor.close();
        }
    }

    /**
     * Check that an Exception is thrown when unsupported operations are performed
     */
    public void testUnsupportedOperations() {
        final ContentResolver cr = getContext().getContentResolver();
        ContentValues dummyValues = new ContentValues();
        Uri[] updateUris = {
                // Can't update most tables in bulk -- only via ID
                FlashCardsContract.Note.CONTENT_URI, FlashCardsContract.Model.CONTENT_URI,
                FlashCardsContract.Deck.CONTENT_ALL_URI,
                FlashCardsContract.Note.CONTENT_URI.buildUpon().appendPath("1234").appendPath("cards").build(), };
        for (Uri uri : updateUris) {
            try {
                cr.update(uri, dummyValues, null, null);
                fail("Update on " + uri + " was supposed to throw exception");
            } catch (UnsupportedOperationException e) {
                // This was expected ...
            } catch (IllegalArgumentException e) {
                // ... or this.
            }
        }
        Uri[] deleteUris = { FlashCardsContract.Note.CONTENT_URI,
                // Only note/<id> is supported
                FlashCardsContract.Note.CONTENT_URI.buildUpon().appendPath("1234").appendPath("cards").build(),
                FlashCardsContract.Note.CONTENT_URI.buildUpon().appendPath("1234").appendPath("cards")
                        .appendPath("2345").build(),
                FlashCardsContract.Model.CONTENT_URI,
                FlashCardsContract.Model.CONTENT_URI.buildUpon().appendPath("1234").build(), };
        for (Uri uri : deleteUris) {
            try {
                cr.delete(uri, null, null);
                fail("Delete on " + uri + " was supposed to throw exception");
            } catch (UnsupportedOperationException e) {
                // This was expected
            }
        }
        Uri[] insertUris = {
                // Can't do an insert with specific ID on the following tables
                FlashCardsContract.Note.CONTENT_URI.buildUpon().appendPath("1234").build(),
                FlashCardsContract.Note.CONTENT_URI.buildUpon().appendPath("1234").appendPath("cards").build(),
                FlashCardsContract.Note.CONTENT_URI.buildUpon().appendPath("1234").appendPath("cards")
                        .appendPath("2345").build(),
                FlashCardsContract.Model.CONTENT_URI.buildUpon().appendPath("1234").build(), };
        for (Uri uri : insertUris) {
            try {
                cr.insert(uri, dummyValues);
                fail("Insert on " + uri + " was supposed to throw exception");
            } catch (UnsupportedOperationException e) {
                // This was expected ...
            } catch (IllegalArgumentException e) {
                // ... or this.
            }
        }
    }

    /**
     * Test query to decks table
     * @throws Exception
     */
    public void testQueryAllDecks() throws Exception {
        Collection col;
        col = CollectionHelper.getInstance().getCol(getContext());
        Decks decks = col.getDecks();

        Cursor decksCursor = getContext().getContentResolver().query(FlashCardsContract.Deck.CONTENT_ALL_URI,
                FlashCardsContract.Deck.DEFAULT_PROJECTION, null, null, null);

        assertNotNull(decksCursor);
        try {
            assertEquals("Check number of results", decks.count(), decksCursor.getCount());
            while (decksCursor.moveToNext()) {
                long deckID = decksCursor.getLong(decksCursor.getColumnIndex(FlashCardsContract.Deck.DECK_ID));
                String deckName = decksCursor
                        .getString(decksCursor.getColumnIndex(FlashCardsContract.Deck.DECK_NAME));

                JSONObject deck = decks.get(deckID);
                assertNotNull("Check that the deck we received actually exists", deck);
                assertEquals("Check that the received deck has the correct name", deck.getString("name"), deckName);
            }
        } finally {
            decksCursor.close();
        }
    }

    /**
     * Test query to specific deck ID
     * @throws Exception
     */
    public void testQueryCertainDeck() throws Exception {
        Collection col;
        col = CollectionHelper.getInstance().getCol(getContext());

        long deckId = mTestDeckIds[0];
        Uri deckUri = Uri.withAppendedPath(FlashCardsContract.Deck.CONTENT_ALL_URI, Long.toString(deckId));
        Cursor decksCursor = getContext().getContentResolver().query(deckUri, null, null, null, null);
        try {
            if (decksCursor == null || !decksCursor.moveToFirst()) {
                fail("No deck received. Should have delivered deck with id " + deckId);
            } else {
                long returnedDeckID = decksCursor
                        .getLong(decksCursor.getColumnIndex(FlashCardsContract.Deck.DECK_ID));
                String returnedDeckName = decksCursor
                        .getString(decksCursor.getColumnIndex(FlashCardsContract.Deck.DECK_NAME));

                JSONObject realDeck = col.getDecks().get(deckId);
                assertEquals("Check that received deck ID equals real deck ID", deckId, returnedDeckID);
                assertEquals("Check that received deck name equals real deck name", realDeck.getString("name"),
                        returnedDeckName);
            }
        } finally {
            decksCursor.close();
        }
    }

    /**
     * Test that query for the next card in the schedule returns a valid result without any deck selector
     */
    public void testQueryNextCard() {
        Collection col;
        col = CollectionHelper.getInstance().getCol(getContext());
        Sched sched = col.getSched();

        Cursor reviewInfoCursor = getContext().getContentResolver().query(FlashCardsContract.ReviewInfo.CONTENT_URI,
                null, null, null, null);
        assertNotNull(reviewInfoCursor);
        assertEquals("Check that we actually received one card", 1, reviewInfoCursor.getCount());

        reviewInfoCursor.moveToFirst();
        int cardOrd = reviewInfoCursor
                .getInt(reviewInfoCursor.getColumnIndex(FlashCardsContract.ReviewInfo.CARD_ORD));
        long noteID = reviewInfoCursor
                .getLong(reviewInfoCursor.getColumnIndex(FlashCardsContract.ReviewInfo.NOTE_ID));

        Card nextCard = null;
        for (int i = 0; i < 10; i++) {//minimizing fails, when sched.reset() randomly chooses between multiple cards
            sched.reset();
            nextCard = sched.getCard();
            if (nextCard.note().getId() == noteID && nextCard.getOrd() == cardOrd)
                break;
        }
        assertNotNull("Check that there actually is a next scheduled card", nextCard);
        assertEquals("Check that received card and actual card have same note id", nextCard.note().getId(), noteID);
        assertEquals("Check that received card and actual card have same card ord", nextCard.getOrd(), cardOrd);

    }

    /**
     * Test that query for the next card in the schedule returns a valid result WITH a deck selector
     */
    public void testQueryCardFromCertainDeck() {
        long deckToTest = mTestDeckIds[0];
        String deckSelector = "deckID=?";
        String deckArguments[] = { Long.toString(deckToTest) };
        Collection col;
        col = CollectionHelper.getInstance().getCol(getContext());
        Sched sched = col.getSched();
        long selectedDeckBeforeTest = col.getDecks().selected();
        col.getDecks().select(1); //select Default deck

        Cursor reviewInfoCursor = getContext().getContentResolver().query(FlashCardsContract.ReviewInfo.CONTENT_URI,
                null, deckSelector, deckArguments, null);
        assertNotNull(reviewInfoCursor);
        assertEquals("Check that we actually received one card", 1, reviewInfoCursor.getCount());
        try {
            reviewInfoCursor.moveToFirst();
            int cardOrd = reviewInfoCursor
                    .getInt(reviewInfoCursor.getColumnIndex(FlashCardsContract.ReviewInfo.CARD_ORD));
            long noteID = reviewInfoCursor
                    .getLong(reviewInfoCursor.getColumnIndex(FlashCardsContract.ReviewInfo.NOTE_ID));
            assertEquals("Check that the selected deck has not changed", 1, col.getDecks().selected());

            col.getDecks().select(deckToTest);
            Card nextCard = null;
            for (int i = 0; i < 10; i++) {//minimizing fails, when sched.reset() randomly chooses between multiple cards
                sched.reset();
                nextCard = sched.getCard();
                if (nextCard.note().getId() == noteID && nextCard.getOrd() == cardOrd)
                    break;
            }
            assertNotNull("Check that there actually is a next scheduled card", nextCard);
            assertEquals("Check that received card and actual card have same note id", nextCard.note().getId(),
                    noteID);
            assertEquals("Check that received card and actual card have same card ord", nextCard.getOrd(), cardOrd);
        } finally {
            reviewInfoCursor.close();
        }

        col.getDecks().select(selectedDeckBeforeTest);
    }

    /**
     * Test changing the selected deck
     */
    public void testSetSelectedDeck() {
        long deckId = mTestDeckIds[0];
        ContentResolver cr = getContext().getContentResolver();
        Uri selectDeckUri = FlashCardsContract.Deck.CONTENT_SELECTED_URI;
        ContentValues values = new ContentValues();
        values.put(FlashCardsContract.Deck.DECK_ID, deckId);
        cr.update(selectDeckUri, values, null, null);

        Collection col;
        col = CollectionHelper.getInstance().getCol(getContext());
        assertEquals("Check that the selected deck has been correctly set", deckId, col.getDecks().selected());
    }

    /**
     * Test giving the answer for a reviewed card
     */
    public void testAnswerCard() {
        Collection col;
        col = CollectionHelper.getInstance().getCol(getContext());
        Sched sched = col.getSched();
        long deckId = mTestDeckIds[0];
        col.getDecks().select(deckId);
        Card card = sched.getCard();

        ContentResolver cr = getContext().getContentResolver();
        Uri reviewInfoUri = FlashCardsContract.ReviewInfo.CONTENT_URI;
        ContentValues values = new ContentValues();
        long noteId = card.note().getId();
        int cardOrd = card.getOrd();
        int ease = AbstractFlashcardViewer.EASE_3; //<- insert real ease here

        values.put(FlashCardsContract.ReviewInfo.NOTE_ID, noteId);
        values.put(FlashCardsContract.ReviewInfo.CARD_ORD, cardOrd);
        values.put(FlashCardsContract.ReviewInfo.EASE, ease);
        int updateCount = cr.update(reviewInfoUri, values, null, null);

        assertEquals("Check if update returns 1", 1, updateCount);

        sched.reset();
        Card newCard = sched.getCard();
        if (newCard != null) {
            if (newCard.note().getId() == card.note().getId() && newCard.getOrd() == card.getOrd()) {
                fail("Next scheduled card has not changed");
            }
        } else {
            //We expected this
        }
    }

}