Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
90a8b056
Unverified
Commit
90a8b056
authored
May 09, 2022
by
Gary Qian
Committed by
GitHub
May 09, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[flutter_tools] MigrateUtils and MigrateManifest classes (#101937)
parent
93cce92e
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
1281 additions
and
0 deletions
+1281
-0
migrate.dart
packages/flutter_tools/lib/src/commands/migrate.dart
+84
-0
migrate_manifest.dart
packages/flutter_tools/lib/src/migrate/migrate_manifest.dart
+241
-0
migrate_result.dart
packages/flutter_tools/lib/src/migrate/migrate_result.dart
+82
-0
migrate_utils.dart
packages/flutter_tools/lib/src/migrate/migrate_utils.dart
+359
-0
migrate_manifest_test.dart
...ols/test/general.shard/migrate/migrate_manifest_test.dart
+292
-0
migrate_utils_test.dart
...tter_tools/test/integration.shard/migrate_utils_test.dart
+223
-0
No files found.
packages/flutter_tools/lib/src/commands/migrate.dart
0 → 100644
View file @
90a8b056
// 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:process/process.dart';
// import '../base/file_system.dart';
import
'../base/logger.dart'
;
// import '../base/platform.dart';
import
'../base/terminal.dart'
;
import
'../migrate/migrate_utils.dart'
;
import
'../runner/flutter_command.dart'
;
// TODO(garyq): Add each of these back in as they land.
// import 'migrate_abandon.dart';
// import 'migrate_apply.dart';
// import 'migrate_resolve_conflicts.dart';
// import 'migrate_start.dart';
// import 'migrate_status.dart';
/// Base command for the migration tool.
class
MigrateCommand
extends
FlutterCommand
{
MigrateCommand
({
// bool verbose = false,
required
this
.
logger
,
// TODO(garyq): Add each of these back in as they land.
// required FileSystem fileSystem,
// required Terminal terminal,
// required Platform platform,
// required ProcessManager processManager,
})
{
// TODO(garyq): Add each of these back in as they land.
// addSubcommand(MigrateAbandonCommand(logger: logger, fileSystem: fileSystem, terminal: terminal, platform: platform, processManager: processManager));
// addSubcommand(MigrateApplyCommand(verbose: verbose, logger: logger, fileSystem: fileSystem, terminal: terminal, platform: platform, processManager: processManager));
// addSubcommand(MigrateResolveConflictsCommand(logger: logger, fileSystem: fileSystem, terminal: terminal));
// addSubcommand(MigrateStartCommand(verbose: verbose, logger: logger, fileSystem: fileSystem, platform: platform, processManager: processManager));
// addSubcommand(MigrateStatusCommand(verbose: verbose, logger: logger, fileSystem: fileSystem, platform: platform, processManager: processManager));
}
final
Logger
logger
;
@override
final
String
name
=
'migrate'
;
@override
final
String
description
=
'Migrates flutter generated project files to the current flutter version'
;
@override
String
get
category
=>
FlutterCommandCategory
.
project
;
@override
Future
<
Set
<
DevelopmentArtifact
>>
get
requiredArtifacts
async
=>
const
<
DevelopmentArtifact
>{};
@override
Future
<
FlutterCommandResult
>
runCommand
()
async
{
return
const
FlutterCommandResult
(
ExitStatus
.
fail
);
}
}
Future
<
bool
>
gitRepoExists
(
String
projectDirectory
,
Logger
logger
,
MigrateUtils
migrateUtils
)
async
{
if
(
await
migrateUtils
.
isGitRepo
(
projectDirectory
))
{
return
true
;
}
logger
.
printStatus
(
'Project is not a git repo. Please initialize a git repo and try again.'
);
printCommandText
(
'git init'
,
logger
);
return
false
;
}
Future
<
bool
>
hasUncommittedChanges
(
String
projectDirectory
,
Logger
logger
,
MigrateUtils
migrateUtils
)
async
{
if
(
await
migrateUtils
.
hasUncommittedChanges
(
projectDirectory
))
{
logger
.
printStatus
(
'There are uncommitted changes in your project. Please git commit, abandon, or stash your changes before trying again.'
);
return
true
;
}
return
false
;
}
/// Prints a command to logger with appropriate formatting.
void
printCommandText
(
String
command
,
Logger
logger
)
{
logger
.
printStatus
(
'
\n
\$
$command
\n
'
,
color:
TerminalColor
.
grey
,
indent:
4
,
newline:
false
,
);
}
packages/flutter_tools/lib/src/migrate/migrate_manifest.dart
0 → 100644
View file @
90a8b056
// 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:yaml/yaml.dart'
;
import
'../base/file_system.dart'
;
import
'../base/logger.dart'
;
import
'../base/terminal.dart'
;
import
'migrate_result.dart'
;
import
'migrate_utils.dart'
;
const
String
_kMergedFilesKey
=
'merged_files'
;
const
String
_kConflictFilesKey
=
'conflict_files'
;
const
String
_kAddedFilesKey
=
'added_files'
;
const
String
_kDeletedFilesKey
=
'deleted_files'
;
/// Represents the manifest file that tracks the contents of the current
/// migration working directory.
///
/// This manifest file is created with the MigrateResult of a computeMigration run.
class
MigrateManifest
{
/// Creates a new manifest from a MigrateResult.
MigrateManifest
({
required
this
.
migrateRootDir
,
required
this
.
migrateResult
,
});
/// Parses an existing migrate manifest.
MigrateManifest
.
fromFile
(
File
manifestFile
)
:
migrateResult
=
MigrateResult
.
empty
(),
migrateRootDir
=
manifestFile
.
parent
{
final
Object
?
yamlContents
=
loadYaml
(
manifestFile
.
readAsStringSync
());
if
(
yamlContents
is
!
YamlMap
)
{
throw
Exception
(
'Invalid .migrate_manifest file in the migrate working directory. File is not a Yaml map.'
);
}
final
YamlMap
map
=
yamlContents
;
bool
valid
=
map
.
containsKey
(
_kMergedFilesKey
)
&&
map
.
containsKey
(
_kConflictFilesKey
)
&&
map
.
containsKey
(
_kAddedFilesKey
)
&&
map
.
containsKey
(
_kDeletedFilesKey
);
if
(!
valid
)
{
throw
Exception
(
'Invalid .migrate_manifest file in the migrate working directory. File is missing an entry.'
);
}
final
Object
?
mergedFilesYaml
=
map
[
_kMergedFilesKey
];
final
Object
?
conflictFilesYaml
=
map
[
_kConflictFilesKey
];
final
Object
?
addedFilesYaml
=
map
[
_kAddedFilesKey
];
final
Object
?
deletedFilesYaml
=
map
[
_kDeletedFilesKey
];
valid
=
valid
&&
(
mergedFilesYaml
is
YamlList
||
mergedFilesYaml
==
null
);
valid
=
valid
&&
(
conflictFilesYaml
is
YamlList
||
conflictFilesYaml
==
null
);
valid
=
valid
&&
(
addedFilesYaml
is
YamlList
||
addedFilesYaml
==
null
);
valid
=
valid
&&
(
deletedFilesYaml
is
YamlList
||
deletedFilesYaml
==
null
);
if
(!
valid
)
{
throw
Exception
(
'Invalid .migrate_manifest file in the migrate working directory. Entry is not a Yaml list.'
);
}
if
(
mergedFilesYaml
!=
null
)
{
for
(
final
Object
?
localPath
in
mergedFilesYaml
as
YamlList
)
{
if
(
localPath
is
String
)
{
// We can fill the maps with partially dummy data as not all properties are used by the manifest.
migrateResult
.
mergeResults
.
add
(
StringMergeResult
.
explicit
(
mergedString:
''
,
hasConflict:
false
,
exitCode:
0
,
localPath:
localPath
));
}
}
}
if
(
conflictFilesYaml
!=
null
)
{
for
(
final
Object
?
localPath
in
conflictFilesYaml
as
YamlList
)
{
if
(
localPath
is
String
)
{
migrateResult
.
mergeResults
.
add
(
StringMergeResult
.
explicit
(
mergedString:
''
,
hasConflict:
true
,
exitCode:
1
,
localPath:
localPath
));
}
}
}
if
(
addedFilesYaml
!=
null
)
{
for
(
final
Object
?
localPath
in
addedFilesYaml
as
YamlList
)
{
if
(
localPath
is
String
)
{
migrateResult
.
addedFiles
.
add
(
FilePendingMigration
(
localPath
,
migrateRootDir
.
childFile
(
localPath
)));
}
}
}
if
(
deletedFilesYaml
!=
null
)
{
for
(
final
Object
?
localPath
in
deletedFilesYaml
as
YamlList
)
{
if
(
localPath
is
String
)
{
migrateResult
.
deletedFiles
.
add
(
FilePendingMigration
(
localPath
,
migrateRootDir
.
childFile
(
localPath
)));
}
}
}
}
final
Directory
migrateRootDir
;
final
MigrateResult
migrateResult
;
/// A list of local paths of files that require conflict resolution.
List
<
String
>
get
conflictFiles
{
final
List
<
String
>
output
=
<
String
>[];
for
(
final
MergeResult
result
in
migrateResult
.
mergeResults
)
{
if
(
result
.
hasConflict
)
{
output
.
add
(
result
.
localPath
);
}
}
return
output
;
}
/// A list of local paths of files that require conflict resolution.
List
<
String
>
remainingConflictFiles
(
Directory
workingDir
)
{
final
List
<
String
>
output
=
<
String
>[];
for
(
final
String
localPath
in
conflictFiles
)
{
if
(!
_conflictsResolved
(
workingDir
.
childFile
(
localPath
).
readAsStringSync
()))
{
output
.
add
(
localPath
);
}
}
return
output
;
}
// A list of local paths of files that had conflicts and are now fully resolved.
List
<
String
>
resolvedConflictFiles
(
Directory
workingDir
)
{
final
List
<
String
>
output
=
<
String
>[];
for
(
final
String
localPath
in
conflictFiles
)
{
if
(
_conflictsResolved
(
workingDir
.
childFile
(
localPath
).
readAsStringSync
()))
{
output
.
add
(
localPath
);
}
}
return
output
;
}
/// A list of local paths of files that were automatically merged.
List
<
String
>
get
mergedFiles
{
final
List
<
String
>
output
=
<
String
>[];
for
(
final
MergeResult
result
in
migrateResult
.
mergeResults
)
{
if
(!
result
.
hasConflict
)
{
output
.
add
(
result
.
localPath
);
}
}
return
output
;
}
/// A list of local paths of files that were newly added.
List
<
String
>
get
addedFiles
{
final
List
<
String
>
output
=
<
String
>[];
for
(
final
FilePendingMigration
file
in
migrateResult
.
addedFiles
)
{
output
.
add
(
file
.
localPath
);
}
return
output
;
}
/// A list of local paths of files that are marked for deletion.
List
<
String
>
get
deletedFiles
{
final
List
<
String
>
output
=
<
String
>[];
for
(
final
FilePendingMigration
file
in
migrateResult
.
deletedFiles
)
{
output
.
add
(
file
.
localPath
);
}
return
output
;
}
/// Returns the manifest file given a migration workind directory.
static
File
getManifestFileFromDirectory
(
Directory
workingDir
)
{
return
workingDir
.
childFile
(
'.migrate_manifest'
);
}
/// Writes the manifest yaml file in the working directory.
void
writeFile
()
{
final
StringBuffer
mergedFileManifestContents
=
StringBuffer
();
final
StringBuffer
conflictFilesManifestContents
=
StringBuffer
();
for
(
final
MergeResult
result
in
migrateResult
.
mergeResults
)
{
if
(
result
.
hasConflict
)
{
conflictFilesManifestContents
.
write
(
' -
${result.localPath}
\n
'
);
}
else
{
mergedFileManifestContents
.
write
(
' -
${result.localPath}
\n
'
);
}
}
final
StringBuffer
newFileManifestContents
=
StringBuffer
();
for
(
final
String
localPath
in
addedFiles
)
{
newFileManifestContents
.
write
(
' -
$localPath
\n
)'
);
}
final
StringBuffer
deletedFileManifestContents
=
StringBuffer
();
for
(
final
String
localPath
in
deletedFiles
)
{
deletedFileManifestContents
.
write
(
' -
$localPath
\n
'
);
}
final
String
migrateManifestContents
=
'merged_files:
\n
${mergedFileManifestContents.toString()}
conflict_files:
\n
${conflictFilesManifestContents.toString()}
added_files:
\n
${newFileManifestContents.toString()}
deleted_files:
\n
${deletedFileManifestContents.toString()}
'
;
final
File
migrateManifest
=
getManifestFileFromDirectory
(
migrateRootDir
);
migrateManifest
.
createSync
(
recursive:
true
);
migrateManifest
.
writeAsStringSync
(
migrateManifestContents
,
flush:
true
);
}
}
/// Returns true if the file does not contain any git conflict markers.
bool
_conflictsResolved
(
String
contents
)
{
if
(
contents
.
contains
(
'>>>>>>>'
)
&&
contents
.
contains
(
'======='
)
&&
contents
.
contains
(
'<<<<<<<'
))
{
return
false
;
}
return
true
;
}
/// Returns true if the migration working directory has all conflicts resolved and prints the migration status.
///
/// The migration status printout lists all added, deleted, merged, and conflicted files.
bool
checkAndPrintMigrateStatus
(
MigrateManifest
manifest
,
Directory
workingDir
,
{
bool
warnConflict
=
false
,
Logger
?
logger
})
{
final
StringBuffer
printout
=
StringBuffer
();
final
StringBuffer
redPrintout
=
StringBuffer
();
bool
result
=
true
;
final
List
<
String
>
remainingConflicts
=
<
String
>[];
final
List
<
String
>
mergedFiles
=
<
String
>[];
for
(
final
String
localPath
in
manifest
.
conflictFiles
)
{
if
(!
_conflictsResolved
(
workingDir
.
childFile
(
localPath
).
readAsStringSync
()))
{
remainingConflicts
.
add
(
localPath
);
}
else
{
mergedFiles
.
add
(
localPath
);
}
}
mergedFiles
.
addAll
(
manifest
.
mergedFiles
);
if
(
manifest
.
addedFiles
.
isNotEmpty
)
{
printout
.
write
(
'Added files:
\n
'
);
for
(
final
String
localPath
in
manifest
.
addedFiles
)
{
printout
.
write
(
' -
$localPath
\n
'
);
}
}
if
(
manifest
.
deletedFiles
.
isNotEmpty
)
{
printout
.
write
(
'Deleted files:
\n
'
);
for
(
final
String
localPath
in
manifest
.
deletedFiles
)
{
printout
.
write
(
' -
$localPath
\n
'
);
}
}
if
(
mergedFiles
.
isNotEmpty
)
{
printout
.
write
(
'Modified files:
\n
'
);
for
(
final
String
localPath
in
mergedFiles
)
{
printout
.
write
(
' -
$localPath
\n
'
);
}
}
if
(
remainingConflicts
.
isNotEmpty
)
{
if
(
warnConflict
)
{
printout
.
write
(
'Unable to apply migration. The following files in the migration working directory still have unresolved conflicts:'
);
}
else
{
printout
.
write
(
'Merge conflicted files:'
);
}
for
(
final
String
localPath
in
remainingConflicts
)
{
redPrintout
.
write
(
' -
$localPath
\n
'
);
}
result
=
false
;
}
if
(
logger
!=
null
)
{
logger
.
printStatus
(
printout
.
toString
());
logger
.
printStatus
(
redPrintout
.
toString
(),
color:
TerminalColor
.
red
,
newline:
false
);
}
return
result
;
}
packages/flutter_tools/lib/src/migrate/migrate_result.dart
0 → 100644
View file @
90a8b056
// 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
'../base/file_system.dart'
;
import
'migrate_utils.dart'
;
/// Data class that holds all results and generated directories from a computeMigration run.
///
/// mergeResults, addedFiles, and deletedFiles includes the sets of files to be migrated while
/// the other members track the temporary sdk and generated app directories created by the tool.
///
/// The compute function does not clean up the temp directories, as the directories may be reused,
/// so this must be done manually afterwards.
class
MigrateResult
{
/// Explicitly initialize the MigrateResult.
MigrateResult
({
required
this
.
mergeResults
,
required
this
.
addedFiles
,
required
this
.
deletedFiles
,
required
this
.
tempDirectories
,
required
this
.
sdkDirs
,
required
this
.
mergeTypeMap
,
required
this
.
diffMap
,
this
.
generatedBaseTemplateDirectory
,
this
.
generatedTargetTemplateDirectory
});
/// Creates a MigrateResult with all empty members.
MigrateResult
.
empty
()
:
mergeResults
=
<
MergeResult
>[],
addedFiles
=
<
FilePendingMigration
>[],
deletedFiles
=
<
FilePendingMigration
>[],
tempDirectories
=
<
Directory
>[],
mergeTypeMap
=
<
String
,
MergeType
>{},
diffMap
=
<
String
,
DiffResult
>{},
sdkDirs
=
<
String
,
Directory
>{};
/// The results of merging existing files with the target files.
final
List
<
MergeResult
>
mergeResults
;
/// Tracks the files that are to be newly added to the project.
final
List
<
FilePendingMigration
>
addedFiles
;
/// Tracks the files that are to be deleted from the project.
final
List
<
FilePendingMigration
>
deletedFiles
;
/// Tracks the temporary directories created during the migrate compute process.
final
List
<
Directory
>
tempDirectories
;
/// Mapping between the local path of a file and the type of merge that should be used.
final
Map
<
String
,
MergeType
>
mergeTypeMap
;
/// Mapping between the local path of a file and the diff between the base and target
/// versions of the file.
final
Map
<
String
,
DiffResult
>
diffMap
;
/// The root directory of the base app.
Directory
?
generatedBaseTemplateDirectory
;
/// The root directory of the target app.
Directory
?
generatedTargetTemplateDirectory
;
/// The root directories of the Flutter SDK for each revision.
Map
<
String
,
Directory
>
sdkDirs
;
}
/// Defines available merge techniques.
enum
MergeType
{
/// A standard three-way merge.
threeWay
,
/// A two way merge that ignores the base version of the file.
twoWay
,
/// A `CustomMerge` manually handles the merge.
custom
,
}
/// Stores a file that has been marked for migration and metadata about the file.
class
FilePendingMigration
{
FilePendingMigration
(
this
.
localPath
,
this
.
file
);
String
localPath
;
File
file
;
}
packages/flutter_tools/lib/src/migrate/migrate_utils.dart
0 → 100644
View file @
90a8b056
// 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
'dart:async'
;
import
'dart:typed_data'
;
import
'package:process/process.dart'
;
import
'../base/common.dart'
;
import
'../base/file_system.dart'
;
import
'../base/logger.dart'
;
import
'../base/platform.dart'
;
import
'../base/process.dart'
;
/// The default name of the migrate working directory used to stage proposed changes.
const
String
kDefaultMigrateWorkingDirectoryName
=
'migrate_working_dir'
;
/// Utility class that contains methods that wrap git and other shell commands.
class
MigrateUtils
{
MigrateUtils
({
required
Logger
logger
,
required
FileSystem
fileSystem
,
required
Platform
platform
,
required
ProcessManager
processManager
,
})
:
_processUtils
=
ProcessUtils
(
processManager:
processManager
,
logger:
logger
),
_logger
=
logger
,
_fileSystem
=
fileSystem
,
_platform
=
platform
;
final
Logger
_logger
;
final
FileSystem
_fileSystem
;
final
Platform
_platform
;
final
ProcessUtils
_processUtils
;
/// Calls `git diff` on two files and returns the diff as a DiffResult.
Future
<
DiffResult
>
diffFiles
(
File
one
,
File
two
)
async
{
if
(
one
.
existsSync
()
&&
!
two
.
existsSync
())
{
return
DiffResult
(
diffType:
DiffType
.
deletion
);
}
if
(!
one
.
existsSync
()
&&
two
.
existsSync
())
{
return
DiffResult
(
diffType:
DiffType
.
addition
);
}
final
List
<
String
>
cmdArgs
=
<
String
>[
'git'
,
'diff'
,
'--no-index'
,
one
.
absolute
.
path
,
two
.
absolute
.
path
];
final
RunResult
result
=
await
_processUtils
.
run
(
cmdArgs
);
// diff exits with 1 if diffs are found.
checkForErrors
(
result
,
allowedExitCodes:
<
int
>[
0
,
1
],
commandDescription:
'git
${cmdArgs.join(' ')}
'
);
return
DiffResult
(
diffType:
DiffType
.
command
,
diff:
result
.
stdout
,
exitCode:
result
.
exitCode
);
}
/// Clones a copy of the flutter repo into the destination directory. Returns false if unsuccessful.
Future
<
bool
>
cloneFlutter
(
String
revision
,
String
destination
)
async
{
// Use https url instead of ssh to avoid need to setup ssh on git.
List
<
String
>
cmdArgs
=
<
String
>[
'git'
,
'clone'
,
'--filter=blob:none'
,
'https://github.com/flutter/flutter.git'
,
destination
];
RunResult
result
=
await
_processUtils
.
run
(
cmdArgs
);
checkForErrors
(
result
,
commandDescription:
cmdArgs
.
join
(
' '
));
cmdArgs
.
clear
();
cmdArgs
=
<
String
>[
'git'
,
'reset'
,
'--hard'
,
revision
];
result
=
await
_processUtils
.
run
(
cmdArgs
,
workingDirectory:
destination
);
if
(!
checkForErrors
(
result
,
commandDescription:
cmdArgs
.
join
(
' '
),
exit:
false
))
{
return
false
;
}
return
true
;
}
/// Calls `flutter create` as a re-entrant command.
Future
<
String
>
createFromTemplates
(
String
flutterBinPath
,
{
required
String
name
,
bool
legacyNameParameter
=
false
,
required
String
androidLanguage
,
required
String
iosLanguage
,
required
String
outputDirectory
,
String
?
createVersion
,
List
<
String
>
platforms
=
const
<
String
>[],
int
iterationsAllowed
=
5
,
})
async
{
// Limit the number of iterations this command is allowed to attempt to prevent infinite looping.
if
(
iterationsAllowed
<=
0
)
{
_logger
.
printError
(
'Unable to `flutter create` with the version of flutter at
$flutterBinPath
'
);
return
outputDirectory
;
}
final
List
<
String
>
cmdArgs
=
<
String
>[
'
$flutterBinPath
/flutter'
,
'create'
];
if
(!
legacyNameParameter
)
{
cmdArgs
.
add
(
'--project-name=
$name
'
);
}
cmdArgs
.
add
(
'--android-language=
$androidLanguage
'
);
cmdArgs
.
add
(
'--ios-language=
$iosLanguage
'
);
if
(
platforms
.
isNotEmpty
)
{
String
platformsArg
=
'--platforms='
;
for
(
int
i
=
0
;
i
<
platforms
.
length
;
i
++)
{
if
(
i
>
0
)
{
platformsArg
+=
','
;
}
platformsArg
+=
platforms
[
i
];
}
cmdArgs
.
add
(
platformsArg
);
}
cmdArgs
.
add
(
'--no-pub'
);
if
(
legacyNameParameter
)
{
cmdArgs
.
add
(
name
);
}
else
{
cmdArgs
.
add
(
outputDirectory
);
}
final
RunResult
result
=
await
_processUtils
.
run
(
cmdArgs
,
workingDirectory:
outputDirectory
,
allowReentrantFlutter:
true
);
final
String
error
=
result
.
stderr
;
// Catch errors due to parameters not existing.
// Old versions of the tool does not include the platforms option.
if
(
error
.
contains
(
'Could not find an option named "platforms".'
))
{
return
createFromTemplates
(
flutterBinPath
,
name:
name
,
legacyNameParameter:
legacyNameParameter
,
androidLanguage:
androidLanguage
,
iosLanguage:
iosLanguage
,
outputDirectory:
outputDirectory
,
iterationsAllowed:
iterationsAllowed
--,
);
}
// Old versions of the tool does not include the project-name option.
if
((
result
.
stderr
).
contains
(
'Could not find an option named "project-name".'
))
{
return
createFromTemplates
(
flutterBinPath
,
name:
name
,
legacyNameParameter:
true
,
androidLanguage:
androidLanguage
,
iosLanguage:
iosLanguage
,
outputDirectory:
outputDirectory
,
platforms:
platforms
,
iterationsAllowed:
iterationsAllowed
--,
);
}
if
(
error
.
contains
(
'Multiple output directories specified.'
))
{
if
(
error
.
contains
(
'Try moving --platforms'
))
{
return
createFromTemplates
(
flutterBinPath
,
name:
name
,
legacyNameParameter:
legacyNameParameter
,
androidLanguage:
androidLanguage
,
iosLanguage:
iosLanguage
,
outputDirectory:
outputDirectory
,
iterationsAllowed:
iterationsAllowed
--,
);
}
}
checkForErrors
(
result
,
commandDescription:
cmdArgs
.
join
(
' '
),
silent:
true
);
if
(
legacyNameParameter
)
{
return
_fileSystem
.
path
.
join
(
outputDirectory
,
name
);
}
return
outputDirectory
;
}
/// Runs the git 3-way merge on three files and returns the results as a MergeResult.
///
/// Passing the same path for base and current will perform a two-way fast forward merge.
Future
<
MergeResult
>
gitMergeFile
({
required
String
base
,
required
String
current
,
required
String
target
,
required
String
localPath
,
})
async
{
final
List
<
String
>
cmdArgs
=
<
String
>[
'git'
,
'merge-file'
,
'-p'
,
current
,
base
,
target
];
final
RunResult
result
=
await
_processUtils
.
run
(
cmdArgs
);
checkForErrors
(
result
,
allowedExitCodes:
<
int
>[-
1
],
commandDescription:
cmdArgs
.
join
(
' '
));
return
StringMergeResult
(
result
,
localPath
);
}
/// Calls `git init` on the workingDirectory.
Future
<
void
>
gitInit
(
String
workingDirectory
)
async
{
final
List
<
String
>
cmdArgs
=
<
String
>[
'git'
,
'init'
];
final
RunResult
result
=
await
_processUtils
.
run
(
cmdArgs
,
workingDirectory:
workingDirectory
);
checkForErrors
(
result
,
commandDescription:
cmdArgs
.
join
(
' '
));
}
/// Returns true if the workingDirectory git repo has any uncommited changes.
Future
<
bool
>
hasUncommittedChanges
(
String
workingDirectory
,
{
String
?
migrateWorkingDir
})
async
{
final
List
<
String
>
cmdArgs
=
<
String
>[
'git'
,
'ls-files'
,
'--deleted'
,
'--modified'
,
'--others'
,
'--exclude=
${migrateWorkingDir ?? kDefaultMigrateWorkingDirectoryName}
'
];
final
RunResult
result
=
await
_processUtils
.
run
(
cmdArgs
,
workingDirectory:
workingDirectory
);
checkForErrors
(
result
,
allowedExitCodes:
<
int
>[-
1
],
commandDescription:
cmdArgs
.
join
(
' '
));
if
(
result
.
stdout
.
isEmpty
)
{
return
false
;
}
return
true
;
}
/// Returns true if the workingDirectory is a git repo.
Future
<
bool
>
isGitRepo
(
String
workingDirectory
)
async
{
final
List
<
String
>
cmdArgs
=
<
String
>[
'git'
,
'rev-parse'
,
'--is-inside-work-tree'
];
final
RunResult
result
=
await
_processUtils
.
run
(
cmdArgs
,
workingDirectory:
workingDirectory
);
checkForErrors
(
result
,
allowedExitCodes:
<
int
>[-
1
],
commandDescription:
cmdArgs
.
join
(
' '
));
if
(
result
.
exitCode
==
0
)
{
return
true
;
}
return
false
;
}
/// Returns true if the file at `filePath` is covered by the `.gitignore`
Future
<
bool
>
isGitIgnored
(
String
filePath
,
String
workingDirectory
)
async
{
final
List
<
String
>
cmdArgs
=
<
String
>[
'git'
,
'check-ignore'
,
filePath
];
final
RunResult
result
=
await
_processUtils
.
run
(
cmdArgs
,
workingDirectory:
workingDirectory
);
checkForErrors
(
result
,
allowedExitCodes:
<
int
>[
0
,
1
,
128
],
commandDescription:
cmdArgs
.
join
(
' '
));
return
result
.
exitCode
==
0
;
}
/// Runs `flutter pub upgrade --major-revisions`.
Future
<
void
>
flutterPubUpgrade
(
String
workingDirectory
)
async
{
final
List
<
String
>
cmdArgs
=
<
String
>[
'flutter'
,
'pub'
,
'upgrade'
,
'--major-versions'
];
final
RunResult
result
=
await
_processUtils
.
run
(
cmdArgs
,
workingDirectory:
workingDirectory
,
allowReentrantFlutter:
true
);
checkForErrors
(
result
,
commandDescription:
cmdArgs
.
join
(
' '
));
}
/// Runs `./gradlew tasks` in the android directory of a flutter project.
Future
<
void
>
gradlewTasks
(
String
workingDirectory
)
async
{
final
String
baseCommand
=
_platform
.
isWindows
?
'gradlew.bat'
:
'./gradlew'
;
final
List
<
String
>
cmdArgs
=
<
String
>[
baseCommand
,
'tasks'
];
final
RunResult
result
=
await
_processUtils
.
run
(
cmdArgs
,
workingDirectory:
workingDirectory
);
checkForErrors
(
result
,
commandDescription:
cmdArgs
.
join
(
' '
));
}
/// Verifies that the RunResult does not contain an error.
///
/// If an error is detected, the error can be optionally logged or exit the tool.
///
/// Passing -1 in allowedExitCodes means all exit codes are valid.
bool
checkForErrors
(
RunResult
result
,
{
List
<
int
>
allowedExitCodes
=
const
<
int
>[
0
],
String
?
commandDescription
,
bool
exit
=
true
,
bool
silent
=
false
})
{
if
(!
allowedExitCodes
.
contains
(
result
.
exitCode
)
&&
!
allowedExitCodes
.
contains
(-
1
))
{
if
(!
silent
)
{
_logger
.
printError
(
'Command encountered an error with exit code
${result.exitCode}
.'
);
if
(
commandDescription
!=
null
)
{
_logger
.
printError
(
'Command:'
);
_logger
.
printError
(
commandDescription
,
indent:
2
);
}
_logger
.
printError
(
'Stdout:'
);
_logger
.
printError
(
result
.
stdout
,
indent:
2
);
_logger
.
printError
(
'Stderr:'
);
_logger
.
printError
(
result
.
stderr
,
indent:
2
);
}
if
(
exit
)
{
throwToolExit
(
'Command failed with exit code
${result.exitCode}
'
,
exitCode:
result
.
exitCode
);
}
return
false
;
}
return
true
;
}
/// Returns true if the file does not contain any git conflit markers.
bool
conflictsResolved
(
String
contents
)
{
if
(
contents
.
contains
(
'>>>>>>>'
)
&&
contents
.
contains
(
'======='
)
&&
contents
.
contains
(
'<<<<<<<'
))
{
return
false
;
}
return
true
;
}
}
/// Defines the classification of difference between files.
enum
DiffType
{
command
,
addition
,
deletion
,
ignored
,
none
,
}
/// Tracks the output of a git diff command or any special cases such as addition of a new
/// file or deletion of an existing file.
class
DiffResult
{
DiffResult
({
required
this
.
diffType
,
this
.
diff
,
this
.
exitCode
,
})
:
assert
(
diffType
==
DiffType
.
command
&&
exitCode
!=
null
||
diffType
!=
DiffType
.
command
&&
exitCode
==
null
);
/// The diff string output by git.
final
String
?
diff
;
final
DiffType
diffType
;
/// The exit code of the command. This is zero when no diffs are found.
///
/// The exitCode is null when the diffType is not `command`.
final
int
?
exitCode
;
}
/// Data class to hold the results of a merge.
abstract
class
MergeResult
{
/// Initializes a MergeResult based off of a RunResult.
MergeResult
(
RunResult
result
,
this
.
localPath
)
:
hasConflict
=
result
.
exitCode
!=
0
,
exitCode
=
result
.
exitCode
;
/// Manually initializes a MergeResult with explicit values.
MergeResult
.
explicit
({
required
this
.
hasConflict
,
required
this
.
exitCode
,
required
this
.
localPath
,
});
/// True when there is a merge conflict.
bool
hasConflict
;
/// The exitcode of the merge command.
int
exitCode
;
/// The local path relative to the project root of the file.
String
localPath
;
}
/// The results of a string merge.
class
StringMergeResult
extends
MergeResult
{
/// Initializes a BinaryMergeResult based off of a RunResult.
StringMergeResult
(
super
.
result
,
super
.
localPath
)
:
mergedString
=
result
.
stdout
;
/// Manually initializes a StringMergeResult with explicit values.
StringMergeResult
.
explicit
({
required
this
.
mergedString
,
required
super
.
hasConflict
,
required
super
.
exitCode
,
required
super
.
localPath
,
})
:
super
.
explicit
();
/// The final merged string.
String
mergedString
;
}
/// The results of a binary merge.
class
BinaryMergeResult
extends
MergeResult
{
/// Initializes a BinaryMergeResult based off of a RunResult.
BinaryMergeResult
(
super
.
result
,
super
.
localPath
)
:
mergedBytes
=
result
.
stdout
as
Uint8List
;
/// Manually initializes a BinaryMergeResult with explicit values.
BinaryMergeResult
.
explicit
({
required
this
.
mergedBytes
,
required
super
.
hasConflict
,
required
super
.
exitCode
,
required
super
.
localPath
,
})
:
super
.
explicit
();
/// The final merged bytes.
Uint8List
mergedBytes
;
}
packages/flutter_tools/test/general.shard/migrate/migrate_manifest_test.dart
0 → 100644
View file @
90a8b056
// 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:file/memory.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/migrate/migrate_manifest.dart'
;
import
'package:flutter_tools/src/migrate/migrate_result.dart'
;
import
'package:flutter_tools/src/migrate/migrate_utils.dart'
;
import
'../../src/common.dart'
;
void
main
(
)
{
late
FileSystem
fileSystem
;
late
File
manifestFile
;
setUpAll
(()
{
fileSystem
=
MemoryFileSystem
.
test
();
manifestFile
=
fileSystem
.
file
(
'.migrate_manifest'
);
});
group
(
'manifest file parsing'
,
()
{
testWithoutContext
(
'empty fails'
,
()
async
{
manifestFile
.
writeAsStringSync
(
''
);
bool
exceptionFound
=
false
;
try
{
MigrateManifest
.
fromFile
(
manifestFile
);
}
on
Exception
catch
(
e
)
{
exceptionFound
=
true
;
expect
(
e
.
toString
(),
'Exception: Invalid .migrate_manifest file in the migrate working directory. File is not a Yaml map.'
);
}
expect
(
exceptionFound
,
true
);
});
testWithoutContext
(
'invalid name fails'
,
()
async
{
manifestFile
.
writeAsStringSync
(
'''
merged_files:
conflict_files:
added_filessssss:
deleted_files:
'''
);
bool
exceptionFound
=
false
;
try
{
MigrateManifest
.
fromFile
(
manifestFile
);
}
on
Exception
catch
(
e
)
{
exceptionFound
=
true
;
expect
(
e
.
toString
(),
'Exception: Invalid .migrate_manifest file in the migrate working directory. File is missing an entry.'
);
}
expect
(
exceptionFound
,
true
);
});
testWithoutContext
(
'missing name fails'
,
()
async
{
manifestFile
.
writeAsStringSync
(
'''
merged_files:
conflict_files:
deleted_files:
'''
);
bool
exceptionFound
=
false
;
try
{
MigrateManifest
.
fromFile
(
manifestFile
);
}
on
Exception
catch
(
e
)
{
exceptionFound
=
true
;
expect
(
e
.
toString
(),
'Exception: Invalid .migrate_manifest file in the migrate working directory. File is missing an entry.'
);
}
expect
(
exceptionFound
,
true
);
});
testWithoutContext
(
'wrong entry type fails'
,
()
async
{
manifestFile
.
writeAsStringSync
(
'''
merged_files:
conflict_files:
other_key:
added_files:
deleted_files:
'''
);
bool
exceptionFound
=
false
;
try
{
MigrateManifest
.
fromFile
(
manifestFile
);
}
on
Exception
catch
(
e
)
{
exceptionFound
=
true
;
expect
(
e
.
toString
(),
'Exception: Invalid .migrate_manifest file in the migrate working directory. Entry is not a Yaml list.'
);
}
expect
(
exceptionFound
,
true
);
});
testWithoutContext
(
'unpopulated succeeds'
,
()
async
{
manifestFile
.
writeAsStringSync
(
'''
merged_files:
conflict_files:
added_files:
deleted_files:
'''
);
final
MigrateManifest
manifest
=
MigrateManifest
.
fromFile
(
manifestFile
);
expect
(
manifest
.
mergedFiles
.
isEmpty
,
true
);
expect
(
manifest
.
conflictFiles
.
isEmpty
,
true
);
expect
(
manifest
.
addedFiles
.
isEmpty
,
true
);
expect
(
manifest
.
deletedFiles
.
isEmpty
,
true
);
});
testWithoutContext
(
'order does not matter'
,
()
async
{
manifestFile
.
writeAsStringSync
(
'''
added_files:
merged_files:
deleted_files:
conflict_files:
'''
);
final
MigrateManifest
manifest
=
MigrateManifest
.
fromFile
(
manifestFile
);
expect
(
manifest
.
mergedFiles
.
isEmpty
,
true
);
expect
(
manifest
.
conflictFiles
.
isEmpty
,
true
);
expect
(
manifest
.
addedFiles
.
isEmpty
,
true
);
expect
(
manifest
.
deletedFiles
.
isEmpty
,
true
);
});
testWithoutContext
(
'basic succeeds'
,
()
async
{
manifestFile
.
writeAsStringSync
(
'''
merged_files:
- file1
conflict_files:
- file2
added_files:
- file3
deleted_files:
- file4
'''
);
final
MigrateManifest
manifest
=
MigrateManifest
.
fromFile
(
manifestFile
);
expect
(
manifest
.
mergedFiles
.
isEmpty
,
false
);
expect
(
manifest
.
conflictFiles
.
isEmpty
,
false
);
expect
(
manifest
.
addedFiles
.
isEmpty
,
false
);
expect
(
manifest
.
deletedFiles
.
isEmpty
,
false
);
expect
(
manifest
.
mergedFiles
.
length
,
1
);
expect
(
manifest
.
conflictFiles
.
length
,
1
);
expect
(
manifest
.
addedFiles
.
length
,
1
);
expect
(
manifest
.
deletedFiles
.
length
,
1
);
expect
(
manifest
.
mergedFiles
[
0
],
'file1'
);
expect
(
manifest
.
conflictFiles
[
0
],
'file2'
);
expect
(
manifest
.
addedFiles
[
0
],
'file3'
);
expect
(
manifest
.
deletedFiles
[
0
],
'file4'
);
});
testWithoutContext
(
'basic multi-list succeeds'
,
()
async
{
manifestFile
.
writeAsStringSync
(
'''
merged_files:
- file1
- file2
conflict_files:
added_files:
deleted_files:
- file3
- file4
'''
);
final
MigrateManifest
manifest
=
MigrateManifest
.
fromFile
(
manifestFile
);
expect
(
manifest
.
mergedFiles
.
isEmpty
,
false
);
expect
(
manifest
.
conflictFiles
.
isEmpty
,
true
);
expect
(
manifest
.
addedFiles
.
isEmpty
,
true
);
expect
(
manifest
.
deletedFiles
.
isEmpty
,
false
);
expect
(
manifest
.
mergedFiles
.
length
,
2
);
expect
(
manifest
.
conflictFiles
.
length
,
0
);
expect
(
manifest
.
addedFiles
.
length
,
0
);
expect
(
manifest
.
deletedFiles
.
length
,
2
);
expect
(
manifest
.
mergedFiles
[
0
],
'file1'
);
expect
(
manifest
.
mergedFiles
[
1
],
'file2'
);
expect
(
manifest
.
deletedFiles
[
0
],
'file3'
);
expect
(
manifest
.
deletedFiles
[
1
],
'file4'
);
});
});
group
(
'manifest MigrateResult creation'
,
()
{
testWithoutContext
(
'empty MigrateResult'
,
()
async
{
final
MigrateManifest
manifest
=
MigrateManifest
(
migrateRootDir:
fileSystem
.
directory
(
'root'
),
migrateResult:
MigrateResult
(
mergeResults:
<
MergeResult
>[],
addedFiles:
<
FilePendingMigration
>[],
deletedFiles:
<
FilePendingMigration
>[],
mergeTypeMap:
<
String
,
MergeType
>{},
diffMap:
<
String
,
DiffResult
>{},
tempDirectories:
<
Directory
>[],
sdkDirs:
<
String
,
Directory
>{},
));
expect
(
manifest
.
mergedFiles
.
isEmpty
,
true
);
expect
(
manifest
.
conflictFiles
.
isEmpty
,
true
);
expect
(
manifest
.
addedFiles
.
isEmpty
,
true
);
expect
(
manifest
.
deletedFiles
.
isEmpty
,
true
);
});
testWithoutContext
(
'simple MigrateResult'
,
()
async
{
final
MigrateManifest
manifest
=
MigrateManifest
(
migrateRootDir:
fileSystem
.
directory
(
'root'
),
migrateResult:
MigrateResult
(
mergeResults:
<
MergeResult
>[
StringMergeResult
.
explicit
(
localPath:
'merged_file'
,
mergedString:
'str'
,
hasConflict:
false
,
exitCode:
0
,
),
StringMergeResult
.
explicit
(
localPath:
'conflict_file'
,
mergedString:
'<<<<<<<<<<<'
,
hasConflict:
true
,
exitCode:
1
,
),
],
addedFiles:
<
FilePendingMigration
>[
FilePendingMigration
(
'added_file'
,
fileSystem
.
file
(
'added_file'
))],
deletedFiles:
<
FilePendingMigration
>[
FilePendingMigration
(
'deleted_file'
,
fileSystem
.
file
(
'deleted_file'
))],
// The following are ignored by the manifest.
mergeTypeMap:
<
String
,
MergeType
>{
'test'
:
MergeType
.
threeWay
},
diffMap:
<
String
,
DiffResult
>{},
tempDirectories:
<
Directory
>[],
sdkDirs:
<
String
,
Directory
>{},
));
expect
(
manifest
.
mergedFiles
.
isEmpty
,
false
);
expect
(
manifest
.
conflictFiles
.
isEmpty
,
false
);
expect
(
manifest
.
addedFiles
.
isEmpty
,
false
);
expect
(
manifest
.
deletedFiles
.
isEmpty
,
false
);
expect
(
manifest
.
mergedFiles
.
length
,
1
);
expect
(
manifest
.
conflictFiles
.
length
,
1
);
expect
(
manifest
.
addedFiles
.
length
,
1
);
expect
(
manifest
.
deletedFiles
.
length
,
1
);
expect
(
manifest
.
mergedFiles
[
0
],
'merged_file'
);
expect
(
manifest
.
conflictFiles
[
0
],
'conflict_file'
);
expect
(
manifest
.
addedFiles
[
0
],
'added_file'
);
expect
(
manifest
.
deletedFiles
[
0
],
'deleted_file'
);
});
});
group
(
'manifest write'
,
()
{
testWithoutContext
(
'empty'
,
()
async
{
manifestFile
.
writeAsStringSync
(
'''
merged_files:
conflict_files:
added_files:
deleted_files:
'''
);
final
MigrateManifest
manifest
=
MigrateManifest
.
fromFile
(
manifestFile
);
expect
(
manifest
.
mergedFiles
.
isEmpty
,
true
);
expect
(
manifest
.
conflictFiles
.
isEmpty
,
true
);
expect
(
manifest
.
addedFiles
.
isEmpty
,
true
);
expect
(
manifest
.
deletedFiles
.
isEmpty
,
true
);
manifest
.
writeFile
();
expect
(
manifestFile
.
readAsStringSync
(),
'''
merged_files:
conflict_files:
added_files:
deleted_files:
'''
);
});
testWithoutContext
(
'basic multi-list'
,
()
async
{
manifestFile
.
writeAsStringSync
(
'''
merged_files:
- file1
- file2
conflict_files:
added_files:
deleted_files:
- file3
- file4
'''
);
final
MigrateManifest
manifest
=
MigrateManifest
.
fromFile
(
manifestFile
);
expect
(
manifest
.
mergedFiles
.
isEmpty
,
false
);
expect
(
manifest
.
conflictFiles
.
isEmpty
,
true
);
expect
(
manifest
.
addedFiles
.
isEmpty
,
true
);
expect
(
manifest
.
deletedFiles
.
isEmpty
,
false
);
expect
(
manifest
.
mergedFiles
.
length
,
2
);
expect
(
manifest
.
conflictFiles
.
length
,
0
);
expect
(
manifest
.
addedFiles
.
length
,
0
);
expect
(
manifest
.
deletedFiles
.
length
,
2
);
expect
(
manifest
.
mergedFiles
[
0
],
'file1'
);
expect
(
manifest
.
mergedFiles
[
1
],
'file2'
);
expect
(
manifest
.
deletedFiles
[
0
],
'file3'
);
expect
(
manifest
.
deletedFiles
[
1
],
'file4'
);
manifest
.
writeFile
();
expect
(
manifestFile
.
readAsStringSync
(),
'''
merged_files:
- file1
- file2
conflict_files:
added_files:
deleted_files:
- file3
- file4
'''
);
});
});
}
packages/flutter_tools/test/integration.shard/migrate_utils_test.dart
0 → 100644
View file @
90a8b056
// 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.
// @dart = 2.8
import
'package:file/file.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/base/logger.dart'
;
import
'package:flutter_tools/src/base/process.dart'
;
import
'package:flutter_tools/src/globals.dart'
as
globals
;
import
'package:flutter_tools/src/migrate/migrate_utils.dart'
;
import
'../src/common.dart'
;
void
main
(
)
{
BufferLogger
logger
;
FileSystem
fileSystem
;
Directory
projectRoot
;
String
projectRootPath
;
MigrateUtils
utils
;
ProcessUtils
processUtils
;
setUpAll
(()
async
{
fileSystem
=
globals
.
localFileSystem
;
logger
=
BufferLogger
.
test
();
utils
=
MigrateUtils
(
logger:
logger
,
fileSystem:
fileSystem
,
platform:
globals
.
platform
,
processManager:
globals
.
processManager
,
);
processUtils
=
ProcessUtils
(
processManager:
globals
.
processManager
,
logger:
logger
);
});
group
(
'git'
,
()
{
setUp
(()
async
{
projectRoot
=
fileSystem
.
systemTempDirectory
.
createTempSync
(
'flutter_migrate_utils_test'
);
projectRoot
.
createSync
(
recursive:
true
);
projectRootPath
=
projectRoot
.
path
;
});
testWithoutContext
(
'init'
,
()
async
{
expect
(
projectRoot
.
existsSync
(),
true
);
expect
(
projectRoot
.
childDirectory
(
'.git'
).
existsSync
(),
false
);
await
utils
.
gitInit
(
projectRootPath
);
expect
(
projectRoot
.
childDirectory
(
'.git'
).
existsSync
(),
true
);
});
testWithoutContext
(
'isGitIgnored'
,
()
async
{
expect
(
projectRoot
.
existsSync
(),
true
);
expect
(
projectRoot
.
childDirectory
(
'.git'
).
existsSync
(),
false
);
await
utils
.
gitInit
(
projectRootPath
);
expect
(
projectRoot
.
childDirectory
(
'.git'
).
existsSync
(),
true
);
projectRoot
.
childFile
(
'.gitignore'
)
..
createSync
()
..
writeAsStringSync
(
'ignored_file.dart'
,
flush:
true
);
expect
(
await
utils
.
isGitIgnored
(
'ignored_file.dart'
,
projectRootPath
),
true
);
expect
(
await
utils
.
isGitIgnored
(
'other_file.dart'
,
projectRootPath
),
false
);
});
testWithoutContext
(
'isGitRepo'
,
()
async
{
expect
(
projectRoot
.
existsSync
(),
true
);
expect
(
projectRoot
.
childDirectory
(
'.git'
).
existsSync
(),
false
);
expect
(
await
utils
.
isGitRepo
(
projectRootPath
),
false
);
await
utils
.
gitInit
(
projectRootPath
);
expect
(
projectRoot
.
childDirectory
(
'.git'
).
existsSync
(),
true
);
expect
(
await
utils
.
isGitRepo
(
projectRootPath
),
true
);
expect
(
await
utils
.
isGitRepo
(
projectRoot
.
parent
.
path
),
false
);
});
testWithoutContext
(
'hasUncommittedChanges false on clean repo'
,
()
async
{
expect
(
projectRoot
.
existsSync
(),
true
);
expect
(
projectRoot
.
childDirectory
(
'.git'
).
existsSync
(),
false
);
await
utils
.
gitInit
(
projectRootPath
);
expect
(
projectRoot
.
childDirectory
(
'.git'
).
existsSync
(),
true
);
projectRoot
.
childFile
(
'.gitignore'
)
..
createSync
()
..
writeAsStringSync
(
'ignored_file.dart'
,
flush:
true
);
await
processUtils
.
run
(<
String
>[
'git'
,
'add'
,
'.'
],
workingDirectory:
projectRootPath
);
await
processUtils
.
run
(<
String
>[
'git'
,
'commit'
,
'-m'
,
'Initial commit'
],
workingDirectory:
projectRootPath
);
expect
(
await
utils
.
hasUncommittedChanges
(
projectRootPath
),
false
);
});
testWithoutContext
(
'hasUncommittedChanges true on dirty repo'
,
()
async
{
expect
(
projectRoot
.
existsSync
(),
true
);
expect
(
projectRoot
.
childDirectory
(
'.git'
).
existsSync
(),
false
);
await
utils
.
gitInit
(
projectRootPath
);
expect
(
projectRoot
.
childDirectory
(
'.git'
).
existsSync
(),
true
);
projectRoot
.
childFile
(
'some_file.dart'
)
..
createSync
()
..
writeAsStringSync
(
'void main() {}'
,
flush:
true
);
expect
(
await
utils
.
hasUncommittedChanges
(
projectRootPath
),
true
);
});
testWithoutContext
(
'diffFiles'
,
()
async
{
expect
(
projectRoot
.
existsSync
(),
true
);
expect
(
projectRoot
.
childDirectory
(
'.git'
).
existsSync
(),
false
);
await
utils
.
gitInit
(
projectRootPath
);
expect
(
projectRoot
.
childDirectory
(
'.git'
).
existsSync
(),
true
);
final
File
file1
=
projectRoot
.
childFile
(
'some_file.dart'
)
..
createSync
()
..
writeAsStringSync
(
'void main() {}
\n
'
,
flush:
true
);
final
File
file2
=
projectRoot
.
childFile
(
'some_other_file.dart'
);
DiffResult
result
=
await
utils
.
diffFiles
(
file1
,
file2
);
expect
(
result
.
diff
,
null
);
expect
(
result
.
diffType
,
DiffType
.
deletion
);
expect
(
result
.
exitCode
,
null
);
result
=
await
utils
.
diffFiles
(
file2
,
file1
);
expect
(
result
.
diff
,
null
);
expect
(
result
.
diffType
,
DiffType
.
addition
);
expect
(
result
.
exitCode
,
null
);
file2
.
createSync
();
file2
.
writeAsStringSync
(
'void main() {}
\n
'
,
flush:
true
);
result
=
await
utils
.
diffFiles
(
file1
,
file2
);
expect
(
result
.
diff
,
''
);
expect
(
result
.
diffType
,
DiffType
.
command
);
expect
(
result
.
exitCode
,
0
);
file2
.
writeAsStringSync
(
'void main() {}
\n
a second line
\n
a third line
\n
'
,
flush:
true
);
result
=
await
utils
.
diffFiles
(
file1
,
file2
);
expect
(
result
.
diff
,
contains
(
'@@ -1 +1,3 @@
\n
void main() {}
\n
+a second line
\n
+a third line'
));
expect
(
result
.
diffType
,
DiffType
.
command
);
expect
(
result
.
exitCode
,
1
);
});
testWithoutContext
(
'merge'
,
()
async
{
expect
(
projectRoot
.
existsSync
(),
true
);
expect
(
projectRoot
.
childDirectory
(
'.git'
).
existsSync
(),
false
);
await
utils
.
gitInit
(
projectRootPath
);
expect
(
projectRoot
.
childDirectory
(
'.git'
).
existsSync
(),
true
);
final
File
file1
=
projectRoot
.
childFile
(
'some_file.dart'
);
file1
.
createSync
();
file1
.
writeAsStringSync
(
'void main() {}
\n\n
line1
\n
line2
\n
line3
\n
line4
\n
line5
\n
'
,
flush:
true
);
final
File
file2
=
projectRoot
.
childFile
(
'some_other_file.dart'
);
file2
.
createSync
();
file2
.
writeAsStringSync
(
'void main() {}
\n\n
line1
\n
line2
\n
line3.0
\n
line3.5
\n
line4
\n
line5
\n
'
,
flush:
true
);
final
File
file3
=
projectRoot
.
childFile
(
'some_other_third_file.dart'
);
file3
.
createSync
();
file3
.
writeAsStringSync
(
'void main() {}
\n\n
line2
\n
line3
\n
line4
\n
line5
\n
'
,
flush:
true
);
StringMergeResult
result
=
await
utils
.
gitMergeFile
(
base:
file1
.
path
,
current:
file2
.
path
,
target:
file3
.
path
,
localPath:
'some_file.dart'
,
)
as
StringMergeResult
;
expect
(
result
.
mergedString
,
'void main() {}
\n\n
line2
\n
line3.0
\n
line3.5
\n
line4
\n
line5
\n
'
);
expect
(
result
.
hasConflict
,
false
);
expect
(
result
.
exitCode
,
0
);
file3
.
writeAsStringSync
(
'void main() {}
\n\n
line1
\n
line2
\n
line3.1
\n
line3.5
\n
line4
\n
line5
\n
'
,
flush:
true
);
result
=
await
utils
.
gitMergeFile
(
base:
file1
.
path
,
current:
file2
.
path
,
target:
file3
.
path
,
localPath:
'some_file.dart'
,
)
as
StringMergeResult
;
expect
(
result
.
mergedString
,
contains
(
'line3.0
\n
=======
\n
line3.1
\n
>>>>>>>'
));
expect
(
result
.
hasConflict
,
true
);
expect
(
result
.
exitCode
,
1
);
// Two way merge
result
=
await
utils
.
gitMergeFile
(
base:
file1
.
path
,
current:
file1
.
path
,
target:
file3
.
path
,
localPath:
'some_file.dart'
,
)
as
StringMergeResult
;
expect
(
result
.
mergedString
,
'void main() {}
\n\n
line1
\n
line2
\n
line3.1
\n
line3.5
\n
line4
\n
line5
\n
'
);
expect
(
result
.
hasConflict
,
false
);
expect
(
result
.
exitCode
,
0
);
});
});
group
(
'legacy app creation'
,
()
{
testWithoutContext
(
'clone and create'
,
()
async
{
projectRoot
=
fileSystem
.
systemTempDirectory
.
createTempSync
(
'flutter_sdk_test'
);
const
String
revision
=
'5391447fae6209bb21a89e6a5a6583cac1af9b4b'
;
expect
(
await
utils
.
cloneFlutter
(
revision
,
projectRoot
.
path
),
true
);
expect
(
projectRoot
.
childFile
(
'README.md'
).
existsSync
(),
true
);
final
Directory
appDir
=
fileSystem
.
systemTempDirectory
.
createTempSync
(
'flutter_app'
);
await
utils
.
createFromTemplates
(
projectRoot
.
childDirectory
(
'bin'
).
path
,
name:
'testapp'
,
androidLanguage:
'java'
,
iosLanguage:
'objc'
,
outputDirectory:
appDir
.
path
,
);
expect
(
appDir
.
childFile
(
'pubspec.yaml'
).
existsSync
(),
true
);
expect
(
appDir
.
childFile
(
'.metadata'
).
existsSync
(),
true
);
expect
(
appDir
.
childFile
(
'.metadata'
).
readAsStringSync
(),
contains
(
revision
));
expect
(
appDir
.
childDirectory
(
'android'
).
existsSync
(),
true
);
expect
(
appDir
.
childDirectory
(
'ios'
).
existsSync
(),
true
);
expect
(
appDir
.
childDirectory
(
'web'
).
existsSync
(),
false
);
projectRoot
.
deleteSync
(
recursive:
true
);
});
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment