1 /* 2 * Copyright (C) 2023 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 package com.android.adservices.data.measurement.migration; 17 18 import static com.android.adservices.common.DbTestUtil.getDbHelperForTest; 19 import static com.android.adservices.data.measurement.migration.MigrationTestHelper.populateDb; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertTrue; 23 24 import android.content.ContentValues; 25 import android.database.Cursor; 26 import android.database.sqlite.SQLiteDatabase; 27 import android.util.Pair; 28 29 import com.android.adservices.data.measurement.MeasurementDbHelper; 30 import com.android.adservices.data.measurement.MeasurementTables; 31 import com.android.adservices.service.measurement.Attribution; 32 33 import org.junit.Test; 34 import org.junit.runner.RunWith; 35 import org.mockito.junit.MockitoJUnitRunner; 36 37 import java.util.ArrayList; 38 import java.util.HashMap; 39 import java.util.LinkedHashMap; 40 import java.util.List; 41 import java.util.Map; 42 import java.util.Set; 43 import java.util.UUID; 44 45 @RunWith(MockitoJUnitRunner.class) 46 public class MeasurementDbMigratorV29Test extends MeasurementDbMigratorTestBase { 47 private static final String ID_ONE = "0001"; 48 private static final String ID_TWO = "0002"; 49 50 @Test performMigration_v28ToV29WithData_maintainsDataIntegrity()51 public void performMigration_v28ToV29WithData_maintainsDataIntegrity() { 52 // Setup 53 MeasurementDbHelper dbHelper = 54 new MeasurementDbHelper( 55 sContext, 56 MEASUREMENT_DATABASE_NAME_FOR_MIGRATION, 57 28, 58 getDbHelperForTest()); 59 SQLiteDatabase db = dbHelper.getWritableDatabase(); 60 String source1Id = UUID.randomUUID().toString(); 61 // Source and Trigger objects must be inserted first to satisfy foreign key dependencies 62 populateDb(db, createFakeDataSourceAndTriggerV28(source1Id)); 63 Pair<Map<String, List<ContentValues>>, List<ContentValues>> fakeDataPair = 64 createFakeDataV28(source1Id); 65 Map<String, List<ContentValues>> fakeData = fakeDataPair.first; 66 List<ContentValues> expectedNewRows = fakeDataPair.second; 67 populateDb(db, fakeData); 68 // Execution 69 getTestSubject().performMigration(db, 28, 29); 70 // Assertion 71 // We expect actual data to include two new rows. 72 fakeData.computeIfPresent(MeasurementTables.AttributionContract.TABLE, 73 (k, v) -> { 74 v.addAll(expectedNewRows); 75 return v; 76 }); 77 Map<String, Set<String>> columnsToBeSkipped = Map.of( 78 MeasurementTables.AttributionContract.TABLE, 79 Set.of(MeasurementTables.AttributionContract.ID)); 80 MigrationTestHelper.verifyDataInDb(db, fakeData, new HashMap<>(), columnsToBeSkipped); 81 try (Cursor cursor = 82 db.query( 83 MeasurementTables.AttributionContract.TABLE, 84 new String[] { 85 MeasurementTables.AttributionContract.ID, 86 MeasurementTables.AttributionContract.SCOPE 87 }, 88 null, 89 null, 90 null, 91 null, 92 /* orderBy */ MeasurementTables.AttributionContract.ID)) { 93 assertEquals(4, cursor.getCount()); 94 // We expect the migration to create two records of scope event and two of scope 95 // aggregate. Confirm record IDs for the first two records since they are known. 96 assertTrue(cursor.moveToPosition(0)); 97 String id1 = cursor.getString( 98 cursor.getColumnIndex(MeasurementTables.AttributionContract.ID)); 99 assertEquals(ID_ONE, id1); 100 @Attribution.Scope int scope1 = cursor.getInt( 101 cursor.getColumnIndex(MeasurementTables.AttributionContract.SCOPE)); 102 assertEquals(Attribution.Scope.EVENT, scope1); 103 104 assertTrue(cursor.moveToPosition(1)); 105 String id2 = cursor.getString( 106 cursor.getColumnIndex(MeasurementTables.AttributionContract.ID)); 107 assertEquals(ID_TWO, id2); 108 @Attribution.Scope int scope2 = cursor.getInt( 109 cursor.getColumnIndex(MeasurementTables.AttributionContract.SCOPE)); 110 assertEquals(Attribution.Scope.EVENT, scope2); 111 112 assertTrue(cursor.moveToPosition(2)); 113 @Attribution.Scope int scope3 = cursor.getInt( 114 cursor.getColumnIndex(MeasurementTables.AttributionContract.SCOPE)); 115 assertEquals(Attribution.Scope.AGGREGATE, scope3); 116 117 assertTrue(cursor.moveToPosition(3)); 118 @Attribution.Scope int scope4 = cursor.getInt( 119 cursor.getColumnIndex(MeasurementTables.AttributionContract.SCOPE)); 120 assertEquals(Attribution.Scope.AGGREGATE, scope4); 121 } 122 } 123 createFakeDataSourceAndTriggerV28(String source1Id)124 private Map<String, List<ContentValues>> createFakeDataSourceAndTriggerV28(String source1Id) { 125 Map<String, List<ContentValues>> tableRowsMap = new LinkedHashMap<>(); 126 // Source Table 127 List<ContentValues> sourceRows = new ArrayList<>(); 128 ContentValues source1 = ContentValueFixtures.generateSourceContentValuesV22(); 129 source1.put(MeasurementTables.SourceContract.ID, source1Id); 130 sourceRows.add(source1); 131 sourceRows.add(ContentValueFixtures.generateSourceContentValuesV22()); 132 tableRowsMap.put(MeasurementTables.SourceContract.TABLE, sourceRows); 133 // Trigger Table 134 List<ContentValues> triggerRows = new ArrayList<>(); 135 ContentValues trigger1 = ContentValueFixtures.generateTriggerContentValuesV21(); 136 trigger1.put(MeasurementTables.TriggerContract.ID, UUID.randomUUID().toString()); 137 triggerRows.add(trigger1); 138 triggerRows.add(ContentValueFixtures.generateTriggerContentValuesV21()); 139 tableRowsMap.put(MeasurementTables.TriggerContract.TABLE, triggerRows); 140 return tableRowsMap; 141 } 142 createFakeDataV28( String source1Id)143 private Pair<Map<String, List<ContentValues>>, List<ContentValues>> createFakeDataV28( 144 String source1Id) { 145 Map<String, List<ContentValues>> tableRowsMap = new LinkedHashMap<>(); 146 // Source Destination Table 147 List<ContentValues> sourceDestinationRows = new ArrayList<>(); 148 ContentValues sourceDestination1 = 149 ContentValueFixtures.generateSourceDestinationContentValuesV22(); 150 sourceDestination1.put(MeasurementTables.SourceDestination.SOURCE_ID, source1Id); 151 sourceDestinationRows.add(sourceDestination1); 152 sourceDestinationRows.add(ContentValueFixtures.generateSourceDestinationContentValuesV22()); 153 tableRowsMap.put(MeasurementTables.SourceDestination.TABLE, sourceDestinationRows); 154 // Attribution Table 155 List<ContentValues> attributionRows = new ArrayList<>(); 156 ContentValues attribution1 = ContentValueFixtures.generateAttributionContentValuesV28(); 157 ContentValues attribution2 = ContentValueFixtures.generateAttributionContentValuesV28(); 158 attribution1.put(MeasurementTables.AttributionContract.ID, ID_ONE); 159 attribution2.put(MeasurementTables.AttributionContract.ID, ID_TWO); 160 attributionRows.add(attribution1); 161 attributionRows.add(attribution2); 162 tableRowsMap.put(MeasurementTables.AttributionContract.TABLE, attributionRows); 163 return Pair.create( 164 tableRowsMap, 165 List.of( 166 attribution1, 167 ContentValueFixtures.generateAttributionContentValuesV28())); 168 } 169 170 @Override getTargetVersion()171 int getTargetVersion() { 172 return 29; 173 } 174 175 @Override getTestSubject()176 AbstractMeasurementDbMigrator getTestSubject() { 177 return new MeasurementDbMigratorV29(); 178 } 179 } 180