1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.telephony.testing;
18 
19 import static com.google.common.truth.Truth.assertAbout;
20 
21 import android.content.ContentValues;
22 import android.database.Cursor;
23 import android.database.CursorWrapper;
24 import android.database.DatabaseUtils;
25 
26 import androidx.annotation.Nullable;
27 
28 import com.google.common.truth.FailureMetadata;
29 import com.google.common.truth.IntegerSubject;
30 import com.google.common.truth.IterableSubject;
31 import com.google.common.truth.StringSubject;
32 import com.google.common.truth.Subject;
33 import com.google.common.truth.Truth;
34 
35 import java.util.ArrayList;
36 import java.util.List;
37 
38 /** Truth subject for making assertions about {@link Cursor}s. */
39 public final class CursorSubject extends Subject {
40 
41     private final Cursor mActual;
42 
CursorSubject(FailureMetadata metadata, @Nullable Cursor actual)43     private CursorSubject(FailureMetadata metadata, @Nullable Cursor actual) {
44         super(metadata, new StringableCursor(actual));
45         this.mActual = new StringableCursor(actual);
46     }
47 
48     /** Returns the factory for this subject. */
cursors()49     public static Factory<CursorSubject, Cursor> cursors() {
50         return CursorSubject::new;
51     }
52 
53     /** Starts an assertion. */
assertThat(Cursor cursor)54     public static CursorSubject assertThat(Cursor cursor) {
55         return assertAbout(cursors()).that(cursor);
56     }
57 
58     /** Asserts {@link Cursor#getCount()} has the specified value. */
hasCount(int count)59     public void hasCount(int count) {
60         check("getCount()").that(mActual.getCount()).isEqualTo(count);
61     }
62 
63     /** Asserts {@link Cursor#getColumnNames()} match those specified. */
hasColumnNames(String... columnNames)64     public void hasColumnNames(String... columnNames) {
65         check("getColumnNames()").that(mActual.getColumnNames()).asList()
66                 .containsExactlyElementsIn(columnNames).inOrder();
67     }
68 
69     /** Positions the cursor under test at the specified row to make an assertion about it. */
atRow(int position)70     public CursorSubject atRow(int position) {
71         check("moveToPosition").that(mActual.moveToPosition(position)).isTrue();
72         return this;
73     }
74 
75     /** Asserts that the row at the cursor's current position has the specified values. */
hasRowValues(Object... values)76     public CursorSubject hasRowValues(Object... values) {
77         check("getColumnCount()").that(mActual.getColumnCount()).isEqualTo(values.length);
78         ContentValues expectedValues = new ContentValues();
79         for (int i = 0; i < values.length; i++) {
80             expectedValues.put(mActual.getColumnName(i), values[i].toString());
81         }
82 
83         ContentValues actualValues = new ContentValues();
84         DatabaseUtils.cursorRowToContentValues(mActual, actualValues);
85 
86         check("Row: %s", mActual.getPosition()).that(actualValues).isEqualTo(expectedValues);
87         return this;
88     }
89 
90     /** Asserts that the cursor has a single row with the specified values. */
hasSingleRow(Object... values)91     public void hasSingleRow(Object... values) {
92         hasCount(1);
93         atRow(0).hasRowValues(values);
94     }
95 
96     /**
97      * Asserts that the row at the cursor's current position has the specified value for the
98      * specified column.
99      */
hasRowValue(String columnName, Object value)100     public CursorSubject hasRowValue(String columnName, Object value) {
101         int index = mActual.getColumnIndex(columnName);
102         check("getColumnIndex()").that(index).isNotEqualTo(-1);
103 
104         check("Row[%s]: %s", columnName, index).that(mActual.getString(index))
105                 .isEqualTo(value.toString());
106         return this;
107     }
108 
109     /** Starts an assertion about the value of the specified column for the current row. */
intField(String columnName)110     public IntegerSubject intField(String columnName) {
111         int index = mActual.getColumnIndex(columnName);
112         check("getColumnIndex()").that(index).isNotEqualTo(-1);
113         check("getType()").that(mActual.getType(index)).isEqualTo(Cursor.FIELD_TYPE_INTEGER);
114 
115         return check("getInt()").that(mActual.getInt(index));
116     }
117 
118     /** Starts an assertion about the value of the specified column for the current row. */
stringField(String columnName)119     public StringSubject stringField(String columnName) {
120         int index = mActual.getColumnIndex(columnName);
121         check("getColumnIndex()").that(index).isNotEqualTo(-1);
122         check("getType()").that(mActual.getType(index)).isEqualTo(Cursor.FIELD_TYPE_STRING);
123 
124         return check("getString()").that(mActual.getString(index));
125     }
126 
127     /** Asserts that the cursor rows match the data specified. */
hasData(Object[][] rows)128     public void hasData(Object[][] rows) {
129         hasCount(rows.length);
130         for (int i = 0; i < rows.length; i++) {
131             atRow(i).hasRowValues(rows[i]);
132         }
133     }
134 
135     /** Starts an assertion about the cursor's rows. */
asLists()136     public IterableSubject asLists() {
137         List<List<String>> result = new ArrayList<>();
138         mActual.moveToPosition(-1);
139         while (mActual.moveToNext()) {
140             List<String> row = new ArrayList<>();
141             for (int i = 0; i < mActual.getColumnCount(); i++) {
142                 row.add(mActual.getString(i));
143             }
144             result.add(row);
145         }
146         return Truth.assertThat(result);
147     }
148 
149     private static class StringableCursor extends CursorWrapper {
150 
StringableCursor(Cursor cursor)151         StringableCursor(Cursor cursor) {
152             super(cursor);
153         }
154 
155         @Override
toString()156         public String toString() {
157             return DatabaseUtils.dumpCursorToString(getWrappedCursor());
158         }
159     }
160 
161 }
162