blob: 02aea1e3f06229602034d330ff8fa40984b387e9 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:args/args.dart';
import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_system/tools/asset_transformer.dart';
import 'package:flutter_tools/src/flutter_manifest.dart';
import '../../../src/common.dart';
import '../../../src/fake_process_manager.dart';
void main() {
testWithoutContext('Invokes dart properly', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
final BufferLogger logger = BufferLogger.test();
final Artifacts artifacts = Artifacts.test();
final File asset = fileSystem.file('asset.txt')..createSync()..writeAsStringSync('hello world');
const String outputPath = 'output.txt';
final FakeProcessManager processManager =
FakeProcessManager.list(<FakeCommand>[
FakeCommand(
command: <String>[
artifacts.getArtifactPath(Artifact.engineDartBinary),
'run',
'my_copy_transformer',
'--input=/.tmp_rand0/asset.txt-transformOutput0.txt',
'--output=/.tmp_rand0/asset.txt-transformOutput1.txt',
'-f',
'--my_option',
'my_option_value',
],
onRun: (List<String> args) {
final ArgResults parsedArgs = (ArgParser()
..addOption('input')
..addOption('output')
..addFlag('foo', abbr: 'f')
..addOption('my_option'))
.parse(args);
fileSystem.file(parsedArgs['input']).copySync(parsedArgs['output'] as String);
},
),
]);
final AssetTransformer transformer = AssetTransformer(
processManager: processManager,
fileSystem: fileSystem,
dartBinaryPath: artifacts.getArtifactPath(Artifact.engineDartBinary),
);
final AssetTransformationFailure? transformationFailure = await transformer.transformAsset(
asset: asset,
outputPath: outputPath,
workingDirectory: fileSystem.currentDirectory.path,
transformerEntries: <AssetTransformerEntry>[
const AssetTransformerEntry(
package: 'my_copy_transformer',
args: <String>[
'-f',
'--my_option',
'my_option_value',
],
)
],
);
expect(transformationFailure, isNull, reason: logger.errorText);
expect(processManager, hasNoRemainingExpectations);
expect(fileSystem.file(outputPath).readAsStringSync(), 'hello world');
expect(fileSystem.directory('.tmp_rand0').listSync(), isEmpty, reason: 'Transformer did not clean up after itself.');
});
testWithoutContext('logs useful error information when transformation process returns a nonzero exit code', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
final Artifacts artifacts = Artifacts.test();
final File asset = fileSystem.file('asset.txt')..createSync();
const String outputPath = 'output.txt';
final String dartBinaryPath = artifacts.getArtifactPath(Artifact.engineDartBinary);
final FakeProcessManager processManager =
FakeProcessManager.list(<FakeCommand>[
FakeCommand(
command: <String>[
dartBinaryPath,
'run',
'my_copy_transformer',
'--input=/.tmp_rand0/asset.txt-transformOutput0.txt',
'--output=/.tmp_rand0/asset.txt-transformOutput1.txt',
],
onRun: (List<String> args) {
final ArgResults parsedArgs = (ArgParser()
..addOption('input')
..addOption('output'))
.parse(args);
fileSystem.file(parsedArgs['input']).copySync(parsedArgs['output'] as String);
},
exitCode: 1,
stdout: 'Beginning transformation',
stderr: 'Something went wrong',
),
]);
final AssetTransformer transformer = AssetTransformer(
processManager: processManager,
fileSystem: fileSystem,
dartBinaryPath: dartBinaryPath,
);
final AssetTransformationFailure? failure = await transformer.transformAsset(
asset: asset,
outputPath: outputPath,
workingDirectory: fileSystem.currentDirectory.path,
transformerEntries: <AssetTransformerEntry>[
const AssetTransformerEntry(
package: 'my_copy_transformer',
args: <String>[],
)
],
);
expect(asset, exists);
expect(processManager, hasNoRemainingExpectations);
expect(failure, isNotNull);
expect(failure!.message,
'''
User-defined transformation of asset "asset.txt" failed.
Transformer process terminated with non-zero exit code: 1
Transformer package: my_copy_transformer
Full command: $dartBinaryPath run my_copy_transformer --input=/.tmp_rand0/asset.txt-transformOutput0.txt --output=/.tmp_rand0/asset.txt-transformOutput1.txt
stdout:
Beginning transformation
stderr:
Something went wrong''');
expect(fileSystem.directory('.tmp_rand0').listSync(), isEmpty, reason: 'Transformer did not clean up after itself.');
});
testWithoutContext('prints error message when the transformer does not produce an output file', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
final Artifacts artifacts = Artifacts.test();
final File asset = fileSystem.file('asset.txt')..createSync();
const String outputPath = 'output.txt';
final String dartBinaryPath = artifacts.getArtifactPath(Artifact.engineDartBinary);
final FakeProcessManager processManager =
FakeProcessManager.list(<FakeCommand>[
FakeCommand(
command: <String>[
dartBinaryPath,
'run',
'my_transformer',
'--input=/.tmp_rand0/asset.txt-transformOutput0.txt',
'--output=/.tmp_rand0/asset.txt-transformOutput1.txt',
],
onRun: (_) {
// Do nothing.
},
stderr: 'Transformation failed, but I forgot to exit with a non-zero code.'
),
]);
final AssetTransformer transformer = AssetTransformer(
processManager: processManager,
fileSystem: fileSystem,
dartBinaryPath: dartBinaryPath,
);
final AssetTransformationFailure? failure = await transformer.transformAsset(
asset: asset,
outputPath: outputPath,
workingDirectory: fileSystem.currentDirectory.path,
transformerEntries: <AssetTransformerEntry>[
const AssetTransformerEntry(
package: 'my_transformer',
args: <String>[],
)
],
);
expect(processManager, hasNoRemainingExpectations);
expect(failure, isNotNull);
expect(failure!.message,
'''
User-defined transformation of asset "asset.txt" failed.
Asset transformer my_transformer did not produce an output file.
Input file provided to transformer: "/.tmp_rand0/asset.txt-transformOutput0.txt"
Expected output file at: "/.tmp_rand0/asset.txt-transformOutput1.txt"
Full command: $dartBinaryPath run my_transformer --input=/.tmp_rand0/asset.txt-transformOutput0.txt --output=/.tmp_rand0/asset.txt-transformOutput1.txt
stdout:
stderr:
Transformation failed, but I forgot to exit with a non-zero code.'''
);
expect(fileSystem.directory('.tmp_rand0').listSync(), isEmpty, reason: 'Transformer did not clean up after itself.');
});
testWithoutContext('correctly chains transformations when there are multiple of them', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
final Artifacts artifacts = Artifacts.test();
final File asset = fileSystem.file('asset.txt')
..createSync()
..writeAsStringSync('ABC');
const String outputPath = 'output.txt';
final String dartBinaryPath = artifacts.getArtifactPath(Artifact.engineDartBinary);
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
FakeCommand(
command: <String>[
dartBinaryPath,
'run',
'my_lowercase_transformer',
'--input=/.tmp_rand0/asset.txt-transformOutput0.txt',
'--output=/.tmp_rand0/asset.txt-transformOutput1.txt',
],
onRun: (List<String> args) {
final ArgResults parsedArgs = (ArgParser()
..addOption('input')
..addOption('output'))
.parse(args);
final String inputFileContents = fileSystem.file(parsedArgs['input']).readAsStringSync();
fileSystem.file(parsedArgs['output'])
..createSync()
..writeAsStringSync(inputFileContents.toLowerCase());
},
),
FakeCommand(
command: <String>[
dartBinaryPath,
'run',
'my_distance_from_ascii_a_transformer',
'--input=/.tmp_rand0/asset.txt-transformOutput1.txt',
'--output=/.tmp_rand0/asset.txt-transformOutput2.txt',
],
onRun: (List<String> args) {
final ArgResults parsedArgs = (ArgParser()
..addOption('input')
..addOption('output'))
.parse(args);
final String inputFileContents = fileSystem.file(parsedArgs['input']).readAsStringSync();
final StringBuffer outputContents = StringBuffer();
for (int i = 0; i < inputFileContents.length; i++) {
outputContents.write(inputFileContents.codeUnitAt(i) - 'a'.codeUnits.first);
}
fileSystem.file(parsedArgs['output'])
..createSync()
..writeAsStringSync(outputContents.toString());
},
),
]);
final AssetTransformer transformer = AssetTransformer(
processManager: processManager,
fileSystem: fileSystem,
dartBinaryPath: dartBinaryPath,
);
final AssetTransformationFailure? failure = await transformer.transformAsset(
asset: asset,
outputPath: outputPath,
workingDirectory: fileSystem.currentDirectory.path,
transformerEntries: <AssetTransformerEntry>[
const AssetTransformerEntry(
package: 'my_lowercase_transformer',
args: <String>[],
),
const AssetTransformerEntry(
package: 'my_distance_from_ascii_a_transformer',
args: <String>[],
),
],
);
expect(processManager, hasNoRemainingExpectations);
expect(failure, isNull);
expect(fileSystem.file(outputPath).readAsStringSync(), '012');
expect(fileSystem.directory('.tmp_rand0').listSync(), isEmpty, reason: 'Transformer did not clean up after itself.');
});
testWithoutContext('prints an error when a transformer in a chain (thats not the first) does not produce an output', () async {
final FileSystem fileSystem = MemoryFileSystem();
final Artifacts artifacts = Artifacts.test();
final File asset = fileSystem.file('asset.txt')
..createSync()
..writeAsStringSync('ABC');
const String outputPath = 'output.txt';
final String dartBinaryPath = artifacts.getArtifactPath(Artifact.engineDartBinary);
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
FakeCommand(
command: <String>[
dartBinaryPath,
'run',
'my_lowercase_transformer',
'--input=/.tmp_rand0/asset.txt-transformOutput0.txt',
'--output=/.tmp_rand0/asset.txt-transformOutput1.txt',
],
onRun: (List<String> args) {
final ArgResults parsedArgs = (ArgParser()
..addOption('input')
..addOption('output'))
.parse(args);
final String inputFileContents = fileSystem.file(parsedArgs['input']).readAsStringSync();
fileSystem.file(parsedArgs['output'])
..createSync()
..writeAsStringSync(inputFileContents.toLowerCase());
},
),
FakeCommand(
command: <String>[
dartBinaryPath,
'run',
'my_distance_from_ascii_a_transformer',
'--input=/.tmp_rand0/asset.txt-transformOutput1.txt',
'--output=/.tmp_rand0/asset.txt-transformOutput2.txt',
],
onRun: (List<String> args) {
// Do nothing.
},
stderr: 'Transformation failed, but I forgot to exit with a non-zero code.'
),
]);
final AssetTransformer transformer = AssetTransformer(
processManager: processManager,
fileSystem: fileSystem,
dartBinaryPath: dartBinaryPath,
);
final AssetTransformationFailure? failure = await transformer.transformAsset(
asset: asset,
outputPath: outputPath,
workingDirectory: fileSystem.currentDirectory.path,
transformerEntries: <AssetTransformerEntry>[
const AssetTransformerEntry(
package: 'my_lowercase_transformer',
args: <String>[],
),
const AssetTransformerEntry(
package: 'my_distance_from_ascii_a_transformer',
args: <String>[],
),
],
);
expect(failure, isNotNull);
expect(failure!.message,
'''
User-defined transformation of asset "asset.txt" failed.
Asset transformer my_distance_from_ascii_a_transformer did not produce an output file.
Input file provided to transformer: "/.tmp_rand0/asset.txt-transformOutput1.txt"
Expected output file at: "/.tmp_rand0/asset.txt-transformOutput2.txt"
Full command: Artifact.engineDartBinary run my_distance_from_ascii_a_transformer --input=/.tmp_rand0/asset.txt-transformOutput1.txt --output=/.tmp_rand0/asset.txt-transformOutput2.txt
stdout:
stderr:
Transformation failed, but I forgot to exit with a non-zero code.'''
);
expect(processManager, hasNoRemainingExpectations);
expect(fileSystem.file(outputPath), isNot(exists));
expect(fileSystem.directory('.tmp_rand0').listSync(), isEmpty, reason: 'Transformer did not clean up after itself.');
});
}