1 /* 2 * Copyright 2017 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.tools.build.jetifier.processor.transform.bytecode 18 19 import com.android.tools.build.jetifier.core.config.Config 20 import com.android.tools.build.jetifier.core.rule.RewriteRule 21 import com.android.tools.build.jetifier.core.rule.RewriteRulesMap 22 import com.android.tools.build.jetifier.core.type.JavaType 23 import com.android.tools.build.jetifier.core.type.TypesMap 24 import com.android.tools.build.jetifier.processor.FileMapping 25 import com.android.tools.build.jetifier.processor.Processor 26 import com.android.tools.build.jetifier.processor.archive.Archive 27 import com.android.tools.build.jetifier.processor.archive.ArchiveFile 28 import com.android.tools.build.jetifier.processor.archive.ArchiveItemVisitor 29 import com.google.common.truth.Truth 30 import org.junit.Test 31 import java.io.File 32 33 /** 34 * Tests that individual files were moved properly due to their owner types rewrites. 35 */ 36 class ClassFilesMoveTest { 37 38 companion object { 39 private val TEST_CONFIG = Config.fromOptional( 40 restrictToPackagePrefixes = setOf("android/support"), 41 reversedRestrictToPackagesPrefixes = setOf("androidx"), 42 rulesMap = RewriteRulesMap( 43 RewriteRule("android/support/annotation/(.*)", "ignore"), 44 RewriteRule("android/support/v7/preference/R(.*)", "ignore"), 45 RewriteRule("android/support/v4/(.*)", "ignore") 46 ), 47 slRules = listOf( 48 RewriteRule("android/support/annotation/(.*)", "ignore"), 49 RewriteRule("android/support/v7/preference/R(.*)", "ignore"), 50 RewriteRule("android/support/v4/(.*)", "ignore") 51 ), 52 typesMap = TypesMap(mapOf( 53 "android/support/v7/preference/Preference" 54 to "androidx/support/preference/Preference", 55 "android/support/v7/preference/TwoStatePreference" 56 to "androidx/support/preference/TwoStatePreference", 57 "android/support/v7/preference/PreferenceGroup" 58 to "androidx/support/preference/PreferenceGroup", 59 "android/support/v7/preference/PreferenceViewHolder" 60 to "androidx/support/preference/PreferenceViewHolder", 61 "android/support/v7/preference/PreferenceManager" 62 to "androidx/support/preference/PreferenceManager", 63 "android/support/v14/preference/SwitchPreference" 64 to "androidx/support/preference/SwitchPreference", 65 "android/support/v7/preference/PreferenceDataStore" 66 to "androidx/support/preference/PreferenceDataStore" 67 ).map { JavaType(it.key) to JavaType(it.value) }.toMap()) 68 ) 69 } 70 71 /** 72 * Tests that after rewrite of a input archive the internal classes are properly moved to new 73 * locations (based on the rewrite rules) which is compared with the expected archive. 74 * 75 * Note: The expected archive does not contain rewritten classes - they were only manually 76 * moved. Which is fine because this test validates only files locations. 77 */ 78 @Test fun fileMove_forwardRewrite_shouldMoveFilesProperly() { 79 val inputZipPath = "/fileRenameTest/inputTestLib.zip" 80 val expectedZipPath = "/fileRenameTest/expectedTestLib.zip" 81 82 val processor = Processor.createProcessor(TEST_CONFIG) 83 val inputFile = File(javaClass.getResource(inputZipPath).file) 84 85 val tempDir = createTempDir() 86 val expectedFile = File(createTempDir(), "test.zip") 87 88 val resultFiles = processor.transform(setOf(FileMapping(inputFile, expectedFile))) 89 90 Truth.assertThat(resultFiles).hasSize(1) 91 testArchivesAreSame(resultFiles.first(), File(javaClass.getResource(expectedZipPath).file)) 92 93 tempDir.delete() 94 } 95 96 /** 97 * Does exactly the same as [fileMove_forwardRewrite_nestedArchive_shouldMoveFilesProperly] but 98 * the files are in a nested archive e.g. archive.zip/classes.jar/some files. 99 */ 100 @Test fun fileMove_forwardRewrite_nestedArchive_shouldMoveFilesProperly() { 101 val inputZipPath = "/fileRenameTest/inputTestLibNested.zip" 102 val expectedZipPath = "/fileRenameTest/expectedTestLibNested.zip" 103 104 val processor = Processor.createProcessor(TEST_CONFIG) 105 val inputFile = File(javaClass.getResource(inputZipPath).file) 106 107 val tempDir = createTempDir() 108 val expectedFile = File(createTempDir(), "test.zip") 109 110 val resultFiles = processor.transform(setOf(FileMapping(inputFile, expectedFile))) 111 112 Truth.assertThat(resultFiles).hasSize(1) 113 testArchivesAreSame(resultFiles.first(), File(javaClass.getResource(expectedZipPath).file)) 114 115 tempDir.delete() 116 } 117 118 /** 119 * Rewrites the input archive and then applies reversed mode to rewrite it back. The final 120 * produced archive has to have the same directory structure as the input one. 121 */ 122 @Test fun fileMove_forwardRewrite_backwardsRewrite_shouldKeepFilesProperly() { 123 val inputZipPath = "/fileRenameTest/inputTestLib.zip" 124 125 // Transform forward 126 val processor = Processor.createProcessor(TEST_CONFIG) 127 val inputFile = File(javaClass.getResource(inputZipPath).file) 128 129 val tempDir = createTempDir() 130 val expectedFile = File(createTempDir(), "test.zip") 131 132 val resultFiles = processor.transform(setOf(FileMapping(inputFile, expectedFile))) 133 134 // Take previous result & reverse it 135 val processor2 = Processor.createProcessor( 136 TEST_CONFIG, 137 rewritingSupportLib = true, 138 reversedMode = true) 139 val expectedFile2 = File(createTempDir(), "test2.zip") 140 val resultFiles2 = processor2.transform(setOf( 141 FileMapping(resultFiles.first(), expectedFile2))) 142 143 testArchivesAreSame(resultFiles2.first(), File(javaClass.getResource(inputZipPath).file)) 144 145 tempDir.delete() 146 } 147 148 fun testArchivesAreSame(givenZip: File, expectedZip: File) { 149 testArchivesAreSame(Archive.Builder.extract(givenZip), Archive.Builder.extract(expectedZip)) 150 } 151 152 fun testArchivesAreSame(givenZip: Archive, expectedZip: Archive) { 153 val givenFiles = ArchiveBrowser.grabAllPathsIn(givenZip) 154 val expectedFiles = ArchiveBrowser.grabAllPathsIn(expectedZip) 155 Truth.assertThat(givenFiles).containsExactlyElementsIn(expectedFiles) 156 } 157 158 /** 159 * Just a helper utility to get all file paths in the archive. 160 */ 161 class ArchiveBrowser : ArchiveItemVisitor { 162 163 companion object { 164 fun grabAllPathsIn(archive: Archive): MutableSet<String> { 165 val grabber = ArchiveBrowser() 166 archive.accept(grabber) 167 return grabber.allPaths 168 } 169 } 170 171 val allPaths = mutableSetOf<String>() 172 173 override fun visit(archiveFile: ArchiveFile) { 174 allPaths.add(archiveFile.relativePath.toString()) 175 println("Visited ${archiveFile.relativePath}") 176 } 177 178 override fun visit(archive: Archive) { 179 archive.files.forEach { it.accept(this) } 180 } 181 } 182 } 183