forked from LeddaZ/frameworks_base
185 lines
5.6 KiB
Python
Executable File
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:]))
|