Merge "Set targetSdkVersion in manifest_fixer"
This commit is contained in:
@@ -67,6 +67,7 @@ type aapt struct {
|
|||||||
rroDirs android.Paths
|
rroDirs android.Paths
|
||||||
rTxt android.Path
|
rTxt android.Path
|
||||||
extraAaptPackagesFile android.Path
|
extraAaptPackagesFile android.Path
|
||||||
|
isLibrary bool
|
||||||
|
|
||||||
aaptProperties aaptProperties
|
aaptProperties aaptProperties
|
||||||
}
|
}
|
||||||
@@ -167,13 +168,16 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext sdkContext, ex
|
|||||||
manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
|
manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
|
||||||
manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
|
manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
|
||||||
|
|
||||||
manifestPath := manifestMerger(ctx, manifestSrcPath, sdkContext, staticLibManifests)
|
manifestPath := manifestMerger(ctx, manifestSrcPath, sdkContext, staticLibManifests, a.isLibrary)
|
||||||
|
|
||||||
linkFlags, linkDeps, resDirs, overlayDirs, rroDirs := a.aapt2Flags(ctx, sdkContext, manifestPath)
|
linkFlags, linkDeps, resDirs, overlayDirs, rroDirs := a.aapt2Flags(ctx, sdkContext, manifestPath)
|
||||||
|
|
||||||
linkFlags = append(linkFlags, libFlags...)
|
linkFlags = append(linkFlags, libFlags...)
|
||||||
linkDeps = append(linkDeps, libDeps...)
|
linkDeps = append(linkDeps, libDeps...)
|
||||||
linkFlags = append(linkFlags, extraLinkFlags...)
|
linkFlags = append(linkFlags, extraLinkFlags...)
|
||||||
|
if a.isLibrary {
|
||||||
|
linkFlags = append(linkFlags, "--static-lib")
|
||||||
|
}
|
||||||
|
|
||||||
packageRes := android.PathForModuleOut(ctx, "package-res.apk")
|
packageRes := android.PathForModuleOut(ctx, "package-res.apk")
|
||||||
srcJar := android.PathForModuleGen(ctx, "R.jar")
|
srcJar := android.PathForModuleGen(ctx, "R.jar")
|
||||||
@@ -286,7 +290,8 @@ func (a *AndroidLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||||
a.aapt.buildActions(ctx, sdkContext(a), "--static-lib")
|
a.isLibrary = true
|
||||||
|
a.aapt.buildActions(ctx, sdkContext(a))
|
||||||
|
|
||||||
ctx.CheckbuildFile(a.proguardOptionsFile)
|
ctx.CheckbuildFile(a.proguardOptionsFile)
|
||||||
ctx.CheckbuildFile(a.exportPackage)
|
ctx.CheckbuildFile(a.exportPackage)
|
||||||
|
@@ -16,6 +16,7 @@ package java
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"android/soong/java/config"
|
"android/soong/java/config"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/google/blueprint"
|
"github.com/google/blueprint"
|
||||||
|
|
||||||
@@ -24,10 +25,10 @@ import (
|
|||||||
|
|
||||||
var manifestFixerRule = pctx.AndroidStaticRule("manifestFixer",
|
var manifestFixerRule = pctx.AndroidStaticRule("manifestFixer",
|
||||||
blueprint.RuleParams{
|
blueprint.RuleParams{
|
||||||
Command: `${config.ManifestFixerCmd} --minSdkVersion ${minSdkVersion} $usesLibraries $in $out`,
|
Command: `${config.ManifestFixerCmd} --minSdkVersion ${minSdkVersion} $args $in $out`,
|
||||||
CommandDeps: []string{"${config.ManifestFixerCmd}"},
|
CommandDeps: []string{"${config.ManifestFixerCmd}"},
|
||||||
},
|
},
|
||||||
"minSdkVersion", "usesLibraries")
|
"minSdkVersion", "args")
|
||||||
|
|
||||||
var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger",
|
var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger",
|
||||||
blueprint.RuleParams{
|
blueprint.RuleParams{
|
||||||
@@ -38,7 +39,12 @@ var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger",
|
|||||||
"libs")
|
"libs")
|
||||||
|
|
||||||
func manifestMerger(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext,
|
func manifestMerger(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext,
|
||||||
staticLibManifests android.Paths) android.Path {
|
staticLibManifests android.Paths, isLibrary bool) android.Path {
|
||||||
|
|
||||||
|
var args []string
|
||||||
|
if isLibrary {
|
||||||
|
args = append(args, "--library")
|
||||||
|
}
|
||||||
|
|
||||||
// Inject minSdkVersion into the manifest
|
// Inject minSdkVersion into the manifest
|
||||||
fixedManifest := android.PathForModuleOut(ctx, "manifest_fixer", "AndroidManifest.xml")
|
fixedManifest := android.PathForModuleOut(ctx, "manifest_fixer", "AndroidManifest.xml")
|
||||||
@@ -48,6 +54,7 @@ func manifestMerger(ctx android.ModuleContext, manifest android.Path, sdkContext
|
|||||||
Output: fixedManifest,
|
Output: fixedManifest,
|
||||||
Args: map[string]string{
|
Args: map[string]string{
|
||||||
"minSdkVersion": sdkVersionOrDefault(ctx, sdkContext.minSdkVersion()),
|
"minSdkVersion": sdkVersionOrDefault(ctx, sdkContext.minSdkVersion()),
|
||||||
|
"args": strings.Join(args, " "),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
manifest = fixedManifest
|
manifest = fixedManifest
|
||||||
|
@@ -49,6 +49,8 @@ def parse_args():
|
|||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('--minSdkVersion', default='', dest='min_sdk_version',
|
parser.add_argument('--minSdkVersion', default='', dest='min_sdk_version',
|
||||||
help='specify minSdkVersion used by the build system')
|
help='specify minSdkVersion used by the build system')
|
||||||
|
parser.add_argument('--library', dest='library', action='store_true',
|
||||||
|
help='manifest is for a static library')
|
||||||
parser.add_argument('--uses-library', dest='uses_libraries', action='append',
|
parser.add_argument('--uses-library', dest='uses_libraries', action='append',
|
||||||
help='specify additional <uses-library> tag to add')
|
help='specify additional <uses-library> tag to add')
|
||||||
parser.add_argument('input', help='input AndroidManifest.xml file')
|
parser.add_argument('input', help='input AndroidManifest.xml file')
|
||||||
@@ -126,7 +128,7 @@ def get_indent(element, default_level):
|
|||||||
return indent
|
return indent
|
||||||
|
|
||||||
|
|
||||||
def raise_min_sdk_version(doc, requested):
|
def raise_min_sdk_version(doc, requested, library):
|
||||||
"""Ensure the manifest contains a <uses-sdk> tag with a minSdkVersion.
|
"""Ensure the manifest contains a <uses-sdk> tag with a minSdkVersion.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -153,16 +155,27 @@ def raise_min_sdk_version(doc, requested):
|
|||||||
# other children of the <manifest> tag.
|
# other children of the <manifest> tag.
|
||||||
manifest.insertBefore(doc.createTextNode(indent), manifest.firstChild)
|
manifest.insertBefore(doc.createTextNode(indent), manifest.firstChild)
|
||||||
|
|
||||||
# Get or insert the minSdkVersion attribute
|
# Get or insert the minSdkVersion attribute. If it is already present, make
|
||||||
|
# sure it as least the requested value.
|
||||||
min_attr = element.getAttributeNodeNS(android_ns, 'minSdkVersion')
|
min_attr = element.getAttributeNodeNS(android_ns, 'minSdkVersion')
|
||||||
if min_attr is None:
|
if min_attr is None:
|
||||||
min_attr = doc.createAttributeNS(android_ns, 'android:minSdkVersion')
|
min_attr = doc.createAttributeNS(android_ns, 'android:minSdkVersion')
|
||||||
min_attr.value = '1'
|
|
||||||
element.setAttributeNode(min_attr)
|
|
||||||
|
|
||||||
# Update the value of the minSdkVersion attribute if necessary
|
|
||||||
if compare_version_gt(requested, min_attr.value):
|
|
||||||
min_attr.value = requested
|
min_attr.value = requested
|
||||||
|
element.setAttributeNode(min_attr)
|
||||||
|
else:
|
||||||
|
if compare_version_gt(requested, min_attr.value):
|
||||||
|
min_attr.value = requested
|
||||||
|
|
||||||
|
# Insert the targetSdkVersion attribute if it is missing. If it is already
|
||||||
|
# present leave it as is.
|
||||||
|
target_attr = element.getAttributeNodeNS(android_ns, 'targetSdkVersion')
|
||||||
|
if target_attr is None:
|
||||||
|
target_attr = doc.createAttributeNS(android_ns, 'android:targetSdkVersion')
|
||||||
|
if library:
|
||||||
|
target_attr.value = '1'
|
||||||
|
else:
|
||||||
|
target_attr.value = requested
|
||||||
|
element.setAttributeNode(target_attr)
|
||||||
|
|
||||||
|
|
||||||
def add_uses_libraries(doc, new_uses_libraries):
|
def add_uses_libraries(doc, new_uses_libraries):
|
||||||
@@ -230,7 +243,7 @@ def main():
|
|||||||
ensure_manifest_android_ns(doc)
|
ensure_manifest_android_ns(doc)
|
||||||
|
|
||||||
if args.min_sdk_version:
|
if args.min_sdk_version:
|
||||||
raise_min_sdk_version(doc, args.min_sdk_version)
|
raise_min_sdk_version(doc, args.min_sdk_version, args.library)
|
||||||
|
|
||||||
if args.uses_libraries:
|
if args.uses_libraries:
|
||||||
add_uses_libraries(doc, args.uses_libraries)
|
add_uses_libraries(doc, args.uses_libraries)
|
||||||
|
@@ -54,9 +54,9 @@ class CompareVersionGtTest(unittest.TestCase):
|
|||||||
class RaiseMinSdkVersionTest(unittest.TestCase):
|
class RaiseMinSdkVersionTest(unittest.TestCase):
|
||||||
"""Unit tests for raise_min_sdk_version function."""
|
"""Unit tests for raise_min_sdk_version function."""
|
||||||
|
|
||||||
def raise_min_sdk_version_test(self, input_manifest, min_sdk_version):
|
def raise_min_sdk_version_test(self, input_manifest, min_sdk_version, library):
|
||||||
doc = minidom.parseString(input_manifest)
|
doc = minidom.parseString(input_manifest)
|
||||||
manifest_fixer.raise_min_sdk_version(doc, min_sdk_version)
|
manifest_fixer.raise_min_sdk_version(doc, min_sdk_version, library)
|
||||||
output = StringIO.StringIO()
|
output = StringIO.StringIO()
|
||||||
manifest_fixer.write_xml(output, doc)
|
manifest_fixer.write_xml(output, doc)
|
||||||
return output.getvalue()
|
return output.getvalue()
|
||||||
@@ -67,67 +67,130 @@ class RaiseMinSdkVersionTest(unittest.TestCase):
|
|||||||
'%s'
|
'%s'
|
||||||
'</manifest>\n')
|
'</manifest>\n')
|
||||||
|
|
||||||
def uses_sdk(self, v, extra=''):
|
# pylint: disable=redefined-builtin
|
||||||
|
def uses_sdk(self, min=None, target=None, extra=''):
|
||||||
|
attrs = ""
|
||||||
|
if min:
|
||||||
|
attrs += ' android:minSdkVersion="%s"' % (min)
|
||||||
|
if target:
|
||||||
|
attrs += ' android:targetSdkVersion="%s"' % (target)
|
||||||
if extra:
|
if extra:
|
||||||
extra = ' ' + extra
|
attrs += ' ' + extra
|
||||||
return ' <uses-sdk android:minSdkVersion="%s"%s/>\n' % (v, extra)
|
return ' <uses-sdk%s/>\n' % (attrs)
|
||||||
|
|
||||||
def test_no_uses_sdk(self):
|
def test_no_uses_sdk(self):
|
||||||
"""Tests inserting a uses-sdk element into a manifest."""
|
"""Tests inserting a uses-sdk element into a manifest."""
|
||||||
|
|
||||||
manifest_input = self.manifest_tmpl % ''
|
manifest_input = self.manifest_tmpl % ''
|
||||||
expected = self.manifest_tmpl % self.uses_sdk('28')
|
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
|
||||||
output = self.raise_min_sdk_version_test(manifest_input, '28')
|
output = self.raise_min_sdk_version_test(manifest_input, '28', False)
|
||||||
self.assertEqual(output, expected)
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
def test_no_min(self):
|
def test_no_min(self):
|
||||||
"""Tests inserting a minSdkVersion attribute into a uses-sdk element."""
|
"""Tests inserting a minSdkVersion attribute into a uses-sdk element."""
|
||||||
|
|
||||||
manifest_input = self.manifest_tmpl % ' <uses-sdk extra="foo"/>\n'
|
manifest_input = self.manifest_tmpl % ' <uses-sdk extra="foo"/>\n'
|
||||||
expected = self.manifest_tmpl % self.uses_sdk('28', 'extra="foo"')
|
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28',
|
||||||
output = self.raise_min_sdk_version_test(manifest_input, '28')
|
extra='extra="foo"')
|
||||||
|
output = self.raise_min_sdk_version_test(manifest_input, '28', False)
|
||||||
self.assertEqual(output, expected)
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
def test_raise_min(self):
|
def test_raise_min(self):
|
||||||
"""Tests inserting a minSdkVersion attribute into a uses-sdk element."""
|
"""Tests inserting a minSdkVersion attribute into a uses-sdk element."""
|
||||||
|
|
||||||
manifest_input = self.manifest_tmpl % self.uses_sdk('27')
|
manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
|
||||||
expected = self.manifest_tmpl % self.uses_sdk('28')
|
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
|
||||||
output = self.raise_min_sdk_version_test(manifest_input, '28')
|
output = self.raise_min_sdk_version_test(manifest_input, '28', False)
|
||||||
self.assertEqual(output, expected)
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
def test_raise(self):
|
def test_raise(self):
|
||||||
"""Tests raising a minSdkVersion attribute."""
|
"""Tests raising a minSdkVersion attribute."""
|
||||||
|
|
||||||
manifest_input = self.manifest_tmpl % self.uses_sdk('27')
|
manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
|
||||||
expected = self.manifest_tmpl % self.uses_sdk('28')
|
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
|
||||||
output = self.raise_min_sdk_version_test(manifest_input, '28')
|
output = self.raise_min_sdk_version_test(manifest_input, '28', False)
|
||||||
self.assertEqual(output, expected)
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
def test_no_raise_min(self):
|
def test_no_raise_min(self):
|
||||||
"""Tests a minSdkVersion that doesn't need raising."""
|
"""Tests a minSdkVersion that doesn't need raising."""
|
||||||
|
|
||||||
manifest_input = self.manifest_tmpl % self.uses_sdk('28')
|
manifest_input = self.manifest_tmpl % self.uses_sdk(min='28')
|
||||||
expected = manifest_input
|
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
|
||||||
output = self.raise_min_sdk_version_test(manifest_input, '27')
|
output = self.raise_min_sdk_version_test(manifest_input, '27', False)
|
||||||
self.assertEqual(output, expected)
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
def test_raise_codename(self):
|
def test_raise_codename(self):
|
||||||
"""Tests raising a minSdkVersion attribute to a codename."""
|
"""Tests raising a minSdkVersion attribute to a codename."""
|
||||||
|
|
||||||
manifest_input = self.manifest_tmpl % self.uses_sdk('28')
|
manifest_input = self.manifest_tmpl % self.uses_sdk(min='28')
|
||||||
expected = self.manifest_tmpl % self.uses_sdk('P')
|
expected = self.manifest_tmpl % self.uses_sdk(min='P', target='28')
|
||||||
output = self.raise_min_sdk_version_test(manifest_input, 'P')
|
output = self.raise_min_sdk_version_test(manifest_input, 'P', False)
|
||||||
self.assertEqual(output, expected)
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
def test_no_raise_codename(self):
|
def test_no_raise_codename(self):
|
||||||
"""Tests a minSdkVersion codename that doesn't need raising."""
|
"""Tests a minSdkVersion codename that doesn't need raising."""
|
||||||
|
|
||||||
manifest_input = self.manifest_tmpl % self.uses_sdk('P')
|
manifest_input = self.manifest_tmpl % self.uses_sdk(min='P')
|
||||||
expected = manifest_input
|
expected = self.manifest_tmpl % self.uses_sdk(min='P', target='P')
|
||||||
output = self.raise_min_sdk_version_test(manifest_input, '28')
|
output = self.raise_min_sdk_version_test(manifest_input, '28', False)
|
||||||
self.assertEqual(output, expected)
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
def test_target(self):
|
||||||
|
"""Tests an existing targetSdkVersion is preserved."""
|
||||||
|
|
||||||
|
manifest_input = self.manifest_tmpl % self.uses_sdk(min='26', target='27')
|
||||||
|
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
|
||||||
|
output = self.raise_min_sdk_version_test(manifest_input, '28', False)
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
def test_no_target(self):
|
||||||
|
"""Tests inserting targetSdkVersion when minSdkVersion exists."""
|
||||||
|
|
||||||
|
manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
|
||||||
|
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
|
||||||
|
output = self.raise_min_sdk_version_test(manifest_input, '28', False)
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
def test_target_no_min(self):
|
||||||
|
"""Tests inserting targetSdkVersion when minSdkVersion exists."""
|
||||||
|
|
||||||
|
manifest_input = self.manifest_tmpl % self.uses_sdk(target='27')
|
||||||
|
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
|
||||||
|
output = self.raise_min_sdk_version_test(manifest_input, '28', False)
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
def test_no_target_no_min(self):
|
||||||
|
"""Tests inserting targetSdkVersion when minSdkVersion does not exist."""
|
||||||
|
|
||||||
|
manifest_input = self.manifest_tmpl % ''
|
||||||
|
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
|
||||||
|
output = self.raise_min_sdk_version_test(manifest_input, '28', False)
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
def test_library_no_target(self):
|
||||||
|
"""Tests inserting targetSdkVersion when minSdkVersion exists."""
|
||||||
|
|
||||||
|
manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
|
||||||
|
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
|
||||||
|
output = self.raise_min_sdk_version_test(manifest_input, '28', True)
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
def test_library_target_no_min(self):
|
||||||
|
"""Tests inserting targetSdkVersion when minSdkVersion exists."""
|
||||||
|
|
||||||
|
manifest_input = self.manifest_tmpl % self.uses_sdk(target='27')
|
||||||
|
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
|
||||||
|
output = self.raise_min_sdk_version_test(manifest_input, '28', True)
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
def test_library_no_target_no_min(self):
|
||||||
|
"""Tests inserting targetSdkVersion when minSdkVersion does not exist."""
|
||||||
|
|
||||||
|
manifest_input = self.manifest_tmpl % ''
|
||||||
|
expected = self.manifest_tmpl % self.uses_sdk(min='28', target='1')
|
||||||
|
output = self.raise_min_sdk_version_test(manifest_input, '28', True)
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
def test_extra(self):
|
def test_extra(self):
|
||||||
"""Tests that extra attributes and elements are maintained."""
|
"""Tests that extra attributes and elements are maintained."""
|
||||||
|
|
||||||
@@ -136,12 +199,13 @@ class RaiseMinSdkVersionTest(unittest.TestCase):
|
|||||||
' <uses-sdk android:minSdkVersion="27" extra="foo"/>\n'
|
' <uses-sdk android:minSdkVersion="27" extra="foo"/>\n'
|
||||||
' <application/>\n')
|
' <application/>\n')
|
||||||
|
|
||||||
|
# pylint: disable=line-too-long
|
||||||
expected = self.manifest_tmpl % (
|
expected = self.manifest_tmpl % (
|
||||||
' <!-- comment -->\n'
|
' <!-- comment -->\n'
|
||||||
' <uses-sdk android:minSdkVersion="28" extra="foo"/>\n'
|
' <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="27" extra="foo"/>\n'
|
||||||
' <application/>\n')
|
' <application/>\n')
|
||||||
|
|
||||||
output = self.raise_min_sdk_version_test(manifest_input, '28')
|
output = self.raise_min_sdk_version_test(manifest_input, '28', False)
|
||||||
|
|
||||||
self.assertEqual(output, expected)
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
@@ -150,11 +214,12 @@ class RaiseMinSdkVersionTest(unittest.TestCase):
|
|||||||
|
|
||||||
manifest_input = self.manifest_tmpl % ' <!-- comment -->\n'
|
manifest_input = self.manifest_tmpl % ' <!-- comment -->\n'
|
||||||
|
|
||||||
|
# pylint: disable=line-too-long
|
||||||
expected = self.manifest_tmpl % (
|
expected = self.manifest_tmpl % (
|
||||||
' <uses-sdk android:minSdkVersion="28"/>\n'
|
' <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28"/>\n'
|
||||||
' <!-- comment -->\n')
|
' <!-- comment -->\n')
|
||||||
|
|
||||||
output = self.raise_min_sdk_version_test(manifest_input, '28')
|
output = self.raise_min_sdk_version_test(manifest_input, '28', False)
|
||||||
|
|
||||||
self.assertEqual(output, expected)
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user