Unverified Commit 41540110 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Gradle artifacts and tasks tests without context (#56335)

parent 06fcf514
...@@ -36,68 +36,74 @@ void main() { ...@@ -36,68 +36,74 @@ void main() {
Cache.flutterRoot = getFlutterRoot(); Cache.flutterRoot = getFlutterRoot();
group('build artifacts', () { group('build artifacts', () {
test('getApkDirectory in app projects', () { FileSystem fileSystem;
setUp(() {
fileSystem = MemoryFileSystem.test();
});
testWithoutContext('getApkDirectory in app projects', () {
final FlutterProject project = MockFlutterProject(); final FlutterProject project = MockFlutterProject();
final AndroidProject androidProject = MockAndroidProject(); final AndroidProject androidProject = MockAndroidProject();
when(project.android).thenReturn(androidProject); when(project.android).thenReturn(androidProject);
when(project.isModule).thenReturn(false); when(project.isModule).thenReturn(false);
when(androidProject.buildDirectory).thenReturn(globals.fs.directory('foo')); when(androidProject.buildDirectory).thenReturn(fileSystem.directory('foo'));
expect( expect(
getApkDirectory(project).path, getApkDirectory(project).path,
equals(globals.fs.path.join('foo', 'app', 'outputs', 'flutter-apk')), equals(fileSystem.path.join('foo', 'app', 'outputs', 'flutter-apk')),
); );
}); });
test('getApkDirectory in module projects', () { testWithoutContext('getApkDirectory in module projects', () {
final FlutterProject project = MockFlutterProject(); final FlutterProject project = MockFlutterProject();
final AndroidProject androidProject = MockAndroidProject(); final AndroidProject androidProject = MockAndroidProject();
when(project.android).thenReturn(androidProject); when(project.android).thenReturn(androidProject);
when(project.isModule).thenReturn(true); when(project.isModule).thenReturn(true);
when(androidProject.buildDirectory).thenReturn(globals.fs.directory('foo')); when(androidProject.buildDirectory).thenReturn(fileSystem.directory('foo'));
expect( expect(
getApkDirectory(project).path, getApkDirectory(project).path,
equals(globals.fs.path.join('foo', 'host', 'outputs', 'apk')), equals(fileSystem.path.join('foo', 'host', 'outputs', 'apk')),
); );
}); });
test('getBundleDirectory in app projects', () { testWithoutContext('getBundleDirectory in app projects', () {
final FlutterProject project = MockFlutterProject(); final FlutterProject project = MockFlutterProject();
final AndroidProject androidProject = MockAndroidProject(); final AndroidProject androidProject = MockAndroidProject();
when(project.android).thenReturn(androidProject); when(project.android).thenReturn(androidProject);
when(project.isModule).thenReturn(false); when(project.isModule).thenReturn(false);
when(androidProject.buildDirectory).thenReturn(globals.fs.directory('foo')); when(androidProject.buildDirectory).thenReturn(fileSystem.directory('foo'));
expect( expect(
getBundleDirectory(project).path, getBundleDirectory(project).path,
equals(globals.fs.path.join('foo', 'app', 'outputs', 'bundle')), equals(fileSystem.path.join('foo', 'app', 'outputs', 'bundle')),
); );
}); });
test('getBundleDirectory in module projects', () { testWithoutContext('getBundleDirectory in module projects', () {
final FlutterProject project = MockFlutterProject(); final FlutterProject project = MockFlutterProject();
final AndroidProject androidProject = MockAndroidProject(); final AndroidProject androidProject = MockAndroidProject();
when(project.android).thenReturn(androidProject); when(project.android).thenReturn(androidProject);
when(project.isModule).thenReturn(true); when(project.isModule).thenReturn(true);
when(androidProject.buildDirectory).thenReturn(globals.fs.directory('foo')); when(androidProject.buildDirectory).thenReturn(fileSystem.directory('foo'));
expect( expect(
getBundleDirectory(project).path, getBundleDirectory(project).path,
equals(globals.fs.path.join('foo', 'host', 'outputs', 'bundle')), equals(fileSystem.path.join('foo', 'host', 'outputs', 'bundle')),
); );
}); });
test('getRepoDirectory', () { testWithoutContext('getRepoDirectory', () {
expect( expect(
getRepoDirectory(globals.fs.directory('foo')).path, getRepoDirectory(fileSystem.directory('foo')).path,
equals(globals.fs.path.join('foo','outputs', 'repo')), equals(fileSystem.path.join('foo','outputs', 'repo')),
); );
}); });
}); });
group('gradle tasks', () { group('gradle tasks', () {
test('assemble release', () { testWithoutContext('assemble release', () {
expect( expect(
getAssembleTaskFor(const BuildInfo(BuildMode.release, null, treeShakeIcons: false)), getAssembleTaskFor(const BuildInfo(BuildMode.release, null, treeShakeIcons: false)),
equals('assembleRelease'), equals('assembleRelease'),
...@@ -108,7 +114,7 @@ void main() { ...@@ -108,7 +114,7 @@ void main() {
); );
}); });
test('assemble debug', () { testWithoutContext('assemble debug', () {
expect( expect(
getAssembleTaskFor(const BuildInfo(BuildMode.debug, null, treeShakeIcons: false)), getAssembleTaskFor(const BuildInfo(BuildMode.debug, null, treeShakeIcons: false)),
equals('assembleDebug'), equals('assembleDebug'),
...@@ -119,7 +125,7 @@ void main() { ...@@ -119,7 +125,7 @@ void main() {
); );
}); });
test('assemble profile', () { testWithoutContext('assemble profile', () {
expect( expect(
getAssembleTaskFor(const BuildInfo(BuildMode.profile, null, treeShakeIcons: false)), getAssembleTaskFor(const BuildInfo(BuildMode.profile, null, treeShakeIcons: false)),
equals('assembleProfile'), equals('assembleProfile'),
...@@ -132,156 +138,117 @@ void main() { ...@@ -132,156 +138,117 @@ void main() {
}); });
group('findBundleFile', () { group('findBundleFile', () {
final Usage mockUsage = MockUsage(); FileSystem fileSystem;
Usage mockUsage;
testUsingContext('Finds app bundle when flavor contains underscores in release mode', () { setUp(() {
final FlutterProject project = generateFakeAppBundle('foo_barRelease', 'app.aab'); fileSystem = MemoryFileSystem.test();
mockUsage = MockUsage();
});
testWithoutContext('Finds app bundle when flavor contains underscores in release mode', () {
final FlutterProject project = generateFakeAppBundle('foo_barRelease', 'app.aab', fileSystem);
final File bundle = findBundleFile(project, const BuildInfo(BuildMode.release, 'foo_bar', treeShakeIcons: false)); final File bundle = findBundleFile(project, const BuildInfo(BuildMode.release, 'foo_bar', treeShakeIcons: false));
expect(bundle, isNotNull); expect(bundle, isNotNull);
expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'foo_barRelease', 'app.aab')); expect(bundle.path, fileSystem.path.join('irrelevant', 'app', 'outputs', 'bundle', 'foo_barRelease', 'app.aab'));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext("Finds app bundle when flavor doesn't contain underscores in release mode", () { testWithoutContext("Finds app bundle when flavor doesn't contain underscores in release mode", () {
final FlutterProject project = generateFakeAppBundle('fooRelease', 'app.aab'); final FlutterProject project = generateFakeAppBundle('fooRelease', 'app.aab', fileSystem);
final File bundle = findBundleFile(project, const BuildInfo(BuildMode.release, 'foo', treeShakeIcons: false)); final File bundle = findBundleFile(project, const BuildInfo(BuildMode.release, 'foo', treeShakeIcons: false));
expect(bundle, isNotNull); expect(bundle, isNotNull);
expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'fooRelease', 'app.aab')); expect(bundle.path, fileSystem.path.join('irrelevant', 'app', 'outputs', 'bundle', 'fooRelease', 'app.aab'));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext('Finds app bundle when no flavor is used in release mode', () { testWithoutContext('Finds app bundle when no flavor is used in release mode', () {
final FlutterProject project = generateFakeAppBundle('release', 'app.aab'); final FlutterProject project = generateFakeAppBundle('release', 'app.aab', fileSystem);
final File bundle = findBundleFile(project, const BuildInfo(BuildMode.release, null, treeShakeIcons: false)); final File bundle = findBundleFile(project, const BuildInfo(BuildMode.release, null, treeShakeIcons: false));
expect(bundle, isNotNull); expect(bundle, isNotNull);
expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'release', 'app.aab')); expect(bundle.path, fileSystem.path.join('irrelevant', 'app', 'outputs', 'bundle', 'release', 'app.aab'));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext('Finds app bundle when flavor contains underscores in debug mode', () { testWithoutContext('Finds app bundle when flavor contains underscores in debug mode', () {
final FlutterProject project = generateFakeAppBundle('foo_barDebug', 'app.aab'); final FlutterProject project = generateFakeAppBundle('foo_barDebug', 'app.aab', fileSystem);
final File bundle = findBundleFile(project, const BuildInfo(BuildMode.debug, 'foo_bar', treeShakeIcons: false)); final File bundle = findBundleFile(project, const BuildInfo(BuildMode.debug, 'foo_bar', treeShakeIcons: false));
expect(bundle, isNotNull); expect(bundle, isNotNull);
expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'foo_barDebug', 'app.aab')); expect(bundle.path, fileSystem.path.join('irrelevant', 'app', 'outputs', 'bundle', 'foo_barDebug', 'app.aab'));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext("Finds app bundle when flavor doesn't contain underscores in debug mode", () { testWithoutContext("Finds app bundle when flavor doesn't contain underscores in debug mode", () {
final FlutterProject project = generateFakeAppBundle('fooDebug', 'app.aab'); final FlutterProject project = generateFakeAppBundle('fooDebug', 'app.aab', fileSystem);
final File bundle = findBundleFile(project, const BuildInfo(BuildMode.debug, 'foo', treeShakeIcons: false)); final File bundle = findBundleFile(project, const BuildInfo(BuildMode.debug, 'foo', treeShakeIcons: false));
expect(bundle, isNotNull); expect(bundle, isNotNull);
expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'fooDebug', 'app.aab')); expect(bundle.path, fileSystem.path.join('irrelevant', 'app', 'outputs', 'bundle', 'fooDebug', 'app.aab'));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext('Finds app bundle when no flavor is used in debug mode', () { testWithoutContext('Finds app bundle when no flavor is used in debug mode', () {
final FlutterProject project = generateFakeAppBundle('debug', 'app.aab'); final FlutterProject project = generateFakeAppBundle('debug', 'app.aab', fileSystem);
final File bundle = findBundleFile(project, const BuildInfo(BuildMode.debug, null, treeShakeIcons: false)); final File bundle = findBundleFile(project, const BuildInfo(BuildMode.debug, null, treeShakeIcons: false));
expect(bundle, isNotNull); expect(bundle, isNotNull);
expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'debug', 'app.aab')); expect(bundle.path, fileSystem.path.join('irrelevant', 'app', 'outputs', 'bundle', 'debug', 'app.aab'));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext('Finds app bundle when flavor contains underscores in profile mode', () { testWithoutContext('Finds app bundle when flavor contains underscores in profile mode', () {
final FlutterProject project = generateFakeAppBundle('foo_barProfile', 'app.aab'); final FlutterProject project = generateFakeAppBundle('foo_barProfile', 'app.aab', fileSystem);
final File bundle = findBundleFile(project, const BuildInfo(BuildMode.profile, 'foo_bar', treeShakeIcons: false)); final File bundle = findBundleFile(project, const BuildInfo(BuildMode.profile, 'foo_bar', treeShakeIcons: false));
expect(bundle, isNotNull); expect(bundle, isNotNull);
expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'foo_barProfile', 'app.aab')); expect(bundle.path, fileSystem.path.join('irrelevant', 'app', 'outputs', 'bundle', 'foo_barProfile', 'app.aab'));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext("Finds app bundle when flavor doesn't contain underscores in profile mode", () { testWithoutContext("Finds app bundle when flavor doesn't contain underscores in profile mode", () {
final FlutterProject project = generateFakeAppBundle('fooProfile', 'app.aab'); final FlutterProject project = generateFakeAppBundle('fooProfile', 'app.aab', fileSystem);
final File bundle = findBundleFile(project, const BuildInfo(BuildMode.profile, 'foo', treeShakeIcons: false)); final File bundle = findBundleFile(project, const BuildInfo(BuildMode.profile, 'foo', treeShakeIcons: false));
expect(bundle, isNotNull); expect(bundle, isNotNull);
expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'fooProfile', 'app.aab')); expect(bundle.path, fileSystem.path.join('irrelevant', 'app', 'outputs', 'bundle', 'fooProfile', 'app.aab'));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext('Finds app bundle when no flavor is used in profile mode', () { testWithoutContext('Finds app bundle when no flavor is used in profile mode', () {
final FlutterProject project = generateFakeAppBundle('profile', 'app.aab'); final FlutterProject project = generateFakeAppBundle('profile', 'app.aab', fileSystem);
final File bundle = findBundleFile(project, const BuildInfo(BuildMode.profile, null, treeShakeIcons: false)); final File bundle = findBundleFile(project, const BuildInfo(BuildMode.profile, null, treeShakeIcons: false));
expect(bundle, isNotNull); expect(bundle, isNotNull);
expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'profile', 'app.aab')); expect(bundle.path, fileSystem.path.join('irrelevant', 'app', 'outputs', 'bundle', 'profile', 'app.aab'));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext('Finds app bundle in release mode - Gradle 3.5', () { testWithoutContext('Finds app bundle in release mode - Gradle 3.5', () {
final FlutterProject project = generateFakeAppBundle('release', 'app-release.aab'); final FlutterProject project = generateFakeAppBundle('release', 'app-release.aab', fileSystem);
final File bundle = findBundleFile(project, const BuildInfo(BuildMode.release, null, treeShakeIcons: false)); final File bundle = findBundleFile(project, const BuildInfo(BuildMode.release, null, treeShakeIcons: false));
expect(bundle, isNotNull); expect(bundle, isNotNull);
expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'release', 'app-release.aab')); expect(bundle.path, fileSystem.path.join('irrelevant', 'app', 'outputs', 'bundle', 'release', 'app-release.aab'));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext('Finds app bundle in profile mode - Gradle 3.5', () { testWithoutContext('Finds app bundle in profile mode - Gradle 3.5', () {
final FlutterProject project = generateFakeAppBundle('profile', 'app-profile.aab'); final FlutterProject project = generateFakeAppBundle('profile', 'app-profile.aab', fileSystem);
final File bundle = findBundleFile(project, const BuildInfo(BuildMode.profile, null, treeShakeIcons: false)); final File bundle = findBundleFile(project, const BuildInfo(BuildMode.profile, null, treeShakeIcons: false));
expect(bundle, isNotNull); expect(bundle, isNotNull);
expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'profile', 'app-profile.aab')); expect(bundle.path, fileSystem.path.join('irrelevant', 'app', 'outputs', 'bundle', 'profile', 'app-profile.aab'));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext('Finds app bundle in debug mode - Gradle 3.5', () { testWithoutContext('Finds app bundle in debug mode - Gradle 3.5', () {
final FlutterProject project = generateFakeAppBundle('debug', 'app-debug.aab'); final FlutterProject project = generateFakeAppBundle('debug', 'app-debug.aab', fileSystem);
final File bundle = findBundleFile(project, const BuildInfo(BuildMode.debug, null, treeShakeIcons: false)); final File bundle = findBundleFile(project, const BuildInfo(BuildMode.debug, null, treeShakeIcons: false));
expect(bundle, isNotNull); expect(bundle, isNotNull);
expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'debug', 'app-debug.aab')); expect(bundle.path, fileSystem.path.join('irrelevant', 'app', 'outputs', 'bundle', 'debug', 'app-debug.aab'));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext('Finds app bundle when flavor contains underscores in release mode - Gradle 3.5', () { testWithoutContext('Finds app bundle when flavor contains underscores in release mode - Gradle 3.5', () {
final FlutterProject project = generateFakeAppBundle('foo_barRelease', 'app-foo_bar-release.aab'); final FlutterProject project = generateFakeAppBundle('foo_barRelease', 'app-foo_bar-release.aab', fileSystem);
final File bundle = findBundleFile(project, const BuildInfo(BuildMode.release, 'foo_bar', treeShakeIcons: false)); final File bundle = findBundleFile(project, const BuildInfo(BuildMode.release, 'foo_bar', treeShakeIcons: false));
expect(bundle, isNotNull); expect(bundle, isNotNull);
expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'foo_barRelease', 'app-foo_bar-release.aab')); expect(bundle.path, fileSystem.path.join('irrelevant', 'app', 'outputs', 'bundle', 'foo_barRelease', 'app-foo_bar-release.aab'));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext('Finds app bundle when flavor contains underscores in profile mode - Gradle 3.5', () { testWithoutContext('Finds app bundle when flavor contains underscores in profile mode - Gradle 3.5', () {
final FlutterProject project = generateFakeAppBundle('foo_barProfile', 'app-foo_bar-profile.aab'); final FlutterProject project = generateFakeAppBundle('foo_barProfile', 'app-foo_bar-profile.aab', fileSystem);
final File bundle = findBundleFile(project, const BuildInfo(BuildMode.profile, 'foo_bar', treeShakeIcons: false)); final File bundle = findBundleFile(project, const BuildInfo(BuildMode.profile, 'foo_bar', treeShakeIcons: false));
expect(bundle, isNotNull); expect(bundle, isNotNull);
expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'foo_barProfile', 'app-foo_bar-profile.aab')); expect(bundle.path, fileSystem.path.join('irrelevant', 'app', 'outputs', 'bundle', 'foo_barProfile', 'app-foo_bar-profile.aab'));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext('Finds app bundle when flavor contains underscores in debug mode - Gradle 3.5', () { testWithoutContext('Finds app bundle when flavor contains underscores in debug mode - Gradle 3.5', () {
final FlutterProject project = generateFakeAppBundle('foo_barDebug', 'app-foo_bar-debug.aab'); final FlutterProject project = generateFakeAppBundle('foo_barDebug', 'app-foo_bar-debug.aab', fileSystem);
final File bundle = findBundleFile(project, const BuildInfo(BuildMode.debug, 'foo_bar', treeShakeIcons: false)); final File bundle = findBundleFile(project, const BuildInfo(BuildMode.debug, 'foo_bar', treeShakeIcons: false));
expect(bundle, isNotNull); expect(bundle, isNotNull);
expect(bundle.path, globals.fs.path.join('irrelevant','app', 'outputs', 'bundle', 'foo_barDebug', 'app-foo_bar-debug.aab')); expect(bundle.path, fileSystem.path.join('irrelevant','app', 'outputs', 'bundle', 'foo_barDebug', 'app-foo_bar-debug.aab'));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext('aab not found', () { testUsingContext('aab not found', () {
...@@ -701,7 +668,7 @@ flutter: ...@@ -701,7 +668,7 @@ flutter:
}); });
group('gradle version', () { group('gradle version', () {
test('should be compatible with the Android plugin version', () { testWithoutContext('should be compatible with the Android plugin version', () {
// Granular versions. // Granular versions.
expect(getGradleVersionFor('1.0.0'), '2.3'); expect(getGradleVersionFor('1.0.0'), '2.3');
expect(getGradleVersionFor('1.0.1'), '2.3'); expect(getGradleVersionFor('1.0.1'), '2.3');
...@@ -740,7 +707,7 @@ flutter: ...@@ -740,7 +707,7 @@ flutter:
expect(getGradleVersionFor('3.5.0'), '5.6.2'); expect(getGradleVersionFor('3.5.0'), '5.6.2');
}); });
test('throws on unsupported versions', () { testWithoutContext('throws on unsupported versions', () {
expect(() => getGradleVersionFor('3.6.0'), expect(() => getGradleVersionFor('3.6.0'),
throwsA(predicate<Exception>((Exception e) => e is ToolExit))); throwsA(predicate<Exception>((Exception e) => e is ToolExit)));
}); });
...@@ -2535,13 +2502,13 @@ plugin1=${plugin1.path} ...@@ -2535,13 +2502,13 @@ plugin1=${plugin1.path}
} }
/// Generates a fake app bundle at the location [directoryName]/[fileName]. /// Generates a fake app bundle at the location [directoryName]/[fileName].
FlutterProject generateFakeAppBundle(String directoryName, String fileName) { FlutterProject generateFakeAppBundle(String directoryName, String fileName, FileSystem fileSystem) {
final FlutterProject project = MockFlutterProject(); final FlutterProject project = MockFlutterProject();
final AndroidProject androidProject = MockAndroidProject(); final AndroidProject androidProject = MockAndroidProject();
when(project.isModule).thenReturn(false); when(project.isModule).thenReturn(false);
when(project.android).thenReturn(androidProject); when(project.android).thenReturn(androidProject);
when(androidProject.buildDirectory).thenReturn(globals.fs.directory('irrelevant')); when(androidProject.buildDirectory).thenReturn(fileSystem.directory('irrelevant'));
final Directory bundleDirectory = getBundleDirectory(project); final Directory bundleDirectory = getBundleDirectory(project);
bundleDirectory bundleDirectory
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment