Veröffentlichte Quellen
Quellen als ZIP herunterladen
CH-J SM Mobile
Durchsuchen Sie Verzeichnisse und Dateien einer bestimmten Anwendungsausgabe.
CH-J / SM Mobile
175 Dateien
sm-mobile-20261016T185235Z-8e93ecc1b59a
Hinzugefügt: · Erstellt:
1
import 'dart:io';2
import 'dart:ui' show DisplayFeature, DisplayFeatureState, DisplayFeatureType;3
import 'package:flutter/material.dart';4
import 'package:flutter_test/flutter_test.dart';5
import 'package:sm_mobile/core/app_controller.dart';6
import 'package:sm_mobile/main.dart';7
import 'package:sm_mobile/models/server_profile.dart';8
import 'package:sm_mobile/ui/files_page.dart';9
import 'package:sm_mobile/ui/terminal_page.dart';10
import 'package:xterm/xterm.dart';11
import '../support.dart';13
/// Real OpenSSH (tool/test_openssh.py): layout changes must keep the same SSH14
/// client, PTY and SFTP state, and the remote PTY must follow the new size.15
void main() {16
final enabled = Platform.environment['SM_OPENSSH_TEST'] == '1';18
DisplayFeature fold(Rect r) => DisplayFeature(19
bounds: r,20
type: DisplayFeatureType.fold,21
state: DisplayFeatureState.postureHalfOpened,22
);24
// Logical sizes; physical = logical × ratio.25
final shapes = <(String, Size, double, double, List<DisplayFeature>)>[26
('phone portrait', const Size(390, 844), 3, 0, []),27
('phone landscape', const Size(844, 390), 3, 0, []),28
('phone landscape + keyboard', const Size(844, 390), 3, 170, []),29
('phone portrait + keyboard', const Size(390, 844), 3, 336, []),30
('small phone', const Size(320, 568), 2, 0, []),31
('iPhone Duo outer @3x (assumed)', const Size(466, 678), 3, 0, []),32
('iPhone Duo inner @3x (assumed)', const Size(626, 890), 3, 0, []),33
(34
'iPhone Duo inner half folded',35
const Size(626, 890),36
3,37
0,38
[fold(const Rect.fromLTWH(305, 0, 16, 890))],39
),40
('tablet portrait', const Size(820, 1180), 2, 0, []),41
('tablet landscape', const Size(1180, 820), 2, 0, []),42
('tablet split 1/3', const Size(375, 820), 2, 0, []),43
('desktop-class', const Size(2560, 1440), 1, 0, []),44
('minimum canvas fallback', const Size(200, 260), 2, 0, []),45
('phone portrait again', const Size(390, 844), 3, 0, []),46
];48
Future<void> apply(49
WidgetTester tester,50
Size s,51
double ratio,52
double keyboard,53
List<DisplayFeature> features,54
) async {55
tester.view.devicePixelRatio = ratio;56
tester.view.physicalSize = s * ratio;57
tester.view.viewInsets = FakeViewPadding(bottom: keyboard * ratio);58
tester.view.displayFeatures = features;59
await tester.pump();60
await tester.pump();61
}63
Future<void> waitFor(64
WidgetTester tester,65
bool Function() done,66
String what,67
) async {68
final deadline = DateTime.now().add(const Duration(seconds: 15));69
while (!done()) {70
if (DateTime.now().isAfter(deadline)) fail('timeout: $what');71
await tester.runAsync(72
() => Future<void>.delayed(const Duration(milliseconds: 50)),73
);74
await tester.pump();75
}76
}78
testWidgets(79
'real OpenSSH session, PTY size and SFTP directory survive every layout',80
(tester) async {81
addTearDown(tester.view.reset);82
final c = AppController(MemoryStore());83
final key = await tester.runAsync(84
() => File(Platform.environment['SM_TEST_KEY']!).readAsString(),85
);86
final root = await tester.runAsync(87
() => Directory(88
Platform.environment['SM_TEST_REMOTE_DIR']!,89
).resolveSymbolicLinks(),90
);91
final dir = '$root/adaptive-${newId()}';92
await tester.runAsync(() async {93
await Directory(dir).create(recursive: true);94
await c.initialize();95
await c.acceptLicense();96
await c.openVault('isolated adaptive password', create: true);97
await c.ssh.connect(98
ServerProfile(99
name: 'Isolated OpenSSH',100
host: '127.0.0.1',101
username: Platform.environment['USER']!,102
port: 22229,103
auth: AuthMethod.privateKey,104
),105
credential: key!,106
passphrase: 'isolated-test-passphrase',107
prompt: (_, _, _) async => true,108
);109
});110
expect(c.ssh.error, null);111
expect(c.ssh.connected, true);112
final client = c.ssh.client, terminal = c.ssh.terminal;113
await apply(tester, const Size(390, 844), 3, 0, const []);114
await tester.pumpWidget(SmMobileApp(c));115
await tester.pump();116
await tester.tap(find.byIcon(Icons.terminal).first);117
await tester.pump();118
final page = tester.state(find.byType(TerminalPage));120
var n = 0;121
for (final (name, size, ratio, keyboard, features) in shapes) {122
await apply(tester, size, ratio, keyboard, features);123
expect(tester.takeException(), null, reason: name);124
expect(c.ssh.connected, true, reason: name);125
expect(c.ssh.client, same(client), reason: '$name: no reconnect');126
expect(c.ssh.terminal, same(terminal), reason: name);127
expect(128
tester.state(find.byType(TerminalPage)),129
same(page),130
reason: name,131
);132
// Ask the server for the PTY size it now has.133
final rows = terminal.viewHeight, cols = terminal.viewWidth;134
final step = n++;135
// The echoed command contains only the format string; the expanded136
// marker can come from the server's answer alone.137
terminal.onOutput!(138
"printf 'SZ%s_%s_\\n' $step \"\$(stty size | tr ' ' x)\"\r",139
);140
final expected = 'SZ${step}_${rows}x${cols}_';141
await waitFor(142
tester,143
() => terminal.buffer.getText().contains(expected),144
'$name: remote PTY ${rows}x$cols',145
);146
}148
// Long output and UTF-8: scrollback keeps it, the view follows the149
// newest line, and painting is clipped to the terminal area.150
await apply(tester, const Size(390, 844), 3, 0, const []);151
terminal.onOutput!(152
"seq 1 3000 | sed 's/^/řádek-/'; printf 'Žluťoučký kůň %s\\n' KONEC\r",153
);154
await waitFor(155
tester,156
() => terminal.buffer.getText().contains('Žluťoučký kůň KONEC'),157
'long UTF-8 output',158
);159
expect(terminal.buffer.getText(), contains('řádek-3000'));160
expect(terminal.buffer.lines.length, greaterThan(1000));161
expect(162
find.ancestor(163
of: find.byType(TerminalView),164
matching: find.byType(ClipRect),165
),166
findsWidgets,167
);168
expect(tester.takeException(), null);170
// SFTP: open a directory, then move through the layouts again.171
await apply(tester, const Size(390, 844), 3, 0, const []);172
await tester.tap(find.byIcon(Icons.folder_outlined).first);173
await tester.pump();174
await waitFor(175
tester,176
() => find.byType(LinearProgressIndicator).evaluate().isEmpty,177
'SFTP home listing',178
);179
final files = tester.state(find.byType(FilesPage)) as dynamic;180
// The SFTP client lives in the test's fake zone, so its replies are181
// processed by pumping (waitFor), not inside runAsync.182
files.load(directory: dir);183
await waitFor(tester, () => find.text(dir).evaluate().isNotEmpty, dir);184
for (final (name, size, ratio, keyboard, features) in shapes) {185
await apply(tester, size, ratio, keyboard, features);186
expect(tester.takeException(), null, reason: name);187
expect(c.ssh.client, same(client), reason: '$name: no reconnect');188
expect(tester.state(find.byType(FilesPage)), same(files), reason: name);189
expect(find.text(dir), findsOneWidget, reason: '$name: SFTP path');190
}192
await tester.pumpWidget(const SizedBox());193
c.dispose();194
await tester.runAsync(() => Directory(dir).delete(recursive: true));195
},196
skip: !enabled,197
);198
}SHA-256
5290c8d7f937db62ed578192e42836377a43668a05dff7aa5e2e4c352813fdcbCH-J Proprietary Software License 1.14 · Die Quellen werden unter der CH-J Proprietary Software License 1.14 bereitgestellt. Ihre Verfügbarkeit ändert die Lizenzbedingungen nicht und gewährt keine zusätzlichen Rechte.
SHA-256 des Archivs
46e427a43e87af091004f36145590bda8fe90086dfb0f631afeb5029fb0b5655