fix(shorebird_cli): add helpful error message if cache clear fails on Windows (#964)

This commit is contained in:
Bryan Oltman
2023-08-01 14:32:39 -04:00
committed by GitHub
parent 65f10929cb
commit 4b0be6c91f
2 changed files with 97 additions and 4 deletions
@@ -1,9 +1,11 @@
import 'dart:async';
import 'dart:io';
import 'package:mason_logger/mason_logger.dart';
import 'package:shorebird_cli/src/cache.dart';
import 'package:shorebird_cli/src/command.dart';
import 'package:shorebird_cli/src/logger.dart';
import 'package:shorebird_cli/src/platform.dart';
import 'package:shorebird_cli/src/shorebird_config_mixin.dart';
/// {@template clean_cache_command}
@@ -25,8 +27,34 @@ class CleanCacheCommand extends ShorebirdCommand with ShorebirdConfigMixin {
@override
Future<int> run() async {
cache.clear();
logger.success('✅ Cleared Cache!');
final progress = logger.progress('Clearing Cache');
try {
cache.clear();
} on FileSystemException catch (error) {
final cachePath = Cache.shorebirdCacheDirectory.path;
progress.fail(
'''Failed to delete cache directory $cachePath: $error''',
);
if (!platform.isWindows) {
return ExitCode.software.code;
}
final superuserLink = link(
uri: Uri.parse(
'https://superuser.com/questions/1333118/cant-delete-empty-folder-because-it-is-used',
),
);
logger.info(
'''
This could be because a program is using a file in the cache directory. To find and stop such a program, see:
${lightCyan.wrap(superuserLink)}
''',
);
return ExitCode.software.code;
}
progress.complete('Cleared cache');
return ExitCode.success.code;
}
}
@@ -1,19 +1,30 @@
import 'dart:io';
import 'package:mason_logger/mason_logger.dart';
import 'package:mocktail/mocktail.dart';
import 'package:path/path.dart' as p;
import 'package:platform/platform.dart';
import 'package:scoped/scoped.dart';
import 'package:shorebird_cli/src/cache.dart';
import 'package:shorebird_cli/src/commands/commands.dart';
import 'package:shorebird_cli/src/logger.dart';
import 'package:shorebird_cli/src/platform.dart';
import 'package:test/test.dart';
class _MockCache extends Mock implements Cache {}
class _MockLogger extends Mock implements Logger {}
class _MockCache extends Mock implements Cache {}
class _MockPlatform extends Mock implements Platform {}
class _MockProgress extends Mock implements Progress {}
void main() {
group('cache clean', () {
late Cache cache;
late Logger logger;
late Platform platform;
late Progress progress;
late CleanCacheCommand command;
R runWithOverrides<R>(R Function() body) {
@@ -22,6 +33,7 @@ void main() {
values: {
cacheRef.overrideWith(() => cache),
loggerRef.overrideWith(() => logger),
platformRef.overrideWith(() => platform),
},
);
}
@@ -29,7 +41,21 @@ void main() {
setUp(() {
cache = _MockCache();
logger = _MockLogger();
platform = _MockPlatform();
progress = _MockProgress();
command = runWithOverrides(CleanCacheCommand.new);
when(() => logger.progress(any())).thenReturn(progress);
when(() => platform.script).thenReturn(
Uri.file(
p.join(
Directory.systemTemp.createTempSync().path,
'bin',
'cache',
'shorebird.snapshot',
),
),
);
});
test('has a description', () {
@@ -39,8 +65,47 @@ void main() {
test('clears the cache', () async {
final result = await runWithOverrides(command.run);
expect(result, equals(ExitCode.success.code));
verify(() => logger.success('Cleared Cache!')).called(1);
verify(() => progress.complete('Cleared cache')).called(1);
verify(cache.clear).called(1);
});
group('on failure', () {
group('on Windows', () {
setUp(() {
when(() => platform.isWindows).thenReturn(true);
});
test('tells the user how to find the issue and exits with code 70',
() async {
when(() => cache.clear()).thenThrow(
const FileSystemException('Failed to delete'),
);
final result = await runWithOverrides(command.run);
expect(result, equals(ExitCode.software.code));
verify(() => progress.fail(any())).called(1);
verify(() => logger.info(any())).called(1);
});
});
group('on a non-Windows OS', () {
setUp(() {
when(() => platform.isWindows).thenReturn(false);
});
test('prints error message and exits with code 70', () async {
when(() => cache.clear()).thenThrow(
const FileSystemException('Failed to delete'),
);
final result = await runWithOverrides(command.run);
expect(result, equals(ExitCode.software.code));
verify(() => progress.fail(any())).called(1);
verifyNever(() => logger.info(any()));
});
});
});
});
}