8584f0459e
- 新增「自动连接」设置开关(默认关,持久化):启动 app 进登录态主界面后,等节点就绪 自动连一次(_RootFlowState 一次性 listenManual nodesProvider → toggle())。全平台生效。 - Windows 开机自启改用 schtasks /rl highest 登录触发任务:本应用 requireAdministrator, 原注册表 Run 键(非提权)开机会弹 UAC/起不来 → 任务计划静默提权拉起。macOS/Linux 仍用 launch_at_startup 登录项。 - 设置页功能组加「自动连接」行;新增 autoConnect/autoConnectSub 文案。 - _load 加 mounted 守卫(异步初始化期间已 dispose 不再写 state)。 - 测试:autoConnect 默认关 / _load 读持久化 / setAutoConnect 落盘。 注:移动端真·常连(Android Always-on + iOS/iPad On-Demand,无 UI 自连)是更大的原生活, 已单列 todo #14 后续做;本轮移动端只有「打开 app 自动连接」。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
60 lines
2.5 KiB
Dart
60 lines
2.5 KiB
Dart
// settings_autoconnect_test.dart — 自动连接开关:默认关 + 改状态并持久化(重启恢复)。
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:package_info_plus/package_info_plus.dart';
|
|
import 'package:pangolin_vpn/bridge/vpn_bridge_mock.dart';
|
|
import 'package:pangolin_vpn/bridge/vpn_bridge_provider.dart';
|
|
import 'package:pangolin_vpn/state/settings_provider.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
void main() {
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
setUp(() {
|
|
SharedPreferences.setMockInitialValues({});
|
|
// 桌面(mac 测试机)构造时会跑 _applyAutostart → PackageInfo.fromPlatform;
|
|
// mock 掉避免无插件挂起、_load 拖到 dispose 之后报「used after dispose」。
|
|
PackageInfo.setMockInitialValues(
|
|
appName: 'Pangolin', packageName: 'com.pangolin',
|
|
version: '1.0.0', buildNumber: '1', buildSignature: '',
|
|
);
|
|
});
|
|
|
|
ProviderContainer makeContainer() => ProviderContainer(overrides: [
|
|
vpnBridgeProvider.overrideWithValue(VpnBridgeMock()),
|
|
]);
|
|
|
|
// 轮询等 _load() 把持久化值读进 state(异步,首次 SharedPreferences 初始化可能偏慢)。
|
|
Future<void> waitAutoConnect(ProviderContainer c, bool want) async {
|
|
for (var i = 0; i < 60; i++) {
|
|
if (c.read(settingsProvider).autoConnect == want) return;
|
|
await Future<void>.delayed(const Duration(milliseconds: 10));
|
|
}
|
|
}
|
|
|
|
test('autoConnect 默认关(const 默认 + 空存储加载)', () async {
|
|
expect(const AppSettings().autoConnect, isFalse);
|
|
final c = makeContainer();
|
|
addTearDown(c.dispose);
|
|
await waitAutoConnect(c, false);
|
|
expect(c.read(settingsProvider).autoConnect, isFalse);
|
|
});
|
|
|
|
test('_load 从持久化恢复 autoConnect=true', () async {
|
|
SharedPreferences.setMockInitialValues({'set_auto_connect': true});
|
|
final c = makeContainer();
|
|
addTearDown(c.dispose);
|
|
await waitAutoConnect(c, true);
|
|
expect(c.read(settingsProvider).autoConnect, isTrue, reason: '应读出持久化值');
|
|
});
|
|
|
|
test('setAutoConnect 写入持久化存储', () async {
|
|
final c = makeContainer();
|
|
addTearDown(c.dispose);
|
|
await waitAutoConnect(c, false); // 等初次加载安定
|
|
await c.read(settingsProvider.notifier).setAutoConnect(true);
|
|
// 校验落盘(状态可能被并发的初次 _load 覆盖,但存储一定被写入)。
|
|
final p = await SharedPreferences.getInstance();
|
|
expect(p.getBool('set_auto_connect'), isTrue, reason: '应持久化');
|
|
});
|
|
}
|