mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[ Widget Preview ] Fix type error when retrieving flags from persistent preferences (#176546)
Also moves `dtd_services_test.dart` to actually use the `WidgetPreviewScaffoldDtdServices` implementation, which would have caught the typecast issue in the implementation.
This commit is contained in:
parent
b95ab963a4
commit
0400e2cf77
@ -92,12 +92,14 @@ class WidgetPreviewScaffoldDtdServices with DtdEditorService {
|
||||
/// preferences map.
|
||||
///
|
||||
/// Returns null if [key] is not in the map.
|
||||
Future<String?> getPreference(String key) async {
|
||||
Future<Object?> getPreference(String key) async {
|
||||
try {
|
||||
final response = StringResponse.fromDTDResponse(
|
||||
(await _call(kGetPreference, params: {'key': key}))!,
|
||||
);
|
||||
return response.value;
|
||||
final response = await _call(kGetPreference, params: {'key': key});
|
||||
return switch (response?.type) {
|
||||
'StringResponse' => StringResponse.fromDTDResponse(response!).value,
|
||||
'BoolResponse' => BoolResponse.fromDTDResponse(response!).value,
|
||||
_ => throw StateError('Unexpected response type: ${response?.type}'),
|
||||
};
|
||||
} on RpcException catch (e) {
|
||||
if (e.code == kNoValueForKey) {
|
||||
return null;
|
||||
@ -110,15 +112,12 @@ class WidgetPreviewScaffoldDtdServices with DtdEditorService {
|
||||
///
|
||||
/// If [key] is not set, [defaultValue] is returned.
|
||||
Future<bool> getFlag(String key, {bool defaultValue = false}) async {
|
||||
final result = await getPreference(key);
|
||||
if (result == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return bool.tryParse(result) ?? defaultValue;
|
||||
final result = await getPreference(key) as bool?;
|
||||
return result ?? defaultValue;
|
||||
}
|
||||
|
||||
/// Sets [key] to [value] in the persistent preferences map.
|
||||
Future<void> setPreference(String key, Object value) async {
|
||||
Future<void> setPreference(String key, Object? value) async {
|
||||
await _call(kSetPreference, params: {'key': key, 'value': value});
|
||||
}
|
||||
|
||||
|
||||
@ -1,146 +0,0 @@
|
||||
// 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 'package:dtd/dtd.dart';
|
||||
import 'package:file/memory.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/convert.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/widget_preview/dtd_services.dart';
|
||||
import 'package:flutter_tools/src/widget_preview/persistent_preferences.dart';
|
||||
import 'package:json_rpc_2/json_rpc_2.dart';
|
||||
import 'package:test/fake.dart';
|
||||
|
||||
import '../../../src/common.dart';
|
||||
import '../../../src/context.dart';
|
||||
import '../../../src/test_flutter_command_runner.dart';
|
||||
import '../utils/project_testing_utils.dart';
|
||||
|
||||
class FakeFlutterProject extends Fake implements FlutterProject {
|
||||
FakeFlutterProject();
|
||||
}
|
||||
|
||||
void main() {
|
||||
late WidgetPreviewDtdServices dtdServer;
|
||||
late LoggingProcessManager loggingProcessManager;
|
||||
late Logger logger;
|
||||
|
||||
setUp(() async {
|
||||
loggingProcessManager = LoggingProcessManager();
|
||||
logger = BufferLogger.test();
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await dtdServer.shutdownHooks.runShutdownHooks(logger);
|
||||
});
|
||||
|
||||
group('$WidgetPreviewDtdServices', () {
|
||||
testUsingContext(
|
||||
'handles ${WidgetPreviewDtdServices.kHotRestartPreviewer} invocations',
|
||||
() async {
|
||||
// Start DTD and register the widget preview DTD services with a custom handler for hot
|
||||
// restart requests.
|
||||
final hotRestartRequestCompleter = Completer<void>();
|
||||
dtdServer = WidgetPreviewDtdServices(
|
||||
fs: MemoryFileSystem.test(),
|
||||
logger: logger,
|
||||
shutdownHooks: ShutdownHooks(),
|
||||
dtdLauncher: DtdLauncher(
|
||||
logger: logger,
|
||||
artifacts: globals.artifacts!,
|
||||
processManager: globals.processManager,
|
||||
),
|
||||
onHotRestartPreviewerRequest: hotRestartRequestCompleter.complete,
|
||||
project: FakeFlutterProject(),
|
||||
);
|
||||
await dtdServer.launchAndConnect();
|
||||
|
||||
// Connect to the DTD instance and invoke the hot restart endpoint.
|
||||
final DartToolingDaemon dtd = await DartToolingDaemon.connect(dtdServer.dtdUri!);
|
||||
final DTDResponse response = await dtd.call(
|
||||
WidgetPreviewDtdServices.kWidgetPreviewService,
|
||||
WidgetPreviewDtdServices.kHotRestartPreviewer,
|
||||
);
|
||||
|
||||
// This will throw if the response is not an instance of Success.
|
||||
expect(() => Success.fromDTDResponse(response), returnsNormally);
|
||||
|
||||
// Ensure the custom handler is actually invoked.
|
||||
await hotRestartRequestCompleter.future;
|
||||
},
|
||||
overrides: <Type, Generator>{ProcessManager: () => loggingProcessManager},
|
||||
);
|
||||
|
||||
testUsingContext('can set and retreive values from $PersistentPreferences', () async {
|
||||
dtdServer = WidgetPreviewDtdServices(
|
||||
fs: MemoryFileSystem.test(),
|
||||
logger: logger,
|
||||
shutdownHooks: ShutdownHooks(),
|
||||
dtdLauncher: DtdLauncher(
|
||||
logger: logger,
|
||||
artifacts: globals.artifacts!,
|
||||
processManager: globals.processManager,
|
||||
),
|
||||
onHotRestartPreviewerRequest: () {},
|
||||
project: FakeFlutterProject(),
|
||||
);
|
||||
await dtdServer.launchAndConnect();
|
||||
|
||||
// The properties file should be created by the PersistentProperties constructor.
|
||||
final File preferencesFile = dtdServer.preferences.file;
|
||||
expect(preferencesFile.existsSync(), true);
|
||||
|
||||
// Connect to the DTD instance.
|
||||
final DartToolingDaemon dtd = await DartToolingDaemon.connect(dtdServer.dtdUri!);
|
||||
|
||||
Future<String?> getPreference(String key) async {
|
||||
try {
|
||||
return StringResponse.fromDTDResponse(
|
||||
await dtd.call(
|
||||
WidgetPreviewDtdServices.kWidgetPreviewService,
|
||||
WidgetPreviewDtdServices.kGetPreference,
|
||||
params: {'key': key},
|
||||
),
|
||||
).value;
|
||||
} on RpcException catch (e) {
|
||||
if (e.code == WidgetPreviewDtdServices.kNoValueForKey) {
|
||||
return null;
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setPreference(String key, String? value) async {
|
||||
await dtd.call(
|
||||
WidgetPreviewDtdServices.kWidgetPreviewService,
|
||||
WidgetPreviewDtdServices.kSetPreference,
|
||||
params: {'key': key, 'value': value},
|
||||
);
|
||||
}
|
||||
|
||||
const kTestKey = 'myKey';
|
||||
|
||||
// The preferences file should be empty.
|
||||
expect(await getPreference(kTestKey), null);
|
||||
expect(preferencesFile.readAsStringSync(), isEmpty);
|
||||
|
||||
// Set a preference and ensure it's read back.
|
||||
const kFirstValue = 'foo';
|
||||
await setPreference(kTestKey, kFirstValue);
|
||||
expect(await getPreference(kTestKey), kFirstValue);
|
||||
expect(json.decode(preferencesFile.readAsStringSync()), {kTestKey: kFirstValue});
|
||||
|
||||
// Overwrite kTestKey and ensure it's read back.
|
||||
const kSecondValue = 'bar';
|
||||
await setPreference(kTestKey, kSecondValue);
|
||||
expect(await getPreference(kTestKey), kSecondValue);
|
||||
expect(json.decode(preferencesFile.readAsStringSync()), {kTestKey: kSecondValue});
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -92,12 +92,14 @@ class WidgetPreviewScaffoldDtdServices with DtdEditorService {
|
||||
/// preferences map.
|
||||
///
|
||||
/// Returns null if [key] is not in the map.
|
||||
Future<String?> getPreference(String key) async {
|
||||
Future<Object?> getPreference(String key) async {
|
||||
try {
|
||||
final response = StringResponse.fromDTDResponse(
|
||||
(await _call(kGetPreference, params: {'key': key}))!,
|
||||
);
|
||||
return response.value;
|
||||
final response = await _call(kGetPreference, params: {'key': key});
|
||||
return switch (response?.type) {
|
||||
'StringResponse' => StringResponse.fromDTDResponse(response!).value,
|
||||
'BoolResponse' => BoolResponse.fromDTDResponse(response!).value,
|
||||
_ => throw StateError('Unexpected response type: ${response?.type}'),
|
||||
};
|
||||
} on RpcException catch (e) {
|
||||
if (e.code == kNoValueForKey) {
|
||||
return null;
|
||||
@ -110,15 +112,12 @@ class WidgetPreviewScaffoldDtdServices with DtdEditorService {
|
||||
///
|
||||
/// If [key] is not set, [defaultValue] is returned.
|
||||
Future<bool> getFlag(String key, {bool defaultValue = false}) async {
|
||||
final result = await getPreference(key);
|
||||
if (result == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return bool.tryParse(result) ?? defaultValue;
|
||||
final result = await getPreference(key) as bool?;
|
||||
return result ?? defaultValue;
|
||||
}
|
||||
|
||||
/// Sets [key] to [value] in the persistent preferences map.
|
||||
Future<void> setPreference(String key, Object value) async {
|
||||
Future<void> setPreference(String key, Object? value) async {
|
||||
await _call(kSetPreference, params: {'key': key, 'value': value});
|
||||
}
|
||||
|
||||
|
||||
@ -45,8 +45,8 @@ dependencies:
|
||||
term_glyph: 1.2.2
|
||||
test_api: 0.7.7
|
||||
typed_data: 1.4.0
|
||||
unified_analytics: 8.0.1
|
||||
url_launcher_android: 6.3.22
|
||||
unified_analytics: 8.0.5
|
||||
url_launcher_android: 6.3.23
|
||||
url_launcher_ios: 6.3.4
|
||||
url_launcher_linux: 3.2.1
|
||||
url_launcher_macos: 3.2.3
|
||||
@ -59,6 +59,11 @@ dependencies:
|
||||
web_socket: 1.0.1
|
||||
web_socket_channel: 3.0.3
|
||||
|
||||
dev_dependencies:
|
||||
flutter_tools:
|
||||
path: ../../../
|
||||
test: 1.26.3
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
# PUBSPEC CHECKSUM: fdcqn8
|
||||
# PUBSPEC CHECKSUM: 130obr
|
||||
|
||||
@ -0,0 +1,140 @@
|
||||
// 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 'package:file/memory.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/convert.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/widget_preview/dtd_services.dart';
|
||||
import 'package:flutter_tools/src/widget_preview/persistent_preferences.dart';
|
||||
import 'package:test/fake.dart';
|
||||
import 'package:widget_preview_scaffold/src/dtd/dtd_services.dart';
|
||||
|
||||
import '../../../src/common.dart';
|
||||
import '../../../src/context.dart';
|
||||
import '../../../src/test_flutter_command_runner.dart';
|
||||
import '../../../commands.shard/permeable/utils/project_testing_utils.dart';
|
||||
|
||||
class FakeFlutterProject extends Fake implements FlutterProject {
|
||||
FakeFlutterProject();
|
||||
}
|
||||
|
||||
void main() {
|
||||
late WidgetPreviewDtdServices dtdServer;
|
||||
late LoggingProcessManager loggingProcessManager;
|
||||
late Logger logger;
|
||||
|
||||
setUp(() async {
|
||||
loggingProcessManager = LoggingProcessManager();
|
||||
logger = BufferLogger.test();
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await dtdServer.shutdownHooks.runShutdownHooks(logger);
|
||||
});
|
||||
|
||||
group('$WidgetPreviewDtdServices', () {
|
||||
testUsingContext(
|
||||
'handles ${WidgetPreviewDtdServices.kHotRestartPreviewer} invocations',
|
||||
() async {
|
||||
// Start DTD and register the widget preview DTD services with a custom handler for hot
|
||||
// restart requests.
|
||||
final hotRestartRequestCompleter = Completer<void>();
|
||||
dtdServer = WidgetPreviewDtdServices(
|
||||
fs: MemoryFileSystem.test(),
|
||||
logger: logger,
|
||||
shutdownHooks: ShutdownHooks(),
|
||||
dtdLauncher: DtdLauncher(
|
||||
logger: logger,
|
||||
artifacts: globals.artifacts!,
|
||||
processManager: globals.processManager,
|
||||
),
|
||||
onHotRestartPreviewerRequest: hotRestartRequestCompleter.complete,
|
||||
project: FakeFlutterProject(),
|
||||
);
|
||||
await dtdServer.launchAndConnect();
|
||||
|
||||
// Connect to the DTD instance and invoke the hot restart endpoint.
|
||||
final dtd = WidgetPreviewScaffoldDtdServices();
|
||||
await dtd.connect(dtdUri: dtdServer.dtdUri);
|
||||
|
||||
await dtd.hotRestartPreviewer();
|
||||
|
||||
// Ensure the custom handler is actually invoked.
|
||||
await hotRestartRequestCompleter.future;
|
||||
},
|
||||
overrides: <Type, Generator>{ProcessManager: () => loggingProcessManager},
|
||||
);
|
||||
|
||||
testUsingContext(
|
||||
'can set and retreive values from $PersistentPreferences',
|
||||
() async {
|
||||
dtdServer = WidgetPreviewDtdServices(
|
||||
fs: MemoryFileSystem.test(),
|
||||
logger: logger,
|
||||
shutdownHooks: ShutdownHooks(),
|
||||
dtdLauncher: DtdLauncher(
|
||||
logger: logger,
|
||||
artifacts: globals.artifacts!,
|
||||
processManager: globals.processManager,
|
||||
),
|
||||
onHotRestartPreviewerRequest: () {},
|
||||
project: FakeFlutterProject(),
|
||||
);
|
||||
await dtdServer.launchAndConnect();
|
||||
|
||||
// The properties file should be created by the PersistentProperties constructor.
|
||||
final File preferencesFile = dtdServer.preferences.file;
|
||||
expect(preferencesFile.existsSync(), true);
|
||||
|
||||
// Connect to the DTD instance.
|
||||
final dtd = WidgetPreviewScaffoldDtdServices();
|
||||
await dtd.connect(dtdUri: dtdServer.dtdUri);
|
||||
|
||||
const kTestKey = 'myKey';
|
||||
|
||||
// The preferences file should be empty.
|
||||
expect(await dtd.getPreference(kTestKey), null);
|
||||
expect(preferencesFile.readAsStringSync(), isEmpty);
|
||||
|
||||
// Set a preference and ensure it's read back.
|
||||
const kFirstValue = 'foo';
|
||||
await dtd.setPreference(kTestKey, kFirstValue);
|
||||
expect(await dtd.getPreference(kTestKey), kFirstValue);
|
||||
expect(json.decode(preferencesFile.readAsStringSync()), {
|
||||
kTestKey: kFirstValue,
|
||||
});
|
||||
|
||||
// Overwrite kTestKey and ensure it's read back.
|
||||
const kSecondValue = 'bar';
|
||||
await dtd.setPreference(kTestKey, kSecondValue);
|
||||
expect(await dtd.getPreference(kTestKey), kSecondValue);
|
||||
expect(json.decode(preferencesFile.readAsStringSync()), {
|
||||
kTestKey: kSecondValue,
|
||||
});
|
||||
|
||||
// Write a flag.
|
||||
const kFlagKey = 'flag';
|
||||
await dtd.setPreference(kFlagKey, true);
|
||||
expect(await dtd.getFlag(kFlagKey), true);
|
||||
expect(json.decode(preferencesFile.readAsStringSync()), {
|
||||
kFlagKey: true,
|
||||
kTestKey: kSecondValue,
|
||||
});
|
||||
|
||||
// Remove an entry.
|
||||
await dtd.setPreference(kTestKey, null);
|
||||
expect(await dtd.getPreference(kTestKey), null);
|
||||
expect(json.decode(preferencesFile.readAsStringSync()), {
|
||||
kFlagKey: true,
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -62,7 +62,7 @@ class FakeWidgetPreviewScaffoldDtdServices extends Fake
|
||||
FakeWidgetPreviewScaffoldDtdServices({this.isWindows = false});
|
||||
|
||||
final navigationEvents = <CodeLocation>[];
|
||||
final preferences = <String, Object>{};
|
||||
final preferences = <String, Object?>{};
|
||||
|
||||
@override
|
||||
Future<void> connect({Uri? dtdUri}) async {}
|
||||
@ -117,7 +117,11 @@ class FakeWidgetPreviewScaffoldDtdServices extends Fake
|
||||
|
||||
/// Sets [key] to [value] in the persistent preferences map.
|
||||
@override
|
||||
Future<void> setPreference(String key, Object value) async {
|
||||
Future<void> setPreference(String key, Object? value) async {
|
||||
if (value == null) {
|
||||
preferences.remove(key);
|
||||
return;
|
||||
}
|
||||
preferences[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -867,10 +867,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: "199bc33e746088546a39cc5f36bac5a278c5e53b40cb3196f99e7345fdcfae6b"
|
||||
sha256: c0fb544b9ac7efa10254efaf00a951615c362d1ea1877472f8f6c0fa00fcf15b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.22"
|
||||
version: "6.3.23"
|
||||
url_launcher_ios:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@ -175,7 +175,7 @@ dependencies:
|
||||
test_core: 0.6.12
|
||||
typed_data: 1.4.0
|
||||
url_launcher: 6.3.2
|
||||
url_launcher_android: 6.3.22
|
||||
url_launcher_android: 6.3.23
|
||||
url_launcher_ios: 6.3.4
|
||||
url_launcher_linux: 3.2.1
|
||||
url_launcher_macos: 3.2.3
|
||||
@ -212,4 +212,4 @@ dependencies:
|
||||
pedantic: 1.11.1
|
||||
quiver: 3.2.2
|
||||
yaml_edit: 2.2.2
|
||||
# PUBSPEC CHECKSUM: pb0r15
|
||||
# PUBSPEC CHECKSUM: 5h4lnl
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user