3
0
Fork 0
android_frameworks_base/ravenwood/scripts/convert-androidtest.py

185 lines
5.6 KiB
Python
Executable File

#!/usr/bin/python3
# Copyright (C) 2024 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This script converts a legacy test class (using AndroidTestCase, TestCase or
# InstrumentationTestCase to a modern style test class, in a best-effort manner.
#
# Usage:
# convert-androidtest.py TARGET-FILE [TARGET-FILE ...]
#
# Caveats:
# - It adds all the extra imports, even if they're not needed.
# - It won't sort imports.
# - It also always adds getContext() and getTestContext().
#
import sys
import fileinput
import re
import subprocess
# Print message on console
def log(msg):
print(msg, file=sys.stderr)
# Matches `extends AndroidTestCase` (or another similar base class)
re_extends = re.compile(
r''' \b extends \s+ (AndroidTestCase|TestCase|InstrumentationTestCase) \s* ''',
re.S + re.X)
# Look into given files and return the files that have `re_extends`.
def find_target_files(files):
ret = []
for file in files:
try:
with open(file, 'r') as f:
data = f.read()
if re_extends.search(data):
ret.append(file)
except FileNotFoundError as e:
log(f'Failed to open file {file}: {e}')
return ret
def main(args):
files = args
# Find the files that should be processed.
files = find_target_files(files)
if len(files) == 0:
log("No target files found.")
return 0
# Process the files.
with fileinput.input(files=(files), inplace = True, backup = '.bak') as f:
import_seen = False
carry_over = ''
class_body_started = False
class_seen = False
def on_file_start():
nonlocal import_seen, carry_over, class_body_started, class_seen
import_seen = False
carry_over = ''
class_body_started = False
class_seen = False
for line in f:
if (fileinput.filelineno() == 1):
log(f"Processing: {fileinput.filename()}")
on_file_start()
line = line.rstrip('\n')
# Carry over a certain line to the next line.
if re.search(r'''@Override\b''', line):
carry_over = carry_over + line + '\n'
continue
if carry_over:
line = carry_over + line
carry_over = ''
# Remove the base class from the class definition.
line = re_extends.sub('', line)
# Add a @RunWith.
if not class_seen and re.search(r'''\b class \b''', line, re.X):
class_seen = True
print("@RunWith(AndroidJUnit4.class)")
# Inject extra imports.
if not import_seen and re.search(r'''^import\b''', line):
import_seen = True
print("""\
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertSame;
import static junit.framework.TestCase.assertNotSame;
import static junit.framework.TestCase.assertTrue;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.fail;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.junit.Test;
import androidx.test.ext.junit.runners.AndroidJUnit4;
""")
# Add @Test to the test methods.
if re.search(r'''^ \s* public \s* void \s* test''', line, re.X):
print(" @Test")
# Convert setUp/tearDown to @Before/@After.
if re.search(r''' ^\s+ ( \@Override \s+ ) ? (public|protected) \s+ void \s+ (setUp|tearDown) ''',
line, re.X):
if re.search('setUp', line):
print(' @Before')
else:
print(' @After')
line = re.sub(r''' \s* \@Override \s* \n ''', '', line, 0, re.X)
line = re.sub(r'''protected''', 'public', line, 0, re.X)
# Remove the super setUp / tearDown call.
if re.search(r''' \b super \. (setUp|tearDown) \b ''', line, re.X):
continue
# Convert mContext to getContext().
line = re.sub(r'''\b mContext \b ''', 'getContext()', line, 0, re.X)
# Print the processed line.
print(line)
# Add getContext() / getTestContext() at the beginning of the class.
if not class_body_started and re.search(r'''\{''', line):
class_body_started = True
print("""\
private Context getContext() {
return InstrumentationRegistry.getInstrumentation().getTargetContext();
}
private Context getTestContext() {
return InstrumentationRegistry.getInstrumentation().getContext();
}
""")
# Run diff
for file in files:
subprocess.call(["diff", "-u", "--color=auto", f"{file}.bak", file])
log(f'{len(files)} file(s) converted.')
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))