@@ -29,6 +29,7 @@ import com.ichi2.anki.FlashCardsContract
2929import com.ichi2.anki.common.utils.annotation.KotlinCleanup
3030import com.ichi2.anki.common.utils.emptyStringArray
3131import com.ichi2.anki.libanki.Card
32+ import com.ichi2.anki.libanki.CardType
3233import com.ichi2.anki.libanki.DeckId
3334import com.ichi2.anki.libanki.Decks
3435import com.ichi2.anki.libanki.Note
@@ -601,6 +602,182 @@ class ContentProviderTest : InstrumentedTest() {
601602 )
602603 }
603604
605+ @Test
606+ fun testQueryCardById_withRawQueueProjection () {
607+ val expectedRawQueue = 2
608+ val card = updateCardForRawFieldQuery(rawQueue = expectedRawQueue)
609+
610+ assertProjectedCardInt(card.id, FlashCardsContract .Card .RAW_QUEUE , expectedRawQueue)
611+ }
612+
613+ @Test
614+ fun testQueryCardById_withRawDueProjection () {
615+ val expectedRawQueue = 1
616+ val expectedRawDue = 123456789
617+ val card =
618+ updateCardForRawFieldQuery(
619+ rawQueue = expectedRawQueue,
620+ rawDue = expectedRawDue,
621+ )
622+
623+ assertProjectedCardInt(card.id, FlashCardsContract .Card .RAW_DUE , expectedRawDue)
624+ }
625+
626+ @Test
627+ fun testQueryCardById_withRawOriginalDueProjection () {
628+ val expectedRawDue = 654321
629+ val expectedRawOriginalDue = 765432
630+ val card =
631+ updateCardForRawFieldQuery(
632+ rawDue = expectedRawDue,
633+ rawOriginalDue = expectedRawOriginalDue,
634+ )
635+
636+ assertProjectedCardInt(
637+ card.id,
638+ FlashCardsContract .Card .RAW_ORIGINAL_DUE ,
639+ expectedRawOriginalDue,
640+ )
641+ }
642+
643+ @Test
644+ fun testQueryCardById_withFilteredDeckRawDueProjection () {
645+ val originalDue = col.sched.today + 25
646+ val card =
647+ updateCardForRawFieldQuery(
648+ rawQueue = 2 ,
649+ rawDue = originalDue,
650+ rawInterval = 50 ,
651+ )
652+
653+ // Move the card into a filtered deck so due is replaced and the original due is kept in oDue.
654+ val filteredDeckId = col.decks.newFiltered(" Raw due filtered deck" )
655+ testDeckIds.add(filteredDeckId)
656+ val filteredDeck = checkNotNull(col.decks.getLegacy(filteredDeckId))
657+ filteredDeck.getJSONArray(" terms" ).getJSONArray(0 ).put(0 , " cid:${card.id} " )
658+ col.decks.save(filteredDeck)
659+ col.sched.rebuildFilteredDeck(filteredDeckId)
660+ card.load(col)
661+
662+ assertNotEquals(originalDue, card.due)
663+ assertEquals(originalDue, card.oDue)
664+ assertProjectedCardInt(card.id, FlashCardsContract .Card .RAW_DUE , card.due)
665+ assertProjectedCardInt(card.id, FlashCardsContract .Card .RAW_ORIGINAL_DUE , card.oDue)
666+ }
667+
668+ @Test
669+ fun testQueryCardById_withRawIntervalProjection () {
670+ val expectedRawInterval = 37
671+ val card = updateCardForRawFieldQuery(rawInterval = expectedRawInterval)
672+
673+ assertProjectedCardInt(card.id, FlashCardsContract .Card .INTERVAL , expectedRawInterval)
674+ }
675+
676+ @Test
677+ fun testQueryCardById_withRawSm2FactorProjection () {
678+ val expectedRawSm2Factor = 2870
679+ val card = updateCardForRawFieldQuery(rawSm2Factor = expectedRawSm2Factor)
680+
681+ assertProjectedCardInt(
682+ card.id,
683+ FlashCardsContract .Card .RAW_SM2_FACTOR ,
684+ expectedRawSm2Factor,
685+ )
686+ }
687+
688+ @Test
689+ fun testQueryCardById_withRawLeftProjection () {
690+ val expectedRawLeft = 2003
691+ val card = updateCardForRawFieldQuery(rawLeft = expectedRawLeft)
692+
693+ assertProjectedCardInt(card.id, FlashCardsContract .Card .RAW_LEFT , expectedRawLeft)
694+ }
695+
696+ @Test
697+ fun testQueryCardById_defaultProjectionDoesNotIncludeRawFields () {
698+ val card =
699+ updateCardForRawFieldQuery(
700+ rawQueue = 2 ,
701+ rawDue = 99 ,
702+ rawOriginalDue = 88 ,
703+ rawInterval = 77 ,
704+ rawSm2Factor = 2500 ,
705+ rawLeft = 66 ,
706+ )
707+
708+ val cardUri =
709+ Uri .withAppendedPath(
710+ FlashCardsContract .Card .CONTENT_URI ,
711+ card.id.toString(),
712+ )
713+
714+ val cursor = contentResolver.cursorFor(cardUri)
715+
716+ cursor.use {
717+ assertEquals(FlashCardsContract .Card .DEFAULT_PROJECTION .toList(), it.columnNames.toList())
718+ assertTrue(" default projection cursor should contain a row" , it.moveToFirst())
719+
720+ assertEquals(- 1 , it.getColumnIndex(FlashCardsContract .Card .RAW_QUEUE ))
721+ assertEquals(- 1 , it.getColumnIndex(FlashCardsContract .Card .RAW_DUE ))
722+ assertEquals(- 1 , it.getColumnIndex(FlashCardsContract .Card .RAW_ORIGINAL_DUE ))
723+ assertEquals(- 1 , it.getColumnIndex(FlashCardsContract .Card .INTERVAL ))
724+ assertEquals(- 1 , it.getColumnIndex(FlashCardsContract .Card .RAW_SM2_FACTOR ))
725+ assertEquals(- 1 , it.getColumnIndex(FlashCardsContract .Card .RAW_LEFT ))
726+ }
727+ }
728+
729+ @Test
730+ fun testSearchCards_withRawQueueProjection () {
731+ val expectedRawQueue = 2
732+ val card = updateCardForRawFieldQuery(rawQueue = expectedRawQueue)
733+
734+ val cursor =
735+ contentResolver.cursorFor(
736+ FlashCardsContract .Card .CONTENT_URI ,
737+ projection = arrayOf(FlashCardsContract .Card .RAW_QUEUE ),
738+ selection = " cid:${card.id} " ,
739+ )
740+
741+ cursor.use {
742+ assertEquals(listOf (FlashCardsContract .Card .RAW_QUEUE ), it.columnNames.toList())
743+ assertTrue(" search cursor should contain a row" , it.moveToFirst())
744+ assertEquals(expectedRawQueue, it.getInt(it.getColumnIndex(FlashCardsContract .Card .RAW_QUEUE )))
745+ }
746+ }
747+
748+ @Test
749+ fun testQueryNoteCardByOrd_withRawDueProjection () {
750+ val expectedRawQueue = 1
751+ val expectedRawDue = 24680
752+ val card =
753+ updateCardForRawFieldQuery(
754+ rawQueue = expectedRawQueue,
755+ rawDue = expectedRawDue,
756+ )
757+
758+ val noteCardsUri =
759+ Uri .withAppendedPath(
760+ Uri .withAppendedPath(
761+ FlashCardsContract .Note .CONTENT_URI ,
762+ card.nid.toString(),
763+ ),
764+ " cards" ,
765+ )
766+ val specificCardUri = Uri .withAppendedPath(noteCardsUri, card.ord.toString())
767+
768+ val cursor =
769+ contentResolver.cursorFor(
770+ specificCardUri,
771+ projection = arrayOf(FlashCardsContract .Card .RAW_DUE ),
772+ )
773+
774+ cursor.use {
775+ assertEquals(listOf (FlashCardsContract .Card .RAW_DUE ), it.columnNames.toList())
776+ assertTrue(" note card cursor should contain a row" , it.moveToFirst())
777+ assertEquals(expectedRawDue, it.getInt(it.getColumnIndex(FlashCardsContract .Card .RAW_DUE )))
778+ }
779+ }
780+
604781 /* *
605782 * Check that inserting a note with an invalid noteTypeId returns a reasonable exception
606783 */
@@ -1821,6 +1998,53 @@ class ContentProviderTest : InstrumentedTest() {
18211998 return contentResolver.delete(emptyCardsUri, null , null )
18221999 }
18232000
2001+ private fun updateCardForRawFieldQuery (
2002+ rawQueue : Int = 0,
2003+ rawDue : Int = 0,
2004+ rawOriginalDue : Int = 0,
2005+ rawInterval : Int = 0,
2006+ rawSm2Factor : Int = 0,
2007+ rawLeft : Int = 0,
2008+ ): Card {
2009+ val card = getFirstCardFromScheduler(col) ? : error(" No card available for raw field test" )
2010+ card.queue = QueueType .fromCode(rawQueue)
2011+ card.type =
2012+ when (rawQueue) {
2013+ 0 -> CardType .New
2014+ 1 -> CardType .Lrn
2015+ 2 -> CardType .Rev
2016+ 3 -> CardType .Relearning
2017+ else -> card.type
2018+ }
2019+ card.due = rawDue
2020+ card.oDue = rawOriginalDue
2021+ card.ivl = rawInterval
2022+ card.factor = rawSm2Factor
2023+ card.left = rawLeft
2024+ col.updateCard(card, skipUndoEntry = true )
2025+ return card
2026+ }
2027+
2028+ private fun assertProjectedCardInt (
2029+ cardId : Long ,
2030+ columnName : String ,
2031+ expectedValue : Int ,
2032+ ) {
2033+ val cardUri =
2034+ Uri .withAppendedPath(
2035+ FlashCardsContract .Card .CONTENT_URI ,
2036+ cardId.toString(),
2037+ )
2038+
2039+ val cursor = contentResolver.cursorFor(cardUri, projection = arrayOf(columnName))
2040+
2041+ cursor.use {
2042+ assertEquals(listOf (columnName), it.columnNames.toList())
2043+ assertTrue(" card cursor should contain a row" , it.moveToFirst())
2044+ assertEquals(expectedValue, it.getInt(it.getColumnIndex(columnName)))
2045+ }
2046+ }
2047+
18242048 /* * Adds a note which will be removed by [tearDown] */
18252049 private fun addTempClozeNote (text : String ): Note =
18262050 addClozeNote(text).update {
@@ -1911,6 +2135,17 @@ class ContentProviderTest : InstrumentedTest() {
19112135 }
19122136}
19132137
2138+ private fun ContentResolver.cursorFor (
2139+ uri : Uri ,
2140+ projection : Array <String >? = null,
2141+ selection : String? = null,
2142+ selectionArgs : Array <String >? = null,
2143+ sortOrder : String? = null,
2144+ ): Cursor =
2145+ checkNotNull(query(uri, projection, selection, selectionArgs, sortOrder)) {
2146+ " null cursor from $uri "
2147+ }
2148+
19142149/* *
19152150 * Unbury all buried cards in all decks. Only used for tests.
19162151 */
0 commit comments