From 787bd9d385fc46e070908283cbada0fb9b965673 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 10:40:41 +0000 Subject: [PATCH 001/106] chore(deps): update github.com/ubuntu/decorate digest to 69db9a4 --- provd/go.mod | 2 +- provd/go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/provd/go.mod b/provd/go.mod index 3b18f428b..a089d90f8 100644 --- a/provd/go.mod +++ b/provd/go.mod @@ -7,7 +7,7 @@ require ( github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.17.0 github.com/stretchr/testify v1.8.4 - github.com/ubuntu/decorate v0.0.0-20230905131025-e968fa48a85c + github.com/ubuntu/decorate v0.0.0-20231211084900-69db9a41777a google.golang.org/grpc v1.58.3 google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v3 v3.0.1 diff --git a/provd/go.sum b/provd/go.sum index 6abbb382c..73c74acd5 100644 --- a/provd/go.sum +++ b/provd/go.sum @@ -194,6 +194,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/ubuntu/decorate v0.0.0-20230905131025-e968fa48a85c h1:jO41xNLddTDkrfz4w4RCMWCmX8Y+ZHz5jSbJWNLDvqU= github.com/ubuntu/decorate v0.0.0-20230905131025-e968fa48a85c/go.mod h1:edGgz97NOqS2oqzbKrZqO9YU9neosRrkEZbVJVQynAA= +github.com/ubuntu/decorate v0.0.0-20231211084900-69db9a41777a h1:VojaxMh5vh94q0uRrJP2mucbItb4gCMzEg6X1RzlM+M= +github.com/ubuntu/decorate v0.0.0-20231211084900-69db9a41777a/go.mod h1:edGgz97NOqS2oqzbKrZqO9YU9neosRrkEZbVJVQynAA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From 9bb088df58c622e9a46bc8e0d426fe204f1accfa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:27:20 +0000 Subject: [PATCH 002/106] chore(deps): update module github.com/spf13/cobra to v1.8.0 --- provd/go.mod | 2 +- provd/go.sum | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/provd/go.mod b/provd/go.mod index 3b18f428b..a2a511b47 100644 --- a/provd/go.mod +++ b/provd/go.mod @@ -4,7 +4,7 @@ go 1.21.4 require ( github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf - github.com/spf13/cobra v1.7.0 + github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.17.0 github.com/stretchr/testify v1.8.4 github.com/ubuntu/decorate v0.0.0-20230905131025-e968fa48a85c diff --git a/provd/go.sum b/provd/go.sum index 6abbb382c..fc6b8bfcf 100644 --- a/provd/go.sum +++ b/provd/go.sum @@ -49,6 +49,7 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -176,6 +177,8 @@ github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= From 910a8934f96c9385801a14e28f9b3833bf4e5582 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:14:47 +0000 Subject: [PATCH 003/106] chore(deps): update actions/setup-go action to v5 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8300f3ee4..5e97aba24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -174,7 +174,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version-file: "./provd/go.mod" From a95b428a9e5171390d89d31089d8685648b2c5d3 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 12 Dec 2023 17:09:38 +0200 Subject: [PATCH 004/106] feat(proto): is_admin flag & naming consistency --- provd/protos/user.proto | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/provd/protos/user.proto b/provd/protos/user.proto index 9d0874f97..c73d08fce 100644 --- a/provd/protos/user.proto +++ b/provd/protos/user.proto @@ -29,7 +29,8 @@ message GetUserResponse { } message CreateUserRequest { - User identity = 1; + User user = 1; + bool is_admin = 2; } message ValidateUsernameRequest { From 717b3593a34cb672ca275aba64d4ba8357f294f7 Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Wed, 13 Dec 2023 13:55:56 +0100 Subject: [PATCH 005/106] fix(init): catch everything in init services --- .../ubuntu_init/lib/src/services/privacy_service.dart | 10 ++++++---- .../lib/src/services/xdg_keyboard_service.dart | 8 +++++--- .../lib/src/services/theme_service.dart | 10 ++++++---- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/packages/ubuntu_init/lib/src/services/privacy_service.dart b/packages/ubuntu_init/lib/src/services/privacy_service.dart index bd95cafa0..565c75b44 100644 --- a/packages/ubuntu_init/lib/src/services/privacy_service.dart +++ b/packages/ubuntu_init/lib/src/services/privacy_service.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_catches_without_on_clauses + import 'package:dbus/dbus.dart'; import 'package:gsettings/gsettings.dart'; import 'package:meta/meta.dart'; @@ -32,7 +34,7 @@ class GnomePrivacyService implements PrivacyService { Future isLocationEnabled() async { try { return await _locationSettings.get('enabled').then((v) => v.asBoolean()); - } on Exception catch (e) { + } catch (e) { _log.error('Error getting location settings: $e'); return false; } @@ -43,7 +45,7 @@ class GnomePrivacyService implements PrivacyService { await setReportingEnabled(true); try { return await _locationSettings.set('enabled', DBusBoolean(enabled)); - } on Exception catch (e) { + } catch (e) { _log.error('Error setting location settings: $e'); return; } @@ -55,7 +57,7 @@ class GnomePrivacyService implements PrivacyService { return await _privacySettings .get('report-technical-problems') .then((v) => v.asBoolean()); - } on Exception catch (e) { + } catch (e) { _log.error('Error getting privacy settings: $e'); return false; } @@ -66,7 +68,7 @@ class GnomePrivacyService implements PrivacyService { try { return await _privacySettings.set( 'report-technical-problems', DBusBoolean(enabled)); - } on Exception catch (e) { + } catch (e) { _log.error('Error setting privacy settings: $e'); return; } diff --git a/packages/ubuntu_init/lib/src/services/xdg_keyboard_service.dart b/packages/ubuntu_init/lib/src/services/xdg_keyboard_service.dart index 4b92c4075..cf0dd4c54 100644 --- a/packages/ubuntu_init/lib/src/services/xdg_keyboard_service.dart +++ b/packages/ubuntu_init/lib/src/services/xdg_keyboard_service.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_catches_without_on_clauses + import 'dart:convert'; import 'package:dbus/dbus.dart'; @@ -87,7 +89,7 @@ class XdgKeyboardService implements KeyboardService { layouts: await _getLayouts(), ); return keyboardSetup; - } on Exception catch (e) { + } catch (e) { _log.error('Failed to get keyboard setup', e); return const KeyboardSetup( setting: KeyboardSetting(layout: ''), @@ -117,7 +119,7 @@ class XdgKeyboardService implements KeyboardService { DBusStruct([const DBusString('xkb'), DBusString(xkbString)]) ]), ); - } on Exception catch (e) { + } catch (e) { _log.error('Failed to set input source', e); } } @@ -134,7 +136,7 @@ class XdgKeyboardService implements KeyboardService { false, false, ); - } on Exception catch (e) { + } catch (e) { _log.error('Failed to set keyboard', e); } } diff --git a/packages/ubuntu_provision/lib/src/services/theme_service.dart b/packages/ubuntu_provision/lib/src/services/theme_service.dart index 918befe83..8ee7bd436 100644 --- a/packages/ubuntu_provision/lib/src/services/theme_service.dart +++ b/packages/ubuntu_provision/lib/src/services/theme_service.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_catches_without_on_clauses + import 'dart:ui'; import 'package:dbus/dbus.dart'; @@ -40,7 +42,7 @@ class GtkThemeService implements ThemeService { final scheme = await settings.get('color-scheme').then((v) => v.asString()); return scheme.hasSuffix('dark') ? Brightness.dark : Brightness.light; - } on Exception catch (e) { + } catch (e) { _log.error('Error getting theme settings: $e'); return Brightness.light; } @@ -61,7 +63,7 @@ class GtkThemeService implements ThemeService { await settings.set('color-scheme', const DBusString('prefer-light')); break; } - } on Exception catch (e) { + } catch (e) { _log.error('Error setting theme settings: $e'); } } @@ -71,7 +73,7 @@ class GtkThemeService implements ThemeService { try { final theme = await settings.get('gtk-theme').then((v) => v.asString()); return theme.removeSuffix('dark').split('-').elementAtOrNull(1); - } on Exception catch (e) { + } catch (e) { _log.error('Error getting accent color: $e'); return null; } @@ -87,7 +89,7 @@ class GtkThemeService implements ThemeService { if (theme.hasSuffix('dark')) 'dark', ].join('-'); return settings.set('gtk-theme', DBusString(value)); - } on Exception catch (e) { + } catch (e) { _log.error('Error setting accent color: $e'); } } From fef0187867abead71c382b600a322d984b81137f Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Wed, 13 Dec 2023 13:56:47 +0100 Subject: [PATCH 006/106] docs: add TODO notes to init services --- packages/ubuntu_init/lib/src/services/privacy_service.dart | 1 + packages/ubuntu_init/lib/src/services/xdg_identity_service.dart | 2 ++ packages/ubuntu_init/lib/src/services/xdg_keyboard_service.dart | 1 + packages/ubuntu_init/lib/src/services/xdg_locale_service.dart | 2 ++ packages/ubuntu_init/lib/src/services/xdg_session_service.dart | 2 ++ packages/ubuntu_init/lib/src/services/xdg_timezone_service.dart | 2 ++ packages/ubuntu_provision/lib/src/services/theme_service.dart | 2 ++ 7 files changed, 12 insertions(+) diff --git a/packages/ubuntu_init/lib/src/services/privacy_service.dart b/packages/ubuntu_init/lib/src/services/privacy_service.dart index 565c75b44..37dc34737 100644 --- a/packages/ubuntu_init/lib/src/services/privacy_service.dart +++ b/packages/ubuntu_init/lib/src/services/privacy_service.dart @@ -1,4 +1,5 @@ // ignore_for_file: avoid_catches_without_on_clauses +// TODO: replace this service with a new one that communicates with provd import 'package:dbus/dbus.dart'; import 'package:gsettings/gsettings.dart'; diff --git a/packages/ubuntu_init/lib/src/services/xdg_identity_service.dart b/packages/ubuntu_init/lib/src/services/xdg_identity_service.dart index b814a07fd..a76fe6d44 100644 --- a/packages/ubuntu_init/lib/src/services/xdg_identity_service.dart +++ b/packages/ubuntu_init/lib/src/services/xdg_identity_service.dart @@ -1,3 +1,5 @@ +// TODO: replace this service with a new one that communicates with provd + import 'package:crypt/crypt.dart'; import 'package:dbus/dbus.dart'; import 'package:meta/meta.dart'; diff --git a/packages/ubuntu_init/lib/src/services/xdg_keyboard_service.dart b/packages/ubuntu_init/lib/src/services/xdg_keyboard_service.dart index cf0dd4c54..39ed7b064 100644 --- a/packages/ubuntu_init/lib/src/services/xdg_keyboard_service.dart +++ b/packages/ubuntu_init/lib/src/services/xdg_keyboard_service.dart @@ -1,4 +1,5 @@ // ignore_for_file: avoid_catches_without_on_clauses +// TODO: replace this service with a new one that communicates with provd import 'dart:convert'; diff --git a/packages/ubuntu_init/lib/src/services/xdg_locale_service.dart b/packages/ubuntu_init/lib/src/services/xdg_locale_service.dart index 2412eb218..d748f44ca 100644 --- a/packages/ubuntu_init/lib/src/services/xdg_locale_service.dart +++ b/packages/ubuntu_init/lib/src/services/xdg_locale_service.dart @@ -1,3 +1,5 @@ +// TODO: replace this service with a new one that communicates with provd + import 'package:dbus/dbus.dart'; import 'package:meta/meta.dart'; import 'package:ubuntu_provision/services.dart'; diff --git a/packages/ubuntu_init/lib/src/services/xdg_session_service.dart b/packages/ubuntu_init/lib/src/services/xdg_session_service.dart index 8b83782fd..452367b0f 100644 --- a/packages/ubuntu_init/lib/src/services/xdg_session_service.dart +++ b/packages/ubuntu_init/lib/src/services/xdg_session_service.dart @@ -1,3 +1,5 @@ +// TODO: replace this service with a new one that communicates with provd + import 'package:meta/meta.dart'; import 'package:ubuntu_provision/services.dart'; import 'package:ubuntu_session/ubuntu_session.dart'; diff --git a/packages/ubuntu_init/lib/src/services/xdg_timezone_service.dart b/packages/ubuntu_init/lib/src/services/xdg_timezone_service.dart index 2431f58a0..d7b5493c1 100644 --- a/packages/ubuntu_init/lib/src/services/xdg_timezone_service.dart +++ b/packages/ubuntu_init/lib/src/services/xdg_timezone_service.dart @@ -1,3 +1,5 @@ +// TODO: replace this service with a new one that communicates with provd + import 'package:dbus/dbus.dart'; import 'package:meta/meta.dart'; import 'package:ubuntu_provision/services.dart'; diff --git a/packages/ubuntu_provision/lib/src/services/theme_service.dart b/packages/ubuntu_provision/lib/src/services/theme_service.dart index 8ee7bd436..653be90bc 100644 --- a/packages/ubuntu_provision/lib/src/services/theme_service.dart +++ b/packages/ubuntu_provision/lib/src/services/theme_service.dart @@ -1,4 +1,6 @@ // ignore_for_file: avoid_catches_without_on_clauses +// TODO: replace this service with a new one that communicates with provd +// TODO: move to ubuntu_init import 'dart:ui'; From c0bf9097f348bd72be8d78805af9bcce4d5b7951 Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Wed, 13 Dec 2023 14:25:42 +0100 Subject: [PATCH 007/106] fix: remove `on` clauses and add todo notes --- .../ubuntu_bootstrap/lib/services/refresh_service.dart | 7 +++++-- .../ubuntu_provision/lib/src/services/product_service.dart | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/ubuntu_bootstrap/lib/services/refresh_service.dart b/packages/ubuntu_bootstrap/lib/services/refresh_service.dart index 2ed0477c0..3be2d54cf 100644 --- a/packages/ubuntu_bootstrap/lib/services/refresh_service.dart +++ b/packages/ubuntu_bootstrap/lib/services/refresh_service.dart @@ -1,3 +1,6 @@ +// ignore_for_file: avoid_catches_without_on_clauses +// TODO: check if catching `Exception` in check() and refresh() is sufficient + import 'dart:async'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -55,7 +58,7 @@ class RefreshService { try { _setState(const RefreshState.checking()); _setStatus(await _client.checkRefresh()); - } on Exception catch (e) { + } catch (e) { _setState(RefreshState.error(e)); } return _state; @@ -69,7 +72,7 @@ class RefreshService { if (_state.ready) { _setState(const RefreshState.done()); } - } on Exception catch (_) { + } catch (_) { // subiquity restarted, consider the refresh done _setState(const RefreshState.done()); return false; diff --git a/packages/ubuntu_provision/lib/src/services/product_service.dart b/packages/ubuntu_provision/lib/src/services/product_service.dart index e114062fd..0caa86f96 100644 --- a/packages/ubuntu_provision/lib/src/services/product_service.dart +++ b/packages/ubuntu_provision/lib/src/services/product_service.dart @@ -90,8 +90,9 @@ class ProductService { .readAsLinesSync() .firstWhere((line) => line.trim().isNotEmpty); return url.replaceAll(r'${LANG}', languageCode); - // ignore: empty_catches - } on Exception catch (_) {} + // TODO: error handling + // ignore: empty_catches, avoid_catches_without_on_clauses + } catch (_) {} } try { final lines = _fileSystem @@ -101,6 +102,7 @@ class ProductService { final codeName = last.split(',')[1].replaceAll(RegExp('\\s+'), ''); assert(codeName.isNotEmpty); return 'https://wiki.ubuntu.com/$codeName/ReleaseNotes'; + // TODO: error handling // ignore: avoid_catches_without_on_clauses } catch (e) { // Those are not actual release notes, From b23b6ef081607f5a0dc5af51886bcf9d6989af39 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Mon, 18 Dec 2023 10:31:53 +0100 Subject: [PATCH 008/106] deps: Remove root ubuntu_lints --- pubspec.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index ec3ad46cf..fe871f085 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,4 +5,3 @@ environment: dev_dependencies: melos: ^3.2.0 - ubuntu_lints: ^0.1.0 \ No newline at end of file From a3064e05fc027d5cc2fb3cfadd0ba5106c70991c Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Mon, 18 Dec 2023 10:30:59 +0100 Subject: [PATCH 009/106] feat: Pre-cache all images from the PageConfigProvider --- packages/ubuntu_bootstrap/lib/installer.dart | 33 +++++------ .../lib/installer/installer_wizard.dart | 4 +- .../lib/pages/loading/loading_page.dart | 7 ++- .../ubuntu_bootstrap/test/installer_test.dart | 6 +- .../test/installer_wizard_test.dart | 5 +- .../test/loading/loading_page_test.dart | 7 ++- .../ubuntu_bootstrap/test/test_utils.dart | 16 ++++++ .../lib/src/locale/locale_page.dart | 2 +- .../lib/{ => src/providers}/flavor.dart | 0 .../lib/{ => src/providers}/locale.dart | 6 +- .../lib/src/providers/page_images.dart | 55 +++++++++++++++++++ .../lib/ubuntu_provision.dart | 5 +- packages/ubuntu_provision/pubspec.yaml | 1 + 13 files changed, 118 insertions(+), 29 deletions(-) rename packages/ubuntu_provision/lib/{ => src/providers}/flavor.dart (100%) rename packages/ubuntu_provision/lib/{ => src/providers}/locale.dart (65%) create mode 100644 packages/ubuntu_provision/lib/src/providers/page_images.dart diff --git a/packages/ubuntu_bootstrap/lib/installer.dart b/packages/ubuntu_bootstrap/lib/installer.dart index eff73859a..e3df47f81 100644 --- a/packages/ubuntu_bootstrap/lib/installer.dart +++ b/packages/ubuntu_bootstrap/lib/installer.dart @@ -78,6 +78,8 @@ Future runInstallerApp( ); tryRegisterService(GnomeService.new); tryRegisterServiceFactory(GSettings.new); + tryRegisterService( + () => PageConfigService(config: tryGetService())); tryRegisterService(() => InstallerService( getService(), pageConfig: tryGetService())); @@ -88,8 +90,6 @@ Future runInstallerApp( () => SubiquityLocaleService(getService())); tryRegisterService( () => SubiquityNetworkService(getService())); - tryRegisterService( - () => PageConfigService(config: tryGetService())); tryRegisterService(() => PostInstallService('/tmp/$baseName.conf')); tryRegisterService(PowerService.new); tryRegisterService(ProductService.new); @@ -137,6 +137,7 @@ Future runInstallerApp( final themeVariantService = getService(); await themeVariantService.load(); final themeVariant = themeVariantService.themeVariant; + await initialized; runApp(ProviderScope( child: SlidesContext( @@ -182,18 +183,11 @@ Future runInstallerApp( ), )); }, (error, stack) => log.error('Unhandled exception', error, stack)); - - return initialized; } Future _initInstallerApp(Endpoint endpoint) async { getService().open(endpoint); - await getService().init(); - await getService().inhibit(); - await getService().check(); - await getService().load(); - var geo = tryGetService(); if (geo == null) { final geodata = Geodata.asset(); @@ -201,14 +195,21 @@ Future _initInstallerApp(Endpoint endpoint) async { geo = GeoService(sources: [geodata, geoname]); registerServiceInstance(geo); } - await geo.init(); - final telemetry = getService(); - await telemetry.init({ - 'Type': 'Flutter', - 'OEM': false, - 'Media': getService().getProductInfo().toString(), - }); + + final services = [ + getService().init(), + getService().inhibit(), + getService().check(), + getService().load(), + geo.init(), + telemetry.init({ + 'Type': 'Flutter', + 'OEM': false, + 'Media': getService().getProductInfo().toString(), + }), + ]; + await Future.wait(services); } Future _closeInstallerApp() async { diff --git a/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart b/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart index ce5c3feab..c009e3c1b 100644 --- a/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart +++ b/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart @@ -122,7 +122,7 @@ class _InstallWizard extends ConsumerWidget { routes: { Routes.loading: WizardRoute( builder: (_) => const LoadingPage(), - onReplace: (_) => LoadingPage.init(ref).then((_) => null), + onReplace: (_) => LoadingPage.init(context, ref).then((_) => null), ), ...preInstall, Routes.confirm: WizardRoute( @@ -172,7 +172,7 @@ class _AutoinstallWizard extends ConsumerWidget { hasPrevious: false, hasNext: false, ), - onReplace: (_) => LoadingPage.init(ref).then((_) => null), + onReplace: (_) => LoadingPage.init(context, ref).then((_) => null), ), Routes.confirm: WizardRoute( builder: (_) => const ConfirmPage(), diff --git a/packages/ubuntu_bootstrap/lib/pages/loading/loading_page.dart b/packages/ubuntu_bootstrap/lib/pages/loading/loading_page.dart index 8c7fdf4a4..3707c6e43 100644 --- a/packages/ubuntu_bootstrap/lib/pages/loading/loading_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/loading/loading_page.dart @@ -9,8 +9,11 @@ import 'package:yaru_widgets/yaru_widgets.dart'; class LoadingPage extends ConsumerStatefulWidget { const LoadingPage({super.key}); - static Future init(WidgetRef ref) { - return ref.read(loadingModelProvider).init(); + static Future init(BuildContext context, WidgetRef ref) async { + await Future.wait([ + ref.read(pageImagesProvider).preCache(context), + ref.read(loadingModelProvider).init(), + ]); } @override diff --git a/packages/ubuntu_bootstrap/test/installer_test.dart b/packages/ubuntu_bootstrap/test/installer_test.dart index 03bafc41c..71d4679b0 100644 --- a/packages/ubuntu_bootstrap/test/installer_test.dart +++ b/packages/ubuntu_bootstrap/test/installer_test.dart @@ -18,7 +18,11 @@ import 'loading/test_loading.dart'; import 'test_utils.dart'; void main() { - setUpAll(YaruTestWindow.ensureInitialized); + setUp(() { + YaruTestWindow.ensureInitialized(); + setupMockPageConfig(); + }); + tearDown(resetAllServices); testWidgets('interactive installation', (tester) async { diff --git a/packages/ubuntu_bootstrap/test/installer_wizard_test.dart b/packages/ubuntu_bootstrap/test/installer_wizard_test.dart index 522106248..9287a9181 100644 --- a/packages/ubuntu_bootstrap/test/installer_wizard_test.dart +++ b/packages/ubuntu_bootstrap/test/installer_wizard_test.dart @@ -47,7 +47,10 @@ import 'welcome/test_welcome.dart'; void main() { LiveTestWidgetsFlutterBinding.ensureInitialized(); - setUp(() => YaruTestWindow.ensureInitialized(state: const YaruWindowState())); + setUp(() { + YaruTestWindow.ensureInitialized(state: const YaruWindowState()); + setupMockPageConfig(); + }); testWidgets('try ubuntu', (tester) async { final loadingModel = buildLoadingModel(delay: const Duration(seconds: 1)); diff --git a/packages/ubuntu_bootstrap/test/loading/loading_page_test.dart b/packages/ubuntu_bootstrap/test/loading/loading_page_test.dart index fd39b67ee..74949453b 100644 --- a/packages/ubuntu_bootstrap/test/loading/loading_page_test.dart +++ b/packages/ubuntu_bootstrap/test/loading/loading_page_test.dart @@ -3,12 +3,17 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:ubuntu_bootstrap/pages/loading/loading_model.dart'; import 'package:ubuntu_bootstrap/pages/loading/loading_page.dart'; +import 'package:ubuntu_bootstrap/services.dart'; +import 'package:ubuntu_provision/services.dart'; import 'test_loading.dart'; void main() { testWidgets('init', (tester) async { WidgetRef? ref; + final pageConfig = MockPageConfigService(); + registerMockService(pageConfig); + when(pageConfig.pages).thenReturn({}); final model = buildLoadingModel(delay: const Duration(seconds: 3)); await tester.pumpApp( @@ -23,7 +28,7 @@ void main() { expect(find.byType(LoadingPage), findsOneWidget); expect(ref, isNotNull); - final future = LoadingPage.init(ref!); + final future = LoadingPage.init(MockBuildContext(), ref!); await tester.pump(const Duration(seconds: 3)); await expectLater(future, completes); verify(model.init()).called(1); diff --git a/packages/ubuntu_bootstrap/test/test_utils.dart b/packages/ubuntu_bootstrap/test/test_utils.dart index 5a4ca6d3e..50cff3bfc 100644 --- a/packages/ubuntu_bootstrap/test/test_utils.dart +++ b/packages/ubuntu_bootstrap/test/test_utils.dart @@ -1,11 +1,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; import 'package:ubuntu_bootstrap/l10n.dart'; import 'package:ubuntu_bootstrap/services.dart'; +import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru/yaru.dart'; +import 'confirm/test_confirm.dart'; + export '../../ubuntu_provision/test/test_utils.mocks.dart'; export 'test_utils.mocks.dart'; @@ -49,3 +53,15 @@ extension WidgetTesterX on WidgetTester { StorageService, ]) class _Dummy {} // ignore: unused_element + +/// Registers a mock [PageConfigService]. +/// +/// The [pages] argument will override the pages that are returned +/// (empty by default) if provided. +void setupMockPageConfig({Map pages = const {}}) { + final pageConfigService = MockPageConfigService(); + registerMockService(pageConfigService); + when(pageConfigService.pages).thenReturn(pages); +} + +class MockBuildContext extends Mock implements BuildContext {} diff --git a/packages/ubuntu_provision/lib/src/locale/locale_page.dart b/packages/ubuntu_provision/lib/src/locale/locale_page.dart index f8538811f..14959e087 100644 --- a/packages/ubuntu_provision/lib/src/locale/locale_page.dart +++ b/packages/ubuntu_provision/lib/src/locale/locale_page.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:ubuntu_provision/flavor.dart'; import 'package:ubuntu_provision/services.dart'; import 'package:ubuntu_provision/src/locale/locale_l10n.dart'; import 'package:ubuntu_provision/src/locale/locale_model.dart'; +import 'package:ubuntu_provision/src/providers/flavor.dart'; import 'package:ubuntu_provision/widgets.dart'; import 'package:ubuntu_service/ubuntu_service.dart'; import 'package:ubuntu_widgets/ubuntu_widgets.dart'; diff --git a/packages/ubuntu_provision/lib/flavor.dart b/packages/ubuntu_provision/lib/src/providers/flavor.dart similarity index 100% rename from packages/ubuntu_provision/lib/flavor.dart rename to packages/ubuntu_provision/lib/src/providers/flavor.dart diff --git a/packages/ubuntu_provision/lib/locale.dart b/packages/ubuntu_provision/lib/src/providers/locale.dart similarity index 65% rename from packages/ubuntu_provision/lib/locale.dart rename to packages/ubuntu_provision/lib/src/providers/locale.dart index 7df96000e..4d05be259 100644 --- a/packages/ubuntu_provision/lib/locale.dart +++ b/packages/ubuntu_provision/lib/src/providers/locale.dart @@ -2,9 +2,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:ubuntu_provision/src/locale/locale_model.dart'; -export 'src/locale/locale_l10n.dart'; -export 'src/locale/locale_model.dart'; -export 'src/locale/locale_page.dart'; +export '../locale/locale_l10n.dart'; +export '../locale/locale_model.dart'; +export '../locale/locale_page.dart'; final localeProvider = Provider((ref) => ref.watch(localeModelProvider).selectedLocale); diff --git a/packages/ubuntu_provision/lib/src/providers/page_images.dart b/packages/ubuntu_provision/lib/src/providers/page_images.dart new file mode 100644 index 000000000..fd92a9ba6 --- /dev/null +++ b/packages/ubuntu_provision/lib/src/providers/page_images.dart @@ -0,0 +1,55 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:path/path.dart' as path; +import 'package:ubuntu_logger/ubuntu_logger.dart'; +import 'package:ubuntu_provision/ubuntu_provision.dart'; +import 'package:ubuntu_service/ubuntu_service.dart'; + +final pageImagesProvider = + Provider((ref) => PageImages(getService())); + +final _log = Logger('page_images'); + +/// Pre-caches and holds images for all pages. +class PageImages { + PageImages(this.pageConfigService); + + final PageConfigService pageConfigService; + + final Map _images = {}; + + Widget get(String pageName) => + _images[pageName] ?? const Text('PLACEHOLDER, REPLACE WITH DEFAULT'); + + Future preCache(BuildContext context) async { + final loadFutures = >[]; + pageConfigService.pages.forEach((pageName, config) { + final imagePath = config.image; + if (imagePath == null) return; + + try { + final file = File(imagePath); + final extension = path.extension(imagePath); + if (extension == '.svg') { + loadFutures.add( + svg.cache.putIfAbsent( + imagePath, + () => SvgFileLoader(file).loadBytes(context), + ), + ); + _images[pageName] = SvgPicture.file(file); + } else { + loadFutures.add(precacheImage(FileImage(file), context)); + _images[pageName] = Image.file(file); + } + } on Exception catch (e) { + _log.error('Error loading image for $pageName from $imagePath: $e'); + } + }); + + await Future.wait(loadFutures); + } +} diff --git a/packages/ubuntu_provision/lib/ubuntu_provision.dart b/packages/ubuntu_provision/lib/ubuntu_provision.dart index 3cf715201..2c82acb18 100644 --- a/packages/ubuntu_provision/lib/ubuntu_provision.dart +++ b/packages/ubuntu_provision/lib/ubuntu_provision.dart @@ -1,13 +1,14 @@ library ubuntu_provision; export 'active_directory.dart'; -export 'flavor.dart'; export 'identity.dart'; export 'keyboard.dart'; export 'l10n.dart'; -export 'locale.dart'; export 'network.dart'; export 'services.dart'; +export 'src/providers/flavor.dart'; +export 'src/providers/locale.dart'; +export 'src/providers/page_images.dart'; export 'theme.dart'; export 'theme_variant.dart'; export 'timezone.dart'; diff --git a/packages/ubuntu_provision/pubspec.yaml b/packages/ubuntu_provision/pubspec.yaml index 2922985c1..b78528383 100644 --- a/packages/ubuntu_provision/pubspec.yaml +++ b/packages/ubuntu_provision/pubspec.yaml @@ -18,6 +18,7 @@ dependencies: sdk: flutter flutter_html: ^3.0.0-beta.1 flutter_riverpod: ^2.4.9 + flutter_svg: ^2.0.9 form_field_validator: ^1.1.0 freezed_annotation: ^2.4.1 gsettings: ^0.2.8 From 73ed92d78f4a052dcb2f60d7ae80355e08996499 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 19 Dec 2023 16:12:01 +0100 Subject: [PATCH 010/106] deps: Bump Flutter to 3.16.4 --- .tool-versions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.tool-versions b/.tool-versions index b5551868c..cbe24c642 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -flutter 3.16.3-stable +flutter 3.16.4-stable From 612984fc8f03c1fd4659963cf5381cd399e02e2c Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 22 Dec 2023 12:26:30 +0100 Subject: [PATCH 011/106] chore: Update mocks --- .tool-versions | 2 +- .../lib/src/types.freezed.dart | 132 +++++++++--------- .../lib/services/refresh_service.freezed.dart | 10 +- .../services/page_config_service.freezed.dart | 4 +- .../theme_variant_service.freezed.dart | 2 +- 5 files changed, 75 insertions(+), 75 deletions(-) diff --git a/.tool-versions b/.tool-versions index cbe24c642..cec28fec1 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -flutter 3.16.4-stable +flutter 3.16.5-stable diff --git a/packages/subiquity_client/lib/src/types.freezed.dart b/packages/subiquity_client/lib/src/types.freezed.dart index 6ba96a26b..014b8f50e 100644 --- a/packages/subiquity_client/lib/src/types.freezed.dart +++ b/packages/subiquity_client/lib/src/types.freezed.dart @@ -178,7 +178,7 @@ class _$ErrorReportRefImpl implements _ErrorReportRef { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$ErrorReportRefImpl && @@ -464,7 +464,7 @@ class _$ApplicationStatusImpl implements _ApplicationStatus { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$ApplicationStatusImpl && @@ -651,7 +651,7 @@ class _$KeyFingerprintImpl implements _KeyFingerprint { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$KeyFingerprintImpl && @@ -901,7 +901,7 @@ class _$LiveSessionSSHInfoImpl implements _LiveSessionSSHInfo { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$LiveSessionSSHInfoImpl && @@ -1109,7 +1109,7 @@ class _$RefreshStatusImpl implements _RefreshStatus { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$RefreshStatusImpl && @@ -1322,7 +1322,7 @@ class _$StepPressKeyImpl implements StepPressKey { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$StepPressKeyImpl && @@ -1508,7 +1508,7 @@ class _$StepKeyPresentImpl implements StepKeyPresent { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$StepKeyPresentImpl && @@ -1685,7 +1685,7 @@ class _$StepResultImpl implements StepResult { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$StepResultImpl && @@ -1923,7 +1923,7 @@ class _$KeyboardSettingImpl implements _KeyboardSetting { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$KeyboardSettingImpl && @@ -2082,7 +2082,7 @@ class _$KeyboardVariantImpl implements _KeyboardVariant { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$KeyboardVariantImpl && @@ -2259,7 +2259,7 @@ class _$KeyboardLayoutImpl implements _KeyboardLayout { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$KeyboardLayoutImpl && @@ -2439,7 +2439,7 @@ class _$KeyboardSetupImpl implements _KeyboardSetup { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$KeyboardSetupImpl && @@ -2666,7 +2666,7 @@ class _$SourceSelectionImpl implements _SourceSelection { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$SourceSelectionImpl && @@ -2872,7 +2872,7 @@ class _$SourceSelectionAndSettingImpl implements _SourceSelectionAndSetting { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$SourceSelectionAndSettingImpl && @@ -3135,7 +3135,7 @@ class _$ZdevInfoImpl implements _ZdevInfo { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$ZdevInfoImpl && @@ -3326,7 +3326,7 @@ class _$NetworkStatusImpl implements _NetworkStatus { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$NetworkStatusImpl && @@ -3537,7 +3537,7 @@ class _$OsProberImpl implements _OsProber { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$OsProberImpl && @@ -3948,7 +3948,7 @@ class _$PartitionImpl implements Partition { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$PartitionImpl && @@ -4262,7 +4262,7 @@ class _$GapImpl implements Gap { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$GapImpl && @@ -4530,7 +4530,7 @@ class _$ZFSImpl implements _ZFS { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$ZFSImpl && @@ -4789,7 +4789,7 @@ class _$ZPoolImpl implements _ZPool { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$ZPoolImpl && @@ -5194,7 +5194,7 @@ class _$DiskImpl implements _Disk { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$DiskImpl && @@ -5450,7 +5450,7 @@ class _$GuidedDisallowedCapabilityImpl implements _GuidedDisallowedCapability { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$GuidedDisallowedCapabilityImpl && @@ -5742,7 +5742,7 @@ class _$StorageResponseImpl implements _StorageResponse { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$StorageResponseImpl && @@ -6024,7 +6024,7 @@ class _$StorageResponseV2Impl implements _StorageResponseV2 { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$StorageResponseV2Impl && @@ -6236,7 +6236,7 @@ class _$GuidedResizeValuesImpl implements _GuidedResizeValues { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$GuidedResizeValuesImpl && @@ -6552,7 +6552,7 @@ class _$GuidedStorageTargetReformatImpl implements GuidedStorageTargetReformat { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$GuidedStorageTargetReformatImpl && @@ -6866,7 +6866,7 @@ class _$GuidedStorageTargetResizeImpl implements GuidedStorageTargetResize { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$GuidedStorageTargetResizeImpl && @@ -7169,7 +7169,7 @@ class _$GuidedStorageTargetUseGapImpl implements GuidedStorageTargetUseGap { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$GuidedStorageTargetUseGapImpl && @@ -7432,7 +7432,7 @@ class _$GuidedStorageTargetManualImpl implements GuidedStorageTargetManual { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$GuidedStorageTargetManualImpl && @@ -7714,7 +7714,7 @@ class _$RecoveryKeyImpl implements _RecoveryKey { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$RecoveryKeyImpl && @@ -7975,7 +7975,7 @@ class _$GuidedChoiceV2Impl implements _GuidedChoiceV2 { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$GuidedChoiceV2Impl && @@ -8236,7 +8236,7 @@ class _$GuidedStorageResponseV2Impl implements _GuidedStorageResponseV2 { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$GuidedStorageResponseV2Impl && @@ -8416,7 +8416,7 @@ class _$AddPartitionV2Impl implements _AddPartitionV2 { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$AddPartitionV2Impl && @@ -8580,7 +8580,7 @@ class _$ModifyPartitionV2Impl implements _ModifyPartitionV2 { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$ModifyPartitionV2Impl && @@ -8736,7 +8736,7 @@ class _$ReformatDiskImpl implements _ReformatDisk { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$ReformatDiskImpl && @@ -8932,7 +8932,7 @@ class _$IdentityDataImpl implements _IdentityData { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$IdentityDataImpl && @@ -9119,7 +9119,7 @@ class _$SSHDataImpl implements _SSHData { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$SSHDataImpl && @@ -9311,7 +9311,7 @@ class _$SSHIdentityImpl implements _SSHIdentity { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$SSHIdentityImpl && @@ -9503,7 +9503,7 @@ class _$SSHFetchIdResponseImpl implements _SSHFetchIdResponse { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$SSHFetchIdResponseImpl && @@ -9734,7 +9734,7 @@ class _$ChannelSnapInfoImpl implements _ChannelSnapInfo { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$ChannelSnapInfoImpl && @@ -10041,7 +10041,7 @@ class _$SnapInfoImpl implements _SnapInfo { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$SnapInfoImpl && @@ -10284,7 +10284,7 @@ class _$DriversResponseImpl implements _DriversResponse { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$DriversResponseImpl && @@ -10445,7 +10445,7 @@ class _$OEMResponseImpl implements _OEMResponse { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$OEMResponseImpl && @@ -10584,7 +10584,7 @@ class _$CodecsDataImpl implements _CodecsData { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$CodecsDataImpl && @@ -10720,7 +10720,7 @@ class _$DriversPayloadImpl implements _DriversPayload { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$DriversPayloadImpl && @@ -10886,7 +10886,7 @@ class _$SnapSelectionImpl implements _SnapSelection { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$SnapSelectionImpl && @@ -11081,7 +11081,7 @@ class _$SnapListResponseImpl implements _SnapListResponse { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$SnapListResponseImpl && @@ -11245,7 +11245,7 @@ class _$TimeZoneInfoImpl implements _TimeZoneInfo { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$TimeZoneInfoImpl && @@ -11388,7 +11388,7 @@ class _$UbuntuProInfoImpl implements _UbuntuProInfo { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$UbuntuProInfoImpl && @@ -11539,7 +11539,7 @@ class _$UbuntuProResponseImpl implements _UbuntuProResponse { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$UbuntuProResponseImpl && @@ -11697,7 +11697,7 @@ class _$UPCSInitiateResponseImpl implements _UPCSInitiateResponse { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$UPCSInitiateResponseImpl && @@ -11856,7 +11856,7 @@ class _$UPCSWaitResponseImpl implements _UPCSWaitResponse { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$UPCSWaitResponseImpl && @@ -12028,7 +12028,7 @@ class _$UbuntuProServiceImpl implements _UbuntuProService { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$UbuntuProServiceImpl && @@ -12237,7 +12237,7 @@ class _$UbuntuProSubscriptionImpl implements _UbuntuProSubscription { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$UbuntuProSubscriptionImpl && @@ -12430,7 +12430,7 @@ class _$UbuntuProCheckTokenAnswerImpl implements _UbuntuProCheckTokenAnswer { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$UbuntuProCheckTokenAnswerImpl && @@ -12630,7 +12630,7 @@ class _$WSLConfigurationBaseImpl implements _WSLConfigurationBase { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$WSLConfigurationBaseImpl && @@ -12864,7 +12864,7 @@ class _$WSLConfigurationAdvancedImpl implements _WSLConfigurationAdvanced { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$WSLConfigurationAdvancedImpl && @@ -13031,7 +13031,7 @@ class _$WSLSetupOptionsImpl implements _WSLSetupOptions { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$WSLSetupOptionsImpl && @@ -13201,7 +13201,7 @@ class _$TaskProgressImpl implements _TaskProgress { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$TaskProgressImpl && @@ -13422,7 +13422,7 @@ class _$TaskImpl implements _Task { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$TaskImpl && @@ -13695,7 +13695,7 @@ class _$ChangeImpl implements _Change { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$ChangeImpl && @@ -13895,7 +13895,7 @@ class _$MirrorCheckResponseImpl implements _MirrorCheckResponse { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$MirrorCheckResponseImpl && @@ -14077,7 +14077,7 @@ class _$MirrorPostImpl implements _MirrorPost { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$MirrorPostImpl && @@ -14281,7 +14281,7 @@ class _$MirrorGetImpl implements _MirrorGet { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$MirrorGetImpl && @@ -14463,7 +14463,7 @@ class _$AdConnectionInfoImpl implements _AdConnectionInfo { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$AdConnectionInfoImpl && diff --git a/packages/ubuntu_bootstrap/lib/services/refresh_service.freezed.dart b/packages/ubuntu_bootstrap/lib/services/refresh_service.freezed.dart index 44547261c..d5886eca8 100644 --- a/packages/ubuntu_bootstrap/lib/services/refresh_service.freezed.dart +++ b/packages/ubuntu_bootstrap/lib/services/refresh_service.freezed.dart @@ -119,7 +119,7 @@ class _$RefreshCheckingImpl extends _RefreshChecking { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$RefreshCheckingImpl); } @@ -268,7 +268,7 @@ class _$RefreshStatusImpl extends _RefreshStatus { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$RefreshStatusImpl && @@ -431,7 +431,7 @@ class _$RefreshProgressImpl extends _RefreshProgress { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$RefreshProgressImpl && @@ -566,7 +566,7 @@ class _$RefreshDoneImpl extends _RefreshDone { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$RefreshDoneImpl); } @@ -702,7 +702,7 @@ class _$RefreshErrorImpl extends _RefreshError { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$RefreshErrorImpl && diff --git a/packages/ubuntu_provision/lib/src/services/page_config_service.freezed.dart b/packages/ubuntu_provision/lib/src/services/page_config_service.freezed.dart index 0076a12fa..5d4c41ed9 100644 --- a/packages/ubuntu_provision/lib/src/services/page_config_service.freezed.dart +++ b/packages/ubuntu_provision/lib/src/services/page_config_service.freezed.dart @@ -138,7 +138,7 @@ class _$PageConfigEntryImpl implements _PageConfigEntry { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$PageConfigEntryImpl && @@ -295,7 +295,7 @@ class _$PageConfigImpl implements _PageConfig { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$PageConfigImpl && diff --git a/packages/ubuntu_provision/lib/src/services/theme_variant_service.freezed.dart b/packages/ubuntu_provision/lib/src/services/theme_variant_service.freezed.dart index f788d2c17..64b092e4f 100644 --- a/packages/ubuntu_provision/lib/src/services/theme_variant_service.freezed.dart +++ b/packages/ubuntu_provision/lib/src/services/theme_variant_service.freezed.dart @@ -162,7 +162,7 @@ class _$ThemeConfigImpl implements _ThemeConfig { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$ThemeConfigImpl && From f1f8a35f9650b29ef015aa888d52048e3fc8e045 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 22 Dec 2023 13:31:58 +0100 Subject: [PATCH 012/106] deps: Downgrade to Flutter 3.16.4 --- .tool-versions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.tool-versions b/.tool-versions index cec28fec1..cbe24c642 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -flutter 3.16.5-stable +flutter 3.16.4-stable From e5cae87e5ba6123489714449fa6e0aaa1623d42d Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Thu, 21 Dec 2023 15:21:54 +0100 Subject: [PATCH 013/106] feat: Use fallback assets config --- .../lib/src/providers/page_images.dart | 79 +- .../lib/src/services/config_service.dart | 34 +- .../lib/src/services/page_config_service.dart | 38 +- .../test/providers/page_images_test.dart | 70 + .../ubuntu_provision/test/test_utils.dart | 13 + .../test/test_utils.mocks.dart | 2633 ++++++++++++++--- 6 files changed, 2447 insertions(+), 420 deletions(-) create mode 100644 packages/ubuntu_provision/test/providers/page_images_test.dart diff --git a/packages/ubuntu_provision/lib/src/providers/page_images.dart b/packages/ubuntu_provision/lib/src/providers/page_images.dart index fd92a9ba6..9bf8364cc 100644 --- a/packages/ubuntu_provision/lib/src/providers/page_images.dart +++ b/packages/ubuntu_provision/lib/src/providers/page_images.dart @@ -19,10 +19,18 @@ class PageImages { final PageConfigService pageConfigService; - final Map _images = {}; + @visibleForTesting + SvgFileLoader Function( + File file, { + SvgTheme? theme, + ColorMapper? colorMapper, + }) svgFileLoader = SvgFileLoader.new; + + @visibleForTesting + final Map images = {}; Widget get(String pageName) => - _images[pageName] ?? const Text('PLACEHOLDER, REPLACE WITH DEFAULT'); + images[pageName] ?? const Text('PLACEHOLDER, REPLACE WITH DEFAULT'); Future preCache(BuildContext context) async { final loadFutures = >[]; @@ -31,19 +39,18 @@ class PageImages { if (imagePath == null) return; try { - final file = File(imagePath); - final extension = path.extension(imagePath); - if (extension == '.svg') { - loadFutures.add( - svg.cache.putIfAbsent( - imagePath, - () => SvgFileLoader(file).loadBytes(context), - ), - ); - _images[pageName] = SvgPicture.file(file); + final isAsset = imagePath.startsWith('assets/') || + imagePath.startsWith('packages/'); + final isAbsolutPath = imagePath.startsWith('/'); + if (isAsset) { + _loadAsset(imagePath, pageName, context); + } else if (isAbsolutPath) { + loadFutures.add(_loadFile(imagePath, pageName, context)); } else { - loadFutures.add(precacheImage(FileImage(file), context)); - _images[pageName] = Image.file(file); + _log.error( + 'Error loading image for $pageName from $imagePath since it is ' + 'neither an asset or absolute path.', + ); } } on Exception catch (e) { _log.error('Error loading image for $pageName from $imagePath: $e'); @@ -52,4 +59,48 @@ class PageImages { await Future.wait(loadFutures); } + + Future _loadFile( + String imagePath, + String pageName, + BuildContext context, + ) async { + final file = File(imagePath); + if (!await file.exists()) { + _log.error( + 'Error loading image for $pageName from $imagePath: File does not exist.', + ); + return; + } + final extension = path.extension(imagePath); + if (extension == '.svg') { + await svg.cache.putIfAbsent( + imagePath, + () => svgFileLoader(file).loadBytes(context), + ); + images[pageName] = SvgPicture.file(file); + } else { + if (context.mounted) { + await precacheImage(FileImage(file), context); + } + images[pageName] = Image.file(file); + } + } + + void _loadAsset( + String imagePath, + String pageName, + BuildContext context, + ) { + final packageRegExp = RegExp(r'packages\/(.*?)\/'); + final match = packageRegExp.firstMatch(imagePath); + final packageName = match?.group(1); + + final extension = path.extension(imagePath); + if (extension == '.svg') { + images[pageName] = SvgPicture.asset(imagePath, package: packageName); + } else { + images[pageName] = Image.asset(imagePath, package: packageName); + } + } } diff --git a/packages/ubuntu_provision/lib/src/services/config_service.dart b/packages/ubuntu_provision/lib/src/services/config_service.dart index 4d44a680a..860ebf9ba 100644 --- a/packages/ubuntu_provision/lib/src/services/config_service.dart +++ b/packages/ubuntu_provision/lib/src/services/config_service.dart @@ -1,5 +1,6 @@ import 'package:file/file.dart'; import 'package:file/local.dart'; +import 'package:flutter/services.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:ubuntu_logger/ubuntu_logger.dart'; @@ -21,6 +22,8 @@ class ConfigService { final String? _scope; final FileSystem _fs; Map? _config; + static const _extensions = ['yaml', 'yml']; + static const _filename = 'ubuntu-provision'; Future get(String key, {String? scope}) async { _config ??= await load(); @@ -30,12 +33,34 @@ class ConfigService { return _config?[scope ?? _scope]?[key] as T?; } + /// Loads the config file, if none are found on the filesystem by + /// [lookupPath], then it will try to load the default config file from the + /// assets. If no config file is found, it will return an empty map. @visibleForTesting Future?> load() async { final file = _fs.file(_path ?? ''); - if (!file.existsSync()) return {}; + String? assetData; + if (!file.existsSync()) { + for (final ext in _extensions) { + try { + assetData = await rootBundle.loadString('assets/$_filename.$ext'); + // Since there isn't any `exists` method for assets we'll just try to + // load the file and catch the exception if it doesn't exist and + // continue. If no file is found, then we'll return an empty map + // and log an error. + // ignore: avoid_catches_without_on_clauses + } catch (_) { + continue; + } + } + if (assetData == null) { + _log.error('No config file found on the filesystem or in assets.'); + return {}; + } + } + try { - final data = await file.readAsString(); + final data = assetData ?? await file.readAsString(); final config = switch (p.extension(_path!)) { '.yml' || '.yaml' => loadYaml(data), _ => throw UnsupportedError( @@ -56,15 +81,14 @@ class ConfigService { /// - /usr/share/ubuntu-provision.{yaml,yml} (distro) @visibleForTesting static String? lookupPath(FileSystem fs) { - const extensions = ['yaml', 'yml']; final dirs = [ ...xdg.configDirs, fs.directory('/etc'), ...xdg.dataDirs, ]; for (final dir in dirs) { - for (final ext in extensions) { - final path = p.join(dir.path, 'ubuntu-provision.$ext'); + for (final ext in _extensions) { + final path = p.join(dir.path, '$_filename.$ext'); if (fs.file(path).existsSync()) return path; } } diff --git a/packages/ubuntu_provision/lib/src/services/page_config_service.dart b/packages/ubuntu_provision/lib/src/services/page_config_service.dart index 153d6525b..d40fd22bf 100644 --- a/packages/ubuntu_provision/lib/src/services/page_config_service.dart +++ b/packages/ubuntu_provision/lib/src/services/page_config_service.dart @@ -9,6 +9,25 @@ part 'page_config_service.g.dart'; final _log = Logger('page'); +class PageConfigService { + PageConfigService({ConfigService? config}) : _config = config; + + final ConfigService? _config; + final Map pages = {}; + + Set get excludedPages => + pages.entries.whereNot((e) => e.value.visible).map((e) => e.key).toSet(); + + Future load() async { + final pageConfig = PageConfig.fromJson({ + 'pages': Map.from( + (await _config!.get('pages'))?.value.cast() ?? {}, + ) + }); + pages.addAll(pageConfig.pages); + } +} + @freezed class PageConfigEntry with _$PageConfigEntry { const factory PageConfigEntry({ @@ -68,22 +87,3 @@ class PageConfigEntryConverter return objects; } } - -class PageConfigService { - PageConfigService({ConfigService? config}) : _config = config; - - final ConfigService? _config; - final Map pages = {}; - - Set get excludedPages => - pages.entries.whereNot((e) => e.value.visible).map((e) => e.key).toSet(); - - Future load() async { - final pageConfig = PageConfig.fromJson({ - 'pages': Map.from( - (await _config!.get('pages'))?.value.cast() ?? {}, - ) - }); - pages.addAll(pageConfig.pages); - } -} diff --git a/packages/ubuntu_provision/test/providers/page_images_test.dart b/packages/ubuntu_provision/test/providers/page_images_test.dart new file mode 100644 index 000000000..e1642e94d --- /dev/null +++ b/packages/ubuntu_provision/test/providers/page_images_test.dart @@ -0,0 +1,70 @@ +// Write tests for page_images.dart +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:ubuntu_provision/ubuntu_provision.dart'; + +import '../test_utils.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('PageImages', () { + test('get returns correct widget for given page name', () { + final mockService = MockPageConfigService(); + final pageImages = PageImages(mockService); + final image = MockImage(); + + pageImages.images['testPage'] = image; + + final result = pageImages.get('testPage'); + + expect(result, isInstanceOf()); + expect(result, equals(image)); + }); + + test('preCache correctly loads images from assets and files', () async { + final mockFile = MockFile(); + when(mockFile.existsSync()).thenReturn(true); + when(mockFile.exists()).thenAnswer((_) async => true); + + await IOOverrides.runZoned( + () async { + final mockService = MockPageConfigService(); + final pageNames = List.generate(4, (i) => 'testPage$i'); + when(mockService.pages).thenReturn({ + pageNames[0]: const PageConfigEntry(image: 'assets/test.png'), + pageNames[1]: const PageConfigEntry(image: '/path/to/test.png'), + pageNames[2]: const PageConfigEntry(image: 'assets/test.svg'), + pageNames[3]: const PageConfigEntry(image: '/path/to/test.svg'), + }); + + final mockSvgFileLoader = MockSvgFileLoader(); + when(mockSvgFileLoader.loadBytes( + any, + )).thenAnswer((_) => Future.value(ByteData(0))); + + final pageImages = PageImages(mockService) + ..svgFileLoader = (file, {colorMapper, theme}) => mockSvgFileLoader; + + await pageImages.preCache(MockBuildContext()); + + expect(pageImages.images, isNotEmpty); + expect(pageImages.images.keys, containsAll(pageNames)); + for (final pageName in pageNames) { + if (pageImages.images[pageName] is SvgPicture) { + expect(pageImages.images[pageName], isInstanceOf()); + } else { + expect(pageImages.images[pageName], isInstanceOf()); + } + } + }, + createFile: (name) => mockFile, + ); + }); + }); +} diff --git a/packages/ubuntu_provision/test/test_utils.dart b/packages/ubuntu_provision/test/test_utils.dart index 4f465efee..16bf368c9 100644 --- a/packages/ubuntu_provision/test/test_utils.dart +++ b/packages/ubuntu_provision/test/test_utils.dart @@ -1,6 +1,10 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; import 'package:timezone_map/timezone_map.dart'; import 'package:ubuntu_localizations/ubuntu_localizations.dart'; import 'package:ubuntu_provision/l10n.dart'; @@ -56,6 +60,7 @@ extension UbuntuProvisionTester on WidgetTester { DesktopService, GeoService, IdentityService, + Image, JournalService, KeyboardService, LocaleService, @@ -73,3 +78,11 @@ extension UbuntuProvisionTester on WidgetTester { UrlLauncher, ]) class _Dummy {} // ignore: unused_element + +@GenerateNiceMocks([ + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), +]) +class _Dummy2 {} // ignore: unused_element diff --git a/packages/ubuntu_provision/test/test_utils.mocks.dart b/packages/ubuntu_provision/test/test_utils.mocks.dart index 55bbd0535..d398a2c50 100644 --- a/packages/ubuntu_provision/test/test_utils.mocks.dart +++ b/packages/ubuntu_provision/test/test_utils.mocks.dart @@ -3,19 +3,30 @@ // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i5; -import 'dart:ui' as _i11; - -import 'package:dbus/dbus.dart' as _i10; +import 'dart:async' as _i12; +import 'dart:convert' as _i22; +import 'dart:io' as _i9; +import 'dart:typed_data' as _i23; +import 'dart:ui' as _i17; + +import 'package:dbus/dbus.dart' as _i19; +import 'package:flutter/foundation.dart' as _i7; +import 'package:flutter/services.dart' as _i6; +import 'package:flutter/src/widgets/basic.dart' as _i4; +import 'package:flutter/src/widgets/framework.dart' as _i5; +import 'package:flutter/src/widgets/notification_listener.dart' as _i21; +import 'package:flutter/widgets.dart' as _i16; +import 'package:flutter_svg/svg.dart' as _i10; import 'package:mockito/mockito.dart' as _i1; -import 'package:mockito/src/dummies.dart' as _i9; -import 'package:nm/nm.dart' as _i4; +import 'package:mockito/src/dummies.dart' as _i18; +import 'package:nm/nm.dart' as _i8; import 'package:subiquity_client/subiquity_client.dart' as _i2; -import 'package:timezone_map/src/location.dart' as _i8; -import 'package:timezone_map/src/service.dart' as _i6; -import 'package:timezone_map/src/source.dart' as _i7; +import 'package:timezone_map/src/location.dart' as _i15; +import 'package:timezone_map/src/service.dart' as _i13; +import 'package:timezone_map/src/source.dart' as _i14; import 'package:ubuntu_provision/services.dart' as _i3; -import 'package:ubuntu_utils/src/url_launcher.dart' as _i12; +import 'package:ubuntu_utils/src/url_launcher.dart' as _i20; +import 'package:vector_graphics/vector_graphics.dart' as _i11; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -51,8 +62,78 @@ class _FakeIdentity_1 extends _i1.SmartFake implements _i3.Identity { ); } -class _FakeKeyboardSetup_2 extends _i1.SmartFake implements _i2.KeyboardSetup { - _FakeKeyboardSetup_2( +class _FakeImageProvider_2 extends _i1.SmartFake + implements _i4.ImageProvider { + _FakeImageProvider_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeAlignmentGeometry_3 extends _i1.SmartFake + implements _i4.AlignmentGeometry { + _FakeAlignmentGeometry_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeState_4 extends _i1.SmartFake + implements _i5.State { + _FakeState_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); + + @override + String toString({_i6.DiagnosticLevel? minLevel = _i6.DiagnosticLevel.info}) => + super.toString(); +} + +class _FakeStatefulElement_5 extends _i1.SmartFake + implements _i5.StatefulElement { + _FakeStatefulElement_5( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); + + @override + String toString({_i6.DiagnosticLevel? minLevel = _i6.DiagnosticLevel.info}) => + super.toString(); +} + +class _FakeDiagnosticsNode_6 extends _i1.SmartFake + implements _i7.DiagnosticsNode { + _FakeDiagnosticsNode_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); + + @override + String toString({ + _i7.TextTreeConfiguration? parentConfiguration, + _i6.DiagnosticLevel? minLevel = _i6.DiagnosticLevel.info, + }) => + super.toString(); +} + +class _FakeKeyboardSetup_7 extends _i1.SmartFake implements _i2.KeyboardSetup { + _FakeKeyboardSetup_7( Object parent, Invocation parentInvocation, ) : super( @@ -61,8 +142,8 @@ class _FakeKeyboardSetup_2 extends _i1.SmartFake implements _i2.KeyboardSetup { ); } -class _FakeAnyStep_3 extends _i1.SmartFake implements _i2.AnyStep { - _FakeAnyStep_3( +class _FakeAnyStep_8 extends _i1.SmartFake implements _i2.AnyStep { + _FakeAnyStep_8( Object parent, Invocation parentInvocation, ) : super( @@ -71,9 +152,9 @@ class _FakeAnyStep_3 extends _i1.SmartFake implements _i2.AnyStep { ); } -class _FakeNetworkManagerSettings_4 extends _i1.SmartFake - implements _i4.NetworkManagerSettings { - _FakeNetworkManagerSettings_4( +class _FakeNetworkManagerSettings_9 extends _i1.SmartFake + implements _i8.NetworkManagerSettings { + _FakeNetworkManagerSettings_9( Object parent, Invocation parentInvocation, ) : super( @@ -82,9 +163,9 @@ class _FakeNetworkManagerSettings_4 extends _i1.SmartFake ); } -class _FakeNetworkManagerDnsManager_5 extends _i1.SmartFake - implements _i4.NetworkManagerDnsManager { - _FakeNetworkManagerDnsManager_5( +class _FakeNetworkManagerDnsManager_10 extends _i1.SmartFake + implements _i8.NetworkManagerDnsManager { + _FakeNetworkManagerDnsManager_10( Object parent, Invocation parentInvocation, ) : super( @@ -93,9 +174,9 @@ class _FakeNetworkManagerDnsManager_5 extends _i1.SmartFake ); } -class _FakeNetworkManagerActiveConnection_6 extends _i1.SmartFake - implements _i4.NetworkManagerActiveConnection { - _FakeNetworkManagerActiveConnection_6( +class _FakeNetworkManagerActiveConnection_11 extends _i1.SmartFake + implements _i8.NetworkManagerActiveConnection { + _FakeNetworkManagerActiveConnection_11( Object parent, Invocation parentInvocation, ) : super( @@ -104,9 +185,9 @@ class _FakeNetworkManagerActiveConnection_6 extends _i1.SmartFake ); } -class _FakeUPowerKbdBacklight_7 extends _i1.SmartFake +class _FakeUPowerKbdBacklight_12 extends _i1.SmartFake implements _i3.UPowerKbdBacklight { - _FakeUPowerKbdBacklight_7( + _FakeUPowerKbdBacklight_12( Object parent, Invocation parentInvocation, ) : super( @@ -115,8 +196,8 @@ class _FakeUPowerKbdBacklight_7 extends _i1.SmartFake ); } -class _FakeUPowerDevice_8 extends _i1.SmartFake implements _i3.UPowerDevice { - _FakeUPowerDevice_8( +class _FakeUPowerDevice_13 extends _i1.SmartFake implements _i3.UPowerDevice { + _FakeUPowerDevice_13( Object parent, Invocation parentInvocation, ) : super( @@ -125,8 +206,8 @@ class _FakeUPowerDevice_8 extends _i1.SmartFake implements _i3.UPowerDevice { ); } -class _FakeProductInfo_9 extends _i1.SmartFake implements _i3.ProductInfo { - _FakeProductInfo_9( +class _FakeProductInfo_14 extends _i1.SmartFake implements _i3.ProductInfo { + _FakeProductInfo_14( Object parent, Invocation parentInvocation, ) : super( @@ -135,15 +216,171 @@ class _FakeProductInfo_9 extends _i1.SmartFake implements _i3.ProductInfo { ); } -class _FakeUdevDeviceInfo_10 extends _i1.SmartFake +class _FakeUdevDeviceInfo_15 extends _i1.SmartFake implements _i3.UdevDeviceInfo { - _FakeUdevDeviceInfo_10( + _FakeUdevDeviceInfo_15( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeWidget_16 extends _i1.SmartFake implements _i5.Widget { + _FakeWidget_16( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); + + @override + String toString({_i6.DiagnosticLevel? minLevel = _i6.DiagnosticLevel.info}) => + super.toString(); +} + +class _FakeInheritedWidget_17 extends _i1.SmartFake + implements _i5.InheritedWidget { + _FakeInheritedWidget_17( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); + + @override + String toString({_i6.DiagnosticLevel? minLevel = _i6.DiagnosticLevel.info}) => + super.toString(); +} + +class _FakeFile_18 extends _i1.SmartFake implements _i9.File { + _FakeFile_18( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeUri_19 extends _i1.SmartFake implements Uri { + _FakeUri_19( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeDirectory_20 extends _i1.SmartFake implements _i9.Directory { + _FakeDirectory_20( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeDateTime_21 extends _i1.SmartFake implements DateTime { + _FakeDateTime_21( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeRandomAccessFile_22 extends _i1.SmartFake + implements _i9.RandomAccessFile { + _FakeRandomAccessFile_22( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeIOSink_23 extends _i1.SmartFake implements _i9.IOSink { + _FakeIOSink_23( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeFileStat_24 extends _i1.SmartFake implements _i9.FileStat { + _FakeFileStat_24( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeFileSystemEntity_25 extends _i1.SmartFake + implements _i9.FileSystemEntity { + _FakeFileSystemEntity_25( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeSvgTheme_26 extends _i1.SmartFake implements _i10.SvgTheme { + _FakeSvgTheme_26( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeSvgCacheKey_27 extends _i1.SmartFake implements _i10.SvgCacheKey { + _FakeSvgCacheKey_27( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeBytesLoader_28 extends _i1.SmartFake implements _i11.BytesLoader { + _FakeBytesLoader_28( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeStatelessElement_29 extends _i1.SmartFake + implements _i5.StatelessElement { + _FakeStatelessElement_29( Object parent, Invocation parentInvocation, ) : super( parent, parentInvocation, ); + + @override + String toString({_i6.DiagnosticLevel? minLevel = _i6.DiagnosticLevel.info}) => + super.toString(); } /// A class which mocks [ActiveDirectoryService]. @@ -156,115 +393,116 @@ class MockActiveDirectoryService extends _i1.Mock } @override - _i5.Future hasSupport() => (super.noSuchMethod( + _i12.Future hasSupport() => (super.noSuchMethod( Invocation.method( #hasSupport, [], ), - returnValue: _i5.Future.value(false), - ) as _i5.Future); + returnValue: _i12.Future.value(false), + ) as _i12.Future); @override - _i5.Future isUsed() => (super.noSuchMethod( + _i12.Future isUsed() => (super.noSuchMethod( Invocation.method( #isUsed, [], ), - returnValue: _i5.Future.value(false), - ) as _i5.Future); + returnValue: _i12.Future.value(false), + ) as _i12.Future); @override - _i5.Future setUsed(bool? used) => (super.noSuchMethod( + _i12.Future setUsed(bool? used) => (super.noSuchMethod( Invocation.method( #setUsed, [used], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future<_i2.AdConnectionInfo> getConnectionInfo() => (super.noSuchMethod( + _i12.Future<_i2.AdConnectionInfo> getConnectionInfo() => (super.noSuchMethod( Invocation.method( #getConnectionInfo, [], ), returnValue: - _i5.Future<_i2.AdConnectionInfo>.value(_FakeAdConnectionInfo_0( + _i12.Future<_i2.AdConnectionInfo>.value(_FakeAdConnectionInfo_0( this, Invocation.method( #getConnectionInfo, [], ), )), - ) as _i5.Future<_i2.AdConnectionInfo>); + ) as _i12.Future<_i2.AdConnectionInfo>); @override - _i5.Future setConnectionInfo(_i2.AdConnectionInfo? info) => + _i12.Future setConnectionInfo(_i2.AdConnectionInfo? info) => (super.noSuchMethod( Invocation.method( #setConnectionInfo, [info], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future> checkDomainName( + _i12.Future> checkDomainName( String? domain) => (super.noSuchMethod( Invocation.method( #checkDomainName, [domain], ), - returnValue: _i5.Future>.value( + returnValue: _i12.Future>.value( <_i2.AdDomainNameValidation>[]), - ) as _i5.Future>); + ) as _i12.Future>); @override - _i5.Future<_i2.AdAdminNameValidation> checkAdminName(String? admin) => + _i12.Future<_i2.AdAdminNameValidation> checkAdminName(String? admin) => (super.noSuchMethod( Invocation.method( #checkAdminName, [admin], ), - returnValue: _i5.Future<_i2.AdAdminNameValidation>.value( + returnValue: _i12.Future<_i2.AdAdminNameValidation>.value( _i2.AdAdminNameValidation.OK), - ) as _i5.Future<_i2.AdAdminNameValidation>); + ) as _i12.Future<_i2.AdAdminNameValidation>); @override - _i5.Future<_i2.AdPasswordValidation> checkPassword(String? password) => + _i12.Future<_i2.AdPasswordValidation> checkPassword(String? password) => (super.noSuchMethod( Invocation.method( #checkPassword, [password], ), - returnValue: _i5.Future<_i2.AdPasswordValidation>.value( + returnValue: _i12.Future<_i2.AdPasswordValidation>.value( _i2.AdPasswordValidation.OK), - ) as _i5.Future<_i2.AdPasswordValidation>); + ) as _i12.Future<_i2.AdPasswordValidation>); @override - _i5.Future<_i2.AdDomainNameValidation> pingDomainController(String? domain) => + _i12.Future<_i2.AdDomainNameValidation> pingDomainController( + String? domain) => (super.noSuchMethod( Invocation.method( #pingDomainController, [domain], ), - returnValue: _i5.Future<_i2.AdDomainNameValidation>.value( + returnValue: _i12.Future<_i2.AdDomainNameValidation>.value( _i2.AdDomainNameValidation.OK), - ) as _i5.Future<_i2.AdDomainNameValidation>); + ) as _i12.Future<_i2.AdDomainNameValidation>); @override - _i5.Future<_i2.AdJoinResult> getJoinResult({bool? wait = true}) => + _i12.Future<_i2.AdJoinResult> getJoinResult({bool? wait = true}) => (super.noSuchMethod( Invocation.method( #getJoinResult, [], {#wait: wait}, ), - returnValue: _i5.Future<_i2.AdJoinResult>.value(_i2.AdJoinResult.OK), - ) as _i5.Future<_i2.AdJoinResult>); + returnValue: _i12.Future<_i2.AdJoinResult>.value(_i2.AdJoinResult.OK), + ) as _i12.Future<_i2.AdJoinResult>); } /// A class which mocks [ConfigService]. @@ -276,7 +514,7 @@ class MockConfigService extends _i1.Mock implements _i3.ConfigService { } @override - _i5.Future get( + _i12.Future get( String? key, { String? scope, }) => @@ -286,17 +524,17 @@ class MockConfigService extends _i1.Mock implements _i3.ConfigService { [key], {#scope: scope}, ), - returnValue: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future?> load() => (super.noSuchMethod( + _i12.Future?> load() => (super.noSuchMethod( Invocation.method( #load, [], ), - returnValue: _i5.Future?>.value(), - ) as _i5.Future?>); + returnValue: _i12.Future?>.value(), + ) as _i12.Future?>); } /// A class which mocks [DesktopService]. @@ -308,36 +546,36 @@ class MockDesktopService extends _i1.Mock implements _i3.DesktopService { } @override - _i5.Future inhibit() => (super.noSuchMethod( + _i12.Future inhibit() => (super.noSuchMethod( Invocation.method( #inhibit, [], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future close() => (super.noSuchMethod( + _i12.Future close() => (super.noSuchMethod( Invocation.method( #close, [], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); } /// A class which mocks [GeoService]. /// /// See the documentation for Mockito's code generation for more information. -class MockGeoService extends _i1.Mock implements _i6.GeoService { +class MockGeoService extends _i1.Mock implements _i13.GeoService { MockGeoService() { _i1.throwOnMissingStub(this); } @override - void addSource(_i7.GeoSource? source) => super.noSuchMethod( + void addSource(_i14.GeoSource? source) => super.noSuchMethod( Invocation.method( #addSource, [source], @@ -346,7 +584,7 @@ class MockGeoService extends _i1.Mock implements _i6.GeoService { ); @override - void removeSource(_i7.GeoSource? source) => super.noSuchMethod( + void removeSource(_i14.GeoSource? source) => super.noSuchMethod( Invocation.method( #removeSource, [source], @@ -355,67 +593,67 @@ class MockGeoService extends _i1.Mock implements _i6.GeoService { ); @override - _i5.Future init() => (super.noSuchMethod( + _i12.Future init() => (super.noSuchMethod( Invocation.method( #init, [], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future<_i8.GeoLocation?> lookupLocation() => (super.noSuchMethod( + _i12.Future<_i15.GeoLocation?> lookupLocation() => (super.noSuchMethod( Invocation.method( #lookupLocation, [], ), - returnValue: _i5.Future<_i8.GeoLocation?>.value(), - ) as _i5.Future<_i8.GeoLocation?>); + returnValue: _i12.Future<_i15.GeoLocation?>.value(), + ) as _i12.Future<_i15.GeoLocation?>); @override - _i5.Future> searchLocation(String? location) => + _i12.Future> searchLocation(String? location) => (super.noSuchMethod( Invocation.method( #searchLocation, [location], ), returnValue: - _i5.Future>.value(<_i8.GeoLocation>[]), - ) as _i5.Future>); + _i12.Future>.value(<_i15.GeoLocation>[]), + ) as _i12.Future>); @override - _i5.Future> searchCoordinates( - _i8.LatLng? coordinates) => + _i12.Future> searchCoordinates( + _i15.LatLng? coordinates) => (super.noSuchMethod( Invocation.method( #searchCoordinates, [coordinates], ), returnValue: - _i5.Future>.value(<_i8.GeoLocation>[]), - ) as _i5.Future>); + _i12.Future>.value(<_i15.GeoLocation>[]), + ) as _i12.Future>); @override - _i5.Future> searchTimezone(String? timezone) => + _i12.Future> searchTimezone(String? timezone) => (super.noSuchMethod( Invocation.method( #searchTimezone, [timezone], ), returnValue: - _i5.Future>.value(<_i8.GeoLocation>[]), - ) as _i5.Future>); + _i12.Future>.value(<_i15.GeoLocation>[]), + ) as _i12.Future>); @override - _i5.Future cancelSearch() => (super.noSuchMethod( + _i12.Future cancelSearch() => (super.noSuchMethod( Invocation.method( #cancelSearch, [], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); } /// A class which mocks [IdentityService]. @@ -427,170 +665,385 @@ class MockIdentityService extends _i1.Mock implements _i3.IdentityService { } @override - _i5.Future<_i3.Identity> getIdentity() => (super.noSuchMethod( + _i12.Future<_i3.Identity> getIdentity() => (super.noSuchMethod( Invocation.method( #getIdentity, [], ), - returnValue: _i5.Future<_i3.Identity>.value(_FakeIdentity_1( + returnValue: _i12.Future<_i3.Identity>.value(_FakeIdentity_1( this, Invocation.method( #getIdentity, [], ), )), - ) as _i5.Future<_i3.Identity>); + ) as _i12.Future<_i3.Identity>); @override - _i5.Future setIdentity(_i3.Identity? identity) => (super.noSuchMethod( + _i12.Future setIdentity(_i3.Identity? identity) => (super.noSuchMethod( Invocation.method( #setIdentity, [identity], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future<_i2.UsernameValidation> validateUsername(String? username) => + _i12.Future<_i2.UsernameValidation> validateUsername(String? username) => (super.noSuchMethod( Invocation.method( #validateUsername, [username], ), - returnValue: - _i5.Future<_i2.UsernameValidation>.value(_i2.UsernameValidation.OK), - ) as _i5.Future<_i2.UsernameValidation>); + returnValue: _i12.Future<_i2.UsernameValidation>.value( + _i2.UsernameValidation.OK), + ) as _i12.Future<_i2.UsernameValidation>); } -/// A class which mocks [JournalService]. +/// A class which mocks [Image]. /// /// See the documentation for Mockito's code generation for more information. -class MockJournalService extends _i1.Mock implements _i3.JournalService { - MockJournalService() { +class MockImage extends _i1.Mock implements _i16.Image { + MockImage() { _i1.throwOnMissingStub(this); } @override - _i5.Stream start( - List? ids, { - _i3.JournalOutput? output = _i3.JournalOutput.short, - }) => - (super.noSuchMethod( - Invocation.method( - #start, - [ids], - {#output: output}, + _i4.ImageProvider get image => (super.noSuchMethod( + Invocation.getter(#image), + returnValue: _FakeImageProvider_2( + this, + Invocation.getter(#image), ), - returnValue: _i5.Stream.empty(), - ) as _i5.Stream); -} + ) as _i4.ImageProvider); -/// A class which mocks [KeyboardService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockKeyboardService extends _i1.Mock implements _i3.KeyboardService { - MockKeyboardService() { - _i1.throwOnMissingStub(this); - } + @override + _i17.FilterQuality get filterQuality => (super.noSuchMethod( + Invocation.getter(#filterQuality), + returnValue: _i17.FilterQuality.none, + ) as _i17.FilterQuality); @override - bool get canDetectLayout => (super.noSuchMethod( - Invocation.getter(#canDetectLayout), + _i4.AlignmentGeometry get alignment => (super.noSuchMethod( + Invocation.getter(#alignment), + returnValue: _FakeAlignmentGeometry_3( + this, + Invocation.getter(#alignment), + ), + ) as _i4.AlignmentGeometry); + + @override + _i4.ImageRepeat get repeat => (super.noSuchMethod( + Invocation.getter(#repeat), + returnValue: _i4.ImageRepeat.repeat, + ) as _i4.ImageRepeat); + + @override + bool get matchTextDirection => (super.noSuchMethod( + Invocation.getter(#matchTextDirection), + returnValue: false, + ) as bool); + + @override + bool get gaplessPlayback => (super.noSuchMethod( + Invocation.getter(#gaplessPlayback), + returnValue: false, + ) as bool); + + @override + bool get excludeFromSemantics => (super.noSuchMethod( + Invocation.getter(#excludeFromSemantics), + returnValue: false, + ) as bool); + + @override + bool get isAntiAlias => (super.noSuchMethod( + Invocation.getter(#isAntiAlias), returnValue: false, ) as bool); @override - _i5.Future<_i2.KeyboardSetup> getKeyboard() => (super.noSuchMethod( + _i5.State<_i16.Image> createState() => (super.noSuchMethod( Invocation.method( - #getKeyboard, + #createState, [], ), - returnValue: _i5.Future<_i2.KeyboardSetup>.value(_FakeKeyboardSetup_2( + returnValue: _FakeState_4<_i16.Image>( this, Invocation.method( - #getKeyboard, + #createState, [], ), - )), - ) as _i5.Future<_i2.KeyboardSetup>); - - @override - _i5.Future setKeyboard(_i2.KeyboardSetting? setting) => - (super.noSuchMethod( - Invocation.method( - #setKeyboard, - [setting], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + ) as _i5.State<_i16.Image>); @override - _i5.Future setInputSource( - _i2.KeyboardSetting? setting, { - String? user, - }) => - (super.noSuchMethod( + void debugFillProperties(_i6.DiagnosticPropertiesBuilder? properties) => + super.noSuchMethod( Invocation.method( - #setInputSource, - [setting], - {#user: user}, + #debugFillProperties, + [properties], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValueForMissingStub: null, + ); @override - _i5.Future<_i2.AnyStep> getKeyboardStep([String? step = r'0']) => - (super.noSuchMethod( + _i5.StatefulElement createElement() => (super.noSuchMethod( Invocation.method( - #getKeyboardStep, - [step], + #createElement, + [], ), - returnValue: _i5.Future<_i2.AnyStep>.value(_FakeAnyStep_3( + returnValue: _FakeStatefulElement_5( this, Invocation.method( - #getKeyboardStep, - [step], + #createElement, + [], ), - )), - ) as _i5.Future<_i2.AnyStep>); -} - -/// A class which mocks [LocaleService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockLocaleService extends _i1.Mock implements _i3.LocaleService { - MockLocaleService() { - _i1.throwOnMissingStub(this); - } + ), + ) as _i5.StatefulElement); @override - _i5.Future getLocale() => (super.noSuchMethod( + String toStringShort() => (super.noSuchMethod( Invocation.method( - #getLocale, + #toStringShort, [], ), - returnValue: _i5.Future.value(_i9.dummyValue( + returnValue: _i18.dummyValue( this, Invocation.method( - #getLocale, + #toStringShort, [], ), - )), - ) as _i5.Future); + ), + ) as String); @override - _i5.Future setLocale(String? locale) => (super.noSuchMethod( + String toStringShallow({ + String? joiner = r', ', + _i6.DiagnosticLevel? minLevel = _i6.DiagnosticLevel.debug, + }) => + (super.noSuchMethod( Invocation.method( - #setLocale, - [locale], - ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); -} + #toStringShallow, + [], + { + #joiner: joiner, + #minLevel: minLevel, + }, + ), + returnValue: _i18.dummyValue( + this, + Invocation.method( + #toStringShallow, + [], + { + #joiner: joiner, + #minLevel: minLevel, + }, + ), + ), + ) as String); + + @override + String toStringDeep({ + String? prefixLineOne = r'', + String? prefixOtherLines, + _i6.DiagnosticLevel? minLevel = _i6.DiagnosticLevel.debug, + }) => + (super.noSuchMethod( + Invocation.method( + #toStringDeep, + [], + { + #prefixLineOne: prefixLineOne, + #prefixOtherLines: prefixOtherLines, + #minLevel: minLevel, + }, + ), + returnValue: _i18.dummyValue( + this, + Invocation.method( + #toStringDeep, + [], + { + #prefixLineOne: prefixLineOne, + #prefixOtherLines: prefixOtherLines, + #minLevel: minLevel, + }, + ), + ), + ) as String); + + @override + _i7.DiagnosticsNode toDiagnosticsNode({ + String? name, + _i7.DiagnosticsTreeStyle? style, + }) => + (super.noSuchMethod( + Invocation.method( + #toDiagnosticsNode, + [], + { + #name: name, + #style: style, + }, + ), + returnValue: _FakeDiagnosticsNode_6( + this, + Invocation.method( + #toDiagnosticsNode, + [], + { + #name: name, + #style: style, + }, + ), + ), + ) as _i7.DiagnosticsNode); + + @override + List<_i7.DiagnosticsNode> debugDescribeChildren() => (super.noSuchMethod( + Invocation.method( + #debugDescribeChildren, + [], + ), + returnValue: <_i7.DiagnosticsNode>[], + ) as List<_i7.DiagnosticsNode>); + + @override + String toString({_i6.DiagnosticLevel? minLevel = _i6.DiagnosticLevel.info}) => + super.toString(); +} + +/// A class which mocks [JournalService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockJournalService extends _i1.Mock implements _i3.JournalService { + MockJournalService() { + _i1.throwOnMissingStub(this); + } + + @override + _i12.Stream start( + List? ids, { + _i3.JournalOutput? output = _i3.JournalOutput.short, + }) => + (super.noSuchMethod( + Invocation.method( + #start, + [ids], + {#output: output}, + ), + returnValue: _i12.Stream.empty(), + ) as _i12.Stream); +} + +/// A class which mocks [KeyboardService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockKeyboardService extends _i1.Mock implements _i3.KeyboardService { + MockKeyboardService() { + _i1.throwOnMissingStub(this); + } + + @override + bool get canDetectLayout => (super.noSuchMethod( + Invocation.getter(#canDetectLayout), + returnValue: false, + ) as bool); + + @override + _i12.Future<_i2.KeyboardSetup> getKeyboard() => (super.noSuchMethod( + Invocation.method( + #getKeyboard, + [], + ), + returnValue: _i12.Future<_i2.KeyboardSetup>.value(_FakeKeyboardSetup_7( + this, + Invocation.method( + #getKeyboard, + [], + ), + )), + ) as _i12.Future<_i2.KeyboardSetup>); + + @override + _i12.Future setKeyboard(_i2.KeyboardSetting? setting) => + (super.noSuchMethod( + Invocation.method( + #setKeyboard, + [setting], + ), + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); + + @override + _i12.Future setInputSource( + _i2.KeyboardSetting? setting, { + String? user, + }) => + (super.noSuchMethod( + Invocation.method( + #setInputSource, + [setting], + {#user: user}, + ), + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); + + @override + _i12.Future<_i2.AnyStep> getKeyboardStep([String? step = r'0']) => + (super.noSuchMethod( + Invocation.method( + #getKeyboardStep, + [step], + ), + returnValue: _i12.Future<_i2.AnyStep>.value(_FakeAnyStep_8( + this, + Invocation.method( + #getKeyboardStep, + [step], + ), + )), + ) as _i12.Future<_i2.AnyStep>); +} + +/// A class which mocks [LocaleService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockLocaleService extends _i1.Mock implements _i3.LocaleService { + MockLocaleService() { + _i1.throwOnMissingStub(this); + } + + @override + _i12.Future getLocale() => (super.noSuchMethod( + Invocation.method( + #getLocale, + [], + ), + returnValue: _i12.Future.value(_i18.dummyValue( + this, + Invocation.method( + #getLocale, + [], + ), + )), + ) as _i12.Future); + + @override + _i12.Future setLocale(String? locale) => (super.noSuchMethod( + Invocation.method( + #setLocale, + [locale], + ), + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); +} /// A class which mocks [NetworkService]. /// @@ -613,60 +1066,61 @@ class MockNetworkService extends _i1.Mock implements _i3.NetworkService { ) as bool); @override - List<_i4.NetworkManagerDevice> get wiredDevices => (super.noSuchMethod( + List<_i8.NetworkManagerDevice> get wiredDevices => (super.noSuchMethod( Invocation.getter(#wiredDevices), - returnValue: <_i4.NetworkManagerDevice>[], - ) as List<_i4.NetworkManagerDevice>); + returnValue: <_i8.NetworkManagerDevice>[], + ) as List<_i8.NetworkManagerDevice>); @override - List<_i4.NetworkManagerDevice> get wirelessDevices => (super.noSuchMethod( + List<_i8.NetworkManagerDevice> get wirelessDevices => (super.noSuchMethod( Invocation.getter(#wirelessDevices), - returnValue: <_i4.NetworkManagerDevice>[], - ) as List<_i4.NetworkManagerDevice>); + returnValue: <_i8.NetworkManagerDevice>[], + ) as List<_i8.NetworkManagerDevice>); @override - _i5.Stream<_i4.NetworkManagerDevice> get deviceAdded => (super.noSuchMethod( + _i12.Stream<_i8.NetworkManagerDevice> get deviceAdded => (super.noSuchMethod( Invocation.getter(#deviceAdded), - returnValue: _i5.Stream<_i4.NetworkManagerDevice>.empty(), - ) as _i5.Stream<_i4.NetworkManagerDevice>); + returnValue: _i12.Stream<_i8.NetworkManagerDevice>.empty(), + ) as _i12.Stream<_i8.NetworkManagerDevice>); @override - _i5.Stream<_i4.NetworkManagerDevice> get deviceRemoved => (super.noSuchMethod( + _i12.Stream<_i8.NetworkManagerDevice> get deviceRemoved => + (super.noSuchMethod( Invocation.getter(#deviceRemoved), - returnValue: _i5.Stream<_i4.NetworkManagerDevice>.empty(), - ) as _i5.Stream<_i4.NetworkManagerDevice>); + returnValue: _i12.Stream<_i8.NetworkManagerDevice>.empty(), + ) as _i12.Stream<_i8.NetworkManagerDevice>); @override - _i5.Stream<_i4.NetworkManagerActiveConnection> get activeConnectionAdded => + _i12.Stream<_i8.NetworkManagerActiveConnection> get activeConnectionAdded => (super.noSuchMethod( Invocation.getter(#activeConnectionAdded), - returnValue: _i5.Stream<_i4.NetworkManagerActiveConnection>.empty(), - ) as _i5.Stream<_i4.NetworkManagerActiveConnection>); + returnValue: _i12.Stream<_i8.NetworkManagerActiveConnection>.empty(), + ) as _i12.Stream<_i8.NetworkManagerActiveConnection>); @override - _i5.Stream<_i4.NetworkManagerActiveConnection> get activeConnectionRemoved => + _i12.Stream<_i8.NetworkManagerActiveConnection> get activeConnectionRemoved => (super.noSuchMethod( Invocation.getter(#activeConnectionRemoved), - returnValue: _i5.Stream<_i4.NetworkManagerActiveConnection>.empty(), - ) as _i5.Stream<_i4.NetworkManagerActiveConnection>); + returnValue: _i12.Stream<_i8.NetworkManagerActiveConnection>.empty(), + ) as _i12.Stream<_i8.NetworkManagerActiveConnection>); @override - _i5.Stream> get propertiesChanged => (super.noSuchMethod( + _i12.Stream> get propertiesChanged => (super.noSuchMethod( Invocation.getter(#propertiesChanged), - returnValue: _i5.Stream>.empty(), - ) as _i5.Stream>); + returnValue: _i12.Stream>.empty(), + ) as _i12.Stream>); @override - List<_i4.NetworkManagerDevice> get devices => (super.noSuchMethod( + List<_i8.NetworkManagerDevice> get devices => (super.noSuchMethod( Invocation.getter(#devices), - returnValue: <_i4.NetworkManagerDevice>[], - ) as List<_i4.NetworkManagerDevice>); + returnValue: <_i8.NetworkManagerDevice>[], + ) as List<_i8.NetworkManagerDevice>); @override - List<_i4.NetworkManagerDevice> get allDevices => (super.noSuchMethod( + List<_i8.NetworkManagerDevice> get allDevices => (super.noSuchMethod( Invocation.getter(#allDevices), - returnValue: <_i4.NetworkManagerDevice>[], - ) as List<_i4.NetworkManagerDevice>); + returnValue: <_i8.NetworkManagerDevice>[], + ) as List<_i8.NetworkManagerDevice>); @override bool get networkingEnabled => (super.noSuchMethod( @@ -699,26 +1153,26 @@ class MockNetworkService extends _i1.Mock implements _i3.NetworkService { ) as bool); @override - List<_i4.NetworkManagerActiveConnection> get activeConnections => + List<_i8.NetworkManagerActiveConnection> get activeConnections => (super.noSuchMethod( Invocation.getter(#activeConnections), - returnValue: <_i4.NetworkManagerActiveConnection>[], - ) as List<_i4.NetworkManagerActiveConnection>); + returnValue: <_i8.NetworkManagerActiveConnection>[], + ) as List<_i8.NetworkManagerActiveConnection>); @override String get primaryConnectionType => (super.noSuchMethod( Invocation.getter(#primaryConnectionType), - returnValue: _i9.dummyValue( + returnValue: _i18.dummyValue( this, Invocation.getter(#primaryConnectionType), ), ) as String); @override - _i4.NetworkManagerMetered get metered => (super.noSuchMethod( + _i8.NetworkManagerMetered get metered => (super.noSuchMethod( Invocation.getter(#metered), - returnValue: _i4.NetworkManagerMetered.unknown, - ) as _i4.NetworkManagerMetered); + returnValue: _i8.NetworkManagerMetered.unknown, + ) as _i8.NetworkManagerMetered); @override bool get startup => (super.noSuchMethod( @@ -729,17 +1183,17 @@ class MockNetworkService extends _i1.Mock implements _i3.NetworkService { @override String get version => (super.noSuchMethod( Invocation.getter(#version), - returnValue: _i9.dummyValue( + returnValue: _i18.dummyValue( this, Invocation.getter(#version), ), ) as String); @override - _i4.NetworkManagerConnectivityState get connectivity => (super.noSuchMethod( + _i8.NetworkManagerConnectivityState get connectivity => (super.noSuchMethod( Invocation.getter(#connectivity), - returnValue: _i4.NetworkManagerConnectivityState.unknown, - ) as _i4.NetworkManagerConnectivityState); + returnValue: _i8.NetworkManagerConnectivityState.unknown, + ) as _i8.NetworkManagerConnectivityState); @override bool get connectivityCheckAvailable => (super.noSuchMethod( @@ -756,38 +1210,38 @@ class MockNetworkService extends _i1.Mock implements _i3.NetworkService { @override String get connectivityCheckUri => (super.noSuchMethod( Invocation.getter(#connectivityCheckUri), - returnValue: _i9.dummyValue( + returnValue: _i18.dummyValue( this, Invocation.getter(#connectivityCheckUri), ), ) as String); @override - _i4.NetworkManagerState get state => (super.noSuchMethod( + _i8.NetworkManagerState get state => (super.noSuchMethod( Invocation.getter(#state), - returnValue: _i4.NetworkManagerState.unknown, - ) as _i4.NetworkManagerState); + returnValue: _i8.NetworkManagerState.unknown, + ) as _i8.NetworkManagerState); @override - _i4.NetworkManagerSettings get settings => (super.noSuchMethod( + _i8.NetworkManagerSettings get settings => (super.noSuchMethod( Invocation.getter(#settings), - returnValue: _FakeNetworkManagerSettings_4( + returnValue: _FakeNetworkManagerSettings_9( this, Invocation.getter(#settings), ), - ) as _i4.NetworkManagerSettings); + ) as _i8.NetworkManagerSettings); @override - _i4.NetworkManagerDnsManager get dnsManager => (super.noSuchMethod( + _i8.NetworkManagerDnsManager get dnsManager => (super.noSuchMethod( Invocation.getter(#dnsManager), - returnValue: _FakeNetworkManagerDnsManager_5( + returnValue: _FakeNetworkManagerDnsManager_10( this, Invocation.getter(#dnsManager), ), - ) as _i4.NetworkManagerDnsManager); + ) as _i8.NetworkManagerDnsManager); @override - Map> getWifiSettings( + Map> getWifiSettings( {required String? ssid}) => (super.noSuchMethod( Invocation.method( @@ -795,65 +1249,65 @@ class MockNetworkService extends _i1.Mock implements _i3.NetworkService { [], {#ssid: ssid}, ), - returnValue: >{}, - ) as Map>); + returnValue: >{}, + ) as Map>); @override - _i5.Future markConfigured() => (super.noSuchMethod( + _i12.Future markConfigured() => (super.noSuchMethod( Invocation.method( #markConfigured, [], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future connect() => (super.noSuchMethod( + _i12.Future connect() => (super.noSuchMethod( Invocation.method( #connect, [], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future setWirelessEnabled(bool? value) => (super.noSuchMethod( + _i12.Future setWirelessEnabled(bool? value) => (super.noSuchMethod( Invocation.method( #setWirelessEnabled, [value], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future setWwanEnabled(bool? value) => (super.noSuchMethod( + _i12.Future setWwanEnabled(bool? value) => (super.noSuchMethod( Invocation.method( #setWwanEnabled, [value], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future setConnectivityCheckEnabled(bool? value) => + _i12.Future setConnectivityCheckEnabled(bool? value) => (super.noSuchMethod( Invocation.method( #setConnectivityCheckEnabled, [value], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future<_i4.NetworkManagerActiveConnection> addAndActivateConnection({ - Map>? connection = const {}, - required _i4.NetworkManagerDevice? device, - _i4.NetworkManagerAccessPoint? accessPoint, + _i12.Future<_i8.NetworkManagerActiveConnection> addAndActivateConnection({ + Map>? connection = const {}, + required _i8.NetworkManagerDevice? device, + _i8.NetworkManagerAccessPoint? accessPoint, }) => (super.noSuchMethod( Invocation.method( @@ -865,8 +1319,8 @@ class MockNetworkService extends _i1.Mock implements _i3.NetworkService { #accessPoint: accessPoint, }, ), - returnValue: _i5.Future<_i4.NetworkManagerActiveConnection>.value( - _FakeNetworkManagerActiveConnection_6( + returnValue: _i12.Future<_i8.NetworkManagerActiveConnection>.value( + _FakeNetworkManagerActiveConnection_11( this, Invocation.method( #addAndActivateConnection, @@ -878,13 +1332,13 @@ class MockNetworkService extends _i1.Mock implements _i3.NetworkService { }, ), )), - ) as _i5.Future<_i4.NetworkManagerActiveConnection>); + ) as _i12.Future<_i8.NetworkManagerActiveConnection>); @override - _i5.Future<_i4.NetworkManagerActiveConnection> activateConnection({ - required _i4.NetworkManagerDevice? device, - _i4.NetworkManagerSettingsConnection? connection, - _i4.NetworkManagerAccessPoint? accessPoint, + _i12.Future<_i8.NetworkManagerActiveConnection> activateConnection({ + required _i8.NetworkManagerDevice? device, + _i8.NetworkManagerSettingsConnection? connection, + _i8.NetworkManagerAccessPoint? accessPoint, }) => (super.noSuchMethod( Invocation.method( @@ -896,8 +1350,8 @@ class MockNetworkService extends _i1.Mock implements _i3.NetworkService { #accessPoint: accessPoint, }, ), - returnValue: _i5.Future<_i4.NetworkManagerActiveConnection>.value( - _FakeNetworkManagerActiveConnection_6( + returnValue: _i12.Future<_i8.NetworkManagerActiveConnection>.value( + _FakeNetworkManagerActiveConnection_11( this, Invocation.method( #activateConnection, @@ -909,29 +1363,29 @@ class MockNetworkService extends _i1.Mock implements _i3.NetworkService { }, ), )), - ) as _i5.Future<_i4.NetworkManagerActiveConnection>); + ) as _i12.Future<_i8.NetworkManagerActiveConnection>); @override - _i5.Future deactivateConnection( - _i4.NetworkManagerActiveConnection? connection) => + _i12.Future deactivateConnection( + _i8.NetworkManagerActiveConnection? connection) => (super.noSuchMethod( Invocation.method( #deactivateConnection, [connection], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future close() => (super.noSuchMethod( + _i12.Future close() => (super.noSuchMethod( Invocation.method( #close, [], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); } /// A class which mocks [PageConfigService]. @@ -955,14 +1409,14 @@ class MockPageConfigService extends _i1.Mock implements _i3.PageConfigService { ) as Set); @override - _i5.Future load() => (super.noSuchMethod( + _i12.Future load() => (super.noSuchMethod( Invocation.method( #load, [], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); } /// A class which mocks [PowerService]. @@ -976,7 +1430,7 @@ class MockPowerService extends _i1.Mock implements _i3.PowerService { @override _i3.UPowerKbdBacklight get kbdBacklight => (super.noSuchMethod( Invocation.getter(#kbdBacklight), - returnValue: _FakeUPowerKbdBacklight_7( + returnValue: _FakeUPowerKbdBacklight_12( this, Invocation.getter(#kbdBacklight), ), @@ -994,7 +1448,7 @@ class MockPowerService extends _i1.Mock implements _i3.PowerService { @override String get daemonVersion => (super.noSuchMethod( Invocation.getter(#daemonVersion), - returnValue: _i9.dummyValue( + returnValue: _i18.dummyValue( this, Invocation.getter(#daemonVersion), ), @@ -1027,64 +1481,64 @@ class MockPowerService extends _i1.Mock implements _i3.PowerService { @override _i3.UPowerDevice get displayDevice => (super.noSuchMethod( Invocation.getter(#displayDevice), - returnValue: _FakeUPowerDevice_8( + returnValue: _FakeUPowerDevice_13( this, Invocation.getter(#displayDevice), ), ) as _i3.UPowerDevice); @override - _i5.Stream<_i3.UPowerDevice> get deviceAdded => (super.noSuchMethod( + _i12.Stream<_i3.UPowerDevice> get deviceAdded => (super.noSuchMethod( Invocation.getter(#deviceAdded), - returnValue: _i5.Stream<_i3.UPowerDevice>.empty(), - ) as _i5.Stream<_i3.UPowerDevice>); + returnValue: _i12.Stream<_i3.UPowerDevice>.empty(), + ) as _i12.Stream<_i3.UPowerDevice>); @override - _i5.Stream<_i3.UPowerDevice> get deviceRemoved => (super.noSuchMethod( + _i12.Stream<_i3.UPowerDevice> get deviceRemoved => (super.noSuchMethod( Invocation.getter(#deviceRemoved), - returnValue: _i5.Stream<_i3.UPowerDevice>.empty(), - ) as _i5.Stream<_i3.UPowerDevice>); + returnValue: _i12.Stream<_i3.UPowerDevice>.empty(), + ) as _i12.Stream<_i3.UPowerDevice>); @override - _i5.Stream> get propertiesChanged => (super.noSuchMethod( + _i12.Stream> get propertiesChanged => (super.noSuchMethod( Invocation.getter(#propertiesChanged), - returnValue: _i5.Stream>.empty(), - ) as _i5.Stream>); + returnValue: _i12.Stream>.empty(), + ) as _i12.Stream>); @override - _i5.Future connect() => (super.noSuchMethod( + _i12.Future connect() => (super.noSuchMethod( Invocation.method( #connect, [], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future getCriticalAction() => (super.noSuchMethod( + _i12.Future getCriticalAction() => (super.noSuchMethod( Invocation.method( #getCriticalAction, [], ), - returnValue: _i5.Future.value(_i9.dummyValue( + returnValue: _i12.Future.value(_i18.dummyValue( this, Invocation.method( #getCriticalAction, [], ), )), - ) as _i5.Future); + ) as _i12.Future); @override - _i5.Future close() => (super.noSuchMethod( + _i12.Future close() => (super.noSuchMethod( Invocation.method( #close, [], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); } /// A class which mocks [ProductService]. @@ -1101,7 +1555,7 @@ class MockProductService extends _i1.Mock implements _i3.ProductService { #getProductInfo, [], ), - returnValue: _FakeProductInfo_9( + returnValue: _FakeProductInfo_14( this, Invocation.method( #getProductInfo, @@ -1116,7 +1570,7 @@ class MockProductService extends _i1.Mock implements _i3.ProductService { #getReleaseNotesURL, [languageCode], ), - returnValue: _i9.dummyValue( + returnValue: _i18.dummyValue( this, Invocation.method( #getReleaseNotesURL, @@ -1135,26 +1589,26 @@ class MockSessionService extends _i1.Mock implements _i3.SessionService { } @override - _i5.Future reboot({bool? immediate = false}) => (super.noSuchMethod( + _i12.Future reboot({bool? immediate = false}) => (super.noSuchMethod( Invocation.method( #reboot, [], {#immediate: immediate}, ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future shutdown({bool? immediate = false}) => (super.noSuchMethod( + _i12.Future shutdown({bool? immediate = false}) => (super.noSuchMethod( Invocation.method( #shutdown, [], {#immediate: immediate}, ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); } /// A class which mocks [SoundService]. @@ -1166,14 +1620,14 @@ class MockSoundService extends _i1.Mock implements _i3.SoundService { } @override - _i5.Future play(String? id) => (super.noSuchMethod( + _i12.Future play(String? id) => (super.noSuchMethod( Invocation.method( #play, [id], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); } /// A class which mocks [TelemetryService]. @@ -1187,35 +1641,35 @@ class MockTelemetryService extends _i1.Mock implements _i3.TelemetryService { @override String get path => (super.noSuchMethod( Invocation.getter(#path), - returnValue: _i9.dummyValue( + returnValue: _i18.dummyValue( this, Invocation.getter(#path), ), ) as String); @override - _i5.Future init([Map? metrics = const {}]) => + _i12.Future init([Map? metrics = const {}]) => (super.noSuchMethod( Invocation.method( #init, [metrics], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future addStage(String? name) => (super.noSuchMethod( + _i12.Future addStage(String? name) => (super.noSuchMethod( Invocation.method( #addStage, [name], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future addMetric( + _i12.Future addMetric( String? key, dynamic value, ) => @@ -1227,20 +1681,20 @@ class MockTelemetryService extends _i1.Mock implements _i3.TelemetryService { value, ], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future addMetrics(Map? entries) => + _i12.Future addMetrics(Map? entries) => (super.noSuchMethod( Invocation.method( #addMetrics, [entries], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); } /// A class which mocks [ThemeService]. @@ -1252,53 +1706,53 @@ class MockThemeService extends _i1.Mock implements _i3.ThemeService { } @override - _i5.Future<_i11.Brightness> getBrightness() => (super.noSuchMethod( + _i12.Future<_i17.Brightness> getBrightness() => (super.noSuchMethod( Invocation.method( #getBrightness, [], ), - returnValue: _i5.Future<_i11.Brightness>.value(_i11.Brightness.dark), - ) as _i5.Future<_i11.Brightness>); + returnValue: _i12.Future<_i17.Brightness>.value(_i17.Brightness.dark), + ) as _i12.Future<_i17.Brightness>); @override - _i5.Future setBrightness(_i11.Brightness? brightness) => + _i12.Future setBrightness(_i17.Brightness? brightness) => (super.noSuchMethod( Invocation.method( #setBrightness, [brightness], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future getAccent() => (super.noSuchMethod( + _i12.Future getAccent() => (super.noSuchMethod( Invocation.method( #getAccent, [], ), - returnValue: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future setAccent(String? accent) => (super.noSuchMethod( + _i12.Future setAccent(String? accent) => (super.noSuchMethod( Invocation.method( #setAccent, [accent], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); @override - _i5.Future close() => (super.noSuchMethod( + _i12.Future close() => (super.noSuchMethod( Invocation.method( #close, [], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); } /// A class which mocks [TimezoneService]. @@ -1310,29 +1764,29 @@ class MockTimezoneService extends _i1.Mock implements _i3.TimezoneService { } @override - _i5.Future getTimezone() => (super.noSuchMethod( + _i12.Future getTimezone() => (super.noSuchMethod( Invocation.method( #getTimezone, [], ), - returnValue: _i5.Future.value(_i9.dummyValue( + returnValue: _i12.Future.value(_i18.dummyValue( this, Invocation.method( #getTimezone, [], ), )), - ) as _i5.Future); + ) as _i12.Future); @override - _i5.Future setTimezone(String? timezone) => (super.noSuchMethod( + _i12.Future setTimezone(String? timezone) => (super.noSuchMethod( Invocation.method( #setTimezone, [timezone], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); } /// A class which mocks [UdevDeviceInfo]. @@ -1346,7 +1800,7 @@ class MockUdevDeviceInfo extends _i1.Mock implements _i3.UdevDeviceInfo { @override String get fullName => (super.noSuchMethod( Invocation.getter(#fullName), - returnValue: _i9.dummyValue( + returnValue: _i18.dummyValue( this, Invocation.getter(#fullName), ), @@ -1367,7 +1821,7 @@ class MockUdevService extends _i1.Mock implements _i3.UdevService { #bySysname, [sysname], ), - returnValue: _FakeUdevDeviceInfo_10( + returnValue: _FakeUdevDeviceInfo_15( this, Invocation.method( #bySysname, @@ -1382,7 +1836,7 @@ class MockUdevService extends _i1.Mock implements _i3.UdevService { #bySyspath, [syspath], ), - returnValue: _FakeUdevDeviceInfo_10( + returnValue: _FakeUdevDeviceInfo_15( this, Invocation.method( #bySyspath, @@ -1395,26 +1849,1441 @@ class MockUdevService extends _i1.Mock implements _i3.UdevService { /// A class which mocks [UrlLauncher]. /// /// See the documentation for Mockito's code generation for more information. -class MockUrlLauncher extends _i1.Mock implements _i12.UrlLauncher { +class MockUrlLauncher extends _i1.Mock implements _i20.UrlLauncher { MockUrlLauncher() { _i1.throwOnMissingStub(this); } @override - _i5.Future canLaunchUrl(String? url) => (super.noSuchMethod( + _i12.Future canLaunchUrl(String? url) => (super.noSuchMethod( Invocation.method( #canLaunchUrl, [url], ), - returnValue: _i5.Future.value(false), - ) as _i5.Future); + returnValue: _i12.Future.value(false), + ) as _i12.Future); @override - _i5.Future launchUrl(String? url) => (super.noSuchMethod( + _i12.Future launchUrl(String? url) => (super.noSuchMethod( Invocation.method( #launchUrl, [url], ), - returnValue: _i5.Future.value(false), - ) as _i5.Future); + returnValue: _i12.Future.value(false), + ) as _i12.Future); +} + +/// A class which mocks [BuildContext]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockBuildContext extends _i1.Mock implements _i5.BuildContext { + @override + _i5.Widget get widget => (super.noSuchMethod( + Invocation.getter(#widget), + returnValue: _FakeWidget_16( + this, + Invocation.getter(#widget), + ), + returnValueForMissingStub: _FakeWidget_16( + this, + Invocation.getter(#widget), + ), + ) as _i5.Widget); + + @override + bool get mounted => (super.noSuchMethod( + Invocation.getter(#mounted), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + bool get debugDoingBuild => (super.noSuchMethod( + Invocation.getter(#debugDoingBuild), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i5.InheritedWidget dependOnInheritedElement( + _i5.InheritedElement? ancestor, { + Object? aspect, + }) => + (super.noSuchMethod( + Invocation.method( + #dependOnInheritedElement, + [ancestor], + {#aspect: aspect}, + ), + returnValue: _FakeInheritedWidget_17( + this, + Invocation.method( + #dependOnInheritedElement, + [ancestor], + {#aspect: aspect}, + ), + ), + returnValueForMissingStub: _FakeInheritedWidget_17( + this, + Invocation.method( + #dependOnInheritedElement, + [ancestor], + {#aspect: aspect}, + ), + ), + ) as _i5.InheritedWidget); + + @override + void visitAncestorElements(_i5.ConditionalElementVisitor? visitor) => + super.noSuchMethod( + Invocation.method( + #visitAncestorElements, + [visitor], + ), + returnValueForMissingStub: null, + ); + + @override + void visitChildElements(_i5.ElementVisitor? visitor) => super.noSuchMethod( + Invocation.method( + #visitChildElements, + [visitor], + ), + returnValueForMissingStub: null, + ); + + @override + void dispatchNotification(_i21.Notification? notification) => + super.noSuchMethod( + Invocation.method( + #dispatchNotification, + [notification], + ), + returnValueForMissingStub: null, + ); + + @override + _i7.DiagnosticsNode describeElement( + String? name, { + _i7.DiagnosticsTreeStyle? style = _i7.DiagnosticsTreeStyle.errorProperty, + }) => + (super.noSuchMethod( + Invocation.method( + #describeElement, + [name], + {#style: style}, + ), + returnValue: _FakeDiagnosticsNode_6( + this, + Invocation.method( + #describeElement, + [name], + {#style: style}, + ), + ), + returnValueForMissingStub: _FakeDiagnosticsNode_6( + this, + Invocation.method( + #describeElement, + [name], + {#style: style}, + ), + ), + ) as _i7.DiagnosticsNode); + + @override + _i7.DiagnosticsNode describeWidget( + String? name, { + _i7.DiagnosticsTreeStyle? style = _i7.DiagnosticsTreeStyle.errorProperty, + }) => + (super.noSuchMethod( + Invocation.method( + #describeWidget, + [name], + {#style: style}, + ), + returnValue: _FakeDiagnosticsNode_6( + this, + Invocation.method( + #describeWidget, + [name], + {#style: style}, + ), + ), + returnValueForMissingStub: _FakeDiagnosticsNode_6( + this, + Invocation.method( + #describeWidget, + [name], + {#style: style}, + ), + ), + ) as _i7.DiagnosticsNode); + + @override + List<_i7.DiagnosticsNode> describeMissingAncestor( + {required Type? expectedAncestorType}) => + (super.noSuchMethod( + Invocation.method( + #describeMissingAncestor, + [], + {#expectedAncestorType: expectedAncestorType}, + ), + returnValue: <_i7.DiagnosticsNode>[], + returnValueForMissingStub: <_i7.DiagnosticsNode>[], + ) as List<_i7.DiagnosticsNode>); + + @override + _i7.DiagnosticsNode describeOwnershipChain(String? name) => + (super.noSuchMethod( + Invocation.method( + #describeOwnershipChain, + [name], + ), + returnValue: _FakeDiagnosticsNode_6( + this, + Invocation.method( + #describeOwnershipChain, + [name], + ), + ), + returnValueForMissingStub: _FakeDiagnosticsNode_6( + this, + Invocation.method( + #describeOwnershipChain, + [name], + ), + ), + ) as _i7.DiagnosticsNode); +} + +/// A class which mocks [File]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockFile extends _i1.Mock implements _i9.File { + @override + _i9.File get absolute => (super.noSuchMethod( + Invocation.getter(#absolute), + returnValue: _FakeFile_18( + this, + Invocation.getter(#absolute), + ), + returnValueForMissingStub: _FakeFile_18( + this, + Invocation.getter(#absolute), + ), + ) as _i9.File); + + @override + String get path => (super.noSuchMethod( + Invocation.getter(#path), + returnValue: _i18.dummyValue( + this, + Invocation.getter(#path), + ), + returnValueForMissingStub: _i18.dummyValue( + this, + Invocation.getter(#path), + ), + ) as String); + + @override + Uri get uri => (super.noSuchMethod( + Invocation.getter(#uri), + returnValue: _FakeUri_19( + this, + Invocation.getter(#uri), + ), + returnValueForMissingStub: _FakeUri_19( + this, + Invocation.getter(#uri), + ), + ) as Uri); + + @override + bool get isAbsolute => (super.noSuchMethod( + Invocation.getter(#isAbsolute), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i9.Directory get parent => (super.noSuchMethod( + Invocation.getter(#parent), + returnValue: _FakeDirectory_20( + this, + Invocation.getter(#parent), + ), + returnValueForMissingStub: _FakeDirectory_20( + this, + Invocation.getter(#parent), + ), + ) as _i9.Directory); + + @override + _i12.Future<_i9.File> create({ + bool? recursive = false, + bool? exclusive = false, + }) => + (super.noSuchMethod( + Invocation.method( + #create, + [], + { + #recursive: recursive, + #exclusive: exclusive, + }, + ), + returnValue: _i12.Future<_i9.File>.value(_FakeFile_18( + this, + Invocation.method( + #create, + [], + { + #recursive: recursive, + #exclusive: exclusive, + }, + ), + )), + returnValueForMissingStub: _i12.Future<_i9.File>.value(_FakeFile_18( + this, + Invocation.method( + #create, + [], + { + #recursive: recursive, + #exclusive: exclusive, + }, + ), + )), + ) as _i12.Future<_i9.File>); + + @override + void createSync({ + bool? recursive = false, + bool? exclusive = false, + }) => + super.noSuchMethod( + Invocation.method( + #createSync, + [], + { + #recursive: recursive, + #exclusive: exclusive, + }, + ), + returnValueForMissingStub: null, + ); + + @override + _i12.Future<_i9.File> rename(String? newPath) => (super.noSuchMethod( + Invocation.method( + #rename, + [newPath], + ), + returnValue: _i12.Future<_i9.File>.value(_FakeFile_18( + this, + Invocation.method( + #rename, + [newPath], + ), + )), + returnValueForMissingStub: _i12.Future<_i9.File>.value(_FakeFile_18( + this, + Invocation.method( + #rename, + [newPath], + ), + )), + ) as _i12.Future<_i9.File>); + + @override + _i9.File renameSync(String? newPath) => (super.noSuchMethod( + Invocation.method( + #renameSync, + [newPath], + ), + returnValue: _FakeFile_18( + this, + Invocation.method( + #renameSync, + [newPath], + ), + ), + returnValueForMissingStub: _FakeFile_18( + this, + Invocation.method( + #renameSync, + [newPath], + ), + ), + ) as _i9.File); + + @override + _i12.Future<_i9.File> copy(String? newPath) => (super.noSuchMethod( + Invocation.method( + #copy, + [newPath], + ), + returnValue: _i12.Future<_i9.File>.value(_FakeFile_18( + this, + Invocation.method( + #copy, + [newPath], + ), + )), + returnValueForMissingStub: _i12.Future<_i9.File>.value(_FakeFile_18( + this, + Invocation.method( + #copy, + [newPath], + ), + )), + ) as _i12.Future<_i9.File>); + + @override + _i9.File copySync(String? newPath) => (super.noSuchMethod( + Invocation.method( + #copySync, + [newPath], + ), + returnValue: _FakeFile_18( + this, + Invocation.method( + #copySync, + [newPath], + ), + ), + returnValueForMissingStub: _FakeFile_18( + this, + Invocation.method( + #copySync, + [newPath], + ), + ), + ) as _i9.File); + + @override + _i12.Future length() => (super.noSuchMethod( + Invocation.method( + #length, + [], + ), + returnValue: _i12.Future.value(0), + returnValueForMissingStub: _i12.Future.value(0), + ) as _i12.Future); + + @override + int lengthSync() => (super.noSuchMethod( + Invocation.method( + #lengthSync, + [], + ), + returnValue: 0, + returnValueForMissingStub: 0, + ) as int); + + @override + _i12.Future lastAccessed() => (super.noSuchMethod( + Invocation.method( + #lastAccessed, + [], + ), + returnValue: _i12.Future.value(_FakeDateTime_21( + this, + Invocation.method( + #lastAccessed, + [], + ), + )), + returnValueForMissingStub: _i12.Future.value(_FakeDateTime_21( + this, + Invocation.method( + #lastAccessed, + [], + ), + )), + ) as _i12.Future); + + @override + DateTime lastAccessedSync() => (super.noSuchMethod( + Invocation.method( + #lastAccessedSync, + [], + ), + returnValue: _FakeDateTime_21( + this, + Invocation.method( + #lastAccessedSync, + [], + ), + ), + returnValueForMissingStub: _FakeDateTime_21( + this, + Invocation.method( + #lastAccessedSync, + [], + ), + ), + ) as DateTime); + + @override + _i12.Future setLastAccessed(DateTime? time) => (super.noSuchMethod( + Invocation.method( + #setLastAccessed, + [time], + ), + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); + + @override + void setLastAccessedSync(DateTime? time) => super.noSuchMethod( + Invocation.method( + #setLastAccessedSync, + [time], + ), + returnValueForMissingStub: null, + ); + + @override + _i12.Future lastModified() => (super.noSuchMethod( + Invocation.method( + #lastModified, + [], + ), + returnValue: _i12.Future.value(_FakeDateTime_21( + this, + Invocation.method( + #lastModified, + [], + ), + )), + returnValueForMissingStub: _i12.Future.value(_FakeDateTime_21( + this, + Invocation.method( + #lastModified, + [], + ), + )), + ) as _i12.Future); + + @override + DateTime lastModifiedSync() => (super.noSuchMethod( + Invocation.method( + #lastModifiedSync, + [], + ), + returnValue: _FakeDateTime_21( + this, + Invocation.method( + #lastModifiedSync, + [], + ), + ), + returnValueForMissingStub: _FakeDateTime_21( + this, + Invocation.method( + #lastModifiedSync, + [], + ), + ), + ) as DateTime); + + @override + _i12.Future setLastModified(DateTime? time) => (super.noSuchMethod( + Invocation.method( + #setLastModified, + [time], + ), + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); + + @override + void setLastModifiedSync(DateTime? time) => super.noSuchMethod( + Invocation.method( + #setLastModifiedSync, + [time], + ), + returnValueForMissingStub: null, + ); + + @override + _i12.Future<_i9.RandomAccessFile> open( + {_i9.FileMode? mode = _i9.FileMode.read}) => + (super.noSuchMethod( + Invocation.method( + #open, + [], + {#mode: mode}, + ), + returnValue: + _i12.Future<_i9.RandomAccessFile>.value(_FakeRandomAccessFile_22( + this, + Invocation.method( + #open, + [], + {#mode: mode}, + ), + )), + returnValueForMissingStub: + _i12.Future<_i9.RandomAccessFile>.value(_FakeRandomAccessFile_22( + this, + Invocation.method( + #open, + [], + {#mode: mode}, + ), + )), + ) as _i12.Future<_i9.RandomAccessFile>); + + @override + _i9.RandomAccessFile openSync({_i9.FileMode? mode = _i9.FileMode.read}) => + (super.noSuchMethod( + Invocation.method( + #openSync, + [], + {#mode: mode}, + ), + returnValue: _FakeRandomAccessFile_22( + this, + Invocation.method( + #openSync, + [], + {#mode: mode}, + ), + ), + returnValueForMissingStub: _FakeRandomAccessFile_22( + this, + Invocation.method( + #openSync, + [], + {#mode: mode}, + ), + ), + ) as _i9.RandomAccessFile); + + @override + _i12.Stream> openRead([ + int? start, + int? end, + ]) => + (super.noSuchMethod( + Invocation.method( + #openRead, + [ + start, + end, + ], + ), + returnValue: _i12.Stream>.empty(), + returnValueForMissingStub: _i12.Stream>.empty(), + ) as _i12.Stream>); + + @override + _i9.IOSink openWrite({ + _i9.FileMode? mode = _i9.FileMode.write, + _i22.Encoding? encoding = const _i22.Utf8Codec(), + }) => + (super.noSuchMethod( + Invocation.method( + #openWrite, + [], + { + #mode: mode, + #encoding: encoding, + }, + ), + returnValue: _FakeIOSink_23( + this, + Invocation.method( + #openWrite, + [], + { + #mode: mode, + #encoding: encoding, + }, + ), + ), + returnValueForMissingStub: _FakeIOSink_23( + this, + Invocation.method( + #openWrite, + [], + { + #mode: mode, + #encoding: encoding, + }, + ), + ), + ) as _i9.IOSink); + + @override + _i12.Future<_i23.Uint8List> readAsBytes() => (super.noSuchMethod( + Invocation.method( + #readAsBytes, + [], + ), + returnValue: _i12.Future<_i23.Uint8List>.value(_i23.Uint8List(0)), + returnValueForMissingStub: + _i12.Future<_i23.Uint8List>.value(_i23.Uint8List(0)), + ) as _i12.Future<_i23.Uint8List>); + + @override + _i23.Uint8List readAsBytesSync() => (super.noSuchMethod( + Invocation.method( + #readAsBytesSync, + [], + ), + returnValue: _i23.Uint8List(0), + returnValueForMissingStub: _i23.Uint8List(0), + ) as _i23.Uint8List); + + @override + _i12.Future readAsString( + {_i22.Encoding? encoding = const _i22.Utf8Codec()}) => + (super.noSuchMethod( + Invocation.method( + #readAsString, + [], + {#encoding: encoding}, + ), + returnValue: _i12.Future.value(_i18.dummyValue( + this, + Invocation.method( + #readAsString, + [], + {#encoding: encoding}, + ), + )), + returnValueForMissingStub: + _i12.Future.value(_i18.dummyValue( + this, + Invocation.method( + #readAsString, + [], + {#encoding: encoding}, + ), + )), + ) as _i12.Future); + + @override + String readAsStringSync({_i22.Encoding? encoding = const _i22.Utf8Codec()}) => + (super.noSuchMethod( + Invocation.method( + #readAsStringSync, + [], + {#encoding: encoding}, + ), + returnValue: _i18.dummyValue( + this, + Invocation.method( + #readAsStringSync, + [], + {#encoding: encoding}, + ), + ), + returnValueForMissingStub: _i18.dummyValue( + this, + Invocation.method( + #readAsStringSync, + [], + {#encoding: encoding}, + ), + ), + ) as String); + + @override + _i12.Future> readAsLines( + {_i22.Encoding? encoding = const _i22.Utf8Codec()}) => + (super.noSuchMethod( + Invocation.method( + #readAsLines, + [], + {#encoding: encoding}, + ), + returnValue: _i12.Future>.value([]), + returnValueForMissingStub: _i12.Future>.value([]), + ) as _i12.Future>); + + @override + List readAsLinesSync( + {_i22.Encoding? encoding = const _i22.Utf8Codec()}) => + (super.noSuchMethod( + Invocation.method( + #readAsLinesSync, + [], + {#encoding: encoding}, + ), + returnValue: [], + returnValueForMissingStub: [], + ) as List); + + @override + _i12.Future<_i9.File> writeAsBytes( + List? bytes, { + _i9.FileMode? mode = _i9.FileMode.write, + bool? flush = false, + }) => + (super.noSuchMethod( + Invocation.method( + #writeAsBytes, + [bytes], + { + #mode: mode, + #flush: flush, + }, + ), + returnValue: _i12.Future<_i9.File>.value(_FakeFile_18( + this, + Invocation.method( + #writeAsBytes, + [bytes], + { + #mode: mode, + #flush: flush, + }, + ), + )), + returnValueForMissingStub: _i12.Future<_i9.File>.value(_FakeFile_18( + this, + Invocation.method( + #writeAsBytes, + [bytes], + { + #mode: mode, + #flush: flush, + }, + ), + )), + ) as _i12.Future<_i9.File>); + + @override + void writeAsBytesSync( + List? bytes, { + _i9.FileMode? mode = _i9.FileMode.write, + bool? flush = false, + }) => + super.noSuchMethod( + Invocation.method( + #writeAsBytesSync, + [bytes], + { + #mode: mode, + #flush: flush, + }, + ), + returnValueForMissingStub: null, + ); + + @override + _i12.Future<_i9.File> writeAsString( + String? contents, { + _i9.FileMode? mode = _i9.FileMode.write, + _i22.Encoding? encoding = const _i22.Utf8Codec(), + bool? flush = false, + }) => + (super.noSuchMethod( + Invocation.method( + #writeAsString, + [contents], + { + #mode: mode, + #encoding: encoding, + #flush: flush, + }, + ), + returnValue: _i12.Future<_i9.File>.value(_FakeFile_18( + this, + Invocation.method( + #writeAsString, + [contents], + { + #mode: mode, + #encoding: encoding, + #flush: flush, + }, + ), + )), + returnValueForMissingStub: _i12.Future<_i9.File>.value(_FakeFile_18( + this, + Invocation.method( + #writeAsString, + [contents], + { + #mode: mode, + #encoding: encoding, + #flush: flush, + }, + ), + )), + ) as _i12.Future<_i9.File>); + + @override + void writeAsStringSync( + String? contents, { + _i9.FileMode? mode = _i9.FileMode.write, + _i22.Encoding? encoding = const _i22.Utf8Codec(), + bool? flush = false, + }) => + super.noSuchMethod( + Invocation.method( + #writeAsStringSync, + [contents], + { + #mode: mode, + #encoding: encoding, + #flush: flush, + }, + ), + returnValueForMissingStub: null, + ); + + @override + _i12.Future exists() => (super.noSuchMethod( + Invocation.method( + #exists, + [], + ), + returnValue: _i12.Future.value(false), + returnValueForMissingStub: _i12.Future.value(false), + ) as _i12.Future); + + @override + bool existsSync() => (super.noSuchMethod( + Invocation.method( + #existsSync, + [], + ), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i12.Future resolveSymbolicLinks() => (super.noSuchMethod( + Invocation.method( + #resolveSymbolicLinks, + [], + ), + returnValue: _i12.Future.value(_i18.dummyValue( + this, + Invocation.method( + #resolveSymbolicLinks, + [], + ), + )), + returnValueForMissingStub: + _i12.Future.value(_i18.dummyValue( + this, + Invocation.method( + #resolveSymbolicLinks, + [], + ), + )), + ) as _i12.Future); + + @override + String resolveSymbolicLinksSync() => (super.noSuchMethod( + Invocation.method( + #resolveSymbolicLinksSync, + [], + ), + returnValue: _i18.dummyValue( + this, + Invocation.method( + #resolveSymbolicLinksSync, + [], + ), + ), + returnValueForMissingStub: _i18.dummyValue( + this, + Invocation.method( + #resolveSymbolicLinksSync, + [], + ), + ), + ) as String); + + @override + _i12.Future<_i9.FileStat> stat() => (super.noSuchMethod( + Invocation.method( + #stat, + [], + ), + returnValue: _i12.Future<_i9.FileStat>.value(_FakeFileStat_24( + this, + Invocation.method( + #stat, + [], + ), + )), + returnValueForMissingStub: + _i12.Future<_i9.FileStat>.value(_FakeFileStat_24( + this, + Invocation.method( + #stat, + [], + ), + )), + ) as _i12.Future<_i9.FileStat>); + + @override + _i9.FileStat statSync() => (super.noSuchMethod( + Invocation.method( + #statSync, + [], + ), + returnValue: _FakeFileStat_24( + this, + Invocation.method( + #statSync, + [], + ), + ), + returnValueForMissingStub: _FakeFileStat_24( + this, + Invocation.method( + #statSync, + [], + ), + ), + ) as _i9.FileStat); + + @override + _i12.Future<_i9.FileSystemEntity> delete({bool? recursive = false}) => + (super.noSuchMethod( + Invocation.method( + #delete, + [], + {#recursive: recursive}, + ), + returnValue: + _i12.Future<_i9.FileSystemEntity>.value(_FakeFileSystemEntity_25( + this, + Invocation.method( + #delete, + [], + {#recursive: recursive}, + ), + )), + returnValueForMissingStub: + _i12.Future<_i9.FileSystemEntity>.value(_FakeFileSystemEntity_25( + this, + Invocation.method( + #delete, + [], + {#recursive: recursive}, + ), + )), + ) as _i12.Future<_i9.FileSystemEntity>); + + @override + void deleteSync({bool? recursive = false}) => super.noSuchMethod( + Invocation.method( + #deleteSync, + [], + {#recursive: recursive}, + ), + returnValueForMissingStub: null, + ); + + @override + _i12.Stream<_i9.FileSystemEvent> watch({ + int? events = 15, + bool? recursive = false, + }) => + (super.noSuchMethod( + Invocation.method( + #watch, + [], + { + #events: events, + #recursive: recursive, + }, + ), + returnValue: _i12.Stream<_i9.FileSystemEvent>.empty(), + returnValueForMissingStub: _i12.Stream<_i9.FileSystemEvent>.empty(), + ) as _i12.Stream<_i9.FileSystemEvent>); +} + +/// A class which mocks [SvgFileLoader]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockSvgFileLoader extends _i1.Mock implements _i10.SvgFileLoader { + @override + _i9.File get file => (super.noSuchMethod( + Invocation.getter(#file), + returnValue: _FakeFile_18( + this, + Invocation.getter(#file), + ), + returnValueForMissingStub: _FakeFile_18( + this, + Invocation.getter(#file), + ), + ) as _i9.File); + + @override + String provideSvg(dynamic message) => (super.noSuchMethod( + Invocation.method( + #provideSvg, + [message], + ), + returnValue: _i18.dummyValue( + this, + Invocation.method( + #provideSvg, + [message], + ), + ), + returnValueForMissingStub: _i18.dummyValue( + this, + Invocation.method( + #provideSvg, + [message], + ), + ), + ) as String); + + @override + _i12.Future prepareMessage(_i5.BuildContext? context) => + (super.noSuchMethod( + Invocation.method( + #prepareMessage, + [context], + ), + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) as _i12.Future); + + @override + _i10.SvgTheme getTheme(_i5.BuildContext? context) => (super.noSuchMethod( + Invocation.method( + #getTheme, + [context], + ), + returnValue: _FakeSvgTheme_26( + this, + Invocation.method( + #getTheme, + [context], + ), + ), + returnValueForMissingStub: _FakeSvgTheme_26( + this, + Invocation.method( + #getTheme, + [context], + ), + ), + ) as _i10.SvgTheme); + + @override + _i12.Future<_i23.ByteData> loadBytes(_i5.BuildContext? context) => + (super.noSuchMethod( + Invocation.method( + #loadBytes, + [context], + ), + returnValue: _i12.Future<_i23.ByteData>.value(_i23.ByteData(0)), + returnValueForMissingStub: + _i12.Future<_i23.ByteData>.value(_i23.ByteData(0)), + ) as _i12.Future<_i23.ByteData>); + + @override + _i10.SvgCacheKey cacheKey(_i5.BuildContext? context) => (super.noSuchMethod( + Invocation.method( + #cacheKey, + [context], + ), + returnValue: _FakeSvgCacheKey_27( + this, + Invocation.method( + #cacheKey, + [context], + ), + ), + returnValueForMissingStub: _FakeSvgCacheKey_27( + this, + Invocation.method( + #cacheKey, + [context], + ), + ), + ) as _i10.SvgCacheKey); +} + +/// A class which mocks [SvgPicture]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockSvgPicture extends _i1.Mock implements _i10.SvgPicture { + @override + _i4.BoxFit get fit => (super.noSuchMethod( + Invocation.getter(#fit), + returnValue: _i4.BoxFit.fill, + returnValueForMissingStub: _i4.BoxFit.fill, + ) as _i4.BoxFit); + + @override + _i4.AlignmentGeometry get alignment => (super.noSuchMethod( + Invocation.getter(#alignment), + returnValue: _FakeAlignmentGeometry_3( + this, + Invocation.getter(#alignment), + ), + returnValueForMissingStub: _FakeAlignmentGeometry_3( + this, + Invocation.getter(#alignment), + ), + ) as _i4.AlignmentGeometry); + + @override + _i11.BytesLoader get bytesLoader => (super.noSuchMethod( + Invocation.getter(#bytesLoader), + returnValue: _FakeBytesLoader_28( + this, + Invocation.getter(#bytesLoader), + ), + returnValueForMissingStub: _FakeBytesLoader_28( + this, + Invocation.getter(#bytesLoader), + ), + ) as _i11.BytesLoader); + + @override + bool get matchTextDirection => (super.noSuchMethod( + Invocation.getter(#matchTextDirection), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + bool get allowDrawingOutsideViewBox => (super.noSuchMethod( + Invocation.getter(#allowDrawingOutsideViewBox), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + bool get excludeFromSemantics => (super.noSuchMethod( + Invocation.getter(#excludeFromSemantics), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i17.Clip get clipBehavior => (super.noSuchMethod( + Invocation.getter(#clipBehavior), + returnValue: _i17.Clip.none, + returnValueForMissingStub: _i17.Clip.none, + ) as _i17.Clip); + + @override + _i5.Widget build(_i5.BuildContext? context) => (super.noSuchMethod( + Invocation.method( + #build, + [context], + ), + returnValue: _FakeWidget_16( + this, + Invocation.method( + #build, + [context], + ), + ), + returnValueForMissingStub: _FakeWidget_16( + this, + Invocation.method( + #build, + [context], + ), + ), + ) as _i5.Widget); + + @override + void debugFillProperties(_i6.DiagnosticPropertiesBuilder? properties) => + super.noSuchMethod( + Invocation.method( + #debugFillProperties, + [properties], + ), + returnValueForMissingStub: null, + ); + + @override + _i5.StatelessElement createElement() => (super.noSuchMethod( + Invocation.method( + #createElement, + [], + ), + returnValue: _FakeStatelessElement_29( + this, + Invocation.method( + #createElement, + [], + ), + ), + returnValueForMissingStub: _FakeStatelessElement_29( + this, + Invocation.method( + #createElement, + [], + ), + ), + ) as _i5.StatelessElement); + + @override + String toStringShort() => (super.noSuchMethod( + Invocation.method( + #toStringShort, + [], + ), + returnValue: _i18.dummyValue( + this, + Invocation.method( + #toStringShort, + [], + ), + ), + returnValueForMissingStub: _i18.dummyValue( + this, + Invocation.method( + #toStringShort, + [], + ), + ), + ) as String); + + @override + String toStringShallow({ + String? joiner = r', ', + _i6.DiagnosticLevel? minLevel = _i6.DiagnosticLevel.debug, + }) => + (super.noSuchMethod( + Invocation.method( + #toStringShallow, + [], + { + #joiner: joiner, + #minLevel: minLevel, + }, + ), + returnValue: _i18.dummyValue( + this, + Invocation.method( + #toStringShallow, + [], + { + #joiner: joiner, + #minLevel: minLevel, + }, + ), + ), + returnValueForMissingStub: _i18.dummyValue( + this, + Invocation.method( + #toStringShallow, + [], + { + #joiner: joiner, + #minLevel: minLevel, + }, + ), + ), + ) as String); + + @override + String toStringDeep({ + String? prefixLineOne = r'', + String? prefixOtherLines, + _i6.DiagnosticLevel? minLevel = _i6.DiagnosticLevel.debug, + }) => + (super.noSuchMethod( + Invocation.method( + #toStringDeep, + [], + { + #prefixLineOne: prefixLineOne, + #prefixOtherLines: prefixOtherLines, + #minLevel: minLevel, + }, + ), + returnValue: _i18.dummyValue( + this, + Invocation.method( + #toStringDeep, + [], + { + #prefixLineOne: prefixLineOne, + #prefixOtherLines: prefixOtherLines, + #minLevel: minLevel, + }, + ), + ), + returnValueForMissingStub: _i18.dummyValue( + this, + Invocation.method( + #toStringDeep, + [], + { + #prefixLineOne: prefixLineOne, + #prefixOtherLines: prefixOtherLines, + #minLevel: minLevel, + }, + ), + ), + ) as String); + + @override + _i7.DiagnosticsNode toDiagnosticsNode({ + String? name, + _i7.DiagnosticsTreeStyle? style, + }) => + (super.noSuchMethod( + Invocation.method( + #toDiagnosticsNode, + [], + { + #name: name, + #style: style, + }, + ), + returnValue: _FakeDiagnosticsNode_6( + this, + Invocation.method( + #toDiagnosticsNode, + [], + { + #name: name, + #style: style, + }, + ), + ), + returnValueForMissingStub: _FakeDiagnosticsNode_6( + this, + Invocation.method( + #toDiagnosticsNode, + [], + { + #name: name, + #style: style, + }, + ), + ), + ) as _i7.DiagnosticsNode); + + @override + List<_i7.DiagnosticsNode> debugDescribeChildren() => (super.noSuchMethod( + Invocation.method( + #debugDescribeChildren, + [], + ), + returnValue: <_i7.DiagnosticsNode>[], + returnValueForMissingStub: <_i7.DiagnosticsNode>[], + ) as List<_i7.DiagnosticsNode>); + + @override + String toString({_i6.DiagnosticLevel? minLevel = _i6.DiagnosticLevel.info}) => + super.toString(); } From 10e797a77908c1e789f3c41e248ebba300c53301 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 22 Dec 2023 13:31:58 +0100 Subject: [PATCH 014/106] deps: Downgrade to Flutter 3.16.4 Signed-off-by: Lukas Klingsbo --- .tool-versions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.tool-versions b/.tool-versions index cec28fec1..cbe24c642 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -flutter 3.16.5-stable +flutter 3.16.4-stable From d3a7a81ea9e76c7e84ada7e4b5e2c1d839fc2989 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 2 Jan 2024 11:15:08 +0100 Subject: [PATCH 015/106] Import provision mocks to test_utils.dart Signed-off-by: Lukas Klingsbo --- packages/ubuntu_bootstrap/test/test_utils.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/ubuntu_bootstrap/test/test_utils.dart b/packages/ubuntu_bootstrap/test/test_utils.dart index 50cff3bfc..dc564ec67 100644 --- a/packages/ubuntu_bootstrap/test/test_utils.dart +++ b/packages/ubuntu_bootstrap/test/test_utils.dart @@ -7,8 +7,7 @@ import 'package:ubuntu_bootstrap/services.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru/yaru.dart'; - -import 'confirm/test_confirm.dart'; +import '../../ubuntu_provision/test/test_utils.mocks.dart'; export '../../ubuntu_provision/test/test_utils.mocks.dart'; export 'test_utils.mocks.dart'; From e231d706d274da22382b928ab10c593e4cb6dd2a Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 2 Jan 2024 11:15:31 +0100 Subject: [PATCH 016/106] Alphabetical order for service initializations Signed-off-by: Lukas Klingsbo --- packages/ubuntu_bootstrap/lib/installer.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ubuntu_bootstrap/lib/installer.dart b/packages/ubuntu_bootstrap/lib/installer.dart index e3df47f81..7d7067dda 100644 --- a/packages/ubuntu_bootstrap/lib/installer.dart +++ b/packages/ubuntu_bootstrap/lib/installer.dart @@ -78,8 +78,6 @@ Future runInstallerApp( ); tryRegisterService(GnomeService.new); tryRegisterServiceFactory(GSettings.new); - tryRegisterService( - () => PageConfigService(config: tryGetService())); tryRegisterService(() => InstallerService( getService(), pageConfig: tryGetService())); @@ -90,6 +88,8 @@ Future runInstallerApp( () => SubiquityLocaleService(getService())); tryRegisterService( () => SubiquityNetworkService(getService())); + tryRegisterService( + () => PageConfigService(config: tryGetService())); tryRegisterService(() => PostInstallService('/tmp/$baseName.conf')); tryRegisterService(PowerService.new); tryRegisterService(ProductService.new); From 8e28a7cd645aeaa8cc81a90cbeff46ae2c53efa6 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 2 Jan 2024 12:04:36 +0100 Subject: [PATCH 017/106] Stub excludedPages Signed-off-by: Lukas Klingsbo --- packages/ubuntu_bootstrap/test/test_utils.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ubuntu_bootstrap/test/test_utils.dart b/packages/ubuntu_bootstrap/test/test_utils.dart index dc564ec67..ba53fe188 100644 --- a/packages/ubuntu_bootstrap/test/test_utils.dart +++ b/packages/ubuntu_bootstrap/test/test_utils.dart @@ -61,6 +61,7 @@ void setupMockPageConfig({Map pages = const {}}) { final pageConfigService = MockPageConfigService(); registerMockService(pageConfigService); when(pageConfigService.pages).thenReturn(pages); + when(pageConfigService.excludedPages).thenReturn({}); } class MockBuildContext extends Mock implements BuildContext {} From 248b5a83608c9120ec7942bb70f2a91edd7a9a65 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 2 Jan 2024 12:26:41 +0100 Subject: [PATCH 018/106] deps: Pin ubuntu_widgets 0.3.0 Signed-off-by: Lukas Klingsbo --- melos.yaml | 2 +- packages/ubuntu_bootstrap/pubspec.yaml | 2 +- packages/ubuntu_provision/pubspec.yaml | 2 +- packages/ubuntu_wizard/pubspec.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/melos.yaml b/melos.yaml index c85340e22..646792946 100644 --- a/melos.yaml +++ b/melos.yaml @@ -45,7 +45,7 @@ command: ubuntu_logger: ^0.1.0 ubuntu_service: ^0.3.0 ubuntu_session: ^0.0.4 - ubuntu_widgets: ^0.3.0 + ubuntu_widgets: 0.3.0 udev: ^0.0.3 upower: ^0.7.0 url_launcher: ^6.2.1 diff --git a/packages/ubuntu_bootstrap/pubspec.yaml b/packages/ubuntu_bootstrap/pubspec.yaml index f77c21770..b264d230b 100644 --- a/packages/ubuntu_bootstrap/pubspec.yaml +++ b/packages/ubuntu_bootstrap/pubspec.yaml @@ -48,7 +48,7 @@ dependencies: ubuntu_provision: ^0.1.0 ubuntu_service: ^0.3.0 ubuntu_utils: ^0.1.0 - ubuntu_widgets: ^0.3.0 + ubuntu_widgets: 0.3.0 ubuntu_wizard: ^0.1.0 yaru: 1.2.0 yaru_icons: 2.2.2 diff --git a/packages/ubuntu_provision/pubspec.yaml b/packages/ubuntu_provision/pubspec.yaml index b78528383..b94a0c44c 100644 --- a/packages/ubuntu_provision/pubspec.yaml +++ b/packages/ubuntu_provision/pubspec.yaml @@ -41,7 +41,7 @@ dependencies: ubuntu_service: ^0.3.0 ubuntu_session: ^0.0.4 ubuntu_utils: ^0.1.0 - ubuntu_widgets: ^0.3.0 + ubuntu_widgets: 0.3.0 ubuntu_wizard: ^0.1.0 udev: ^0.0.3 upower: ^0.7.0 diff --git a/packages/ubuntu_wizard/pubspec.yaml b/packages/ubuntu_wizard/pubspec.yaml index 9adf24a9e..a1339198f 100644 --- a/packages/ubuntu_wizard/pubspec.yaml +++ b/packages/ubuntu_wizard/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: sdk: flutter ubuntu_flavor: ^0.2.1 ubuntu_localizations: ^0.3.4 - ubuntu_widgets: ^0.3.0 + ubuntu_widgets: 0.3.0 wizard_router: ^1.0.4 yaru: 1.2.0 yaru_widgets: 3.3.1 From 4779ee3c3b1cd767a734f976516dee363b91f6db Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 2 Jan 2024 13:46:23 +0100 Subject: [PATCH 019/106] Update packages/ubuntu_provision/test/providers/page_images_test.dart Co-authored-by: Dennis Loose --- packages/ubuntu_provision/test/providers/page_images_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ubuntu_provision/test/providers/page_images_test.dart b/packages/ubuntu_provision/test/providers/page_images_test.dart index e1642e94d..3ebf8b821 100644 --- a/packages/ubuntu_provision/test/providers/page_images_test.dart +++ b/packages/ubuntu_provision/test/providers/page_images_test.dart @@ -1,4 +1,3 @@ -// Write tests for page_images.dart import 'dart:io'; import 'dart:typed_data'; From dfaf3e0098c78f8db917dd4160252ff199c28eb5 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 22 Dec 2023 14:33:29 +0100 Subject: [PATCH 020/106] Loading image from cache --- .../assets/ubuntu-provision.yml | 29 ++++++++++ .../lib/src/locale/locale_page.dart | 7 ++- .../lib/src/providers/page_images.dart | 2 +- .../lib/src/services/config_service.dart | 58 ++++++++++++------- .../ubuntu_provision/test/test_utils.dart | 1 - 5 files changed, 72 insertions(+), 25 deletions(-) create mode 100644 packages/ubuntu_bootstrap/assets/ubuntu-provision.yml diff --git a/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml b/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml new file mode 100644 index 000000000..637825d08 --- /dev/null +++ b/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml @@ -0,0 +1,29 @@ +pages: + locale: + image: "packages/ubuntu_provision/assets/mascot.png" + title: "some/key" + visible: true + welcome: + visible: false + rst: + image: "assets/storage/rst.svg" + title: "My Page Title" + visible: true + keyboard: + title: "Keyboard layout" + visible: true + network: + title: "Connect to a network" + visible: true + refresh: + title: "Applications and updates" + visible: true + source: + title: "Type of installation" + visible: true + secureBoot: + title: "My Page Title" + visible: true + storage: + title: "Ready to install" + visible: true diff --git a/packages/ubuntu_provision/lib/src/locale/locale_page.dart b/packages/ubuntu_provision/lib/src/locale/locale_page.dart index 14959e087..e145538cb 100644 --- a/packages/ubuntu_provision/lib/src/locale/locale_page.dart +++ b/packages/ubuntu_provision/lib/src/locale/locale_page.dart @@ -4,7 +4,7 @@ import 'package:ubuntu_provision/services.dart'; import 'package:ubuntu_provision/src/locale/locale_l10n.dart'; import 'package:ubuntu_provision/src/locale/locale_model.dart'; import 'package:ubuntu_provision/src/providers/flavor.dart'; -import 'package:ubuntu_provision/widgets.dart'; +import 'package:ubuntu_provision/src/providers/page_images.dart'; import 'package:ubuntu_service/ubuntu_service.dart'; import 'package:ubuntu_widgets/ubuntu_widgets.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; @@ -23,6 +23,9 @@ class LocalePage extends ConsumerWidget { final flavor = ref.watch(flavorProvider); final model = ref.watch(localeModelProvider); final lang = LocaleLocalizations.of(context); + final pageImages = ref.watch(pageImagesProvider); + print(pageImages.images); + return WizardPage( title: YaruWindowTitleBar( title: Text(lang.localePageTitle(flavor.name)), @@ -32,7 +35,7 @@ class LocalePage extends ConsumerWidget { child: Column( children: [ const SizedBox(height: kWizardSpacing / 2), - const MascotAvatar(), + pageImages.get('locale'), const SizedBox(height: kWizardSpacing), Text(lang.localeHeader), const SizedBox(height: kWizardSpacing / 2), diff --git a/packages/ubuntu_provision/lib/src/providers/page_images.dart b/packages/ubuntu_provision/lib/src/providers/page_images.dart index 9bf8364cc..ee9e83e19 100644 --- a/packages/ubuntu_provision/lib/src/providers/page_images.dart +++ b/packages/ubuntu_provision/lib/src/providers/page_images.dart @@ -26,7 +26,7 @@ class PageImages { ColorMapper? colorMapper, }) svgFileLoader = SvgFileLoader.new; - @visibleForTesting + //@visibleForTesting final Map images = {}; Widget get(String pageName) => diff --git a/packages/ubuntu_provision/lib/src/services/config_service.dart b/packages/ubuntu_provision/lib/src/services/config_service.dart index 860ebf9ba..8a217fed1 100644 --- a/packages/ubuntu_provision/lib/src/services/config_service.dart +++ b/packages/ubuntu_provision/lib/src/services/config_service.dart @@ -38,30 +38,22 @@ class ConfigService { /// assets. If no config file is found, it will return an empty map. @visibleForTesting Future?> load() async { - final file = _fs.file(_path ?? ''); - String? assetData; - if (!file.existsSync()) { - for (final ext in _extensions) { - try { - assetData = await rootBundle.loadString('assets/$_filename.$ext'); - // Since there isn't any `exists` method for assets we'll just try to - // load the file and catch the exception if it doesn't exist and - // continue. If no file is found, then we'll return an empty map - // and log an error. - // ignore: avoid_catches_without_on_clauses - } catch (_) { - continue; - } - } - if (assetData == null) { - _log.error('No config file found on the filesystem or in assets.'); - return {}; - } + final file = _path != null ? _fs.file(_path ?? '') : null; + final path = _path; + if (path == null || file == null || !file.existsSync()) { + return _loadFromAssets(); + } else { + return _loadFromFilesystem(path, file); } + } + Future?> _loadFromFilesystem( + String path, + File file, + ) async { try { - final data = assetData ?? await file.readAsString(); - final config = switch (p.extension(_path!)) { + final data = await file.readAsString(); + final config = switch (p.extension(path)) { '.yml' || '.yaml' => loadYaml(data), _ => throw UnsupportedError( 'Only supports yaml/yml files, so $_path is not supported.'), @@ -74,6 +66,30 @@ class ConfigService { } } + Future?> _loadFromAssets() async { + String? data; + for (final ext in _extensions) { + try { + final assetsPath = 'assets/$_filename.$ext'; + data = await rootBundle.loadString(assetsPath); + break; + // Since there isn't any `exists` method for assets we'll just try to + // load the file and catch the exception if it doesn't exist and + // continue. If no file is found, then we'll return null and log an + // error. + // ignore: avoid_catches_without_on_clauses + } catch (_) { + continue; + } + } + if (data == null) { + // TODO(Lukas): Should we throw an exception here instead? + _log.error('No config file found on the filesystem or in assets.'); + return null; + } + return (loadYaml(data) as Map).cast(); + } + /// Looks up the config file path in the following order: /// /// - /etc/ubuntu-provision.{yaml,yml} (admin) diff --git a/packages/ubuntu_provision/test/test_utils.dart b/packages/ubuntu_provision/test/test_utils.dart index 16bf368c9..a36e53185 100644 --- a/packages/ubuntu_provision/test/test_utils.dart +++ b/packages/ubuntu_provision/test/test_utils.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; -import 'package:mockito/mockito.dart'; import 'package:timezone_map/timezone_map.dart'; import 'package:ubuntu_localizations/ubuntu_localizations.dart'; import 'package:ubuntu_provision/l10n.dart'; From e9feb976c6a300b121e2252b32e8260e8be7de2d Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Wed, 27 Dec 2023 15:07:44 +0100 Subject: [PATCH 021/106] refactor: Create ProvisionPage interface --- .../lib/installer/installation_step.dart | 42 +++++++++++ .../lib/installer/installer_wizard.dart | 72 ++++--------------- .../lib/pages/confirm/confirm_page.dart | 6 +- .../lib/pages/install/install_page.dart | 35 ++++----- .../lib/pages/refresh/refresh_page.dart | 6 +- .../lib/pages/rst/rst_page.dart | 5 +- .../pages/secure_boot/secure_boot_page.dart | 6 +- .../lib/pages/source/source_page.dart | 5 +- .../lib/pages/source/source_wizard.dart | 2 +- .../lib/pages/storage/storage_page.dart | 5 +- .../lib/pages/storage/storage_wizard.dart | 12 ++-- .../lib/pages/welcome/welcome_page.dart | 5 +- packages/ubuntu_bootstrap/lib/routes.dart | 57 ++++++++++----- .../test/source/source_wizard_test.dart | 2 +- packages/ubuntu_init/lib/src/init_wizard.dart | 7 +- packages/ubuntu_provision/lib/interfaces.dart | 3 + packages/ubuntu_provision/lib/providers.dart | 5 ++ .../lib/src/interfaces/provisioning_page.dart | 7 ++ .../lib/src/keyboard/keyboard_page.dart | 6 +- .../lib/src/locale/locale_page.dart | 6 +- .../lib/src/network/network_page.dart | 6 +- .../lib/src/services/page_config_service.dart | 3 + .../lib/ubuntu_provision.dart | 5 +- .../test/network/test_network.dart | 2 +- 24 files changed, 182 insertions(+), 128 deletions(-) create mode 100644 packages/ubuntu_bootstrap/lib/installer/installation_step.dart create mode 100644 packages/ubuntu_provision/lib/interfaces.dart create mode 100644 packages/ubuntu_provision/lib/providers.dart create mode 100644 packages/ubuntu_provision/lib/src/interfaces/provisioning_page.dart diff --git a/packages/ubuntu_bootstrap/lib/installer/installation_step.dart b/packages/ubuntu_bootstrap/lib/installer/installation_step.dart new file mode 100644 index 000000000..6dba4d350 --- /dev/null +++ b/packages/ubuntu_bootstrap/lib/installer/installation_step.dart @@ -0,0 +1,42 @@ +import 'package:dartx/dartx.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:subiquity_client/subiquity_client.dart'; +import 'package:ubuntu_bootstrap/installer/installer_model.dart'; +import 'package:ubuntu_bootstrap/pages.dart'; +import 'package:ubuntu_bootstrap/routes.dart'; +import 'package:ubuntu_bootstrap/services.dart'; +import 'package:ubuntu_provision/ubuntu_provision.dart'; +import 'package:ubuntu_wizard/ubuntu_wizard.dart'; +import 'package:yaru_widgets/yaru_widgets.dart'; + +enum InstallationStep { + locale(LocalePage.new), + welcome(WelcomePage.new), + rst(RstPage.new), + keyboard(KeyboardPage.new), + network(NetworkPage.new), + refresh(RefreshPage.new), + source(SourcePage.new), + secureBoot(SecureBootPage.new), + storage(StoragePage.new); + + const InstallationStep(this.pageFactory); + + final ProvisioningPage Function() pageFactory; + + WizardRoute? toRoute(BuildContext context, WidgetRef ref) { + final page = pageFactory(); + return WizardRoute( + builder: (_) => page, + userData: WizardRouteData(step: index), + onLoad: (_) => page.load(context, ref), + ); + } + + String get name => toString().split('.').last; + + static InstallationStep? fromName(String name) { + return InstallationStep.values.firstOrNullWhere((e) => e.name == name); + } +} diff --git a/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart b/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart index c009e3c1b..76cede4a1 100644 --- a/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart +++ b/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart @@ -10,15 +10,7 @@ import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; -enum InstallationStep { - locale, - keyboard, - network, - refresh, - source, - type, - storage, -} +import 'installation_step.dart'; class InstallerWizard extends ConsumerStatefulWidget { const InstallerWizard({ @@ -68,52 +60,11 @@ class _InstallWizard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final pagesConfig = getService(); final preInstall = { - Routes.locale: WizardRoute( - builder: (_) => const LocalePage(), - userData: WizardRouteData(step: InstallationStep.locale.index), - onLoad: (_) => LocalePage.load(context, ref), - ), - if (welcome) - Routes.welcome: WizardRoute( - builder: (_) => const WelcomePage(), - userData: WizardRouteData(step: InstallationStep.locale.index), - onLoad: (_) => WelcomePage.load(context, ref), - ), - Routes.rst: WizardRoute( - builder: (_) => const RstPage(), - onLoad: (_) => RstPage.load(ref), - ), - Routes.keyboard: WizardRoute( - builder: (_) => const KeyboardPage(), - userData: WizardRouteData(step: InstallationStep.keyboard.index), - onLoad: (settings) => KeyboardPage.load(ref), - ), - Routes.network: WizardRoute( - builder: (_) => const NetworkPage(), - userData: WizardRouteData(step: InstallationStep.network.index), - onLoad: (_) => NetworkPage.load(ref), - ), - Routes.refresh: WizardRoute( - builder: (_) => const RefreshPage(), - userData: WizardRouteData(step: InstallationStep.refresh.index), - onLoad: (_) => RefreshPage.load(ref), - ), - Routes.source: WizardRoute( - builder: (_) => const SourceWizard(), - userData: WizardRouteData(step: InstallationStep.source.index), - onLoad: (_) => SourcePage.load(ref), - ), - Routes.secureBoot: WizardRoute( - builder: (_) => const SecureBootPage(), - userData: WizardRouteData(step: InstallationStep.type.index), - onLoad: (_) => SecureBootPage.load(ref), - ), - Routes.storage: WizardRoute( - builder: (_) => const StorageWizard(), - userData: WizardRouteData(step: InstallationStep.storage.index), - onLoad: (_) => StorageWizard.load(ref), - ), + for (final pageName in pagesConfig.includedPages) + routes[pageName]!: + InstallationStep.fromName(pageName)!.toRoute(context, ref)! }; return WizardBuilder( @@ -128,16 +79,19 @@ class _InstallWizard extends ConsumerWidget { Routes.confirm: WizardRoute( builder: (_) => const ConfirmPage(), userData: WizardRouteData(step: InstallationStep.storage.index), - onLoad: (_) => ConfirmPage.load(ref), + onLoad: (_) => const ConfirmPage().load(context, ref), ), Routes.install: WizardRoute( builder: (_) => const InstallPage(), - onLoad: (_) => InstallPage.load(context, ref), + onLoad: (_) => const InstallPage().load(context, ref), ), }, - predicate: (route) => switch (route) { - Routes.loading || Routes.confirm || Routes.install => true, - _ => ref.read(installerModelProvider).hasRoute(route) + predicate: (route) { + if ([Routes.loading, Routes.confirm, Routes.install].contains(route)) { + return true; + } else { + return ref.read(installerModelProvider).hasRoute(route); + } }, observers: [_InstallerObserver(getService())], ); diff --git a/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_page.dart b/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_page.dart index ea8c8e08d..eb95f7289 100644 --- a/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_page.dart @@ -4,13 +4,15 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:subiquity_client/subiquity_client.dart'; import 'package:ubuntu_bootstrap/l10n.dart'; import 'package:ubuntu_bootstrap/pages/confirm/confirm_model.dart'; +import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; -class ConfirmPage extends ConsumerWidget { +class ConfirmPage extends ConsumerWidget with ProvisioningPage { const ConfirmPage({super.key}); - static Future load(WidgetRef ref) { + @override + Future load(BuildContext context, WidgetRef ref) { return ref.read(confirmModelProvider).init().then((_) => true); } diff --git a/packages/ubuntu_bootstrap/lib/pages/install/install_page.dart b/packages/ubuntu_bootstrap/lib/pages/install/install_page.dart index 904b65d78..a6df51183 100644 --- a/packages/ubuntu_bootstrap/lib/pages/install/install_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/install/install_page.dart @@ -12,25 +12,11 @@ import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru_icons/yaru_icons.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; -extension InstallationActionL10n on InstallationAction { - String localize(UbuntuBootstrapLocalizations lang) { - switch (this) { - case InstallationAction.installingSystem: - return lang.installingSystem; - case InstallationAction.configuringSystem: - return lang.configuringSystem; - case InstallationAction.copyingFiles: - return lang.copyingFiles; - case InstallationAction.none: - return ''; - } - } -} - -class InstallPage extends ConsumerWidget { +class InstallPage extends ConsumerWidget with ProvisioningPage { const InstallPage({super.key}); - static Future load(BuildContext context, WidgetRef ref) { + @override + Future load(BuildContext context, WidgetRef ref) { final model = ref.read(installModelProvider); return Future.wait([ model.init(), @@ -254,3 +240,18 @@ class _DonePage extends ConsumerWidget { ); } } + +extension InstallationActionL10n on InstallationAction { + String localize(UbuntuBootstrapLocalizations lang) { + switch (this) { + case InstallationAction.installingSystem: + return lang.installingSystem; + case InstallationAction.configuringSystem: + return lang.configuringSystem; + case InstallationAction.copyingFiles: + return lang.copyingFiles; + case InstallationAction.none: + return ''; + } + } +} diff --git a/packages/ubuntu_bootstrap/lib/pages/refresh/refresh_page.dart b/packages/ubuntu_bootstrap/lib/pages/refresh/refresh_page.dart index 07055367e..8807b035e 100644 --- a/packages/ubuntu_bootstrap/lib/pages/refresh/refresh_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/refresh/refresh_page.dart @@ -3,13 +3,15 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:ubuntu_bootstrap/l10n.dart'; import 'package:ubuntu_bootstrap/pages/refresh/refresh_model.dart'; import 'package:ubuntu_bootstrap/pages/refresh/refresh_widgets.dart'; +import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; -class RefreshPage extends ConsumerWidget { +class RefreshPage extends ConsumerWidget with ProvisioningPage { const RefreshPage({super.key}); - static Future load(WidgetRef ref) { + @override + Future load(BuildContext context, WidgetRef ref) { return ref.read(refreshModelProvider).init(); } diff --git a/packages/ubuntu_bootstrap/lib/pages/rst/rst_page.dart b/packages/ubuntu_bootstrap/lib/pages/rst/rst_page.dart index 3de2a79f7..11ee39304 100644 --- a/packages/ubuntu_bootstrap/lib/pages/rst/rst_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/rst/rst_page.dart @@ -11,10 +11,11 @@ import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru/yaru.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; -class RstPage extends ConsumerWidget { +class RstPage extends ConsumerWidget with ProvisioningPage { const RstPage({super.key}); - static Future load(WidgetRef ref) { + @override + Future load(BuildContext context, WidgetRef ref) { return ref.read(rstModelProvider).hasRst(); } diff --git a/packages/ubuntu_bootstrap/lib/pages/secure_boot/secure_boot_page.dart b/packages/ubuntu_bootstrap/lib/pages/secure_boot/secure_boot_page.dart index 6bc9d1ffc..d8aee776d 100644 --- a/packages/ubuntu_bootstrap/lib/pages/secure_boot/secure_boot_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/secure_boot/secure_boot_page.dart @@ -3,13 +3,15 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:ubuntu_bootstrap/l10n.dart'; import 'package:ubuntu_bootstrap/pages/secure_boot/secure_boot_model.dart'; import 'package:ubuntu_bootstrap/pages/secure_boot/secure_boot_widgets.dart'; +import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; -class SecureBootPage extends ConsumerStatefulWidget { +class SecureBootPage extends ConsumerStatefulWidget with ProvisioningPage { const SecureBootPage({super.key}); - static Future load(WidgetRef ref) { + @override + Future load(BuildContext context, WidgetRef ref) { return ref.read(secureBootModelProvider.notifier).hasSecureBoot(); } diff --git a/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart b/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart index bbe18b396..0c7e01599 100644 --- a/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart @@ -12,10 +12,11 @@ import 'package:yaru_widgets/yaru_widgets.dart'; export 'source_model.dart' show kFullSourceId, kMinimalSourceId; -class SourcePage extends ConsumerWidget { +class SourcePage extends ConsumerWidget with ProvisioningPage { const SourcePage({super.key}); - static Future load(WidgetRef ref) { + @override + Future load(BuildContext context, WidgetRef ref) { return ref.read(sourceModelProvider).init().then((_) => true); } diff --git a/packages/ubuntu_bootstrap/lib/pages/source/source_wizard.dart b/packages/ubuntu_bootstrap/lib/pages/source/source_wizard.dart index 030dd9b73..08faba764 100644 --- a/packages/ubuntu_bootstrap/lib/pages/source/source_wizard.dart +++ b/packages/ubuntu_bootstrap/lib/pages/source/source_wizard.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:ubuntu_bootstrap/installer.dart'; +import 'package:ubuntu_bootstrap/installer/installation_step.dart'; import 'package:ubuntu_bootstrap/pages/source/not_enough_disk_space/not_enough_disk_space_page.dart'; import 'package:ubuntu_bootstrap/pages/source/source_page.dart'; import 'package:ubuntu_bootstrap/routes.dart'; diff --git a/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart b/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart index 22867799d..65ea8b703 100644 --- a/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart @@ -13,10 +13,11 @@ import 'package:yaru_widgets/yaru_widgets.dart'; export 'storage_model.dart' show StorageType; /// Select between guided and manual partitioning. -class StoragePage extends ConsumerWidget { +class StoragePage extends ConsumerWidget with ProvisioningPage { const StoragePage({super.key}); - static Future load(WidgetRef ref) { + @override + Future load(BuildContext context, WidgetRef ref) { return ref.read(storageModelProvider.notifier).init().then((_) => true); } diff --git a/packages/ubuntu_bootstrap/lib/pages/storage/storage_wizard.dart b/packages/ubuntu_bootstrap/lib/pages/storage/storage_wizard.dart index 644b87fa0..b7d3b285c 100644 --- a/packages/ubuntu_bootstrap/lib/pages/storage/storage_wizard.dart +++ b/packages/ubuntu_bootstrap/lib/pages/storage/storage_wizard.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:ubuntu_bootstrap/installer.dart'; +import 'package:ubuntu_bootstrap/installer/installation_step.dart'; import 'package:ubuntu_bootstrap/pages/storage/bitlocker/bitlocker_page.dart'; import 'package:ubuntu_bootstrap/pages/storage/guided_reformat/guided_reformat_page.dart'; import 'package:ubuntu_bootstrap/pages/storage/guided_resize/guided_resize_page.dart'; @@ -10,6 +10,7 @@ import 'package:ubuntu_bootstrap/pages/storage/security_key/security_key_page.da import 'package:ubuntu_bootstrap/pages/storage/storage_model.dart'; import 'package:ubuntu_bootstrap/pages/storage/storage_page.dart'; import 'package:ubuntu_bootstrap/pages/storage/storage_routes.dart'; +import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; export 'bitlocker/bitlocker_page.dart'; @@ -21,11 +22,12 @@ export 'security_key/security_key_page.dart'; export 'storage_page.dart'; export 'storage_routes.dart'; -class StorageWizard extends ConsumerWidget { +class StorageWizard extends ConsumerWidget with ProvisioningPage { const StorageWizard({super.key}); - static Future load(WidgetRef ref) { - return StoragePage.load(ref); + @override + Future load(BuildContext context, WidgetRef ref) { + return const StoragePage().load(context, ref); } @override @@ -35,7 +37,7 @@ class StorageWizard extends ConsumerWidget { final routes = { Navigator.defaultRouteName: WizardRoute( builder: (_) => const StoragePage(), - userData: WizardRouteData(step: InstallationStep.type.index), + userData: WizardRouteData(step: InstallationStep.secureBoot.index), ), if (type != StorageType.manual) StorageRoutes.bitlocker: WizardRoute( diff --git a/packages/ubuntu_bootstrap/lib/pages/welcome/welcome_page.dart b/packages/ubuntu_bootstrap/lib/pages/welcome/welcome_page.dart index a0b18d653..a45ef18f0 100644 --- a/packages/ubuntu_bootstrap/lib/pages/welcome/welcome_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/welcome/welcome_page.dart @@ -13,10 +13,11 @@ import 'package:yaru_widgets/yaru_widgets.dart'; export 'welcome_model.dart' show Option; -class WelcomePage extends ConsumerWidget { +class WelcomePage extends ConsumerWidget with ProvisioningPage { const WelcomePage({super.key}); - static Future load(BuildContext context, WidgetRef ref) { + @override + Future load(BuildContext context, WidgetRef ref) { return Future.wait([ ref.read(welcomeModelProvider).init(), MascotAvatar.precacheAsset(context), diff --git a/packages/ubuntu_bootstrap/lib/routes.dart b/packages/ubuntu_bootstrap/lib/routes.dart index c16a05bf4..85bb80874 100644 --- a/packages/ubuntu_bootstrap/lib/routes.dart +++ b/packages/ubuntu_bootstrap/lib/routes.dart @@ -1,20 +1,41 @@ +const Map routes = { + 'initial': '/loading', + 'loading': '/loading', + 'locale': '/locale', + 'welcome': '/welcome', + 'rst': '/rst', + 'keyboard': '/keyboard', + 'network': '/network', + 'secureBoot': '/secure-boot', + 'source': '/source', + 'confirm': '/confirm', + 'theme': '/theme', + 'identity': '/identity', + 'install': '/install', + 'timezone': '/timezone', + 'notEnoughDiskSpace': '/not-enough-disk-space', + 'activeDirectory': '/active-directory', + 'storage': '/storage', + 'refresh': '/refresh', +}; + abstract class Routes { - static const initial = loading; - static const loading = '/loading'; - static const locale = '/locale'; - static const welcome = '/welcome'; - static const rst = '/rst'; - static const keyboard = '/keyboard'; - static const network = '/network'; - static const secureBoot = '/secure-boot'; - static const source = '/source'; - static const confirm = '/confirm'; - static const theme = '/theme'; - static const identity = '/identity'; - static const install = '/install'; - static const timezone = '/timezone'; - static const notEnoughDiskSpace = '/not-enough-disk-space'; - static const activeDirectory = '/active-directory'; - static const storage = '/storage'; - static const refresh = '/refresh'; + static String get initial => loading; + static String get loading => routes['loading']!; + static String get locale => routes['locale']!; + static String get welcome => routes['welcome']!; + static String get rst => routes['rst']!; + static String get keyboard => routes['keyboard']!; + static String get network => routes['network']!; + static String get secureBoot => routes['secure-boot']!; + static String get source => routes['source']!; + static String get confirm => routes['confirm']!; + static String get theme => routes['theme']!; + static String get identity => routes['identity']!; + static String get install => routes['install']!; + static String get timezone => routes['timezone']!; + static String get notEnoughDiskSpace => routes['not-enough-disk-space']!; + static String get activeDirectory => routes['active-directory']!; + static String get storage => routes['storage']!; + static String get refresh => routes['refresh']!; } diff --git a/packages/ubuntu_bootstrap/test/source/source_wizard_test.dart b/packages/ubuntu_bootstrap/test/source/source_wizard_test.dart index 430136117..bccc535dc 100644 --- a/packages/ubuntu_bootstrap/test/source/source_wizard_test.dart +++ b/packages/ubuntu_bootstrap/test/source/source_wizard_test.dart @@ -126,7 +126,7 @@ extension on WidgetTester { ), Routes.source: WizardRoute( builder: (_) => const SourceWizard(), - onLoad: (_) => SourcePage.load(ref), + onLoad: (_) => const SourcePage().load(context, ref), ), '/last': WizardRoute( builder: (context) => WizardPage( diff --git a/packages/ubuntu_init/lib/src/init_wizard.dart b/packages/ubuntu_init/lib/src/init_wizard.dart index a45317b4d..dcb2a7bce 100644 --- a/packages/ubuntu_init/lib/src/init_wizard.dart +++ b/packages/ubuntu_init/lib/src/init_wizard.dart @@ -41,6 +41,7 @@ class InitWizard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + // TODO(Lukas): Create init_step.dart, like installation_step.dart return WizardBuilder( routes: { // TODO: loading screen? @@ -58,21 +59,21 @@ class InitWizard extends ConsumerWidget { userData: WizardRouteData( step: InitStep.locale.index, ), - onLoad: (_) => LocalePage.load(context, ref), + onLoad: (_) => const LocalePage().load(context, ref), ), InitRoutes.keyboard: WizardRoute( builder: (_) => const KeyboardPage(), userData: WizardRouteData( step: InitStep.keyboard.index, ), - onLoad: (_) => KeyboardPage.load(ref), + onLoad: (_) => const KeyboardPage().load(context, ref), ), InitRoutes.network: WizardRoute( builder: (_) => const NetworkPage(), userData: WizardRouteData( step: InitStep.network.index, ), - onLoad: (_) => NetworkPage.load(ref), + onLoad: (_) => const NetworkPage().load(context, ref), ), InitRoutes.identity: WizardRoute( builder: (_) => const IdentityPage(), diff --git a/packages/ubuntu_provision/lib/interfaces.dart b/packages/ubuntu_provision/lib/interfaces.dart new file mode 100644 index 000000000..072f73c90 --- /dev/null +++ b/packages/ubuntu_provision/lib/interfaces.dart @@ -0,0 +1,3 @@ +library ubuntu_provision; + +export 'src/interfaces/provisioning_page.dart'; diff --git a/packages/ubuntu_provision/lib/providers.dart b/packages/ubuntu_provision/lib/providers.dart new file mode 100644 index 000000000..b534d7c2e --- /dev/null +++ b/packages/ubuntu_provision/lib/providers.dart @@ -0,0 +1,5 @@ +library ubuntu_provision; + +export 'src/providers/flavor.dart'; +export 'src/providers/locale.dart'; +export 'src/providers/page_images.dart'; diff --git a/packages/ubuntu_provision/lib/src/interfaces/provisioning_page.dart b/packages/ubuntu_provision/lib/src/interfaces/provisioning_page.dart new file mode 100644 index 000000000..eab1966d4 --- /dev/null +++ b/packages/ubuntu_provision/lib/src/interfaces/provisioning_page.dart @@ -0,0 +1,7 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +mixin ProvisioningPage on Widget { + /// Performs any pre-loading that needs to be done for the page. + Future load(BuildContext context, WidgetRef ref); +} diff --git a/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart b/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart index c36df29a5..1db71a699 100644 --- a/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart +++ b/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_provision/src/keyboard/keyboard_dialogs.dart'; import 'package:ubuntu_provision/src/keyboard/keyboard_l10n.dart'; import 'package:ubuntu_provision/src/keyboard/keyboard_model.dart'; @@ -7,10 +8,11 @@ import 'package:ubuntu_widgets/ubuntu_widgets.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; -class KeyboardPage extends ConsumerWidget { +class KeyboardPage extends ConsumerWidget with ProvisioningPage { const KeyboardPage({super.key}); - static Future load(WidgetRef ref) { + @override + Future load(BuildContext context, WidgetRef ref) { final model = ref.read(keyboardModelProvider); return model .init() diff --git a/packages/ubuntu_provision/lib/src/locale/locale_page.dart b/packages/ubuntu_provision/lib/src/locale/locale_page.dart index e145538cb..3f8864ad7 100644 --- a/packages/ubuntu_provision/lib/src/locale/locale_page.dart +++ b/packages/ubuntu_provision/lib/src/locale/locale_page.dart @@ -5,15 +5,17 @@ import 'package:ubuntu_provision/src/locale/locale_l10n.dart'; import 'package:ubuntu_provision/src/locale/locale_model.dart'; import 'package:ubuntu_provision/src/providers/flavor.dart'; import 'package:ubuntu_provision/src/providers/page_images.dart'; +import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_service/ubuntu_service.dart'; import 'package:ubuntu_widgets/ubuntu_widgets.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; -class LocalePage extends ConsumerWidget { +class LocalePage extends ConsumerWidget with ProvisioningPage { const LocalePage({super.key}); - static Future load(BuildContext context, WidgetRef ref) { + @override + Future load(BuildContext context, WidgetRef ref) { final model = ref.read(localeModelProvider); return model.init().then((_) => model.playWelcomeSound()).then((_) => true); } diff --git a/packages/ubuntu_provision/lib/src/network/network_page.dart b/packages/ubuntu_provision/lib/src/network/network_page.dart index 687784d25..2a6feb9dc 100644 --- a/packages/ubuntu_provision/lib/src/network/network_page.dart +++ b/packages/ubuntu_provision/lib/src/network/network_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:ubuntu_localizations/ubuntu_localizations.dart'; +import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_provision/src/network/connect_model.dart'; import 'package:ubuntu_provision/src/network/connect_view.dart'; import 'package:ubuntu_provision/src/network/ethernet_model.dart'; @@ -17,10 +18,11 @@ import 'package:yaru_widgets/yaru_widgets.dart'; export 'connect_model.dart' show ConnectMode; /// https://github.com/canonical/ubuntu-desktop-installer/issues/30 -class NetworkPage extends ConsumerWidget { +class NetworkPage extends ConsumerWidget with ProvisioningPage { const NetworkPage({super.key}); - static Future load(WidgetRef ref) { + @override + Future load(BuildContext context, WidgetRef ref) { final model = ref.read(networkModelProvider); model.addConnectMode(ref.read(ethernetModelProvider)); model.addConnectMode(ref.read(wifiModelProvider)); diff --git a/packages/ubuntu_provision/lib/src/services/page_config_service.dart b/packages/ubuntu_provision/lib/src/services/page_config_service.dart index d40fd22bf..86be270b4 100644 --- a/packages/ubuntu_provision/lib/src/services/page_config_service.dart +++ b/packages/ubuntu_provision/lib/src/services/page_config_service.dart @@ -15,6 +15,9 @@ class PageConfigService { final ConfigService? _config; final Map pages = {}; + Set get includedPages => + pages.entries.where((e) => e.value.visible).map((e) => e.key).toSet(); + Set get excludedPages => pages.entries.whereNot((e) => e.value.visible).map((e) => e.key).toSet(); diff --git a/packages/ubuntu_provision/lib/ubuntu_provision.dart b/packages/ubuntu_provision/lib/ubuntu_provision.dart index 2c82acb18..3b304a723 100644 --- a/packages/ubuntu_provision/lib/ubuntu_provision.dart +++ b/packages/ubuntu_provision/lib/ubuntu_provision.dart @@ -2,13 +2,12 @@ library ubuntu_provision; export 'active_directory.dart'; export 'identity.dart'; +export 'interfaces.dart'; export 'keyboard.dart'; export 'l10n.dart'; export 'network.dart'; +export 'providers.dart'; export 'services.dart'; -export 'src/providers/flavor.dart'; -export 'src/providers/locale.dart'; -export 'src/providers/page_images.dart'; export 'theme.dart'; export 'theme_variant.dart'; export 'timezone.dart'; diff --git a/packages/ubuntu_provision/test/network/test_network.dart b/packages/ubuntu_provision/test/network/test_network.dart index bc293683f..72bb457c4 100644 --- a/packages/ubuntu_provision/test/network/test_network.dart +++ b/packages/ubuntu_provision/test/network/test_network.dart @@ -95,7 +95,7 @@ Widget buildNetworkPage({ ], child: Consumer(builder: (context, ref, child) { return FutureBuilder( - future: NetworkPage.load(ref), + future: const NetworkPage().load(context, ref), builder: (context, snapshot) { return (snapshot.data ?? false) ? const NetworkPage() From 6a7b61422f59d058b6cfedb1460c13e7a2fbeca1 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Wed, 27 Dec 2023 16:34:28 +0100 Subject: [PATCH 022/106] fix: Correctly show amount of pages --- .../assets/ubuntu-provision.yml | 7 +++++-- .../lib/installer/installation_step.dart | 7 +++++-- .../lib/installer/installer_wizard.dart | 21 +++++++++---------- .../lib/pages/confirm/confirm_model.dart | 7 +++++-- .../lib/pages/refresh/refresh_page.dart | 2 ++ .../lib/pages/rst/rst_page.dart | 10 +++++++-- .../lib/pages/source/source_page.dart | 3 +++ .../lib/services/installer_service.dart | 2 +- packages/ubuntu_init/lib/src/init_model.dart | 2 +- .../lib/src/services/config_service.dart | 2 ++ .../lib/src/services/page_config_service.dart | 12 +++++++---- 11 files changed, 50 insertions(+), 25 deletions(-) diff --git a/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml b/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml index 637825d08..a8a8ff635 100644 --- a/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml +++ b/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml @@ -16,14 +16,17 @@ pages: title: "Connect to a network" visible: true refresh: - title: "Applications and updates" + title: "Update available" visible: true source: title: "Type of installation" visible: true secureBoot: - title: "My Page Title" + title: "Configure Secure Boot" visible: true storage: title: "Ready to install" visible: true + confirm: + title: "Ready to install" + visible: true diff --git a/packages/ubuntu_bootstrap/lib/installer/installation_step.dart b/packages/ubuntu_bootstrap/lib/installer/installation_step.dart index 6dba4d350..17a639f3b 100644 --- a/packages/ubuntu_bootstrap/lib/installer/installation_step.dart +++ b/packages/ubuntu_bootstrap/lib/installer/installation_step.dart @@ -19,17 +19,20 @@ enum InstallationStep { refresh(RefreshPage.new), source(SourcePage.new), secureBoot(SecureBootPage.new), - storage(StoragePage.new); + storage(StoragePage.new), + confirm(ConfirmPage.new); const InstallationStep(this.pageFactory); final ProvisioningPage Function() pageFactory; WizardRoute? toRoute(BuildContext context, WidgetRef ref) { + final pageConfig = ref.watch(pageConfigProvider); + final includedIndex = pageConfig.includedPages.indexOf(name); final page = pageFactory(); return WizardRoute( builder: (_) => page, - userData: WizardRouteData(step: index), + userData: WizardRouteData(step: includedIndex), onLoad: (_) => page.load(context, ref), ); } diff --git a/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart b/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart index 76cede4a1..d2c18c8f7 100644 --- a/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart +++ b/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart @@ -60,27 +60,26 @@ class _InstallWizard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final pagesConfig = getService(); - final preInstall = { - for (final pageName in pagesConfig.includedPages) + final pageConfig = ref.watch(pageConfigProvider); + final preInstallRoutes = { + for (final pageName in pageConfig.includedPages) routes[pageName]!: InstallationStep.fromName(pageName)!.toRoute(context, ref)! }; return WizardBuilder( initialRoute: Routes.initial, - userData: WizardData(totalSteps: InstallationStep.values.length), - routes: { + userData: WizardData(totalSteps: preInstallRoutes.length), + routes: { Routes.loading: WizardRoute( builder: (_) => const LoadingPage(), + userData: const WizardRouteData( + hasPrevious: false, + hasNext: false, + ), onReplace: (_) => LoadingPage.init(context, ref).then((_) => null), ), - ...preInstall, - Routes.confirm: WizardRoute( - builder: (_) => const ConfirmPage(), - userData: WizardRouteData(step: InstallationStep.storage.index), - onLoad: (_) => const ConfirmPage().load(context, ref), - ), + ...preInstallRoutes, Routes.install: WizardRoute( builder: (_) => const InstallPage(), onLoad: (_) => const InstallPage().load(context, ref), diff --git a/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_model.dart b/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_model.dart index a2dc5135d..296e2abfb 100644 --- a/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_model.dart +++ b/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_model.dart @@ -6,8 +6,11 @@ import 'package:ubuntu_bootstrap/services.dart'; import 'package:ubuntu_provision/services.dart'; final confirmModelProvider = ChangeNotifierProvider( - (_) => ConfirmModel(getService(), - getService(), getService()), + (_) => ConfirmModel( + getService(), + getService(), + getService(), + ), ); /// View model for [ConfirmPage]. diff --git a/packages/ubuntu_bootstrap/lib/pages/refresh/refresh_page.dart b/packages/ubuntu_bootstrap/lib/pages/refresh/refresh_page.dart index 8807b035e..f71e5627c 100644 --- a/packages/ubuntu_bootstrap/lib/pages/refresh/refresh_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/refresh/refresh_page.dart @@ -7,6 +7,8 @@ import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; +/// A page to let the user know that the installer has a newer version +/// available that can be installed. class RefreshPage extends ConsumerWidget with ProvisioningPage { const RefreshPage({super.key}); diff --git a/packages/ubuntu_bootstrap/lib/pages/rst/rst_page.dart b/packages/ubuntu_bootstrap/lib/pages/rst/rst_page.dart index 11ee39304..d2dba4a05 100644 --- a/packages/ubuntu_bootstrap/lib/pages/rst/rst_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/rst/rst_page.dart @@ -11,6 +11,10 @@ import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru/yaru.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; +/// Page inform the user that they have Intel Rapid Storage Technology (RST) +/// enabled and need to disable it. +/// +/// If RST is not enabled, this page is skipped. class RstPage extends ConsumerWidget with ProvisioningPage { const RstPage({super.key}); @@ -51,8 +55,10 @@ class RstPage extends ConsumerWidget with ProvisioningPage { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - Text(lang.rstHeader, - style: Theme.of(context).textTheme.headlineSmall), + Text( + lang.rstHeader, + style: Theme.of(context).textTheme.headlineSmall, + ), const SizedBox(height: kWizardSpacing), Text(lang.rstDescription), const SizedBox(height: kWizardSpacing), diff --git a/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart b/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart index 0c7e01599..45addbe71 100644 --- a/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart @@ -12,6 +12,9 @@ import 'package:yaru_widgets/yaru_widgets.dart'; export 'source_model.dart' show kFullSourceId, kMinimalSourceId; +/// A page where the user can decide what type of installation that they would +/// like to perform, and whether they want to install 3rd party drivers and +/// codecs. class SourcePage extends ConsumerWidget with ProvisioningPage { const SourcePage({super.key}); diff --git a/packages/ubuntu_bootstrap/lib/services/installer_service.dart b/packages/ubuntu_bootstrap/lib/services/installer_service.dart index 36da2066c..2d2d17d1b 100644 --- a/packages/ubuntu_bootstrap/lib/services/installer_service.dart +++ b/packages/ubuntu_bootstrap/lib/services/installer_service.dart @@ -37,7 +37,7 @@ class InstallerService { Future load() async { await monitorStatus().firstWhere((s) => s?.isLoading == false); _subiquityPages = (await _client.getInteractiveSections())?.toSet(); - _excludedPages = _pageConfig?.excludedPages; + _excludedPages = _pageConfig?.excludedPages.toSet(); final requiredExcludedPages = _excludedPages?.intersection(_subiquityPages ?? {}); diff --git a/packages/ubuntu_init/lib/src/init_model.dart b/packages/ubuntu_init/lib/src/init_model.dart index 33223f18a..9f6e8420c 100644 --- a/packages/ubuntu_init/lib/src/init_model.dart +++ b/packages/ubuntu_init/lib/src/init_model.dart @@ -18,7 +18,7 @@ class InitModel { : _pageConfig = pageConfig; final PageConfigService? _pageConfig; - Set? _excludedPages; + List? _excludedPages; Future init() async { _excludedPages = _pageConfig?.excludedPages; diff --git a/packages/ubuntu_provision/lib/src/services/config_service.dart b/packages/ubuntu_provision/lib/src/services/config_service.dart index 8a217fed1..90aff1810 100644 --- a/packages/ubuntu_provision/lib/src/services/config_service.dart +++ b/packages/ubuntu_provision/lib/src/services/config_service.dart @@ -1,9 +1,11 @@ import 'package:file/file.dart'; import 'package:file/local.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:ubuntu_logger/ubuntu_logger.dart'; +import 'package:ubuntu_service/ubuntu_service.dart'; import 'package:xdg_directories/xdg_directories.dart' as xdg; import 'package:yaml/yaml.dart'; diff --git a/packages/ubuntu_provision/lib/src/services/page_config_service.dart b/packages/ubuntu_provision/lib/src/services/page_config_service.dart index 86be270b4..8fbebb47f 100644 --- a/packages/ubuntu_provision/lib/src/services/page_config_service.dart +++ b/packages/ubuntu_provision/lib/src/services/page_config_service.dart @@ -1,12 +1,16 @@ import 'package:collection/collection.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:ubuntu_logger/ubuntu_logger.dart'; import 'package:ubuntu_provision/services.dart'; +import 'package:ubuntu_service/ubuntu_service.dart'; import 'package:yaml/yaml.dart'; part 'page_config_service.freezed.dart'; part 'page_config_service.g.dart'; +final pageConfigProvider = Provider((ref) => getService()); + final _log = Logger('page'); class PageConfigService { @@ -15,11 +19,11 @@ class PageConfigService { final ConfigService? _config; final Map pages = {}; - Set get includedPages => - pages.entries.where((e) => e.value.visible).map((e) => e.key).toSet(); + List get includedPages => + pages.entries.where((e) => e.value.visible).map((e) => e.key).toList(); - Set get excludedPages => - pages.entries.whereNot((e) => e.value.visible).map((e) => e.key).toSet(); + List get excludedPages => + pages.entries.whereNot((e) => e.value.visible).map((e) => e.key).toList(); Future load() async { final pageConfig = PageConfig.fromJson({ From c64a8d454aa53af3a0d152c9e61db389ccad2ba9 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Thu, 28 Dec 2023 09:53:39 +0100 Subject: [PATCH 023/106] refactor: Trailing commas --- packages/ubuntu_bootstrap/lib/installer.dart | 91 ++++++++++++-------- 1 file changed, 57 insertions(+), 34 deletions(-) diff --git a/packages/ubuntu_bootstrap/lib/installer.dart b/packages/ubuntu_bootstrap/lib/installer.dart index 7d7067dda..824201a33 100644 --- a/packages/ubuntu_bootstrap/lib/installer.dart +++ b/packages/ubuntu_bootstrap/lib/installer.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:io'; -import 'package:args/args.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -35,21 +34,33 @@ Future runInstallerApp( Iterable>? localizationsDelegates, }) async { final options = parseCommandLine(args, onPopulateOptions: (parser) { - parser.addOption('config', - valueHelp: 'path', help: 'Path to a config file'); - parser.addFlag('dry-run', - defaultsTo: Platform.environment['LIVE_RUN'] != '1', - help: 'Run Subiquity server in dry-run mode'); - parser.addOption('dry-run-config', - valueHelp: 'path', help: 'Path of the dry-run config file'); - parser.addOption('machine-config', - valueHelp: 'path', - defaultsTo: 'examples/machines/simple.json', - help: 'Path of the machine config (dry-run only)'); - parser.addOption('source-catalog', - valueHelp: 'path', - defaultsTo: 'examples/sources/desktop.yaml', - help: 'Path of the source catalog (dry-run only)'); + parser.addOption( + 'config', + valueHelp: 'path', + help: 'Path to a config file', + ); + parser.addFlag( + 'dry-run', + defaultsTo: Platform.environment['LIVE_RUN'] != '1', + help: 'Run Subiquity server in dry-run mode', + ); + parser.addOption( + 'dry-run-config', + valueHelp: 'path', + help: 'Path of the dry-run config file', + ); + parser.addOption( + 'machine-config', + valueHelp: 'path', + defaultsTo: 'examples/machines/simple.json', + help: 'Path of the machine config (dry-run only)', + ); + parser.addOption( + 'source-catalog', + valueHelp: 'path', + defaultsTo: 'examples/sources/desktop.yaml', + help: 'Path of the source catalog (dry-run only)', + ); parser.addFlag('welcome', aliases: ['try-or-install'], hide: true); })!; final liveRun = options['dry-run'] != true; @@ -72,45 +83,57 @@ Future runInstallerApp( final baseName = p.basename(Platform.resolvedExecutable); // conditional registration if not already registered by flavors or tests - tryRegisterServiceInstance(options); - tryRegisterService( + tryRegisterServiceInstance(options); + tryRegisterService( () => ConfigService(path: options['config'] as String?), ); tryRegisterService(GnomeService.new); tryRegisterServiceFactory(GSettings.new); - tryRegisterService(() => InstallerService( + tryRegisterService( + () => InstallerService( getService(), - pageConfig: tryGetService())); + pageConfig: tryGetService(), + ), + ); tryRegisterService(JournalService.new); - tryRegisterService( - () => SubiquityKeyboardService(getService())); - tryRegisterService( - () => SubiquityLocaleService(getService())); - tryRegisterService( - () => SubiquityNetworkService(getService())); tryRegisterService( - () => PageConfigService(config: tryGetService())); + () => SubiquityKeyboardService(getService()), + ); + tryRegisterService( + () => SubiquityLocaleService(getService()), + ); + tryRegisterService( + () => SubiquityNetworkService(getService()), + ); + tryRegisterService( + () => PageConfigService(config: tryGetService()), + ); tryRegisterService(() => PostInstallService('/tmp/$baseName.conf')); tryRegisterService(PowerService.new); tryRegisterService(ProductService.new); tryRegisterService(() => RefreshService(getService())); - tryRegisterService( - () => SubiquitySessionService(getService())); + tryRegisterService( + () => SubiquitySessionService(getService()), + ); if (liveRun) tryRegisterService(SoundService.new); tryRegisterService(() => StorageService(getService())); tryRegisterService(SubiquityClient.new); tryRegisterService( - () => SubiquityServer(process: process, endpoint: endpoint)); - tryRegisterService(() { - var path = '/var/log/installer/telemetry'; + () => SubiquityServer(process: process, endpoint: endpoint), + ); + tryRegisterService(() { + final String path; if (kDebugMode) { final exe = Platform.resolvedExecutable; path = '${p.dirname(exe)}/.${p.basename(exe)}/telemetry'; + } else { + path = '/var/log/installer/telemetry'; } return TelemetryService(path); }); - tryRegisterService( - () => ThemeVariantService(config: tryGetService())); + tryRegisterService( + () => ThemeVariantService(config: tryGetService()), + ); tryRegisterService(UdevService.new); tryRegisterService(UrlLauncher.new); From 71d37d75f1813862dca50950a2d5a8851fb9fd93 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Thu, 28 Dec 2023 14:48:06 +0100 Subject: [PATCH 024/106] refactor: Better readability in subiquity initialization --- packages/ubuntu_bootstrap/lib/installer.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/ubuntu_bootstrap/lib/installer.dart b/packages/ubuntu_bootstrap/lib/installer.dart index 824201a33..1696dfd8e 100644 --- a/packages/ubuntu_bootstrap/lib/installer.dart +++ b/packages/ubuntu_bootstrap/lib/installer.dart @@ -137,7 +137,7 @@ Future runInstallerApp( tryRegisterService(UdevService.new); tryRegisterService(UrlLauncher.new); - final initialized = getService().start(args: [ + final initializeSubiquity = getService().start(args: [ if (options['dry-run-config'] != null) '--dry-run-config=${options['dry-run-config']}', if (options['machine-config'] != null) @@ -147,7 +147,7 @@ Future runInstallerApp( '--storage-version=2', '--dry-run-config=examples/dry-run-configs/tpm.yaml', ...options.rest, - ]).then(_initInstallerApp); + ]); await runZonedGuarded(() async { FlutterError.onError = (error) { @@ -160,7 +160,9 @@ Future runInstallerApp( final themeVariantService = getService(); await themeVariantService.load(); final themeVariant = themeVariantService.themeVariant; - await initialized; + + final endpoint = await initializeSubiquity; + await _initInstallerApp(endpoint); runApp(ProviderScope( child: SlidesContext( From 84e94345bfa9a0e3afa35d2d353b8f7878f0378f Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Thu, 28 Dec 2023 17:10:38 +0100 Subject: [PATCH 025/106] fix: Add StorageWizard to steps --- packages/ubuntu_bootstrap/lib/installer.dart | 5 ++--- .../lib/installer/installation_step.dart | 7 +------ .../lib/installer/installer_wizard.dart | 3 +-- .../lib/pages/confirm/confirm_model.dart | 11 ++++++++--- .../lib/pages/confirm/confirm_page.dart | 8 +++++++- .../test/services/installer_service_test.dart | 2 +- packages/ubuntu_init/test/init_model_test.dart | 2 +- packages/ubuntu_init/test/init_model_test.mocks.dart | 12 +++++++++--- .../ubuntu_provision/lib/src/locale/locale_page.dart | 6 ------ .../lib/src/services/config_service.dart | 2 -- packages/ubuntu_provision/test/test_utils.mocks.dart | 12 +++++++++--- 11 files changed, 39 insertions(+), 31 deletions(-) diff --git a/packages/ubuntu_bootstrap/lib/installer.dart b/packages/ubuntu_bootstrap/lib/installer.dart index 1696dfd8e..b704473a7 100644 --- a/packages/ubuntu_bootstrap/lib/installer.dart +++ b/packages/ubuntu_bootstrap/lib/installer.dart @@ -137,7 +137,7 @@ Future runInstallerApp( tryRegisterService(UdevService.new); tryRegisterService(UrlLauncher.new); - final initializeSubiquity = getService().start(args: [ + final initialized = getService().start(args: [ if (options['dry-run-config'] != null) '--dry-run-config=${options['dry-run-config']}', if (options['machine-config'] != null) @@ -160,8 +160,7 @@ Future runInstallerApp( final themeVariantService = getService(); await themeVariantService.load(); final themeVariant = themeVariantService.themeVariant; - - final endpoint = await initializeSubiquity; + final endpoint = await initialized; await _initInstallerApp(endpoint); runApp(ProviderScope( diff --git a/packages/ubuntu_bootstrap/lib/installer/installation_step.dart b/packages/ubuntu_bootstrap/lib/installer/installation_step.dart index 17a639f3b..5c25e3f09 100644 --- a/packages/ubuntu_bootstrap/lib/installer/installation_step.dart +++ b/packages/ubuntu_bootstrap/lib/installer/installation_step.dart @@ -1,14 +1,9 @@ import 'package:dartx/dartx.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:subiquity_client/subiquity_client.dart'; -import 'package:ubuntu_bootstrap/installer/installer_model.dart'; import 'package:ubuntu_bootstrap/pages.dart'; -import 'package:ubuntu_bootstrap/routes.dart'; -import 'package:ubuntu_bootstrap/services.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; -import 'package:yaru_widgets/yaru_widgets.dart'; enum InstallationStep { locale(LocalePage.new), @@ -19,7 +14,7 @@ enum InstallationStep { refresh(RefreshPage.new), source(SourcePage.new), secureBoot(SecureBootPage.new), - storage(StoragePage.new), + storage(StorageWizard.new), confirm(ConfirmPage.new); const InstallationStep(this.pageFactory); diff --git a/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart b/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart index d2c18c8f7..99f510fa5 100644 --- a/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart +++ b/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart @@ -2,6 +2,7 @@ import 'package:dartx/dartx.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:subiquity_client/subiquity_client.dart'; +import 'package:ubuntu_bootstrap/installer/installation_step.dart'; import 'package:ubuntu_bootstrap/installer/installer_model.dart'; import 'package:ubuntu_bootstrap/pages.dart'; import 'package:ubuntu_bootstrap/routes.dart'; @@ -10,8 +11,6 @@ import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; -import 'installation_step.dart'; - class InstallerWizard extends ConsumerStatefulWidget { const InstallerWizard({ super.key, diff --git a/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_model.dart b/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_model.dart index 296e2abfb..50cfdbbdb 100644 --- a/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_model.dart +++ b/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_model.dart @@ -43,9 +43,14 @@ class ConfirmModel extends SafeChangeNotifier { await _storage.setGuidedStorage(); } await _storage.getStorage().then(_updateDisks); - _originals = await _storage.getOriginalStorage().then((disks) => - Map.fromEntries(disks.map((d) => MapEntry( - d.sysname, d.partitions.whereType().toList())))); + _originals = await _storage.getOriginalStorage().then( + (disks) => Map.fromEntries(disks.map( + (d) => MapEntry( + d.sysname, + d.partitions.whereType().toList(), + ), + )), + ); notifyListeners(); } diff --git a/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_page.dart b/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_page.dart index eb95f7289..4d72bd775 100644 --- a/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_page.dart @@ -16,6 +16,10 @@ class ConfirmPage extends ConsumerWidget with ProvisioningPage { return ref.read(confirmModelProvider).init().then((_) => true); } + static Future loadStatic(WidgetRef ref) { + return ref.read(confirmModelProvider).init().then((_) => true); + } + String prettyFormatDisk(Disk disk) { final fullName = [ disk.model, @@ -52,7 +56,9 @@ class ConfirmPage extends ConsumerWidget with ProvisioningPage { child: YaruBorderContainer( color: Theme.of(context).colorScheme.surface, padding: EdgeInsets.symmetric( - horizontal: kWizardPadding.left, vertical: 10), + horizontal: kWizardPadding.left, + vertical: 10, + ), child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/packages/ubuntu_bootstrap/test/services/installer_service_test.dart b/packages/ubuntu_bootstrap/test/services/installer_service_test.dart index a756d0fc5..12e605274 100644 --- a/packages/ubuntu_bootstrap/test/services/installer_service_test.dart +++ b/packages/ubuntu_bootstrap/test/services/installer_service_test.dart @@ -151,7 +151,7 @@ void main() { (_) => Stream.value(fakeApplicationStatus(ApplicationState.WAITING))); final pageConfig = MockPageConfigService(); - when(pageConfig.excludedPages).thenReturn({'c'}); + when(pageConfig.excludedPages).thenReturn(['c']); final service = InstallerService(client, pageConfig: pageConfig); await service.load(); diff --git a/packages/ubuntu_init/test/init_model_test.dart b/packages/ubuntu_init/test/init_model_test.dart index 8d45e9188..768f7e572 100644 --- a/packages/ubuntu_init/test/init_model_test.dart +++ b/packages/ubuntu_init/test/init_model_test.dart @@ -11,7 +11,7 @@ import 'init_model_test.mocks.dart'; void main() { test('configured page array', () async { final config = MockPageConfigService(); - when(config.excludedPages).thenReturn({'c'}); + when(config.excludedPages).thenReturn(['c']); final model = InitModel(pageConfig: config); await model.init(); diff --git a/packages/ubuntu_init/test/init_model_test.mocks.dart b/packages/ubuntu_init/test/init_model_test.mocks.dart index 3a4d680d5..f28d0b77b 100644 --- a/packages/ubuntu_init/test/init_model_test.mocks.dart +++ b/packages/ubuntu_init/test/init_model_test.mocks.dart @@ -79,10 +79,16 @@ class MockPageConfigService extends _i1.Mock implements _i3.PageConfigService { ) as Map); @override - Set get excludedPages => (super.noSuchMethod( + List get includedPages => (super.noSuchMethod( + Invocation.getter(#includedPages), + returnValue: [], + ) as List); + + @override + List get excludedPages => (super.noSuchMethod( Invocation.getter(#excludedPages), - returnValue: {}, - ) as Set); + returnValue: [], + ) as List); @override _i4.Future load() => (super.noSuchMethod( diff --git a/packages/ubuntu_provision/lib/src/locale/locale_page.dart b/packages/ubuntu_provision/lib/src/locale/locale_page.dart index 3f8864ad7..5a19be9db 100644 --- a/packages/ubuntu_provision/lib/src/locale/locale_page.dart +++ b/packages/ubuntu_provision/lib/src/locale/locale_page.dart @@ -1,10 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:ubuntu_provision/services.dart'; -import 'package:ubuntu_provision/src/locale/locale_l10n.dart'; -import 'package:ubuntu_provision/src/locale/locale_model.dart'; -import 'package:ubuntu_provision/src/providers/flavor.dart'; -import 'package:ubuntu_provision/src/providers/page_images.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_service/ubuntu_service.dart'; import 'package:ubuntu_widgets/ubuntu_widgets.dart'; @@ -26,7 +21,6 @@ class LocalePage extends ConsumerWidget with ProvisioningPage { final model = ref.watch(localeModelProvider); final lang = LocaleLocalizations.of(context); final pageImages = ref.watch(pageImagesProvider); - print(pageImages.images); return WizardPage( title: YaruWindowTitleBar( diff --git a/packages/ubuntu_provision/lib/src/services/config_service.dart b/packages/ubuntu_provision/lib/src/services/config_service.dart index 90aff1810..8a217fed1 100644 --- a/packages/ubuntu_provision/lib/src/services/config_service.dart +++ b/packages/ubuntu_provision/lib/src/services/config_service.dart @@ -1,11 +1,9 @@ import 'package:file/file.dart'; import 'package:file/local.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:ubuntu_logger/ubuntu_logger.dart'; -import 'package:ubuntu_service/ubuntu_service.dart'; import 'package:xdg_directories/xdg_directories.dart' as xdg; import 'package:yaml/yaml.dart'; diff --git a/packages/ubuntu_provision/test/test_utils.mocks.dart b/packages/ubuntu_provision/test/test_utils.mocks.dart index d398a2c50..6fcef25be 100644 --- a/packages/ubuntu_provision/test/test_utils.mocks.dart +++ b/packages/ubuntu_provision/test/test_utils.mocks.dart @@ -1403,10 +1403,16 @@ class MockPageConfigService extends _i1.Mock implements _i3.PageConfigService { ) as Map); @override - Set get excludedPages => (super.noSuchMethod( + List get includedPages => (super.noSuchMethod( + Invocation.getter(#includedPages), + returnValue: [], + ) as List); + + @override + List get excludedPages => (super.noSuchMethod( Invocation.getter(#excludedPages), - returnValue: {}, - ) as Set); + returnValue: [], + ) as List); @override _i12.Future load() => (super.noSuchMethod( From f96631902fe1de8a31c3a0c3b7826be1697cd6c4 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 29 Dec 2023 14:48:36 +0100 Subject: [PATCH 026/106] feat: Migrate ubuntu_init to use init_step and routes --- .../lib/installer/installer_wizard.dart | 2 +- packages/ubuntu_bootstrap/lib/routes.dart | 76 ++++++------- .../ubuntu_init/assets/ubuntu-provision.yml | 32 ++++++ .../integration_test/screenshot_test.dart | 19 ++-- packages/ubuntu_init/lib/src/init_step.dart | 40 +++++++ packages/ubuntu_init/lib/src/init_wizard.dart | 105 +++--------------- .../lib/src/privacy/privacy_page.dart | 6 +- packages/ubuntu_init/lib/src/routes.dart | 27 +++++ .../lib/src/telemetry/telemetry_page.dart | 5 +- .../lib/src/ubuntu_pro/ubuntu_pro_page.dart | 5 +- .../lib/src/welcome/welcome_page.dart | 6 +- .../ubuntu_init/test/init_wizard_test.dart | 9 +- .../lib/src/identity/identity_page.dart | 6 +- .../lib/src/interfaces/provisioning_page.dart | 5 +- .../lib/src/providers/page_images.dart | 2 +- .../lib/src/theme/theme_page.dart | 6 +- .../lib/src/timezone/timezone_page.dart | 6 +- 17 files changed, 200 insertions(+), 157 deletions(-) create mode 100644 packages/ubuntu_init/assets/ubuntu-provision.yml create mode 100644 packages/ubuntu_init/lib/src/init_step.dart create mode 100644 packages/ubuntu_init/lib/src/routes.dart diff --git a/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart b/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart index 99f510fa5..b11d21670 100644 --- a/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart +++ b/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart @@ -62,7 +62,7 @@ class _InstallWizard extends ConsumerWidget { final pageConfig = ref.watch(pageConfigProvider); final preInstallRoutes = { for (final pageName in pageConfig.includedPages) - routes[pageName]!: + Routes.routeMap[pageName]!: InstallationStep.fromName(pageName)!.toRoute(context, ref)! }; diff --git a/packages/ubuntu_bootstrap/lib/routes.dart b/packages/ubuntu_bootstrap/lib/routes.dart index 85bb80874..db04290b0 100644 --- a/packages/ubuntu_bootstrap/lib/routes.dart +++ b/packages/ubuntu_bootstrap/lib/routes.dart @@ -1,41 +1,41 @@ -const Map routes = { - 'initial': '/loading', - 'loading': '/loading', - 'locale': '/locale', - 'welcome': '/welcome', - 'rst': '/rst', - 'keyboard': '/keyboard', - 'network': '/network', - 'secureBoot': '/secure-boot', - 'source': '/source', - 'confirm': '/confirm', - 'theme': '/theme', - 'identity': '/identity', - 'install': '/install', - 'timezone': '/timezone', - 'notEnoughDiskSpace': '/not-enough-disk-space', - 'activeDirectory': '/active-directory', - 'storage': '/storage', - 'refresh': '/refresh', -}; +sealed class Routes { + static Map routeMap = { + 'initial': '/loading', + 'loading': '/loading', + 'locale': '/locale', + 'welcome': '/welcome', + 'rst': '/rst', + 'keyboard': '/keyboard', + 'network': '/network', + 'secureBoot': '/secure-boot', + 'source': '/source', + 'confirm': '/confirm', + 'theme': '/theme', + 'identity': '/identity', + 'install': '/install', + 'timezone': '/timezone', + 'notEnoughDiskSpace': '/not-enough-disk-space', + 'activeDirectory': '/active-directory', + 'storage': '/storage', + 'refresh': '/refresh', + }; -abstract class Routes { static String get initial => loading; - static String get loading => routes['loading']!; - static String get locale => routes['locale']!; - static String get welcome => routes['welcome']!; - static String get rst => routes['rst']!; - static String get keyboard => routes['keyboard']!; - static String get network => routes['network']!; - static String get secureBoot => routes['secure-boot']!; - static String get source => routes['source']!; - static String get confirm => routes['confirm']!; - static String get theme => routes['theme']!; - static String get identity => routes['identity']!; - static String get install => routes['install']!; - static String get timezone => routes['timezone']!; - static String get notEnoughDiskSpace => routes['not-enough-disk-space']!; - static String get activeDirectory => routes['active-directory']!; - static String get storage => routes['storage']!; - static String get refresh => routes['refresh']!; + static String get loading => routeMap['loading']!; + static String get locale => routeMap['locale']!; + static String get welcome => routeMap['welcome']!; + static String get rst => routeMap['rst']!; + static String get keyboard => routeMap['keyboard']!; + static String get network => routeMap['network']!; + static String get secureBoot => routeMap['secure-boot']!; + static String get source => routeMap['source']!; + static String get confirm => routeMap['confirm']!; + static String get theme => routeMap['theme']!; + static String get identity => routeMap['identity']!; + static String get install => routeMap['install']!; + static String get timezone => routeMap['timezone']!; + static String get notEnoughDiskSpace => routeMap['not-enough-disk-space']!; + static String get activeDirectory => routeMap['active-directory']!; + static String get storage => routeMap['storage']!; + static String get refresh => routeMap['refresh']!; } diff --git a/packages/ubuntu_init/assets/ubuntu-provision.yml b/packages/ubuntu_init/assets/ubuntu-provision.yml new file mode 100644 index 000000000..623bcb402 --- /dev/null +++ b/packages/ubuntu_init/assets/ubuntu-provision.yml @@ -0,0 +1,32 @@ +pages: + welcome: + title: "Welcome" + visible: true + locale: + image: "packages/ubuntu_provision/assets/mascot.png" + title: "Welcome to Ubuntu" + visible: true + keyboard: + title: "Keyboard layout" + visible: true + network: + title: "Connect to a network" + visible: true + identity: + title: "Set up your account" + visible: false + ubuntuPro: + title: "Ubuntu Pro" + visible: true + privacy: + title: "Location services" + visible: true + timezone: + title: "Select your timezone" + visible: true + telemetry: + title: "Telemetry" + visible: true + theme: + title: "Choose your theme" + visible: true diff --git a/packages/ubuntu_init/integration_test/screenshot_test.dart b/packages/ubuntu_init/integration_test/screenshot_test.dart index 11dd1a95e..ead3873ae 100644 --- a/packages/ubuntu_init/integration_test/screenshot_test.dart +++ b/packages/ubuntu_init/integration_test/screenshot_test.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; +import 'package:ubuntu_init/src/routes.dart'; import 'package:ubuntu_init/ubuntu_init.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_provision_test/ubuntu_provision_test.dart'; @@ -17,7 +18,7 @@ Future main() async { await tester.runApp(() => runInitApp([], theme: currentTheme)); await tester.pumpAndSettle(); - await tester.jumpToPage(InitRoutes.locale); + await tester.jumpToPage(Routes.locale); await tester.pumpAndSettle(); await tester.testLocalePage( @@ -29,7 +30,7 @@ Future main() async { await tester.runApp(() => runInitApp([], theme: currentTheme)); await tester.pumpAndSettle(); - await tester.jumpToPage(InitRoutes.keyboard); + await tester.jumpToPage(Routes.keyboard); await tester.pumpAndSettle(); await tester.testKeyboardPage( @@ -41,7 +42,7 @@ Future main() async { await tester.runApp(() => runInitApp([], theme: currentTheme)); await tester.pumpAndSettle(); - await tester.jumpToPage(InitRoutes.network); + await tester.jumpToPage(Routes.network); await tester.pumpAndSettle(); await tester.testNetworkPage( @@ -54,7 +55,7 @@ Future main() async { await tester.runApp(() => runInitApp([], theme: currentTheme)); await tester.pumpAndSettle(); - await tester.jumpToPage(InitRoutes.timezone); + await tester.jumpToPage(Routes.timezone); await tester.pumpAndSettle(); await tester.testTimezonePage( @@ -66,7 +67,7 @@ Future main() async { await tester.runApp(() => runInitApp([], theme: currentTheme)); await tester.pumpAndSettle(); - await tester.jumpToPage(InitRoutes.identity); + await tester.jumpToPage(Routes.identity); await tester.pumpAndSettle(); await tester.testIdentityPage( @@ -84,7 +85,7 @@ Future main() async { await tester.runApp(() => runInitApp([], theme: currentTheme)); await tester.pumpAndSettle(); - await tester.jumpToPage(InitRoutes.theme); + await tester.jumpToPage(Routes.theme); await tester.pumpAndSettle(); await tester.testThemePage( @@ -105,7 +106,7 @@ Future main() async { await tester.runApp(() => runInitApp([], theme: currentTheme)); await tester.pumpAndSettle(); - await tester.jumpToPage(InitRoutes.telemetry); + await tester.jumpToPage(Routes.telemetry); await tester.pumpAndSettle(); await tester.testTelemetryPage( @@ -117,7 +118,7 @@ Future main() async { await tester.runApp(() => runInitApp([], theme: currentTheme)); await tester.pumpAndSettle(); - await tester.jumpToPage(InitRoutes.privacy); + await tester.jumpToPage(Routes.privacy); await tester.pumpAndSettle(); await tester.testPrivacyPage( @@ -129,7 +130,7 @@ Future main() async { await tester.runApp(() => runInitApp([], theme: currentTheme)); await tester.pumpAndSettle(); - await tester.jumpToPage(InitRoutes.ubuntuPro); + await tester.jumpToPage(Routes.ubuntuPro); await tester.pumpAndSettle(); await tester.testUbuntuProPage( diff --git a/packages/ubuntu_init/lib/src/init_step.dart b/packages/ubuntu_init/lib/src/init_step.dart new file mode 100644 index 000000000..f19ac89b4 --- /dev/null +++ b/packages/ubuntu_init/lib/src/init_step.dart @@ -0,0 +1,40 @@ +import 'package:dartx/dartx.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:ubuntu_init/ubuntu_init.dart'; +import 'package:ubuntu_provision/ubuntu_provision.dart'; +import 'package:ubuntu_wizard/ubuntu_wizard.dart'; + +enum InitStep { + welcome(WelcomePage.new), + locale(LocalePage.new), + keyboard(KeyboardPage.new), + network(NetworkPage.new), + identity(IdentityPage.new), + ubuntuPro(UbuntuProPage.new), + privacy(PrivacyPage.new), + timezone(TimezonePage.new), + telemetry(TelemetryPage.new), + theme(ThemePage.new); // Special + + const InitStep(this.pageFactory); + + final ProvisioningPage Function() pageFactory; + + WizardRoute? toRoute(BuildContext context, WidgetRef ref) { + final pageConfig = ref.watch(pageConfigProvider); + final includedIndex = pageConfig.includedPages.indexOf(name); + final page = pageFactory(); + return WizardRoute( + builder: (_) => page, + userData: WizardRouteData(step: includedIndex), + onLoad: (_) => page.load(context, ref), + ); + } + + String get name => toString().split('.').last; + + static InitStep? fromName(String name) { + return InitStep.values.firstOrNullWhere((e) => e.name == name); + } +} diff --git a/packages/ubuntu_init/lib/src/init_wizard.dart b/packages/ubuntu_init/lib/src/init_wizard.dart index dcb2a7bce..bf362fad2 100644 --- a/packages/ubuntu_init/lib/src/init_wizard.dart +++ b/packages/ubuntu_init/lib/src/init_wizard.dart @@ -3,34 +3,12 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:ubuntu_init/src/init_model.dart'; -import 'package:ubuntu_init/src/init_pages.dart'; +import 'package:ubuntu_init/src/init_step.dart'; +import 'package:ubuntu_init/src/routes.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; -enum InitStep { - locale, - keyboard, - network, - identity, - timezone, - theme, -} - -class InitRoutes { - static const String initial = '/'; - static const String welcome = '/welcome'; - static const String locale = '/locale'; - static const String keyboard = '/keyboard'; - static const String network = '/network'; - static const String identity = '/identity'; - static const String ubuntuPro = '/ubuntuPro'; - static const String privacy = '/privacy'; - static const String timezone = '/timezone'; - static const String telemetry = '/telemetry'; - static const String theme = '/theme'; -} - class InitWizard extends ConsumerWidget { const InitWizard({ super.key, @@ -41,88 +19,39 @@ class InitWizard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - // TODO(Lukas): Create init_step.dart, like installation_step.dart + final pageConfig = ref.watch(pageConfigProvider); + final routes = { + for (final pageName in pageConfig.includedPages) + Routes.routeMap[pageName]!: + InitStep.fromName(pageName)!.toRoute(context, ref)! + }; + return WizardBuilder( routes: { - // TODO: loading screen? - InitRoutes.initial: WizardRoute( + Routes.initial: WizardRoute( builder: (_) => const SizedBox.shrink(), onReplace: (_) => ref.read(initModelProvider).init().then((_) => null), ), - InitRoutes.welcome: WizardRoute( - builder: (_) => const WelcomePage(), - onLoad: (_) => WelcomePage.load(ref), - ), - InitRoutes.locale: WizardRoute( - builder: (_) => const LocalePage(), - userData: WizardRouteData( - step: InitStep.locale.index, - ), - onLoad: (_) => const LocalePage().load(context, ref), - ), - InitRoutes.keyboard: WizardRoute( - builder: (_) => const KeyboardPage(), - userData: WizardRouteData( - step: InitStep.keyboard.index, - ), - onLoad: (_) => const KeyboardPage().load(context, ref), - ), - InitRoutes.network: WizardRoute( - builder: (_) => const NetworkPage(), - userData: WizardRouteData( - step: InitStep.network.index, - ), - onLoad: (_) => const NetworkPage().load(context, ref), - ), - InitRoutes.identity: WizardRoute( - builder: (_) => const IdentityPage(), - userData: WizardRouteData( - step: InitStep.identity.index, - ), - onLoad: (_) => IdentityPage.load(ref), - ), - InitRoutes.ubuntuPro: WizardRoute( - builder: (_) => const UbuntuProPage(), - userData: WizardRouteData( - step: InitStep.identity.index, - ), - onLoad: (_) => UbuntuProPage.load(ref), - ), - InitRoutes.privacy: WizardRoute( - builder: (_) => const PrivacyPage(), - onLoad: (_) => PrivacyPage.load(ref), - ), - InitRoutes.timezone: WizardRoute( - builder: (_) => const TimezonePage(), - userData: WizardRouteData( - step: InitStep.timezone.index, - ), - onLoad: (_) => TimezonePage.load(context, ref), - ), - InitRoutes.telemetry: WizardRoute( - builder: (_) => const TelemetryPage(), - onLoad: (_) => TelemetryPage.load(ref), - ), - InitRoutes.theme: WizardRoute( + ...routes, + Routes.theme: WizardRoute( builder: (_) => const ThemePage(), userData: WizardRouteData( step: InitStep.theme.index, ), - onLoad: (_) => ThemePage.load(ref), + onLoad: (_) => const ThemePage().load(context, ref), onNext: (_) async { final window = YaruWindow.of(context); await _onDone?.call(); await window.close(); - return InitRoutes.initial; + return Routes.initial; }, ), }, userData: WizardData(totalSteps: InitStep.values.length), - predicate: (route) => switch (route) { - InitRoutes.initial => true, - _ => ref.read(initModelProvider).hasRoute(route), - }, + predicate: (route) => route == Routes.initial + ? true + : ref.read(initModelProvider).hasRoute(route), ); } } diff --git a/packages/ubuntu_init/lib/src/privacy/privacy_page.dart b/packages/ubuntu_init/lib/src/privacy/privacy_page.dart index 40cf84183..a07c96994 100644 --- a/packages/ubuntu_init/lib/src/privacy/privacy_page.dart +++ b/packages/ubuntu_init/lib/src/privacy/privacy_page.dart @@ -4,15 +4,17 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:ubuntu_init/src/privacy/privacy_l10n.dart'; import 'package:ubuntu_init/src/privacy/privacy_model.dart'; +import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_utils/ubuntu_utils.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru/yaru.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; -class PrivacyPage extends ConsumerWidget { +class PrivacyPage extends ConsumerWidget with ProvisioningPage { const PrivacyPage({super.key}); - static Future load(WidgetRef ref) { + @override + Future load(BuildContext context, WidgetRef ref) { return ref.read(privacyModelProvider).init(); } diff --git a/packages/ubuntu_init/lib/src/routes.dart b/packages/ubuntu_init/lib/src/routes.dart new file mode 100644 index 000000000..fe2477738 --- /dev/null +++ b/packages/ubuntu_init/lib/src/routes.dart @@ -0,0 +1,27 @@ +sealed class Routes { + static Map routeMap = { + 'initial': '/', + 'welcome': '/welcome', + 'locale': '/locale', + 'keyboard': '/keyboard', + 'network': '/network', + 'identity': '/identity', + 'ubuntuPro': '/ubuntuPro', + 'privacy': '/privacy', + 'timezone': '/timezone', + 'telemetry': '/telemetry', + 'theme': '/theme', + }; + + static String get initial => routeMap['initial']!; + static String get welcome => routeMap['welcome']!; + static String get locale => routeMap['locale']!; + static String get keyboard => routeMap['keyboard']!; + static String get network => routeMap['network']!; + static String get identity => routeMap['identity']!; + static String get ubuntuPro => routeMap['ubuntuPro']!; + static String get privacy => routeMap['privacy']!; + static String get timezone => routeMap['timezone']!; + static String get telemetry => routeMap['telemetry']!; + static String get theme => routeMap['theme']!; +} diff --git a/packages/ubuntu_init/lib/src/telemetry/telemetry_page.dart b/packages/ubuntu_init/lib/src/telemetry/telemetry_page.dart index 86d125352..b324a1196 100644 --- a/packages/ubuntu_init/lib/src/telemetry/telemetry_page.dart +++ b/packages/ubuntu_init/lib/src/telemetry/telemetry_page.dart @@ -12,10 +12,11 @@ import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru/yaru.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; -class TelemetryPage extends ConsumerWidget { +class TelemetryPage extends ConsumerWidget with ProvisioningPage { const TelemetryPage({super.key}); - static Future load(WidgetRef ref) { + @override + Future load(BuildContext context, WidgetRef ref) { return ref.read(telemetryModelProvider).init(); } diff --git a/packages/ubuntu_init/lib/src/ubuntu_pro/ubuntu_pro_page.dart b/packages/ubuntu_init/lib/src/ubuntu_pro/ubuntu_pro_page.dart index 42ad654e7..eaa65e4a3 100644 --- a/packages/ubuntu_init/lib/src/ubuntu_pro/ubuntu_pro_page.dart +++ b/packages/ubuntu_init/lib/src/ubuntu_pro/ubuntu_pro_page.dart @@ -1,14 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:ubuntu_init/src/ubuntu_pro/ubuntu_pro_l10n.dart'; +import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; -class UbuntuProPage extends ConsumerWidget { +class UbuntuProPage extends ConsumerWidget with ProvisioningPage { const UbuntuProPage({super.key}); - static Future load(WidgetRef ref) async => true; - @override Widget build(BuildContext context, WidgetRef ref) { final l10n = UbuntuProLocalizations.of(context); diff --git a/packages/ubuntu_init/lib/src/welcome/welcome_page.dart b/packages/ubuntu_init/lib/src/welcome/welcome_page.dart index b7515f228..b0fb19158 100644 --- a/packages/ubuntu_init/lib/src/welcome/welcome_page.dart +++ b/packages/ubuntu_init/lib/src/welcome/welcome_page.dart @@ -4,16 +4,18 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:ubuntu_init/src/welcome/welcome_l10n.dart'; import 'package:ubuntu_init/src/welcome/welcome_model.dart'; +import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_utils/ubuntu_utils.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru/yaru.dart'; import 'package:yaru_icons/yaru_icons.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; -class WelcomePage extends ConsumerWidget { +class WelcomePage extends ConsumerWidget with ProvisioningPage { const WelcomePage({super.key}); - static Future load(WidgetRef ref) async => true; + @override + Future load(BuildContext context, WidgetRef ref) async => true; @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/packages/ubuntu_init/test/init_wizard_test.dart b/packages/ubuntu_init/test/init_wizard_test.dart index 2a161b052..5a4d5d1bb 100644 --- a/packages/ubuntu_init/test/init_wizard_test.dart +++ b/packages/ubuntu_init/test/init_wizard_test.dart @@ -7,6 +7,7 @@ import 'package:ubuntu_init/l10n.dart'; import 'package:ubuntu_init/src/init_model.dart'; import 'package:ubuntu_init/src/init_pages.dart'; import 'package:ubuntu_init/src/init_wizard.dart'; +import 'package:ubuntu_init/src/routes.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_test/ubuntu_test.dart'; import 'package:ubuntu_utils/ubuntu_utils.dart'; @@ -125,10 +126,10 @@ void main() { testWidgets('pages', (tester) async { final initModel = buildInitModel(pages: [ - InitRoutes.locale, - InitRoutes.keyboard, - InitRoutes.identity, - InitRoutes.theme, + Routes.locale, + Routes.keyboard, + Routes.identity, + Routes.theme, ]); final localeModel = buildLocaleModel(); final keyboardModel = buildKeyboardModel(); diff --git a/packages/ubuntu_provision/lib/src/identity/identity_page.dart b/packages/ubuntu_provision/lib/src/identity/identity_page.dart index 2baa3da7d..51e4db947 100644 --- a/packages/ubuntu_provision/lib/src/identity/identity_page.dart +++ b/packages/ubuntu_provision/lib/src/identity/identity_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_provision/src/identity/identity_l10n.dart'; import 'package:ubuntu_provision/src/identity/identity_model.dart'; import 'package:ubuntu_provision/src/identity/identity_widgets.dart'; @@ -12,11 +13,12 @@ import 'package:yaru_widgets/yaru_widgets.dart'; /// The installer page for setting up the user data. /// /// It uses [WizardPage] and `WizardAction` to create an installer page. -class IdentityPage extends ConsumerWidget { +class IdentityPage extends ConsumerWidget with ProvisioningPage { /// Creates a the installer page for setting up the user data. const IdentityPage({super.key}); - static Future load(WidgetRef ref) { + @override + Future load(BuildContext context, WidgetRef ref) { return ref.read(identityModelProvider).init().then((_) => true); } diff --git a/packages/ubuntu_provision/lib/src/interfaces/provisioning_page.dart b/packages/ubuntu_provision/lib/src/interfaces/provisioning_page.dart index eab1966d4..648d62b02 100644 --- a/packages/ubuntu_provision/lib/src/interfaces/provisioning_page.dart +++ b/packages/ubuntu_provision/lib/src/interfaces/provisioning_page.dart @@ -3,5 +3,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; mixin ProvisioningPage on Widget { /// Performs any pre-loading that needs to be done for the page. - Future load(BuildContext context, WidgetRef ref); + /// + /// If it returns true it will be added to the wizard, otherwise it will be + /// skipped. + Future load(BuildContext context, WidgetRef ref) => Future.value(true); } diff --git a/packages/ubuntu_provision/lib/src/providers/page_images.dart b/packages/ubuntu_provision/lib/src/providers/page_images.dart index ee9e83e19..9bf8364cc 100644 --- a/packages/ubuntu_provision/lib/src/providers/page_images.dart +++ b/packages/ubuntu_provision/lib/src/providers/page_images.dart @@ -26,7 +26,7 @@ class PageImages { ColorMapper? colorMapper, }) svgFileLoader = SvgFileLoader.new; - //@visibleForTesting + @visibleForTesting final Map images = {}; Widget get(String pageName) => diff --git a/packages/ubuntu_provision/lib/src/theme/theme_page.dart b/packages/ubuntu_provision/lib/src/theme/theme_page.dart index 58d067e50..c8368abb8 100644 --- a/packages/ubuntu_provision/lib/src/theme/theme_page.dart +++ b/packages/ubuntu_provision/lib/src/theme/theme_page.dart @@ -1,15 +1,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_provision/src/theme/theme_l10n.dart'; import 'package:ubuntu_provision/src/theme/theme_model.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru/yaru.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; -class ThemePage extends ConsumerWidget { +class ThemePage extends ConsumerWidget with ProvisioningPage { const ThemePage({super.key}); - static Future load(WidgetRef ref) { + @override + Future load(BuildContext context, WidgetRef ref) { return ref.read(themeModelProvider).init(); } diff --git a/packages/ubuntu_provision/lib/src/timezone/timezone_page.dart b/packages/ubuntu_provision/lib/src/timezone/timezone_page.dart index 69e1d7d10..933e10b16 100644 --- a/packages/ubuntu_provision/lib/src/timezone/timezone_page.dart +++ b/packages/ubuntu_provision/lib/src/timezone/timezone_page.dart @@ -2,16 +2,18 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:timezone_map/timezone_map.dart'; +import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_provision/src/timezone/timezone_l10n.dart'; import 'package:ubuntu_provision/src/timezone/timezone_model.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; /// https://github.com/canonical/ubuntu-desktop-installer/issues/38 -class TimezonePage extends ConsumerWidget { +class TimezonePage extends ConsumerWidget with ProvisioningPage { const TimezonePage({super.key}); - static Future load(BuildContext context, WidgetRef ref) { + @override + Future load(BuildContext context, WidgetRef ref) { return Future.wait([ TimezoneMap.precacheAssets(context), ref.read(timezoneModelProvider).init(), From 045305278e56943ce40d4ee98678db717c76b77d Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 2 Jan 2024 13:56:39 +0100 Subject: [PATCH 027/106] fix: toWizardRoute doesn't need to be nullable --- packages/ubuntu_bootstrap/lib/installer/installation_step.dart | 2 +- packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart | 2 +- packages/ubuntu_bootstrap/test/test_utils.dart | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/ubuntu_bootstrap/lib/installer/installation_step.dart b/packages/ubuntu_bootstrap/lib/installer/installation_step.dart index 5c25e3f09..8df2b1b7f 100644 --- a/packages/ubuntu_bootstrap/lib/installer/installation_step.dart +++ b/packages/ubuntu_bootstrap/lib/installer/installation_step.dart @@ -21,7 +21,7 @@ enum InstallationStep { final ProvisioningPage Function() pageFactory; - WizardRoute? toRoute(BuildContext context, WidgetRef ref) { + WizardRoute toRoute(BuildContext context, WidgetRef ref) { final pageConfig = ref.watch(pageConfigProvider); final includedIndex = pageConfig.includedPages.indexOf(name); final page = pageFactory(); diff --git a/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart b/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart index b11d21670..9ae970b08 100644 --- a/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart +++ b/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart @@ -63,7 +63,7 @@ class _InstallWizard extends ConsumerWidget { final preInstallRoutes = { for (final pageName in pageConfig.includedPages) Routes.routeMap[pageName]!: - InstallationStep.fromName(pageName)!.toRoute(context, ref)! + InstallationStep.fromName(pageName)!.toRoute(context, ref) }; return WizardBuilder( diff --git a/packages/ubuntu_bootstrap/test/test_utils.dart b/packages/ubuntu_bootstrap/test/test_utils.dart index ba53fe188..691fbb278 100644 --- a/packages/ubuntu_bootstrap/test/test_utils.dart +++ b/packages/ubuntu_bootstrap/test/test_utils.dart @@ -61,7 +61,7 @@ void setupMockPageConfig({Map pages = const {}}) { final pageConfigService = MockPageConfigService(); registerMockService(pageConfigService); when(pageConfigService.pages).thenReturn(pages); - when(pageConfigService.excludedPages).thenReturn({}); + when(pageConfigService.excludedPages).thenReturn([]); } class MockBuildContext extends Mock implements BuildContext {} From 0570f7b0ac8cc1ccf09c83fca748dceac9382d83 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 2 Jan 2024 14:01:56 +0100 Subject: [PATCH 028/106] fix: Remove unused static loaded method --- packages/ubuntu_bootstrap/lib/pages/confirm/confirm_page.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_page.dart b/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_page.dart index 4d72bd775..ca8dc99fe 100644 --- a/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/confirm/confirm_page.dart @@ -16,10 +16,6 @@ class ConfirmPage extends ConsumerWidget with ProvisioningPage { return ref.read(confirmModelProvider).init().then((_) => true); } - static Future loadStatic(WidgetRef ref) { - return ref.read(confirmModelProvider).init().then((_) => true); - } - String prettyFormatDisk(Disk disk) { final fullName = [ disk.model, From a08ed78820b9886269107d66b512d18e9e3c2e2b Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 2 Jan 2024 14:05:13 +0100 Subject: [PATCH 029/106] Remove unused import --- packages/ubuntu_provision/test/test_utils.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ubuntu_provision/test/test_utils.dart b/packages/ubuntu_provision/test/test_utils.dart index 16bf368c9..a36e53185 100644 --- a/packages/ubuntu_provision/test/test_utils.dart +++ b/packages/ubuntu_provision/test/test_utils.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; -import 'package:mockito/mockito.dart'; import 'package:timezone_map/timezone_map.dart'; import 'package:ubuntu_localizations/ubuntu_localizations.dart'; import 'package:ubuntu_provision/l10n.dart'; From 26f08d961a57b1765d21352bcc5819657218c4d5 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 2 Jan 2024 14:18:41 +0100 Subject: [PATCH 030/106] Update lock-file --- pubspec.lock | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 9ea759537..5a8d1a5b1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -81,14 +81,6 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.4" - flutter_lints: - dependency: transitive - description: - name: flutter_lints - sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 - url: "https://pub.dev" - source: hosted - version: "3.0.1" glob: dependency: transitive description: @@ -137,14 +129,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.8.1" - lints: - dependency: transitive - description: - name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 - url: "https://pub.dev" - source: hosted - version: "3.0.0" matcher: dependency: transitive description: @@ -305,14 +289,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" - ubuntu_lints: - dependency: "direct dev" - description: - name: ubuntu_lints - sha256: ea1aa7d2d4cd110aa449448db0a4241e08dd11ece1226fb012db1fa53421b7e1 - url: "https://pub.dev" - source: hosted - version: "0.1.0" uri: dependency: transitive description: From 129c6a926685e32439a732c1fd55973609956cb5 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 2 Jan 2024 14:26:49 +0100 Subject: [PATCH 031/106] Re-add super types to service registration --- packages/ubuntu_bootstrap/lib/installer.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/ubuntu_bootstrap/lib/installer.dart b/packages/ubuntu_bootstrap/lib/installer.dart index b704473a7..fedb0f620 100644 --- a/packages/ubuntu_bootstrap/lib/installer.dart +++ b/packages/ubuntu_bootstrap/lib/installer.dart @@ -96,13 +96,13 @@ Future runInstallerApp( ), ); tryRegisterService(JournalService.new); - tryRegisterService( + tryRegisterService( () => SubiquityKeyboardService(getService()), ); - tryRegisterService( + tryRegisterService( () => SubiquityLocaleService(getService()), ); - tryRegisterService( + tryRegisterService( () => SubiquityNetworkService(getService()), ); tryRegisterService( @@ -112,7 +112,7 @@ Future runInstallerApp( tryRegisterService(PowerService.new); tryRegisterService(ProductService.new); tryRegisterService(() => RefreshService(getService())); - tryRegisterService( + tryRegisterService( () => SubiquitySessionService(getService()), ); if (liveRun) tryRegisterService(SoundService.new); From 54088af32c5c68d7a2096adcbe67b2cc2185fc6c Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 2 Jan 2024 15:28:38 +0100 Subject: [PATCH 032/106] Add ubuntu-provision.yml files --- .../assets/ubuntu-provision.yml | 33 +++++++++++++++++++ .../ubuntu_init/assets/ubuntu-provision.yml | 32 ++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 packages/ubuntu_bootstrap/assets/ubuntu-provision.yml create mode 100644 packages/ubuntu_init/assets/ubuntu-provision.yml diff --git a/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml b/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml new file mode 100644 index 000000000..3451bd697 --- /dev/null +++ b/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml @@ -0,0 +1,33 @@ +pages: + locale: + image: "packages/ubuntu_provision/assets/mascot.png" + title: "Welcome" + visible: true + welcome: + title: "Welcome to Ubuntu" + visible: true + rst: + image: "assets/storage/rst.svg" + title: "Rapid Storage Technology" + visible: true + keyboard: + title: "Keyboard layout" + visible: true + network: + title: "Connect to a network" + visible: true + refresh: + title: "Update available" + visible: true + source: + title: "Type of installation" + visible: true + secureBoot: + title: "Configure Secure Boot" + visible: true + storage: + title: "Ready to install" + visible: true + confirm: + title: "Ready to install" + visible: true diff --git a/packages/ubuntu_init/assets/ubuntu-provision.yml b/packages/ubuntu_init/assets/ubuntu-provision.yml new file mode 100644 index 000000000..a297bdbf4 --- /dev/null +++ b/packages/ubuntu_init/assets/ubuntu-provision.yml @@ -0,0 +1,32 @@ +pages: + welcome: + title: "Welcome" + visible: true + locale: + title: "Welcome to Ubuntu" + image: "packages/ubuntu_provision/assets/mascot.png" + visible: true + keyboard: + title: "Keyboard layout" + visible: true + network: + title: "Connect to a network" + visible: true + identity: + title: "Set up your account" + visible: false + ubuntuPro: + title: "Ubuntu Pro" + visible: true + privacy: + title: "Location services" + visible: true + timezone: + title: "Select your timezone" + visible: true + telemetry: + title: "Telemetry" + visible: true + theme: + title: "Choose your theme" + visible: true From d84a2e5d8f4c1d5c0e7fcdda61d38cc967dbda0f Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 2 Jan 2024 16:06:01 +0100 Subject: [PATCH 033/106] fix: Set path variable when its an asset file too --- .../lib/src/services/config_service.dart | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/ubuntu_provision/lib/src/services/config_service.dart b/packages/ubuntu_provision/lib/src/services/config_service.dart index 860ebf9ba..967137312 100644 --- a/packages/ubuntu_provision/lib/src/services/config_service.dart +++ b/packages/ubuntu_provision/lib/src/services/config_service.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_catches_without_on_clauses + import 'package:file/file.dart'; import 'package:file/local.dart'; import 'package:flutter/services.dart'; @@ -38,17 +40,19 @@ class ConfigService { /// assets. If no config file is found, it will return an empty map. @visibleForTesting Future?> load() async { - final file = _fs.file(_path ?? ''); + var path = _path ?? ''; + final file = _fs.file(path); String? assetData; if (!file.existsSync()) { for (final ext in _extensions) { try { - assetData = await rootBundle.loadString('assets/$_filename.$ext'); + path = 'assets/$_filename.$ext'; + assetData = await rootBundle.loadString(path); + break; // Since there isn't any `exists` method for assets we'll just try to // load the file and catch the exception if it doesn't exist and // continue. If no file is found, then we'll return an empty map // and log an error. - // ignore: avoid_catches_without_on_clauses } catch (_) { continue; } @@ -61,15 +65,11 @@ class ConfigService { try { final data = assetData ?? await file.readAsString(); - final config = switch (p.extension(_path!)) { - '.yml' || '.yaml' => loadYaml(data), - _ => throw UnsupportedError( - 'Only supports yaml/yml files, so $_path is not supported.'), - }; - _log.debug('loaded $_path'); + final config = loadYaml(data); + _log.debug('Loaded config file from $path'); return (config as Map).cast(); - } on FileSystemException catch (e) { - _log.error('failed to load $_path', e); + } catch (e) { + _log.error('Failed to load file from $path', e); return null; } } From 018c5dd951c63a8a3c3a2789c3b29b28f3cb94af Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 2 Jan 2024 16:23:10 +0100 Subject: [PATCH 034/106] Return null if no config file is found --- .../lib/src/services/config_service.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/ubuntu_provision/lib/src/services/config_service.dart b/packages/ubuntu_provision/lib/src/services/config_service.dart index 967137312..b3a752f53 100644 --- a/packages/ubuntu_provision/lib/src/services/config_service.dart +++ b/packages/ubuntu_provision/lib/src/services/config_service.dart @@ -40,10 +40,10 @@ class ConfigService { /// assets. If no config file is found, it will return an empty map. @visibleForTesting Future?> load() async { - var path = _path ?? ''; - final file = _fs.file(path); + var path = _path; + final file = _path != null ? _fs.file(_path) : null; String? assetData; - if (!file.existsSync()) { + if (file == null || !file.existsSync()) { for (final ext in _extensions) { try { path = 'assets/$_filename.$ext'; @@ -59,12 +59,12 @@ class ConfigService { } if (assetData == null) { _log.error('No config file found on the filesystem or in assets.'); - return {}; + return null; } } try { - final data = assetData ?? await file.readAsString(); + final data = assetData ?? await file!.readAsString(); final config = loadYaml(data); _log.debug('Loaded config file from $path'); return (config as Map).cast(); From 76d912016e1d5d12a5abec541967c9b3fd267913 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 2 Jan 2024 16:37:07 +0100 Subject: [PATCH 035/106] Identity page should be visible --- packages/ubuntu_init/assets/ubuntu-provision.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ubuntu_init/assets/ubuntu-provision.yml b/packages/ubuntu_init/assets/ubuntu-provision.yml index a297bdbf4..005b505c0 100644 --- a/packages/ubuntu_init/assets/ubuntu-provision.yml +++ b/packages/ubuntu_init/assets/ubuntu-provision.yml @@ -14,7 +14,7 @@ pages: visible: true identity: title: "Set up your account" - visible: false + visible: true ubuntuPro: title: "Ubuntu Pro" visible: true From 1f57b983bca972abea994e11a3758d3f3e1fca10 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 2 Jan 2024 16:44:51 +0100 Subject: [PATCH 036/106] Use same titles as previously --- packages/ubuntu_bootstrap/assets/ubuntu-provision.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml b/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml index 3451bd697..2fb04444c 100644 --- a/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml +++ b/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml @@ -8,7 +8,7 @@ pages: visible: true rst: image: "assets/storage/rst.svg" - title: "Rapid Storage Technology" + title: "RST is enabled" visible: true keyboard: title: "Keyboard layout" @@ -20,13 +20,13 @@ pages: title: "Update available" visible: true source: - title: "Type of installation" + title: "Applications and updates" visible: true secureBoot: title: "Configure Secure Boot" visible: true storage: - title: "Ready to install" + title: "Type of installation" visible: true confirm: title: "Ready to install" From e66258988f85e32bd1c75c0e09bf46f24407b124 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Wed, 3 Jan 2024 09:52:28 +0100 Subject: [PATCH 037/106] test: Test for loading asset file by config service --- packages/ubuntu_provision/assets/ubuntu-provision.yml | 6 ++++++ .../lib/src/services/config_service.dart | 3 ++- .../test/services/config_service_test.dart | 11 +++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 packages/ubuntu_provision/assets/ubuntu-provision.yml diff --git a/packages/ubuntu_provision/assets/ubuntu-provision.yml b/packages/ubuntu_provision/assets/ubuntu-provision.yml new file mode 100644 index 000000000..7a43a774e --- /dev/null +++ b/packages/ubuntu_provision/assets/ubuntu-provision.yml @@ -0,0 +1,6 @@ +# This file is only to be used for tests +scope: + test: + title: "Welcome" + image: "mascot.png" + visible: true \ No newline at end of file diff --git a/packages/ubuntu_provision/lib/src/services/config_service.dart b/packages/ubuntu_provision/lib/src/services/config_service.dart index b3a752f53..0a04fab79 100644 --- a/packages/ubuntu_provision/lib/src/services/config_service.dart +++ b/packages/ubuntu_provision/lib/src/services/config_service.dart @@ -24,6 +24,7 @@ class ConfigService { final String? _scope; final FileSystem _fs; Map? _config; + static const _extensions = ['yaml', 'yml']; static const _filename = 'ubuntu-provision'; @@ -37,7 +38,7 @@ class ConfigService { /// Loads the config file, if none are found on the filesystem by /// [lookupPath], then it will try to load the default config file from the - /// assets. If no config file is found, it will return an empty map. + /// assets. If no config file is found, it will return null. @visibleForTesting Future?> load() async { var path = _path; diff --git a/packages/ubuntu_provision/test/services/config_service_test.dart b/packages/ubuntu_provision/test/services/config_service_test.dart index 89e20947c..7065fc8d3 100644 --- a/packages/ubuntu_provision/test/services/config_service_test.dart +++ b/packages/ubuntu_provision/test/services/config_service_test.dart @@ -1,6 +1,7 @@ import 'package:file/memory.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:ubuntu_provision/src/services/config_service.dart'; +import 'package:yaml/yaml.dart'; void main() { test('lookup path', () { @@ -69,4 +70,14 @@ test2: expect(await config.get('foo', scope: 'test2'), 'bar'); } }); + + test('load from assets', () async { + TestWidgetsFlutterBinding.ensureInitialized(); + final configService = ConfigService(); + final result = await configService.get('test', scope: 'scope'); + expect(result, isNotNull); + expect(result!['title'], 'Welcome'); + expect(result['image'], 'mascot.png'); + expect(result['visible'], isTrue); + }); } From 68050bd77c728b74f755f19eb1f34fd93c3d7a3e Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Wed, 3 Jan 2024 11:00:38 +0100 Subject: [PATCH 038/106] Split up loading of config file --- .../lib/src/services/config_service.dart | 54 ++++++++----------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/packages/ubuntu_provision/lib/src/services/config_service.dart b/packages/ubuntu_provision/lib/src/services/config_service.dart index 424ed42c7..a2d018cb3 100644 --- a/packages/ubuntu_provision/lib/src/services/config_service.dart +++ b/packages/ubuntu_provision/lib/src/services/config_service.dart @@ -41,28 +41,19 @@ class ConfigService { /// assets. If no config file is found, it will return null. @visibleForTesting Future?> load() async { - var path = _path; - final file = _path != null ? _fs.file(_path) : null; - String? assetData; - if (file == null || !file.existsSync()) { - for (final ext in _extensions) { - try { - path = 'assets/$_filename.$ext'; - assetData = await rootBundle.loadString(path); - break; - // Since there isn't any `exists` method for assets we'll just try to - // load the file and catch the exception if it doesn't exist and - // continue. If no file is found, then we'll return an empty map - // and log an error. - } catch (_) { - continue; - } - } - if (assetData == null) { - _log.error('No config file found on the filesystem or in assets.'); - return null; - } + final path = _path; + final file = path != null ? _fs.file(path) : null; + final Map? data; + if (path == null || file == null || !file.existsSync()) { + data = await _loadFromAssets(); + } else { + data = await _loadFromFilesystem(path, file); } + + if (data == null) { + _log.error('No config file found on path or in assets.'); + } + return data; } Future?> _loadFromFilesystem( @@ -70,7 +61,7 @@ class ConfigService { File file, ) async { try { - final data = assetData ?? await file!.readAsString(); + final data = await file.readAsString(); final config = loadYaml(data); _log.debug('Loaded config file from $path'); return (config as Map).cast(); @@ -81,27 +72,26 @@ class ConfigService { } Future?> _loadFromAssets() async { - String? data; + String? path; + String? assetData; for (final ext in _extensions) { try { - final assetsPath = 'assets/$_filename.$ext'; - data = await rootBundle.loadString(assetsPath); + path = 'assets/$_filename.$ext'; + assetData = await rootBundle.loadString(path); break; // Since there isn't any `exists` method for assets we'll just try to // load the file and catch the exception if it doesn't exist and - // continue. If no file is found, then we'll return null and log an - // error. - // ignore: avoid_catches_without_on_clauses + // continue. If no file is found, then we'll return an empty map + // and log an error. } catch (_) { continue; } } - if (data == null) { - // TODO(Lukas): Should we throw an exception here instead? - _log.error('No config file found on the filesystem or in assets.'); + if (assetData == null) { + _log.error('No config file found in assets.'); return null; } - return (loadYaml(data) as Map).cast(); + return (loadYaml(assetData) as Map).cast(); } /// Looks up the config file path in the following order: From df44107cebbd1f140ce3d74ed274803cee05cfc1 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Wed, 3 Jan 2024 13:56:09 +0100 Subject: [PATCH 039/106] Register SessionService mock --- packages/ubuntu_bootstrap/lib/installer.dart | 14 ++++++------ .../ubuntu_bootstrap/test/installer_test.dart | 10 +++++++-- .../test/services/storage_service_test.dart | 22 ++++++++++--------- .../ubuntu_bootstrap/test/test_utils.dart | 1 + 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/packages/ubuntu_bootstrap/lib/installer.dart b/packages/ubuntu_bootstrap/lib/installer.dart index fedb0f620..f781345ec 100644 --- a/packages/ubuntu_bootstrap/lib/installer.dart +++ b/packages/ubuntu_bootstrap/lib/installer.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:io'; +import 'package:args/args.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -83,13 +84,12 @@ Future runInstallerApp( final baseName = p.basename(Platform.resolvedExecutable); // conditional registration if not already registered by flavors or tests - tryRegisterServiceInstance(options); - tryRegisterService( - () => ConfigService(path: options['config'] as String?), - ); + tryRegisterServiceInstance(options); + tryRegisterService( + () => ConfigService(path: options['config'] as String?)); tryRegisterService(GnomeService.new); tryRegisterServiceFactory(GSettings.new); - tryRegisterService( + tryRegisterService( () => InstallerService( getService(), pageConfig: tryGetService(), @@ -121,7 +121,7 @@ Future runInstallerApp( tryRegisterService( () => SubiquityServer(process: process, endpoint: endpoint), ); - tryRegisterService(() { + tryRegisterService(() { final String path; if (kDebugMode) { final exe = Platform.resolvedExecutable; @@ -131,7 +131,7 @@ Future runInstallerApp( } return TelemetryService(path); }); - tryRegisterService( + tryRegisterService( () => ThemeVariantService(config: tryGetService()), ); tryRegisterService(UdevService.new); diff --git a/packages/ubuntu_bootstrap/test/installer_test.dart b/packages/ubuntu_bootstrap/test/installer_test.dart index 71d4679b0..b82617b25 100644 --- a/packages/ubuntu_bootstrap/test/installer_test.dart +++ b/packages/ubuntu_bootstrap/test/installer_test.dart @@ -127,14 +127,20 @@ extension on WidgetTester { final locale = MockLocaleService(); when(locale.getLocale()).thenAnswer((_) async => 'en_US.UTF-8'); + final subiquityClient = MockSubiquityClient(); + when(subiquityClient.getStatus()).thenAnswer((_) async => status); + registerMockService(MockDesktopService()); registerMockService(installer); registerMockService(journal); registerMockService(locale); registerMockService(ProductService()); registerMockService(refresh); - registerMockService(StorageService(MockSubiquityClient())); - registerMockService(MockSubiquityClient()); + registerMockService( + SubiquitySessionService(subiquityClient), + ); + registerMockService(StorageService(subiquityClient)); + registerMockService(subiquityClient); registerMockService(MockTelemetryService()); registerMockService(MockNetworkService()); diff --git a/packages/ubuntu_bootstrap/test/services/storage_service_test.dart b/packages/ubuntu_bootstrap/test/services/storage_service_test.dart index 47c26b81e..fde25c996 100644 --- a/packages/ubuntu_bootstrap/test/services/storage_service_test.dart +++ b/packages/ubuntu_bootstrap/test/services/storage_service_test.dart @@ -20,16 +20,18 @@ void main() { .thenAnswer((_) async => fakeStorageResponse(disks: testDisks)); when(client.hasRst()).thenAnswer((_) async => false); when(client.hasBitLocker()).thenAnswer((_) async => false); - when(client.getStatus()).thenAnswer((_) async => const ApplicationStatus( - cloudInitOk: null, - confirmingTty: '', - echoSyslogId: '', - error: null, - eventSyslogId: '', - interactive: null, - logSyslogId: '', - state: ApplicationState.RUNNING, - )); + when(client.getStatus()).thenAnswer( + (_) async => const ApplicationStatus( + cloudInitOk: null, + confirmingTty: '', + echoSyslogId: '', + error: null, + eventSyslogId: '', + interactive: null, + logSyslogId: '', + state: ApplicationState.RUNNING, + ), + ); }); test('get guided storage', () async { diff --git a/packages/ubuntu_bootstrap/test/test_utils.dart b/packages/ubuntu_bootstrap/test/test_utils.dart index 691fbb278..088df0b9f 100644 --- a/packages/ubuntu_bootstrap/test/test_utils.dart +++ b/packages/ubuntu_bootstrap/test/test_utils.dart @@ -62,6 +62,7 @@ void setupMockPageConfig({Map pages = const {}}) { registerMockService(pageConfigService); when(pageConfigService.pages).thenReturn(pages); when(pageConfigService.excludedPages).thenReturn([]); + when(pageConfigService.includedPages).thenReturn(pages.keys.toList()); } class MockBuildContext extends Mock implements BuildContext {} From d3ab346d7f87fffe1cb38bf7cf344be1a40eb639 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Thu, 4 Jan 2024 16:57:22 +0100 Subject: [PATCH 040/106] fix tests --- packages/ubuntu_bootstrap/lib/installer.dart | 5 +-- .../lib/installer/installer_wizard.dart | 13 ++---- .../lib/pages/storage/storage_wizard.dart | 1 + packages/ubuntu_bootstrap/lib/routes.dart | 6 +-- .../ubuntu_bootstrap/test/installer_test.dart | 4 +- .../test/installer_wizard_test.dart | 34 ++++++++++---- .../test/services/keyboard_service_test.dart | 26 +---------- .../ubuntu_bootstrap/test/test_utils.dart | 44 +++++++++++++++++-- .../ubuntu_init/test/init_wizard_test.dart | 5 ++- packages/ubuntu_init/test/test_utils.dart | 21 +++++++++ .../test/locale/locale_page_test.dart | 6 +++ 11 files changed, 111 insertions(+), 54 deletions(-) diff --git a/packages/ubuntu_bootstrap/lib/installer.dart b/packages/ubuntu_bootstrap/lib/installer.dart index f781345ec..2845abc83 100644 --- a/packages/ubuntu_bootstrap/lib/installer.dart +++ b/packages/ubuntu_bootstrap/lib/installer.dart @@ -62,7 +62,6 @@ Future runInstallerApp( defaultsTo: 'examples/sources/desktop.yaml', help: 'Path of the source catalog (dry-run only)', ); - parser.addFlag('welcome', aliases: ['try-or-install'], hide: true); })!; final liveRun = options['dry-run'] != true; final exe = p.basename(Platform.resolvedExecutable); @@ -197,9 +196,7 @@ Future runInstallerApp( rootBundle, package: 'ubuntu_bootstrap', ), - child: InstallerWizard( - welcome: options['welcome'] as bool? ?? false, - ), + child: const InstallerWizard(), ), ); }, diff --git a/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart b/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart index 9ae970b08..82c9dd5bc 100644 --- a/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart +++ b/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart @@ -12,12 +12,7 @@ import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; class InstallerWizard extends ConsumerStatefulWidget { - const InstallerWizard({ - super.key, - this.welcome = false, - }); - - final bool welcome; + const InstallerWizard({super.key}); @override ConsumerState createState() => _InstallerWizardState(); @@ -48,14 +43,12 @@ class _InstallerWizardState extends ConsumerState { } return status?.interactive == false ? _AutoinstallWizard(status: status) - : _InstallWizard(welcome: widget.welcome); + : const _InstallWizard(); } } class _InstallWizard extends ConsumerWidget { - const _InstallWizard({this.welcome = false}); - - final bool welcome; + const _InstallWizard(); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/packages/ubuntu_bootstrap/lib/pages/storage/storage_wizard.dart b/packages/ubuntu_bootstrap/lib/pages/storage/storage_wizard.dart index b7d3b285c..f18944c11 100644 --- a/packages/ubuntu_bootstrap/lib/pages/storage/storage_wizard.dart +++ b/packages/ubuntu_bootstrap/lib/pages/storage/storage_wizard.dart @@ -34,6 +34,7 @@ class StorageWizard extends ConsumerWidget with ProvisioningPage { Widget build(BuildContext context, WidgetRef ref) { final type = ref.watch(storageModelProvider.select((m) => m.type)); + // TODO(Lukas): Convert to new system final routes = { Navigator.defaultRouteName: WizardRoute( builder: (_) => const StoragePage(), diff --git a/packages/ubuntu_bootstrap/lib/routes.dart b/packages/ubuntu_bootstrap/lib/routes.dart index db04290b0..0abae6bcd 100644 --- a/packages/ubuntu_bootstrap/lib/routes.dart +++ b/packages/ubuntu_bootstrap/lib/routes.dart @@ -27,15 +27,15 @@ sealed class Routes { static String get rst => routeMap['rst']!; static String get keyboard => routeMap['keyboard']!; static String get network => routeMap['network']!; - static String get secureBoot => routeMap['secure-boot']!; + static String get secureBoot => routeMap['secureBoot']!; static String get source => routeMap['source']!; static String get confirm => routeMap['confirm']!; static String get theme => routeMap['theme']!; static String get identity => routeMap['identity']!; static String get install => routeMap['install']!; static String get timezone => routeMap['timezone']!; - static String get notEnoughDiskSpace => routeMap['not-enough-disk-space']!; - static String get activeDirectory => routeMap['active-directory']!; + static String get notEnoughDiskSpace => routeMap['notEnoughDiskSpace']!; + static String get activeDirectory => routeMap['activeDirectory']!; static String get storage => routeMap['storage']!; static String get refresh => routeMap['refresh']!; } diff --git a/packages/ubuntu_bootstrap/test/installer_test.dart b/packages/ubuntu_bootstrap/test/installer_test.dart index b82617b25..5564428dd 100644 --- a/packages/ubuntu_bootstrap/test/installer_test.dart +++ b/packages/ubuntu_bootstrap/test/installer_test.dart @@ -128,7 +128,9 @@ extension on WidgetTester { when(locale.getLocale()).thenAnswer((_) async => 'en_US.UTF-8'); final subiquityClient = MockSubiquityClient(); - when(subiquityClient.getStatus()).thenAnswer((_) async => status); + when(subiquityClient.getStatus()).thenAnswer( + (_) async => status.copyWith(state: ApplicationState.RUNNING), + ); registerMockService(MockDesktopService()); registerMockService(installer); diff --git a/packages/ubuntu_bootstrap/test/installer_wizard_test.dart b/packages/ubuntu_bootstrap/test/installer_wizard_test.dart index 9287a9181..74b542431 100644 --- a/packages/ubuntu_bootstrap/test/installer_wizard_test.dart +++ b/packages/ubuntu_bootstrap/test/installer_wizard_test.dart @@ -47,10 +47,7 @@ import 'welcome/test_welcome.dart'; void main() { LiveTestWidgetsFlutterBinding.ensureInitialized(); - setUp(() { - YaruTestWindow.ensureInitialized(state: const YaruWindowState()); - setupMockPageConfig(); - }); + setUp(() => YaruTestWindow.ensureInitialized(state: const YaruWindowState())); testWidgets('try ubuntu', (tester) async { final loadingModel = buildLoadingModel(delay: const Duration(seconds: 1)); @@ -66,7 +63,7 @@ void main() { localeModelProvider.overrideWith((_) => localeModel), welcomeModelProvider.overrideWith((_) => welcomeModel), ], - child: tester.buildTestWizard(welcome: true), + child: tester.buildTestWizard(excludePages: []), ), ); @@ -140,7 +137,7 @@ void main() { confirmModelProvider.overrideWith((_) => confirmModel), installModelProvider.overrideWith((_) => installModel), ], - child: tester.buildTestWizard(welcome: true), + child: tester.buildTestWizard(excludePages: []), ), ); @@ -197,7 +194,14 @@ void main() { final localeModel = buildLocaleModel(); final rstModel = buildRstModel(hasRst: true); + final subiquityClient = MockSubiquityClient(); + final productService = MockProductService(); registerMockService(MockTelemetryService()); + registerMockService( + SubiquityNetworkService(subiquityClient), + ); + registerMockService(productService); + when(productService.getReleaseNotesURL(any)).thenReturn(''); await tester.pumpWidget( ProviderScope( @@ -351,7 +355,10 @@ void main() { } extension on WidgetTester { - Widget buildTestWizard({bool welcome = false, List? pages}) { + Widget buildTestWizard({ + List? pages, + List excludePages = const ['welcome'], + }) { final installer = MockInstallerService(); when(installer.hasRoute(any)).thenAnswer((i) { return pages?.contains(i.positionalArguments.single) ?? true; @@ -359,6 +366,17 @@ extension on WidgetTester { when(installer.monitorStatus()).thenAnswer((_) => const Stream.empty()); registerMockService(installer); + setupMockPageConfig(excludePages: excludePages); + final subiquityClient = MockSubiquityClient(); + when(subiquityClient.hasRst()).thenAnswer((_) async => false); + registerMockService(subiquityClient); + registerMockService( + SubiquitySessionService(subiquityClient), + ); + final keyboardService = SubiquityKeyboardService(subiquityClient); + registerMockService(keyboardService); + when(keyboardService.getKeyboard()).thenAnswer((_) async => keyboardSetup); + final refresh = MockRefreshService(); when(refresh.state).thenReturn(const RefreshState.status( RefreshStatus(availability: RefreshCheckState.UNAVAILABLE))); @@ -369,7 +387,7 @@ extension on WidgetTester { localizationsDelegates: GlobalUbuntuBootstrapLocalizations.delegates, supportedLocales: supportedLocales, theme: yaruLight, - home: InstallerWizard(welcome: welcome), + home: const InstallerWizard(), ); } diff --git a/packages/ubuntu_bootstrap/test/services/keyboard_service_test.dart b/packages/ubuntu_bootstrap/test/services/keyboard_service_test.dart index c585f0e10..ed3a1ca22 100644 --- a/packages/ubuntu_bootstrap/test/services/keyboard_service_test.dart +++ b/packages/ubuntu_bootstrap/test/services/keyboard_service_test.dart @@ -4,31 +4,9 @@ import 'package:subiquity_client/subiquity_client.dart'; import 'package:subiquity_test/subiquity_test.dart'; import 'package:ubuntu_bootstrap/services/keyboard_service.dart'; +import '../test_utils.dart'; + void main() { - const keyboardSetup = KeyboardSetup( - setting: KeyboardSetting(layout: 'us', variant: 'altgr-intl'), - layouts: [ - KeyboardLayout( - code: 'us', - name: 'English (US)', - variants: [ - KeyboardVariant( - code: '', - name: 'English (US)', - ), - KeyboardVariant( - code: 'altgr-intl', - name: 'English (US) - English (intl., with AltGr dead keys)', - ), - ], - ), - KeyboardLayout( - code: 'de', - name: 'German', - variants: [KeyboardVariant(code: '', name: 'German')], - ), - ], - ); test('get keyboard', () async { final client = MockSubiquityClient(); when(client.getKeyboard()).thenAnswer((_) async => keyboardSetup); diff --git a/packages/ubuntu_bootstrap/test/test_utils.dart b/packages/ubuntu_bootstrap/test/test_utils.dart index 088df0b9f..b18b1037c 100644 --- a/packages/ubuntu_bootstrap/test/test_utils.dart +++ b/packages/ubuntu_bootstrap/test/test_utils.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; +import 'package:ubuntu_bootstrap/installer/installation_step.dart'; import 'package:ubuntu_bootstrap/l10n.dart'; import 'package:ubuntu_bootstrap/services.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; @@ -55,9 +56,21 @@ class _Dummy {} // ignore: unused_element /// Registers a mock [PageConfigService]. /// -/// The [pages] argument will override the pages that are returned -/// (empty by default) if provided. -void setupMockPageConfig({Map pages = const {}}) { +/// The [overridePages] argument will override the pages that are returned +/// if provided. If [overridePages] are defined, [excludePages] will be ignored. +/// All pages defined in [InstallationStep] except `welcome` are returned by default. +void setupMockPageConfig({ + Map? overridePages, + List excludePages = const ['welcome'], +}) { + final pages = overridePages ?? + Map.fromEntries( + InstallationStep.values + .map( + (step) => MapEntry(step.name, const PageConfigEntry()), + ) + .where((entry) => !excludePages.contains(entry.key)), + ); final pageConfigService = MockPageConfigService(); registerMockService(pageConfigService); when(pageConfigService.pages).thenReturn(pages); @@ -65,4 +78,29 @@ void setupMockPageConfig({Map pages = const {}}) { when(pageConfigService.includedPages).thenReturn(pages.keys.toList()); } +const keyboardSetup = KeyboardSetup( + setting: KeyboardSetting(layout: 'us', variant: 'altgr-intl'), + layouts: [ + KeyboardLayout( + code: 'us', + name: 'English (US)', + variants: [ + KeyboardVariant( + code: '', + name: 'English (US)', + ), + KeyboardVariant( + code: 'altgr-intl', + name: 'English (US) - English (intl., with AltGr dead keys)', + ), + ], + ), + KeyboardLayout( + code: 'de', + name: 'German', + variants: [KeyboardVariant(code: '', name: 'German')], + ), + ], +); + class MockBuildContext extends Mock implements BuildContext {} diff --git a/packages/ubuntu_init/test/init_wizard_test.dart b/packages/ubuntu_init/test/init_wizard_test.dart index 5a4d5d1bb..0554d23f7 100644 --- a/packages/ubuntu_init/test/init_wizard_test.dart +++ b/packages/ubuntu_init/test/init_wizard_test.dart @@ -29,7 +29,10 @@ import 'welcome/test_welcome.dart'; void main() { LiveTestWidgetsFlutterBinding.ensureInitialized(); - setUp(() => YaruTestWindow.ensureInitialized(state: const YaruWindowState())); + setUp(() { + YaruTestWindow.ensureInitialized(state: const YaruWindowState()); + setupMockPageConfig(); + }); testWidgets('init', (tester) async { final initModel = buildInitModel(); diff --git a/packages/ubuntu_init/test/test_utils.dart b/packages/ubuntu_init/test/test_utils.dart index 83ba977e2..3570d89c9 100644 --- a/packages/ubuntu_init/test/test_utils.dart +++ b/packages/ubuntu_init/test/test_utils.dart @@ -3,12 +3,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; +import 'package:ubuntu_init/src/init_step.dart'; import 'package:ubuntu_init/ubuntu_init.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; +import 'package:ubuntu_service/ubuntu_service.dart'; import 'package:ubuntu_utils/ubuntu_utils.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru/yaru.dart'; +import 'init_model_test.mocks.dart'; import 'test_utils.mocks.dart'; export 'test_utils.mocks.dart'; @@ -30,6 +33,24 @@ extension UbuntuInitTester on WidgetTester { } } +/// Registers a mock [PageConfigService]. +/// +/// The [overridePages] argument will override the pages that are returned +/// if provided. +/// All pages defined in [InitStep] are returned by default. +void setupMockPageConfig({Map? overridePages}) { + final pages = overridePages ?? + Map.fromEntries( + InitStep.values + .map((step) => MapEntry(step.name, const PageConfigEntry())), + ); + final pageConfigService = MockPageConfigService(); + registerMockService(pageConfigService); + when(pageConfigService.pages).thenReturn(pages); + when(pageConfigService.excludedPages).thenReturn([]); + when(pageConfigService.includedPages).thenReturn(pages.keys.toList()); +} + @GenerateMocks([InitModel]) InitModel buildInitModel({List? pages}) { final model = MockInitModel(); diff --git a/packages/ubuntu_provision/test/locale/locale_page_test.dart b/packages/ubuntu_provision/test/locale/locale_page_test.dart index 54bcd3c36..034d2d78e 100644 --- a/packages/ubuntu_provision/test/locale/locale_page_test.dart +++ b/packages/ubuntu_provision/test/locale/locale_page_test.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:ubuntu_provision/services.dart'; import 'package:ubuntu_provision/src/locale/locale_page.dart'; +import 'package:ubuntu_service/ubuntu_service.dart'; import 'package:ubuntu_test/ubuntu_test.dart'; import 'package:ubuntu_widgets/ubuntu_widgets.dart'; import 'package:yaru_test/yaru_test.dart'; @@ -9,6 +11,10 @@ import 'package:yaru_test/yaru_test.dart'; import 'test_locale.dart'; void main() { + setUp(() { + registerMockService(MockPageConfigService()); + }); + testWidgets('should display a list of languages', (tester) async { final model = buildLocaleModel(); await model.init(); From c7e8c9f2ac9ff035f9b15c52bb5b116fd3fe2672 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Thu, 4 Jan 2024 18:11:00 +0100 Subject: [PATCH 041/106] Remove --welcome --- melos.yaml | 4 ++-- .../ubuntu_bootstrap/integration_test/screenshot_test.dart | 3 +-- .../integration_test/ubuntu_bootstrap_test.dart | 2 +- packages/ubuntu_bootstrap/pubspec.yaml | 2 +- packages/ubuntu_provision/pubspec.yaml | 2 +- packages/ubuntu_wizard/pubspec.yaml | 2 +- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/melos.yaml b/melos.yaml index 646792946..d13e31e51 100644 --- a/melos.yaml +++ b/melos.yaml @@ -43,9 +43,9 @@ command: ubuntu_flavor: ^0.2.1 ubuntu_localizations: ^0.3.4 ubuntu_logger: ^0.1.0 - ubuntu_service: ^0.3.0 + ubuntu_service: ^0.3. ubuntu_session: ^0.0.4 - ubuntu_widgets: 0.3.0 + ubuntu_widgets: ^0.3.2 udev: ^0.0.3 upower: ^0.7.0 url_launcher: ^6.2.1 diff --git a/packages/ubuntu_bootstrap/integration_test/screenshot_test.dart b/packages/ubuntu_bootstrap/integration_test/screenshot_test.dart index 4e86e384b..69262cafb 100644 --- a/packages/ubuntu_bootstrap/integration_test/screenshot_test.dart +++ b/packages/ubuntu_bootstrap/integration_test/screenshot_test.dart @@ -46,8 +46,7 @@ Future main() async { }, variant: themeVariant); testWidgets('02.welcome', (tester) async { - await tester - .runApp(() => runInstallerApp(['--welcome'], theme: currentTheme)); + await tester.runApp(() => runInstallerApp([], theme: currentTheme)); await tester.pumpAndSettle(); await tester.jumpToPage(Routes.welcome); diff --git a/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart b/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart index 8e8a0843d..4992ec819 100644 --- a/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart +++ b/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart @@ -383,7 +383,7 @@ void main() { }); testWidgets('welcome', (tester) async { - await tester.runApp(() => app.main(['--welcome'])); + await tester.runApp(() => app.main([])); await tester.pumpAndSettle(); await tester.testLocalePage(); diff --git a/packages/ubuntu_bootstrap/pubspec.yaml b/packages/ubuntu_bootstrap/pubspec.yaml index b264d230b..78264ab0a 100644 --- a/packages/ubuntu_bootstrap/pubspec.yaml +++ b/packages/ubuntu_bootstrap/pubspec.yaml @@ -48,7 +48,7 @@ dependencies: ubuntu_provision: ^0.1.0 ubuntu_service: ^0.3.0 ubuntu_utils: ^0.1.0 - ubuntu_widgets: 0.3.0 + ubuntu_widgets: ^0.3.2 ubuntu_wizard: ^0.1.0 yaru: 1.2.0 yaru_icons: 2.2.2 diff --git a/packages/ubuntu_provision/pubspec.yaml b/packages/ubuntu_provision/pubspec.yaml index b94a0c44c..bd65e7f25 100644 --- a/packages/ubuntu_provision/pubspec.yaml +++ b/packages/ubuntu_provision/pubspec.yaml @@ -41,7 +41,7 @@ dependencies: ubuntu_service: ^0.3.0 ubuntu_session: ^0.0.4 ubuntu_utils: ^0.1.0 - ubuntu_widgets: 0.3.0 + ubuntu_widgets: ^0.3.2 ubuntu_wizard: ^0.1.0 udev: ^0.0.3 upower: ^0.7.0 diff --git a/packages/ubuntu_wizard/pubspec.yaml b/packages/ubuntu_wizard/pubspec.yaml index a1339198f..aee78f5b1 100644 --- a/packages/ubuntu_wizard/pubspec.yaml +++ b/packages/ubuntu_wizard/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: sdk: flutter ubuntu_flavor: ^0.2.1 ubuntu_localizations: ^0.3.4 - ubuntu_widgets: 0.3.0 + ubuntu_widgets: ^0.3.2 wizard_router: ^1.0.4 yaru: 1.2.0 yaru_widgets: 3.3.1 From b68d7f7ff22028d1f7efc20a8b6c0a72fb0e6e68 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Thu, 4 Jan 2024 18:20:57 +0100 Subject: [PATCH 042/106] Fix version of ubuntu_service --- melos.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/melos.yaml b/melos.yaml index d13e31e51..29ba350ef 100644 --- a/melos.yaml +++ b/melos.yaml @@ -43,7 +43,7 @@ command: ubuntu_flavor: ^0.2.1 ubuntu_localizations: ^0.3.4 ubuntu_logger: ^0.1.0 - ubuntu_service: ^0.3. + ubuntu_service: ^0.3.0 ubuntu_session: ^0.0.4 ubuntu_widgets: ^0.3.2 udev: ^0.0.3 From d9eefb30b57a14bb3facc172e80ec7d46df5cc13 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 5 Jan 2024 13:18:25 +0100 Subject: [PATCH 043/106] Bring back --welcome --- .../integration_test/screenshot_test.dart | 3 ++- .../integration_test/ubuntu_bootstrap_test.dart | 2 +- packages/ubuntu_bootstrap/lib/installer.dart | 11 ++++++++++- packages/ubuntu_bootstrap/test/test_utils.dart | 4 +--- .../lib/src/services/page_config_service.dart | 10 +++++++--- 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/packages/ubuntu_bootstrap/integration_test/screenshot_test.dart b/packages/ubuntu_bootstrap/integration_test/screenshot_test.dart index 69262cafb..4e86e384b 100644 --- a/packages/ubuntu_bootstrap/integration_test/screenshot_test.dart +++ b/packages/ubuntu_bootstrap/integration_test/screenshot_test.dart @@ -46,7 +46,8 @@ Future main() async { }, variant: themeVariant); testWidgets('02.welcome', (tester) async { - await tester.runApp(() => runInstallerApp([], theme: currentTheme)); + await tester + .runApp(() => runInstallerApp(['--welcome'], theme: currentTheme)); await tester.pumpAndSettle(); await tester.jumpToPage(Routes.welcome); diff --git a/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart b/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart index 4992ec819..8e8a0843d 100644 --- a/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart +++ b/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart @@ -383,7 +383,7 @@ void main() { }); testWidgets('welcome', (tester) async { - await tester.runApp(() => app.main([])); + await tester.runApp(() => app.main(['--welcome'])); await tester.pumpAndSettle(); await tester.testLocalePage(); diff --git a/packages/ubuntu_bootstrap/lib/installer.dart b/packages/ubuntu_bootstrap/lib/installer.dart index 2845abc83..ef17072d7 100644 --- a/packages/ubuntu_bootstrap/lib/installer.dart +++ b/packages/ubuntu_bootstrap/lib/installer.dart @@ -40,6 +40,10 @@ Future runInstallerApp( valueHelp: 'path', help: 'Path to a config file', ); + parser.addOption( + 'exclude-pages', + help: 'Pages that you want to exclude from the wizard, comma separated.', + ); parser.addFlag( 'dry-run', defaultsTo: Platform.environment['LIVE_RUN'] != '1', @@ -62,6 +66,7 @@ Future runInstallerApp( defaultsTo: 'examples/sources/desktop.yaml', help: 'Path of the source catalog (dry-run only)', ); + parser.addFlag('welcome', aliases: ['try-or-install'], hide: true); })!; final liveRun = options['dry-run'] != true; final exe = p.basename(Platform.resolvedExecutable); @@ -72,6 +77,7 @@ Future runInstallerApp( final subiquityPath = await getSubiquityPath() .then((dir) => Directory(dir).existsSync() ? dir : null); final endpoint = await defaultEndpoint(serverMode); + final includeWelcome = options['welcome'] as bool? ?? false; final process = liveRun ? null : SubiquityProcess.python( @@ -105,7 +111,10 @@ Future runInstallerApp( () => SubiquityNetworkService(getService()), ); tryRegisterService( - () => PageConfigService(config: tryGetService()), + () => PageConfigService( + config: tryGetService(), + includeWelcome: includeWelcome, + ), ); tryRegisterService(() => PostInstallService('/tmp/$baseName.conf')); tryRegisterService(PowerService.new); diff --git a/packages/ubuntu_bootstrap/test/test_utils.dart b/packages/ubuntu_bootstrap/test/test_utils.dart index b18b1037c..004d728b3 100644 --- a/packages/ubuntu_bootstrap/test/test_utils.dart +++ b/packages/ubuntu_bootstrap/test/test_utils.dart @@ -66,9 +66,7 @@ void setupMockPageConfig({ final pages = overridePages ?? Map.fromEntries( InstallationStep.values - .map( - (step) => MapEntry(step.name, const PageConfigEntry()), - ) + .map((step) => MapEntry(step.name, const PageConfigEntry())) .where((entry) => !excludePages.contains(entry.key)), ); final pageConfigService = MockPageConfigService(); diff --git a/packages/ubuntu_provision/lib/src/services/page_config_service.dart b/packages/ubuntu_provision/lib/src/services/page_config_service.dart index 8fbebb47f..ffe726e95 100644 --- a/packages/ubuntu_provision/lib/src/services/page_config_service.dart +++ b/packages/ubuntu_provision/lib/src/services/page_config_service.dart @@ -14,13 +14,17 @@ final pageConfigProvider = Provider((ref) => getService()); final _log = Logger('page'); class PageConfigService { - PageConfigService({ConfigService? config}) : _config = config; + PageConfigService({ConfigService? config, this.includeWelcome = false}) + : _config = config; final ConfigService? _config; final Map pages = {}; + final bool includeWelcome; - List get includedPages => - pages.entries.where((e) => e.value.visible).map((e) => e.key).toList(); + List get includedPages => pages.entries + .where((e) => e.value.visible && (includeWelcome || e.key != 'welcome')) + .map((e) => e.key) + .toList(); List get excludedPages => pages.entries.whereNot((e) => e.value.visible).map((e) => e.key).toList(); From 4aea75fdf8ba634d0b96215f5e5ef2f3c196bba6 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 5 Jan 2024 14:16:54 +0100 Subject: [PATCH 044/106] Update mocks --- packages/ubuntu_init/test/init_model_test.mocks.dart | 6 ++++++ packages/ubuntu_provision/test/test_utils.mocks.dart | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/packages/ubuntu_init/test/init_model_test.mocks.dart b/packages/ubuntu_init/test/init_model_test.mocks.dart index f28d0b77b..211d1e360 100644 --- a/packages/ubuntu_init/test/init_model_test.mocks.dart +++ b/packages/ubuntu_init/test/init_model_test.mocks.dart @@ -78,6 +78,12 @@ class MockPageConfigService extends _i1.Mock implements _i3.PageConfigService { returnValue: {}, ) as Map); + @override + bool get includeWelcome => (super.noSuchMethod( + Invocation.getter(#includeWelcome), + returnValue: false, + ) as bool); + @override List get includedPages => (super.noSuchMethod( Invocation.getter(#includedPages), diff --git a/packages/ubuntu_provision/test/test_utils.mocks.dart b/packages/ubuntu_provision/test/test_utils.mocks.dart index 6fcef25be..bd8439f7a 100644 --- a/packages/ubuntu_provision/test/test_utils.mocks.dart +++ b/packages/ubuntu_provision/test/test_utils.mocks.dart @@ -1402,6 +1402,12 @@ class MockPageConfigService extends _i1.Mock implements _i3.PageConfigService { returnValue: {}, ) as Map); + @override + bool get includeWelcome => (super.noSuchMethod( + Invocation.getter(#includeWelcome), + returnValue: false, + ) as bool); + @override List get includedPages => (super.noSuchMethod( Invocation.getter(#includedPages), From 10fddaf2af5605603ed791750c41c789a6fbb372 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 5 Jan 2024 14:45:26 +0100 Subject: [PATCH 045/106] Remove exclude-pages parameter --- packages/ubuntu_bootstrap/lib/installer.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/ubuntu_bootstrap/lib/installer.dart b/packages/ubuntu_bootstrap/lib/installer.dart index ef17072d7..298e88eba 100644 --- a/packages/ubuntu_bootstrap/lib/installer.dart +++ b/packages/ubuntu_bootstrap/lib/installer.dart @@ -40,10 +40,6 @@ Future runInstallerApp( valueHelp: 'path', help: 'Path to a config file', ); - parser.addOption( - 'exclude-pages', - help: 'Pages that you want to exclude from the wizard, comma separated.', - ); parser.addFlag( 'dry-run', defaultsTo: Platform.environment['LIVE_RUN'] != '1', From e628a737f0a60e75087bf5812d140e40fa8a0b4d Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 5 Jan 2024 17:37:16 +0100 Subject: [PATCH 046/106] Clear rootbundle in tearDown --- .../ubuntu_bootstrap/integration_test/screenshot_test.dart | 3 ++- packages/ubuntu_init/integration_test/screenshot_test.dart | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/ubuntu_bootstrap/integration_test/screenshot_test.dart b/packages/ubuntu_bootstrap/integration_test/screenshot_test.dart index 4e86e384b..d4e600428 100644 --- a/packages/ubuntu_bootstrap/integration_test/screenshot_test.dart +++ b/packages/ubuntu_bootstrap/integration_test/screenshot_test.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:subiquity_client/subiquity_client.dart'; @@ -14,7 +15,6 @@ import 'package:yaru_widgets/yaru_widgets.dart'; Future main() async { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - final window = await YaruWindow.ensureInitialized(); setUpAll(() => autoUpdateGoldenFiles = true); @@ -34,6 +34,7 @@ Future main() async { await window.close(); await windowClosed; await resetAllServices(); + rootBundle.clear(); }); testWidgets('01.locale', (tester) async { diff --git a/packages/ubuntu_init/integration_test/screenshot_test.dart b/packages/ubuntu_init/integration_test/screenshot_test.dart index ead3873ae..8d2bcebd8 100644 --- a/packages/ubuntu_init/integration_test/screenshot_test.dart +++ b/packages/ubuntu_init/integration_test/screenshot_test.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:ubuntu_init/src/routes.dart'; @@ -14,6 +15,8 @@ Future main() async { setUp(registerFakeInitServices); + tearDown(rootBundle.clear); + testWidgets('01.locale', (tester) async { await tester.runApp(() => runInitApp([], theme: currentTheme)); await tester.pumpAndSettle(); From d1d2f56be98c37c2aae5425c211bc1387d8d4494 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Mon, 8 Jan 2024 15:02:59 +0100 Subject: [PATCH 047/106] fix: Not enough disk space should not be in SourceWizard --- .../assets/ubuntu-provision.yml | 3 ++ .../lib/installer/installation_step.dart | 2 ++ packages/ubuntu_bootstrap/lib/pages.dart | 3 +- .../not_enough_disk_space_page.dart | 5 +-- .../lib/pages/source/source_page.dart | 5 ++- .../lib/pages/source/source_wizard.dart | 31 ------------------- .../test/source/source_wizard_test.dart | 4 +-- .../lib/src/interfaces/provisioning_page.dart | 2 +- .../lib/src/locale/locale_model.dart | 4 ++- 9 files changed, 18 insertions(+), 41 deletions(-) delete mode 100644 packages/ubuntu_bootstrap/lib/pages/source/source_wizard.dart diff --git a/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml b/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml index 9236d1369..45b5e0069 100644 --- a/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml +++ b/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml @@ -21,6 +21,9 @@ pages: source: title: "Applications and updates" visible: true + notEnoughDiskSpace: + title: "Not enough disk space" + visible: true secureBoot: title: "Configure Secure Boot" visible: true diff --git a/packages/ubuntu_bootstrap/lib/installer/installation_step.dart b/packages/ubuntu_bootstrap/lib/installer/installation_step.dart index 8df2b1b7f..47dc824ff 100644 --- a/packages/ubuntu_bootstrap/lib/installer/installation_step.dart +++ b/packages/ubuntu_bootstrap/lib/installer/installation_step.dart @@ -5,6 +5,7 @@ import 'package:ubuntu_bootstrap/pages.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; +// TODO(Lukas): Add whether they should be a hidden step or not enum InstallationStep { locale(LocalePage.new), welcome(WelcomePage.new), @@ -13,6 +14,7 @@ enum InstallationStep { network(NetworkPage.new), refresh(RefreshPage.new), source(SourcePage.new), + notEnoughDiskSpace(NotEnoughDiskSpacePage.new), secureBoot(SecureBootPage.new), storage(StorageWizard.new), confirm(ConfirmPage.new); diff --git a/packages/ubuntu_bootstrap/lib/pages.dart b/packages/ubuntu_bootstrap/lib/pages.dart index ed49b240a..806eaaf2a 100644 --- a/packages/ubuntu_bootstrap/lib/pages.dart +++ b/packages/ubuntu_bootstrap/lib/pages.dart @@ -4,6 +4,7 @@ export 'pages/loading/loading_page.dart'; export 'pages/refresh/refresh_page.dart'; export 'pages/rst/rst_page.dart'; export 'pages/secure_boot/secure_boot_page.dart'; -export 'pages/source/source_wizard.dart'; +export 'pages/source/not_enough_disk_space/not_enough_disk_space_page.dart'; +export 'pages/source/source_page.dart'; export 'pages/storage/storage_wizard.dart'; export 'pages/welcome/welcome_page.dart'; diff --git a/packages/ubuntu_bootstrap/lib/pages/source/not_enough_disk_space/not_enough_disk_space_page.dart b/packages/ubuntu_bootstrap/lib/pages/source/not_enough_disk_space/not_enough_disk_space_page.dart index afa21d468..f49414622 100644 --- a/packages/ubuntu_bootstrap/lib/pages/source/not_enough_disk_space/not_enough_disk_space_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/source/not_enough_disk_space/not_enough_disk_space_page.dart @@ -9,10 +9,11 @@ import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru_icons/yaru_icons.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; -class NotEnoughDiskSpacePage extends ConsumerWidget { +class NotEnoughDiskSpacePage extends ConsumerWidget with ProvisioningPage { const NotEnoughDiskSpacePage({super.key}); - static Future load(WidgetRef ref) { + @override + Future load(BuildContext context, WidgetRef ref) async { return ref.read(notEnoughDiskSpaceModelProvider).init(); } diff --git a/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart b/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart index 45addbe71..15336a6f8 100644 --- a/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart @@ -12,9 +12,8 @@ import 'package:yaru_widgets/yaru_widgets.dart'; export 'source_model.dart' show kFullSourceId, kMinimalSourceId; -/// A page where the user can decide what type of installation that they would -/// like to perform, and whether they want to install 3rd party drivers and -/// codecs. +/// A page where the user can decide whether they want to install 3rd party +/// drivers or codecs. class SourcePage extends ConsumerWidget with ProvisioningPage { const SourcePage({super.key}); diff --git a/packages/ubuntu_bootstrap/lib/pages/source/source_wizard.dart b/packages/ubuntu_bootstrap/lib/pages/source/source_wizard.dart deleted file mode 100644 index 08faba764..000000000 --- a/packages/ubuntu_bootstrap/lib/pages/source/source_wizard.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:ubuntu_bootstrap/installer/installation_step.dart'; -import 'package:ubuntu_bootstrap/pages/source/not_enough_disk_space/not_enough_disk_space_page.dart'; -import 'package:ubuntu_bootstrap/pages/source/source_page.dart'; -import 'package:ubuntu_bootstrap/routes.dart'; -import 'package:ubuntu_wizard/ubuntu_wizard.dart'; - -export 'not_enough_disk_space/not_enough_disk_space_page.dart'; -export 'source_page.dart'; - -class SourceWizard extends ConsumerWidget { - const SourceWizard({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return Wizard( - userData: WizardData(totalSteps: InstallationStep.values.length), - routes: { - Navigator.defaultRouteName: WizardRoute( - builder: (_) => const SourcePage(), - userData: WizardRouteData(step: InstallationStep.source.index), - ), - Routes.notEnoughDiskSpace: WizardRoute( - builder: (_) => const NotEnoughDiskSpacePage(), - onLoad: (_) => NotEnoughDiskSpacePage.load(ref), - ), - }, - ); - } -} diff --git a/packages/ubuntu_bootstrap/test/source/source_wizard_test.dart b/packages/ubuntu_bootstrap/test/source/source_wizard_test.dart index bccc535dc..d492bf821 100644 --- a/packages/ubuntu_bootstrap/test/source/source_wizard_test.dart +++ b/packages/ubuntu_bootstrap/test/source/source_wizard_test.dart @@ -5,7 +5,7 @@ import 'package:mockito/mockito.dart'; import 'package:subiquity_client/subiquity_client.dart'; import 'package:subiquity_test/subiquity_test.dart'; import 'package:ubuntu_bootstrap/l10n.dart'; -import 'package:ubuntu_bootstrap/pages/source/source_wizard.dart'; +import 'package:ubuntu_bootstrap/pages.dart'; import 'package:ubuntu_bootstrap/routes.dart'; import 'package:ubuntu_bootstrap/services.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; @@ -125,7 +125,7 @@ extension on WidgetTester { ), ), Routes.source: WizardRoute( - builder: (_) => const SourceWizard(), + builder: (_) => const SourcePage(), onLoad: (_) => const SourcePage().load(context, ref), ), '/last': WizardRoute( diff --git a/packages/ubuntu_provision/lib/src/interfaces/provisioning_page.dart b/packages/ubuntu_provision/lib/src/interfaces/provisioning_page.dart index 648d62b02..badd5fbfe 100644 --- a/packages/ubuntu_provision/lib/src/interfaces/provisioning_page.dart +++ b/packages/ubuntu_provision/lib/src/interfaces/provisioning_page.dart @@ -6,5 +6,5 @@ mixin ProvisioningPage on Widget { /// /// If it returns true it will be added to the wizard, otherwise it will be /// skipped. - Future load(BuildContext context, WidgetRef ref) => Future.value(true); + Future load(BuildContext context, WidgetRef ref) async => true; } diff --git a/packages/ubuntu_provision/lib/src/locale/locale_model.dart b/packages/ubuntu_provision/lib/src/locale/locale_model.dart index 673775c6b..0cefcc7be 100644 --- a/packages/ubuntu_provision/lib/src/locale/locale_model.dart +++ b/packages/ubuntu_provision/lib/src/locale/locale_model.dart @@ -52,7 +52,9 @@ class LocaleModel extends SafeChangeNotifier { /// Loads available languages. Future init() async { - assert(_languageList.isEmpty); + if (_languageList.isNotEmpty) { + return; + } final languages = await loadLocalizedLanguages(supportedLocales); _languageList = List.of(languages); _log.info('Loaded ${_languageList.length} languages'); From 05544664dadc64349d46bc914b2fb45e08d36394 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Mon, 8 Jan 2024 15:20:52 +0100 Subject: [PATCH 048/106] Add rootBundle.clear to other integration tests --- .../integration_test/ubuntu_bootstrap_test.dart | 6 +++++- packages/ubuntu_init/integration_test/ubuntu_init_test.dart | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart b/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart index 8e8a0843d..b43fb304d 100644 --- a/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart +++ b/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:collection/collection.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:subiquity_client/subiquity_client.dart'; @@ -23,7 +24,10 @@ void main() { await cleanUpSubiquity(); registerMockService(FakeDesktopService()); }); - tearDown(() async => resetAllServices()); + tearDown(() async { + await resetAllServices(); + rootBundle.clear(); + }); testWidgets('minimal installation', (tester) async { const language = 'Français'; diff --git a/packages/ubuntu_init/integration_test/ubuntu_init_test.dart b/packages/ubuntu_init/integration_test/ubuntu_init_test.dart index b1378415c..a041be15b 100644 --- a/packages/ubuntu_init/integration_test/ubuntu_init_test.dart +++ b/packages/ubuntu_init/integration_test/ubuntu_init_test.dart @@ -1,4 +1,5 @@ import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:ubuntu_init/ubuntu_init.dart'; @@ -14,7 +15,10 @@ void main() { setUpAll(YaruTestWindow.ensureInitialized); setUp(registerFakeInitServices); - tearDown(resetAllServices); + tearDown(() async { + await resetAllServices(); + rootBundle.clear(); + }); testWidgets('init', (tester) async { final windowClosed = YaruTestWindow.waitForClosed(); From 8cce6dcbab4905d0109c13a8a8007495f3eeb2cd Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Mon, 8 Jan 2024 15:49:01 +0100 Subject: [PATCH 049/106] chore: update go dependencies --- provd/go.mod | 14 +++++++------- provd/go.sum | 29 ++++++++++++----------------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/provd/go.mod b/provd/go.mod index bd8091d2d..c2680f21b 100644 --- a/provd/go.mod +++ b/provd/go.mod @@ -1,6 +1,6 @@ module github.com/canonical/ubuntu-desktop-provision/provd -go 1.21.4 +go 1.21.5 require ( github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf @@ -8,8 +8,8 @@ require ( github.com/spf13/viper v1.17.0 github.com/stretchr/testify v1.8.4 github.com/ubuntu/decorate v0.0.0-20231211084900-69db9a41777a - google.golang.org/grpc v1.58.3 - google.golang.org/protobuf v1.31.0 + google.golang.org/grpc v1.60.1 + google.golang.org/protobuf v1.32.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -35,10 +35,10 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) diff --git a/provd/go.sum b/provd/go.sum index 7b68213c2..d2e83c4cc 100644 --- a/provd/go.sum +++ b/provd/go.sum @@ -48,7 +48,6 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -175,8 +174,6 @@ github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -195,8 +192,6 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/ubuntu/decorate v0.0.0-20230905131025-e968fa48a85c h1:jO41xNLddTDkrfz4w4RCMWCmX8Y+ZHz5jSbJWNLDvqU= -github.com/ubuntu/decorate v0.0.0-20230905131025-e968fa48a85c/go.mod h1:edGgz97NOqS2oqzbKrZqO9YU9neosRrkEZbVJVQynAA= github.com/ubuntu/decorate v0.0.0-20231211084900-69db9a41777a h1:VojaxMh5vh94q0uRrJP2mucbItb4gCMzEg6X1RzlM+M= github.com/ubuntu/decorate v0.0.0-20231211084900-69db9a41777a/go.mod h1:edGgz97NOqS2oqzbKrZqO9YU9neosRrkEZbVJVQynAA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -285,8 +280,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -342,8 +337,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -353,8 +348,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -471,8 +466,8 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -489,8 +484,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= -google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= +google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -503,8 +498,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= From 86e3d88e962461eab3bcb38d63710203e669aa01 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Mon, 8 Jan 2024 16:31:34 +0100 Subject: [PATCH 050/106] Fix init tests --- .../integration_test/screenshot_test.dart | 18 +++++++++--------- .../integration_test/ubuntu_init_test.dart | 1 - .../ubuntu_init/lib/src/init_services.dart | 6 ++++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/ubuntu_init/integration_test/screenshot_test.dart b/packages/ubuntu_init/integration_test/screenshot_test.dart index 8d2bcebd8..7dc733316 100644 --- a/packages/ubuntu_init/integration_test/screenshot_test.dart +++ b/packages/ubuntu_init/integration_test/screenshot_test.dart @@ -17,6 +17,15 @@ Future main() async { tearDown(rootBundle.clear); + testWidgets('00.welcome', (tester) async { + await tester.runApp(() => runInitApp([], theme: currentTheme)); + await tester.pumpAndSettle(); + + await tester.testWelcomeInitPage( + screenshot: '$currentThemeName/00.welcome', + ); + }, variant: themeVariant); + testWidgets('01.locale', (tester) async { await tester.runApp(() => runInitApp([], theme: currentTheme)); await tester.pumpAndSettle(); @@ -96,15 +105,6 @@ Future main() async { ); }, variant: themeVariant); - testWidgets('07.welcome', (tester) async { - await tester.runApp(() => runInitApp([], theme: currentTheme)); - await tester.pumpAndSettle(); - - await tester.testWelcomeInitPage( - screenshot: '$currentThemeName/07.welcome', - ); - }, variant: themeVariant); - testWidgets('08.telemetry', (tester) async { await tester.runApp(() => runInitApp([], theme: currentTheme)); await tester.pumpAndSettle(); diff --git a/packages/ubuntu_init/integration_test/ubuntu_init_test.dart b/packages/ubuntu_init/integration_test/ubuntu_init_test.dart index a041be15b..20882b67a 100644 --- a/packages/ubuntu_init/integration_test/ubuntu_init_test.dart +++ b/packages/ubuntu_init/integration_test/ubuntu_init_test.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; diff --git a/packages/ubuntu_init/lib/src/init_services.dart b/packages/ubuntu_init/lib/src/init_services.dart index 1f80d1815..327bec145 100644 --- a/packages/ubuntu_init/lib/src/init_services.dart +++ b/packages/ubuntu_init/lib/src/init_services.dart @@ -43,8 +43,10 @@ Future registerInitServices(List args) { tryRegisterService(XdgKeyboardService.new); tryRegisterService(XdgLocaleService.new); tryRegisterService(NetworkService.new); - tryRegisterService( - () => PageConfigService(config: tryGetService())); + tryRegisterService(() => PageConfigService( + config: tryGetService(), + includeWelcome: true, + )); tryRegisterService(GnomePrivacyService.new); tryRegisterService(ProductService.new); tryRegisterService(XdgSessionService.new); From c54017b3d672d7572d39548a9c825eb9ba8bcf54 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Mon, 8 Jan 2024 18:07:50 +0100 Subject: [PATCH 051/106] Fix steps for wizard to be like previously --- .../assets/ubuntu-provision.yml | 1 + packages/ubuntu_bootstrap/lib/installer.dart | 1 + .../lib/installer/installation_step.dart | 47 ++++++++++++------- .../lib/installer/installer_wizard.dart | 5 +- .../lib/pages/storage/storage_routes.dart | 2 +- .../lib/pages/storage/storage_wizard.dart | 26 ++++++---- ...t.dart => not_enough_disk_space_test.dart} | 5 ++ 7 files changed, 61 insertions(+), 26 deletions(-) rename packages/ubuntu_bootstrap/test/source/{source_wizard_test.dart => not_enough_disk_space_test.dart} (95%) diff --git a/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml b/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml index 45b5e0069..2c4ccdedb 100644 --- a/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml +++ b/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml @@ -7,6 +7,7 @@ pages: title: "Welcome to Ubuntu" visible: true rst: + title: "RST is enabled" image: "assets/storage/rst.svg" visible: true keyboard: diff --git a/packages/ubuntu_bootstrap/lib/installer.dart b/packages/ubuntu_bootstrap/lib/installer.dart index 298e88eba..0ea7e3e21 100644 --- a/packages/ubuntu_bootstrap/lib/installer.dart +++ b/packages/ubuntu_bootstrap/lib/installer.dart @@ -62,6 +62,7 @@ Future runInstallerApp( defaultsTo: 'examples/sources/desktop.yaml', help: 'Path of the source catalog (dry-run only)', ); + // TODO: Remove in favor of using the ubuntu-provision.yml config file. parser.addFlag('welcome', aliases: ['try-or-install'], hide: true); })!; final liveRun = options['dry-run'] != true; diff --git a/packages/ubuntu_bootstrap/lib/installer/installation_step.dart b/packages/ubuntu_bootstrap/lib/installer/installation_step.dart index 47dc824ff..0211af36e 100644 --- a/packages/ubuntu_bootstrap/lib/installer/installation_step.dart +++ b/packages/ubuntu_bootstrap/lib/installer/installation_step.dart @@ -5,27 +5,28 @@ import 'package:ubuntu_bootstrap/pages.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; -// TODO(Lukas): Add whether they should be a hidden step or not enum InstallationStep { - locale(LocalePage.new), - welcome(WelcomePage.new), - rst(RstPage.new), - keyboard(KeyboardPage.new), - network(NetworkPage.new), - refresh(RefreshPage.new), - source(SourcePage.new), - notEnoughDiskSpace(NotEnoughDiskSpacePage.new), - secureBoot(SecureBootPage.new), - storage(StorageWizard.new), - confirm(ConfirmPage.new); - - const InstallationStep(this.pageFactory); + locale(LocalePage.new, true), + welcome(WelcomePage.new, true), + rst(RstPage.new, false), + keyboard(KeyboardPage.new, true), + network(NetworkPage.new, true), + refresh(RefreshPage.new, true), + source(SourcePage.new, true), + notEnoughDiskSpace(NotEnoughDiskSpacePage.new, false), + secureBoot(SecureBootPage.new, false), + storage(StorageWizard.new, true), + confirm(ConfirmPage.new, true); + + const InstallationStep(this.pageFactory, this.discreteStep); final ProvisioningPage Function() pageFactory; + /// If this is true the page has its own step in the wizard progress bar. + final bool discreteStep; + WizardRoute toRoute(BuildContext context, WidgetRef ref) { - final pageConfig = ref.watch(pageConfigProvider); - final includedIndex = pageConfig.includedPages.indexOf(name); + final includedIndex = pageIndex(ref); final page = pageFactory(); return WizardRoute( builder: (_) => page, @@ -34,6 +35,20 @@ enum InstallationStep { ); } + /// The index of this page in the wizard progress bar. + int pageIndex(WidgetRef ref) { + final pageConfig = ref.watch(pageConfigProvider); + final initialIndex = pageConfig.includedPages.indexOf(name); + var index = 0; + for (var i = 0; i < initialIndex; i++) { + if (InstallationStep.fromName(pageConfig.includedPages[i])! + .discreteStep) { + index++; + } + } + return index; + } + String get name => toString().split('.').last; static InstallationStep? fromName(String name) { diff --git a/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart b/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart index 82c9dd5bc..ecf5f0e37 100644 --- a/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart +++ b/packages/ubuntu_bootstrap/lib/installer/installer_wizard.dart @@ -58,10 +58,13 @@ class _InstallWizard extends ConsumerWidget { Routes.routeMap[pageName]!: InstallationStep.fromName(pageName)!.toRoute(context, ref) }; + final totalSteps = InstallationStep.fromName(pageConfig.includedPages.last)! + .pageIndex(ref) + + 1; return WizardBuilder( initialRoute: Routes.initial, - userData: WizardData(totalSteps: preInstallRoutes.length), + userData: WizardData(totalSteps: totalSteps), routes: { Routes.loading: WizardRoute( builder: (_) => const LoadingPage(), diff --git a/packages/ubuntu_bootstrap/lib/pages/storage/storage_routes.dart b/packages/ubuntu_bootstrap/lib/pages/storage/storage_routes.dart index e697355bd..1d859aba0 100644 --- a/packages/ubuntu_bootstrap/lib/pages/storage/storage_routes.dart +++ b/packages/ubuntu_bootstrap/lib/pages/storage/storage_routes.dart @@ -1,4 +1,4 @@ -abstract class StorageRoutes { +sealed class StorageRoutes { static const bitlocker = '/bitlocker'; static const guidedReformat = '/guided-reformat'; static const guidedResize = '/guided-resize'; diff --git a/packages/ubuntu_bootstrap/lib/pages/storage/storage_wizard.dart b/packages/ubuntu_bootstrap/lib/pages/storage/storage_wizard.dart index f18944c11..81b81a478 100644 --- a/packages/ubuntu_bootstrap/lib/pages/storage/storage_wizard.dart +++ b/packages/ubuntu_bootstrap/lib/pages/storage/storage_wizard.dart @@ -11,6 +11,7 @@ import 'package:ubuntu_bootstrap/pages/storage/storage_model.dart'; import 'package:ubuntu_bootstrap/pages/storage/storage_page.dart'; import 'package:ubuntu_bootstrap/pages/storage/storage_routes.dart'; import 'package:ubuntu_provision/interfaces.dart'; +import 'package:ubuntu_provision/services.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; export 'bitlocker/bitlocker_page.dart'; @@ -33,12 +34,16 @@ class StorageWizard extends ConsumerWidget with ProvisioningPage { @override Widget build(BuildContext context, WidgetRef ref) { final type = ref.watch(storageModelProvider.select((m) => m.type)); + final pageConfig = ref.watch(pageConfigProvider); + final totalSteps = InstallationStep.fromName(pageConfig.includedPages.last)! + .pageIndex(ref) + + 1; - // TODO(Lukas): Convert to new system final routes = { Navigator.defaultRouteName: WizardRoute( builder: (_) => const StoragePage(), - userData: WizardRouteData(step: InstallationStep.secureBoot.index), + userData: + WizardRouteData(step: InstallationStep.secureBoot.pageIndex(ref)), ), if (type != StorageType.manual) StorageRoutes.bitlocker: WizardRoute( @@ -48,40 +53,45 @@ class StorageWizard extends ConsumerWidget with ProvisioningPage { if (type == StorageType.erase) StorageRoutes.guidedReformat: WizardRoute( builder: (_) => const GuidedReformatPage(), - userData: WizardRouteData(step: InstallationStep.storage.index), + userData: + WizardRouteData(step: InstallationStep.storage.pageIndex(ref)), onLoad: (_) => GuidedReformatPage.load(ref), onReplace: (_) => StorageRoutes.manual, ), if (type == StorageType.alongside) StorageRoutes.guidedResize: WizardRoute( builder: (_) => const GuidedResizePage(), - userData: WizardRouteData(step: InstallationStep.storage.index), + userData: + WizardRouteData(step: InstallationStep.storage.pageIndex(ref)), onLoad: (_) => GuidedResizePage.load(ref), onReplace: (_) => StorageRoutes.manual, ), if (type == StorageType.manual) StorageRoutes.manual: WizardRoute( builder: (_) => const ManualStoragePage(), - userData: WizardRouteData(step: InstallationStep.storage.index), + userData: + WizardRouteData(step: InstallationStep.storage.pageIndex(ref)), onLoad: (_) => ManualStoragePage.load(ref), ), if (type != StorageType.manual) StorageRoutes.securityKey: WizardRoute( builder: (_) => const SecurityKeyPage(), - userData: WizardRouteData(step: InstallationStep.storage.index), + userData: + WizardRouteData(step: InstallationStep.storage.pageIndex(ref)), onLoad: (_) => SecurityKeyPage.load(ref), ), if (type != StorageType.manual) StorageRoutes.recoveryKey: WizardRoute( builder: (_) => const RecoveryKeyPage(), - userData: WizardRouteData(step: InstallationStep.storage.index), + userData: + WizardRouteData(step: InstallationStep.storage.pageIndex(ref)), onLoad: (_) => RecoveryKeyPage.load(ref), ), }; return Wizard( key: ValueKey(type), - userData: WizardData(totalSteps: InstallationStep.values.length), + userData: WizardData(totalSteps: totalSteps), routes: routes, ); } diff --git a/packages/ubuntu_bootstrap/test/source/source_wizard_test.dart b/packages/ubuntu_bootstrap/test/source/not_enough_disk_space_test.dart similarity index 95% rename from packages/ubuntu_bootstrap/test/source/source_wizard_test.dart rename to packages/ubuntu_bootstrap/test/source/not_enough_disk_space_test.dart index d492bf821..49d829716 100644 --- a/packages/ubuntu_bootstrap/test/source/source_wizard_test.dart +++ b/packages/ubuntu_bootstrap/test/source/not_enough_disk_space_test.dart @@ -128,6 +128,11 @@ extension on WidgetTester { builder: (_) => const SourcePage(), onLoad: (_) => const SourcePage().load(context, ref), ), + Routes.notEnoughDiskSpace: WizardRoute( + builder: (_) => const NotEnoughDiskSpacePage(), + onLoad: (_) => + const NotEnoughDiskSpacePage().load(context, ref), + ), '/last': WizardRoute( builder: (context) => WizardPage( content: const Text('last route'), From 12490c1eec7c62257912467b55298819fde47e64 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 9 Jan 2024 16:55:42 +0200 Subject: [PATCH 052/106] feat: generate.go & user stubs --- provd/example.config.yaml | 2 +- provd/generate.go | 8 + provd/go.mod | 1 + provd/go.sum | 4 +- provd/internal/services/manager.go | 6 + .../testdata/TestRegisterGRPCServices/golden | 12 + .../golden/successfully_creates_a_user | 0 provd/internal/services/user/user.go | 266 ++++++++ provd/internal/services/user/user_test.go | 116 ++++ provd/internal/testutils/dbus.go | 102 +++ provd/protos/user.pb.go | 582 ++++++++++++++++++ provd/protos/user_grpc.pb.go | 183 ++++++ 12 files changed, 1280 insertions(+), 2 deletions(-) create mode 100644 provd/generate.go create mode 100644 provd/internal/services/user/testdata/TestCreateUser/golden/successfully_creates_a_user create mode 100644 provd/internal/services/user/user.go create mode 100644 provd/internal/services/user/user_test.go create mode 100644 provd/internal/testutils/dbus.go create mode 100644 provd/protos/user.pb.go create mode 100644 provd/protos/user_grpc.pb.go diff --git a/provd/example.config.yaml b/provd/example.config.yaml index e2fd1c2cd..c9d9a0519 100644 --- a/provd/example.config.yaml +++ b/provd/example.config.yaml @@ -1,2 +1,2 @@ Paths: - Socket: "/run/test.sock" + Socket: "/home/ubuntu/sock" diff --git a/provd/generate.go b/provd/generate.go new file mode 100644 index 000000000..7de14dc03 --- /dev/null +++ b/provd/generate.go @@ -0,0 +1,8 @@ +// Package provd contains the autogenerated gRPC API used by the Ubuntu Desktop Init frontend. +package provd + +//TODO: Watch https://github.com/protocolbuffers/protobuf for any changes on the experimental status of optional fields, +// previously described on: https://github.com/protocolbuffers/protobuf/blob/main/docs/implementing_proto3_presence.md. +// +// Should it become default, remove the --experimental_allow_proto3_optional flag from the go generate command below. +//go:generate sh -c "PATH=\"$PATH:`go env GOPATH`/bin\" protoc --proto_path=. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./proto/user.proto --experimental_allow_proto3_optional" diff --git a/provd/go.mod b/provd/go.mod index c2680f21b..37b935817 100644 --- a/provd/go.mod +++ b/provd/go.mod @@ -16,6 +16,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/godbus/dbus/v5 v5.1.0 github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect diff --git a/provd/go.sum b/provd/go.sum index d2e83c4cc..c87140b48 100644 --- a/provd/go.sum +++ b/provd/go.sum @@ -3,7 +3,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:z60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -66,6 +66,8 @@ github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyT github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= diff --git a/provd/internal/services/manager.go b/provd/internal/services/manager.go index f1b8359f1..b5c3167d8 100644 --- a/provd/internal/services/manager.go +++ b/provd/internal/services/manager.go @@ -7,6 +7,8 @@ import ( "github.com/canonical/ubuntu-desktop-provision/provd" "github.com/canonical/ubuntu-desktop-provision/provd/internal/services/hello" + "github.com/canonical/ubuntu-desktop-provision/provd/internal/services/user" + proto "github.com/canonical/ubuntu-desktop-provision/provd/proto" "github.com/ubuntu/decorate" "google.golang.org/grpc" ) @@ -14,6 +16,7 @@ import ( // Manager mediates the whole business logic of the application. type Manager struct { helloService hello.Service + userService user.Service } // NewManager returns a new manager after creating all necessary items for our business logic. @@ -23,9 +26,11 @@ func NewManager(ctx context.Context) (m Manager, err error) { slog.Debug("Building provd object") helloService := hello.Service{} + userService := user.Service{} return Manager{ helloService: helloService, + userService: userService, }, nil } @@ -36,5 +41,6 @@ func (m Manager) RegisterGRPCServices(ctx context.Context) *grpc.Server { grpcServer := grpc.NewServer() provd.RegisterHelloWorldServiceServer(grpcServer, &m.helloService) + proto.RegisterUserServiceServer(grpcServer, &m.userService) return grpcServer } diff --git a/provd/internal/services/testdata/TestRegisterGRPCServices/golden b/provd/internal/services/testdata/TestRegisterGRPCServices/golden index 4838d5d03..4136aa632 100644 --- a/provd/internal/services/testdata/TestRegisterGRPCServices/golden +++ b/provd/internal/services/testdata/TestRegisterGRPCServices/golden @@ -4,3 +4,15 @@ provd.HelloWorldService: isclientstream: false isserverstream: false metadata: provd.protos +user.UserService: + methods: + - name: CreateUser + isclientstream: false + isserverstream: false + - name: ValidateUsername + isclientstream: false + isserverstream: false + - name: GetUser + isclientstream: false + isserverstream: false + metadata: protos/user.proto diff --git a/provd/internal/services/user/testdata/TestCreateUser/golden/successfully_creates_a_user b/provd/internal/services/user/testdata/TestCreateUser/golden/successfully_creates_a_user new file mode 100644 index 000000000..e69de29bb diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go new file mode 100644 index 000000000..19f6db218 --- /dev/null +++ b/provd/internal/services/user/user.go @@ -0,0 +1,266 @@ +// Package user implements the User gRPC service. +package user + +import ( + "context" + "crypto/rand" + "crypto/sha512" + "encoding/base64" + "fmt" + "strconv" + + "github.com/canonical/ubuntu-desktop-provision/provd/proto" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/godbus/dbus/v5" +) + +const ( + accountsDBusName string = "org.freedesktop.Accounts" + accountsDBusPath string = "/org/freedesktop/Accounts" + hostnameDBusName string = "org.freedesktop.hostname1" + hostnameDBusPath string = "/org/freedesktop/hostname1" + accountsUserIface string = "org.freedesktop.Accounts.User" + hostnameIface string = "org.freedesktop.hostname1" +) + +// Service is the implementation of the User module service. +type Service struct { + proto.UnimplementedUserServiceServer + string +} + +func hashPassword(password string) (string, error) { + // Generate a 16-byte salt + salt := make([]byte, 16) + if _, err := rand.Read(salt); err != nil { + return "", err + } + + // Hash the password with the salt + hasher := sha512.New() + hasher.Write(salt) + hasher.Write([]byte(password)) + hashedBytes := hasher.Sum(nil) + hashedPassword := base64.RawStdEncoding.EncodeToString(hashedBytes) + + // Format the hash in the SHA-512 crypt format + saltStr := base64.RawStdEncoding.EncodeToString(salt) + return fmt.Sprintf("$6$%s$%s", saltStr, hashedPassword), nil +} + +func (s *Service) CreateUser(ctx context.Context, req *proto.CreateUserRequest) (*proto.Empty, error) { + + // Validate requtest + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "received a nil request") + } + user := req.GetUser() + if user == nil { + return nil, status.Errorf(codes.InvalidArgument, "received a nil user") + } + username := user.GetUsername() + if username == "" { + return nil, status.Errorf(codes.InvalidArgument, "recieved an empty username") + } + realName := user.GetRealName() + if realName == "" { + return nil, status.Errorf(codes.InvalidArgument, "recieved an empty realName") + } + isAdmin := req.GetIsAdmin() + var accountType int32 + if isAdmin { + accountType = 1 + } else { + accountType = 0 + } + password := user.GetPassword() // allow empty password? + autologin := user.GetAutoLogin() + hostname := user.GetHostname() + if hostname == "" { + return nil, status.Errorf(codes.InvalidArgument, "recieved an empty hostname") + } + + // Connect to dbus + conn, err := dbus.ConnectSystemBus() + if err != nil { + return nil, status.Errorf(codes.Internal, "Failed to connect to session bus: %s", err) + } + defer conn.Close() + + // Create the user + accountObject := conn.Object(accountsDBusName, dbus.ObjectPath(accountsDBusPath)) + resp := accountObject.CallWithContext(ctx, "org.freedesktop.Accounts.CreateUser", 0, username, realName, accountType) + if resp.Err != nil { + return nil, status.Errorf(codes.Internal, "failed to create user: %s", resp.Err) + } + + // Retrieve the object path of the newly created user + var userObjectPath dbus.ObjectPath + err = resp.Store(&userObjectPath) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get user object path: %s", err) + } + + hashed, err := hashPassword(password) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to generate hashed password: %s", err) + } + + // Set the password for the user + userObject := conn.Object(accountsDBusName, userObjectPath) + fmt.Printf(hashed) + err = userObject.CallWithContext(ctx, "org.freedesktop.Accounts.User.SetPassword", 0, hashed, "").Err + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to set password: %s", err) + } + + // Set autologin for the user + err = userObject.CallWithContext(ctx, "org.freedesktop.Accounts.User.SetAutomaticLogin", 0, autologin).Err + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to set autologin: %s", err) + } + + // Set the hostname + hostnameObject := conn.Object(hostnameDBusName, dbus.ObjectPath(hostnameDBusPath)) + err = hostnameObject.CallWithContext(ctx, "org.freedesktop.hostname1.SetStaticHostname", 0, hostname, false).Err + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to set hostname: %s", err) + } + + return &proto.Empty{}, nil +} + +func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsernameRequest) (*proto.ValidateUsernameResponse, error) { + // Validate request + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "received a nil request") + } + username := req.GetUsername() + if username == "" { + return nil, status.Errorf(codes.InvalidArgument, "username cannot be empty") + } + + // Connect to dbus + conn, err := dbus.ConnectSystemBus() + if err != nil { + return nil, status.Errorf(codes.Internal, "Failed to connect to system bus: %s", err) + } + defer conn.Close() + + accountObject := conn.Object(accountsDBusName, dbus.ObjectPath(accountsDBusPath)) + + // Try to find the user by username + err = accountObject.CallWithContext(ctx, "org.freedesktop.Accounts.FindUserByName", 0, username).Err + if err != nil { + if _, ok := err.(dbus.Error); ok { + // User not found + return &proto.ValidateUsernameResponse{Valid: true}, nil + } + } + + // User found + return &proto.ValidateUsernameResponse{Valid: false}, nil +} + +func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*proto.GetUserResponse, error) { + + // Validate request + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "received a nil request") + } + uid := req.GetUserId() + if uid == "" { + return nil, status.Errorf(codes.InvalidArgument, "uid cannot be empty") + } + + // Connect to dbus + conn, err := dbus.ConnectSystemBus() + if err != nil { + return nil, status.Errorf(codes.Internal, "Failed to connect to session bus: %s", err) + } + defer conn.Close() + + accountObject := conn.Object(accountsDBusName, dbus.ObjectPath(accountsDBusPath)) + var userObjectPath dbus.ObjectPath + + uidInt, err := strconv.ParseInt(uid, 10, 64) + if err != nil { + // handle error: uid is not a valid integer + return nil, status.Errorf(codes.Internal, "int convert: %s", err) + + } + + err = accountObject.CallWithContext(ctx, "org.freedesktop.Accounts.FindUserById", 0, uidInt).Store(&userObjectPath) + if err != nil { + return nil, err + } + + userObject := conn.Object(accountsDBusName, userObjectPath) + hostnameObject := conn.Object(hostnameDBusName, dbus.ObjectPath(hostnameDBusPath)) + + // Get the username via dbus + response, err := userObject.GetProperty("org.freedesktop.Accounts.User.UserName") + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get username: %s", err) + } + if response.Value() == nil { + return nil, status.Errorf(codes.NotFound, "could not find username for given uid") + } + username, ok := response.Value().(string) + if !ok { + return nil, status.Errorf(codes.Internal, "unexpected type returned for username: %s", err) + } + + // Get the realName via dbus + response, err = userObject.GetProperty("org.freedesktop.Accounts.User." + "RealName") + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get realName: %s", err) + } + if response.Value() == nil { + return nil, status.Errorf(codes.NotFound, "could not find realName for given uid") + } + realName, ok := response.Value().(string) + if !ok { + return nil, status.Errorf(codes.Internal, "unexpected type returned for realName: %s", err) + } + + // Get autoLogin via dbus + response, err = userObject.GetProperty("org.freedesktop.Accounts.User." + "AutomaticLogin") + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get autoLogin: %s", err) + } + if response.Value() == nil { + return nil, status.Errorf(codes.NotFound, "could not find autoLogin for given uid") + } + autoLogin, ok := response.Value().(bool) + if !ok { + return nil, status.Errorf(codes.Internal, "unexpected type returned for autoLogin: %s", err) + } + + // Get the hostname via dbus + response, err = hostnameObject.GetProperty("org.freedesktop.hostname1." + "Hostname") + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get hostname: %s", err) + } + if response.Value() == nil { + return nil, status.Errorf(codes.NotFound, "could not find hostname") + } + hostname, ok := response.Value().(string) + if !ok { + return nil, status.Errorf(codes.Internal, "unexpected type returned for hostname: %s", err) + } + + user := &proto.User{ + RealName: realName, + Hostname: hostname, + Username: username, + AutoLogin: autoLogin, + } + + return &proto.GetUserResponse{ + User: user, + }, nil + +} diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go new file mode 100644 index 000000000..071c0bab2 --- /dev/null +++ b/provd/internal/services/user/user_test.go @@ -0,0 +1,116 @@ +package user_test + +import ( + "context" + "flag" + "fmt" + "net" + "os" + "path/filepath" + "testing" + + "github.com/canonical/ubuntu-desktop-provision/provd/internal/services/user" + "github.com/canonical/ubuntu-desktop-provision/provd/internal/testutils" + "github.com/canonical/ubuntu-desktop-provision/provd/proto" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func TestCreateUser(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + realName string + username string + password string + hostname string + autoLogin bool + + wantErr bool + }{ + "Successfully creates a user": {realName: "Matt", username: "matt", password: "password", hostname: "ubuntu", autoLogin: true}, + "Error when realName is empty": {realName: "", username: "matt", password: "password", hostname: "ubuntu", autoLogin: true, wantErr: true}, + "Error when username is empty": {realName: "Matt", username: "", password: "password", hostname: "ubuntu", autoLogin: true, wantErr: true}, + "Error when hostname is empty": {realName: "Matt", username: "matt", password: "password", hostname: "", autoLogin: true, wantErr: true}, + } + + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + + client := newUserClient(t) + + userReq := &proto.CreateUserRequest{ + User: &proto.User{ + RealName: tc.realName, + Username: tc.username, + Password: tc.password, + Hostname: tc.hostname, + AutoLogin: tc.autoLogin, + }, + } + + userResp, err := client.CreateUser(context.Background(), userReq) + if tc.wantErr { + require.Error(t, err, "CreateUser should return an error, but did not") + return + } + require.NoError(t, err, "CreateUser should not return an error, but did") + + got := userResp + want := testutils.LoadWithUpdateFromGolden(t, got.String()) + require.Equal(t, want, got, "CreateUser returned an unexpected response") + }) + } +} + +func newUserClient(t *testing.T) (client proto.UserServiceClient) { + t.Helper() + + // socket path is limited in length. + tmpDir, err := os.MkdirTemp("", "hello-socket-dir") + require.NoError(t, err, "Setup: could not setup temporary socket dir path") + t.Cleanup(func() { _ = os.RemoveAll(tmpDir) }) + socketPath := filepath.Join(tmpDir, "provd.sock") + + lis, err := net.Listen("unix", socketPath) + require.NoError(t, err, "Setup: could not create unix socket") + + service := user.Service{} + + grpcServer := grpc.NewServer() + proto.RegisterUserServiceServer(grpcServer, &service) + done := make(chan struct{}) + go func() { + defer close(done) + _ = grpcServer.Serve(lis) + }() + t.Cleanup(func() { + grpcServer.Stop() + <-done + }) + + conn, err := grpc.Dial("unix://"+socketPath, grpc.WithTransportCredentials(insecure.NewCredentials())) + require.NoError(t, err, "Setup: Could not connect to GRPC server") + t.Cleanup(func() { _ = conn.Close() }) + + return proto.NewUserServiceClient(conn) +} + +func TestMain(m *testing.M) { + testutils.InstallUpdateFlag() + flag.Parse() + + // Start system bus mock. + busCleanup, err := testutils.StartSystemBusMock() + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to start system bus mock: %v\n", err) + os.Exit(1) + } + defer busCleanup() + + // Run the tests. + os.Exit(m.Run()) +} diff --git a/provd/internal/testutils/dbus.go b/provd/internal/testutils/dbus.go new file mode 100644 index 000000000..3843ad837 --- /dev/null +++ b/provd/internal/testutils/dbus.go @@ -0,0 +1,102 @@ +// Package testutils provides utility functions and behaviors for testing. +package testutils + +import ( + "context" + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + "testing" + "time" + + "github.com/godbus/dbus/v5" +) + +const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket" + +var systemBusMockCfg = ` + + system + + unix:path=%s + + + + + + + +` + +// StartSystemBusMock starts a mock dbus daemon and returns a cancel function to stop it. +// +// This function uses t.Setenv to set the DBUS_SYSTEM_BUS_ADDRESS environment, so it shouldn't be used in parallel tests +// that rely on the mentioned variable. +func StartSystemBusMock() (func(), error) { + if isRunning() { + return nil, errors.New("system bus mock is already running") + } + + tmp, err := os.MkdirTemp(os.TempDir(), "authd-system-bus-mock") + if err != nil { + return nil, err + } + + cfgPath := filepath.Join(tmp, "bus.conf") + listenPath := filepath.Join(tmp, "bus.sock") + + err = os.WriteFile(cfgPath, []byte(fmt.Sprintf(systemBusMockCfg, listenPath)), 0600) + if err != nil { + err = errors.Join(err, os.RemoveAll(tmp)) + return nil, err + } + + busCtx, busCancel := context.WithCancel(context.Background()) + //#nosec:G204 // This is a test helper and we are in control of the arguments. + cmd := exec.CommandContext(busCtx, "dbus-daemon", "--config-file="+cfgPath) + if err := cmd.Start(); err != nil { + busCancel() + err = errors.Join(err, os.RemoveAll(tmp)) + return nil, err + } + // Give some time for the daemon to start. + time.Sleep(500 * time.Millisecond) + + prev, set := os.LookupEnv("DBUS_SYSTEM_BUS_ADDRESS") + os.Setenv("DBUS_SYSTEM_BUS_ADDRESS", "unix:path="+listenPath) + + return func() { + busCancel() + _ = cmd.Wait() + _ = os.RemoveAll(tmp) + + if !set { + os.Unsetenv("DBUS_SYSTEM_BUS_ADDRESS") + } else { + os.Setenv("DBUS_SYSTEM_BUS_ADDRESS", prev) + } + }, nil +} + +// GetSystemBusConnection returns a connection to the system bus with a safety check to avoid mistakenly connecting to the +// actual system bus. +func GetSystemBusConnection(t *testing.T) (*dbus.Conn, error) { + t.Helper() + if !isRunning() { + return nil, errors.New("system bus mock is not running. If that's intended, manually connect to the system bus instead of using this function") + } + conn, err := dbus.ConnectSystemBus() + if err != nil { + return nil, err + } + return conn, nil +} + +// isRunning checks if the system bus mock is running. +func isRunning() bool { + busAddr := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS") + return !(busAddr == "" || busAddr == defaultSystemBusAddress) +} diff --git a/provd/protos/user.pb.go b/provd/protos/user.pb.go new file mode 100644 index 000000000..4c60e5771 --- /dev/null +++ b/provd/protos/user.pb.go @@ -0,0 +1,582 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v3.20.3 +// source: proto/user.proto + +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Empty struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Empty) Reset() { + *x = Empty{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_user_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Empty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Empty) ProtoMessage() {} + +func (x *Empty) ProtoReflect() protoreflect.Message { + mi := &file_proto_user_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Empty.ProtoReflect.Descriptor instead. +func (*Empty) Descriptor() ([]byte, []int) { + return file_proto_user_proto_rawDescGZIP(), []int{0} +} + +type GetUserRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` +} + +func (x *GetUserRequest) Reset() { + *x = GetUserRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_user_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUserRequest) ProtoMessage() {} + +func (x *GetUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_user_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetUserRequest.ProtoReflect.Descriptor instead. +func (*GetUserRequest) Descriptor() ([]byte, []int) { + return file_proto_user_proto_rawDescGZIP(), []int{1} +} + +func (x *GetUserRequest) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +type User struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RealName string `protobuf:"bytes,1,opt,name=realName,proto3" json:"realName,omitempty"` + Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"` + Username string `protobuf:"bytes,3,opt,name=username,proto3" json:"username,omitempty"` + Password string `protobuf:"bytes,4,opt,name=password,proto3" json:"password,omitempty"` + AutoLogin bool `protobuf:"varint,5,opt,name=autoLogin,proto3" json:"autoLogin,omitempty"` +} + +func (x *User) Reset() { + *x = User{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_user_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *User) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*User) ProtoMessage() {} + +func (x *User) ProtoReflect() protoreflect.Message { + mi := &file_proto_user_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use User.ProtoReflect.Descriptor instead. +func (*User) Descriptor() ([]byte, []int) { + return file_proto_user_proto_rawDescGZIP(), []int{2} +} + +func (x *User) GetRealName() string { + if x != nil { + return x.RealName + } + return "" +} + +func (x *User) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +func (x *User) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *User) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +func (x *User) GetAutoLogin() bool { + if x != nil { + return x.AutoLogin + } + return false +} + +type GetUserResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` +} + +func (x *GetUserResponse) Reset() { + *x = GetUserResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_user_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetUserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUserResponse) ProtoMessage() {} + +func (x *GetUserResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_user_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetUserResponse.ProtoReflect.Descriptor instead. +func (*GetUserResponse) Descriptor() ([]byte, []int) { + return file_proto_user_proto_rawDescGZIP(), []int{3} +} + +func (x *GetUserResponse) GetUser() *User { + if x != nil { + return x.User + } + return nil +} + +type CreateUserRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + IsAdmin bool `protobuf:"varint,2,opt,name=is_admin,json=isAdmin,proto3" json:"is_admin,omitempty"` +} + +func (x *CreateUserRequest) Reset() { + *x = CreateUserRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_user_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateUserRequest) ProtoMessage() {} + +func (x *CreateUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_user_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateUserRequest.ProtoReflect.Descriptor instead. +func (*CreateUserRequest) Descriptor() ([]byte, []int) { + return file_proto_user_proto_rawDescGZIP(), []int{4} +} + +func (x *CreateUserRequest) GetUser() *User { + if x != nil { + return x.User + } + return nil +} + +func (x *CreateUserRequest) GetIsAdmin() bool { + if x != nil { + return x.IsAdmin + } + return false +} + +type ValidateUsernameRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` +} + +func (x *ValidateUsernameRequest) Reset() { + *x = ValidateUsernameRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_user_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValidateUsernameRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidateUsernameRequest) ProtoMessage() {} + +func (x *ValidateUsernameRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_user_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValidateUsernameRequest.ProtoReflect.Descriptor instead. +func (*ValidateUsernameRequest) Descriptor() ([]byte, []int) { + return file_proto_user_proto_rawDescGZIP(), []int{5} +} + +func (x *ValidateUsernameRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +type ValidateUsernameResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Valid bool `protobuf:"varint,1,opt,name=valid,proto3" json:"valid,omitempty"` +} + +func (x *ValidateUsernameResponse) Reset() { + *x = ValidateUsernameResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_user_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValidateUsernameResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidateUsernameResponse) ProtoMessage() {} + +func (x *ValidateUsernameResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_user_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValidateUsernameResponse.ProtoReflect.Descriptor instead. +func (*ValidateUsernameResponse) Descriptor() ([]byte, []int) { + return file_proto_user_proto_rawDescGZIP(), []int{6} +} + +func (x *ValidateUsernameResponse) GetValid() bool { + if x != nil { + return x.Valid + } + return false +} + +var File_proto_user_proto protoreflect.FileDescriptor + +var file_proto_user_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x22, 0x29, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x94, 0x01, 0x0a, + 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x4e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x67, + 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x75, 0x74, 0x6f, 0x4c, 0x6f, + 0x67, 0x69, 0x6e, 0x22, 0x31, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, + 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x4e, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x04, 0x75, + 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x75, 0x73, 0x65, 0x72, + 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x69, + 0x73, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, + 0x73, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x22, 0x35, 0x0a, 0x17, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x30, 0x0a, + 0x18, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x32, + 0xcc, 0x01, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, + 0x36, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x14, 0x2e, 0x75, 0x73, 0x65, + 0x72, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x15, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x51, 0x0a, 0x10, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x1d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, + 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3b, + 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x61, 0x6e, + 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x75, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x2d, 0x64, 0x65, + 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x2d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2f, + 0x70, 0x72, 0x6f, 0x76, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_proto_user_proto_rawDescOnce sync.Once + file_proto_user_proto_rawDescData = file_proto_user_proto_rawDesc +) + +func file_proto_user_proto_rawDescGZIP() []byte { + file_proto_user_proto_rawDescOnce.Do(func() { + file_proto_user_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_user_proto_rawDescData) + }) + return file_proto_user_proto_rawDescData +} + +var file_proto_user_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_proto_user_proto_goTypes = []interface{}{ + (*Empty)(nil), // 0: user.Empty + (*GetUserRequest)(nil), // 1: user.GetUserRequest + (*User)(nil), // 2: user.User + (*GetUserResponse)(nil), // 3: user.GetUserResponse + (*CreateUserRequest)(nil), // 4: user.CreateUserRequest + (*ValidateUsernameRequest)(nil), // 5: user.ValidateUsernameRequest + (*ValidateUsernameResponse)(nil), // 6: user.ValidateUsernameResponse +} +var file_proto_user_proto_depIdxs = []int32{ + 2, // 0: user.GetUserResponse.user:type_name -> user.User + 2, // 1: user.CreateUserRequest.user:type_name -> user.User + 1, // 2: user.UserService.GetUser:input_type -> user.GetUserRequest + 4, // 3: user.UserService.CreateUser:input_type -> user.CreateUserRequest + 5, // 4: user.UserService.ValidateUsername:input_type -> user.ValidateUsernameRequest + 3, // 5: user.UserService.GetUser:output_type -> user.GetUserResponse + 0, // 6: user.UserService.CreateUser:output_type -> user.Empty + 6, // 7: user.UserService.ValidateUsername:output_type -> user.ValidateUsernameResponse + 5, // [5:8] is the sub-list for method output_type + 2, // [2:5] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_proto_user_proto_init() } +func file_proto_user_proto_init() { + if File_proto_user_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proto_user_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Empty); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_user_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetUserRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_user_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*User); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_user_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetUserResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_user_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateUserRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_user_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateUsernameRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_user_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateUsernameResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_user_proto_rawDesc, + NumEnums: 0, + NumMessages: 7, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_user_proto_goTypes, + DependencyIndexes: file_proto_user_proto_depIdxs, + MessageInfos: file_proto_user_proto_msgTypes, + }.Build() + File_proto_user_proto = out.File + file_proto_user_proto_rawDesc = nil + file_proto_user_proto_goTypes = nil + file_proto_user_proto_depIdxs = nil +} diff --git a/provd/protos/user_grpc.pb.go b/provd/protos/user_grpc.pb.go new file mode 100644 index 000000000..f01f962c0 --- /dev/null +++ b/provd/protos/user_grpc.pb.go @@ -0,0 +1,183 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v3.20.3 +// source: proto/user.proto + +package proto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + UserService_GetUser_FullMethodName = "/user.UserService/GetUser" + UserService_CreateUser_FullMethodName = "/user.UserService/CreateUser" + UserService_ValidateUsername_FullMethodName = "/user.UserService/ValidateUsername" +) + +// UserServiceClient is the client API for UserService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type UserServiceClient interface { + GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error) + CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*Empty, error) + ValidateUsername(ctx context.Context, in *ValidateUsernameRequest, opts ...grpc.CallOption) (*ValidateUsernameResponse, error) +} + +type userServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewUserServiceClient(cc grpc.ClientConnInterface) UserServiceClient { + return &userServiceClient{cc} +} + +func (c *userServiceClient) GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error) { + out := new(GetUserResponse) + err := c.cc.Invoke(ctx, UserService_GetUser_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *userServiceClient) CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*Empty, error) { + out := new(Empty) + err := c.cc.Invoke(ctx, UserService_CreateUser_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *userServiceClient) ValidateUsername(ctx context.Context, in *ValidateUsernameRequest, opts ...grpc.CallOption) (*ValidateUsernameResponse, error) { + out := new(ValidateUsernameResponse) + err := c.cc.Invoke(ctx, UserService_ValidateUsername_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// UserServiceServer is the server API for UserService service. +// All implementations must embed UnimplementedUserServiceServer +// for forward compatibility +type UserServiceServer interface { + GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) + CreateUser(context.Context, *CreateUserRequest) (*Empty, error) + ValidateUsername(context.Context, *ValidateUsernameRequest) (*ValidateUsernameResponse, error) + mustEmbedUnimplementedUserServiceServer() +} + +// UnimplementedUserServiceServer must be embedded to have forward compatible implementations. +type UnimplementedUserServiceServer struct { +} + +func (UnimplementedUserServiceServer) GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetUser not implemented") +} +func (UnimplementedUserServiceServer) CreateUser(context.Context, *CreateUserRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateUser not implemented") +} +func (UnimplementedUserServiceServer) ValidateUsername(context.Context, *ValidateUsernameRequest) (*ValidateUsernameResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValidateUsername not implemented") +} +func (UnimplementedUserServiceServer) mustEmbedUnimplementedUserServiceServer() {} + +// UnsafeUserServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to UserServiceServer will +// result in compilation errors. +type UnsafeUserServiceServer interface { + mustEmbedUnimplementedUserServiceServer() +} + +func RegisterUserServiceServer(s grpc.ServiceRegistrar, srv UserServiceServer) { + s.RegisterService(&UserService_ServiceDesc, srv) +} + +func _UserService_GetUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetUserRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).GetUser(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: UserService_GetUser_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).GetUser(ctx, req.(*GetUserRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _UserService_CreateUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateUserRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).CreateUser(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: UserService_CreateUser_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).CreateUser(ctx, req.(*CreateUserRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _UserService_ValidateUsername_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ValidateUsernameRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).ValidateUsername(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: UserService_ValidateUsername_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).ValidateUsername(ctx, req.(*ValidateUsernameRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// UserService_ServiceDesc is the grpc.ServiceDesc for UserService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var UserService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "user.UserService", + HandlerType: (*UserServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetUser", + Handler: _UserService_GetUser_Handler, + }, + { + MethodName: "CreateUser", + Handler: _UserService_CreateUser_Handler, + }, + { + MethodName: "ValidateUsername", + Handler: _UserService_ValidateUsername_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/user.proto", +} From 0924f3161941f1c14bd69b926aa28ad79c54f2e4 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 9 Jan 2024 16:55:42 +0200 Subject: [PATCH 053/106] feat: adding systembus testutil --- provd/internal/testutils/systembus.go | 126 ++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 provd/internal/testutils/systembus.go diff --git a/provd/internal/testutils/systembus.go b/provd/internal/testutils/systembus.go new file mode 100644 index 000000000..c6924c445 --- /dev/null +++ b/provd/internal/testutils/systembus.go @@ -0,0 +1,126 @@ +package testutils + +import ( + "context" + "log" + "os" + "os/exec" + "path/filepath" + "sync" + "testing" + + "github.com/godbus/dbus/v5" + "github.com/stretchr/testify/require" +) + +var ( + sdbus sync.Once + + sdbusMU sync.Mutex + nbRunningTestsSdbus uint + stopDbus context.CancelFunc + dbusCmd *exec.Cmd + config string + savedDbusSystemAddress string +) + +// StartLocalSystemBus allows to start and set environment variable to a local bus, preventing polluting system ones. +func StartLocalSystemBus() func() { + sdbusMU.Lock() + defer sdbusMU.Unlock() + nbRunningTestsSdbus++ + + sdbus.Do(func() { + dir, err := os.MkdirTemp("", "tests-dbus") + if err != nil { + log.Fatalf("Setup: can’t create dbus system directory: %v", err) + } + + savedDbusSystemAddress = os.Getenv("DBUS_SYSTEM_BUS_ADDRESS") + config = filepath.Join(dir, "dbus.config") + err = os.WriteFile(config, []byte(` + + system + + unix:tmpdir=/tmp + + + + + + +`), 0600) + if err != nil { + log.Fatalf("Setup: can’t create dbus configuration: %v", err) + } + var ctx context.Context + ctx, stopDbus = context.WithCancel(context.Background()) + // #nosec G204 - this is only for tests, we are in control of the config + dbusCmd = exec.CommandContext(ctx, "dbus-daemon", "--print-address=1", "--config-file="+config) + dbusStdout, err := dbusCmd.StdoutPipe() + if err != nil { + _ = os.RemoveAll(dir) + log.Fatalf("couldn't get stdout of dbus-daemon: %v", err) + } + if err := dbusCmd.Start(); err != nil { + _ = os.RemoveAll(dir) + log.Fatalf("couldn't start dbus-daemon: %v", err) + } + dbusAddr := make([]byte, 256) + n, err := dbusStdout.Read(dbusAddr) + if err != nil { + _ = os.RemoveAll(dir) + log.Fatalf("couldn't get dbus address: %v", err) + } + dbusAddr = dbusAddr[:n] + if err := os.Setenv("DBUS_SYSTEM_BUS_ADDRESS", string(dbusAddr)); err != nil { + _ = os.RemoveAll(dir) + log.Fatalf("couldn't set DBUS_SYSTEM_BUS_ADDRESS: %v", err) + } + }) + + return func() { + sdbusMU.Lock() + defer sdbusMU.Unlock() + nbRunningTestsSdbus-- + + if nbRunningTestsSdbus != 0 { + return + } + + stopDbus() + // dbus command is killed + _ = dbusCmd.Wait() + + if err := os.RemoveAll(filepath.Dir(config)); err != nil { + log.Fatalf("couldn't remove dbus configuration directory: %v", err) + } + + if err := os.Setenv("DBUS_SYSTEM_BUS_ADDRESS", savedDbusSystemAddress); err != nil { + log.Fatalf("couldn't restore DBUS_SYSTEM_BUS_ADDRESS: %v", err) + } + + // Restore dbus system launcher + sdbus = sync.Once{} + } +} + +// NewDbusConn returns a system dbus connection which automatically close on test shutdown. +func NewDbusConn(t *testing.T) *dbus.Conn { + t.Helper() + + bus, err := dbus.SystemBusPrivate() + require.NoError(t, err, "Setup: can’t get a private system bus") + + t.Cleanup(func() { + err = bus.Close() + require.NoError(t, err, "Teardown: can’t close system dbus connection") + }) + err = bus.Auth(nil) + require.NoError(t, err, "Setup: can’t auth on private system bus") + err = bus.Hello() + require.NoError(t, err, "Setup: can’t send hello message on private system bus") + + return bus +} From d859520a500cb75cfe3f54c398628eabe0026a4f Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 9 Jan 2024 16:55:42 +0200 Subject: [PATCH 054/106] refacor: use userObjectFactory --- provd/cmd/provd/daemon/daemon.go | 2 + provd/internal/services/manager.go | 33 ++++++++++- provd/internal/services/user/user.go | 86 +++++++++++++++++++++------- 3 files changed, 97 insertions(+), 24 deletions(-) diff --git a/provd/cmd/provd/daemon/daemon.go b/provd/cmd/provd/daemon/daemon.go index fdb18f075..cb1242149 100644 --- a/provd/cmd/provd/daemon/daemon.go +++ b/provd/cmd/provd/daemon/daemon.go @@ -97,6 +97,8 @@ func (a *App) serve(config daemonConfig) error { return err } + defer func() { _ = m.Stop() }() + socketPath := config.Paths.Socket var daemonopts []daemon.Option if socketPath != "" { diff --git a/provd/internal/services/manager.go b/provd/internal/services/manager.go index b5c3167d8..b36675380 100644 --- a/provd/internal/services/manager.go +++ b/provd/internal/services/manager.go @@ -9,28 +9,50 @@ import ( "github.com/canonical/ubuntu-desktop-provision/provd/internal/services/hello" "github.com/canonical/ubuntu-desktop-provision/provd/internal/services/user" proto "github.com/canonical/ubuntu-desktop-provision/provd/proto" + "github.com/godbus/dbus/v5" + "github.com/ubuntu/decorate" "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +const ( + accountsDBusName string = "org.freedesktop.Accounts" + accountsDBusPath string = "/org/freedesktop/Accounts" + hostnameDBusName string = "org.freedesktop.hostname1" + hostnameDBusPath string = "/org/freedesktop/hostname1" + accountsUserIface string = "org.freedesktop.Accounts.User" + hostnameIface string = "org.freedesktop.hostname1" ) // Manager mediates the whole business logic of the application. type Manager struct { helloService hello.Service userService user.Service + bus *dbus.Conn } // NewManager returns a new manager after creating all necessary items for our business logic. func NewManager(ctx context.Context) (m Manager, err error) { defer decorate.OnError(&err, "can't create provd object") + // Connect to dbus + bus, err := dbus.ConnectSystemBus() + if err != nil { + return Manager{}, status.Errorf(codes.Internal, "Failed to connect to system bus: %s", err) + } + slog.Debug("Building provd object") helloService := hello.Service{} - userService := user.Service{} + + userService := user.New(bus) return Manager{ helloService: helloService, - userService: userService, + userService: *userService, + bus: bus, }, nil } @@ -44,3 +66,10 @@ func (m Manager) RegisterGRPCServices(ctx context.Context) *grpc.Server { proto.RegisterUserServiceServer(grpcServer, &m.userService) return grpcServer } + +// stop closes the dbus connection. +func (m *Manager) Stop() error { + slog.Debug("Closing grpc manager and dbus connection") + + return m.bus.Close() +} diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index 19f6db218..6b4152e73 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -7,6 +7,7 @@ import ( "crypto/sha512" "encoding/base64" "fmt" + "log/slog" "strconv" "github.com/canonical/ubuntu-desktop-provision/provd/proto" @@ -25,10 +26,61 @@ const ( hostnameIface string = "org.freedesktop.hostname1" ) +type Caller interface { + Call(method string, flags dbus.Flags, args ...interface{}) *dbus.Call +} + +type UserObjectFactory interface { + GetUserObject(userObjectPath dbus.ObjectPath) Caller +} + +type userObjectFactoryImpl struct { + conn *dbus.Conn +} + +func (f *userObjectFactoryImpl) GetUserObject(userObjectPath dbus.ObjectPath) Caller { + userObject := f.conn.Object(accountsDBusName, userObjectPath) + return userObject +} + +type options struct { + accounts Caller + hostname Caller + userFactory UserObjectFactory +} + +type option func(*options) + // Service is the implementation of the User module service. type Service struct { proto.UnimplementedUserServiceServer - string + accounts Caller + hostname Caller + userFactory UserObjectFactory +} + +// New retuns a new instance of the User service. +func New(bus *dbus.Conn, args ...option) *Service { + accounts := bus.Object("org.freedesktop.Accounts", "/org/freedesktop/Accounts") + hostname := bus.Object("org.freedesktop.hostname1", "/org/freedesktop/hostname1") + userFactory := &userObjectFactoryImpl{conn: bus} + + opts := options{ + accounts: accounts, + hostname: hostname, + userFactory: userFactory, + } + + // Apply given options + for _, f := range args { + f(&opts) + } + + return &Service{ + accounts: opts.accounts, + hostname: opts.hostname, + userFactory: opts.userFactory, + } } func hashPassword(password string) (string, error) { @@ -81,26 +133,17 @@ func (s *Service) CreateUser(ctx context.Context, req *proto.CreateUserRequest) if hostname == "" { return nil, status.Errorf(codes.InvalidArgument, "recieved an empty hostname") } + // Create the user + var userObjectPath dbus.ObjectPath + call := s.accounts.Call("org.freedesktop.Accounts.CreateUser", 0, username, realName, accountType) - // Connect to dbus - conn, err := dbus.ConnectSystemBus() - if err != nil { - return nil, status.Errorf(codes.Internal, "Failed to connect to session bus: %s", err) - } - defer conn.Close() + // Log the response (assuming you have a logger set up) - // Create the user - accountObject := conn.Object(accountsDBusName, dbus.ObjectPath(accountsDBusPath)) - resp := accountObject.CallWithContext(ctx, "org.freedesktop.Accounts.CreateUser", 0, username, realName, accountType) - if resp.Err != nil { - return nil, status.Errorf(codes.Internal, "failed to create user: %s", resp.Err) - } + slog.Info(fmt.Sprintf("%v", call.Body)) - // Retrieve the object path of the newly created user - var userObjectPath dbus.ObjectPath - err = resp.Store(&userObjectPath) + err := call.Store(&userObjectPath) if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get user object path: %s", err) + return nil, status.Errorf(codes.Internal, "failed to create user: %s", err) } hashed, err := hashPassword(password) @@ -109,22 +152,21 @@ func (s *Service) CreateUser(ctx context.Context, req *proto.CreateUserRequest) } // Set the password for the user - userObject := conn.Object(accountsDBusName, userObjectPath) + userObject := s.userFactory.GetUserObject(userObjectPath) fmt.Printf(hashed) - err = userObject.CallWithContext(ctx, "org.freedesktop.Accounts.User.SetPassword", 0, hashed, "").Err + err = userObject.Call("org.freedesktop.Accounts.User.SetPassword", 0, hashed, "").Err if err != nil { return nil, status.Errorf(codes.Internal, "failed to set password: %s", err) } // Set autologin for the user - err = userObject.CallWithContext(ctx, "org.freedesktop.Accounts.User.SetAutomaticLogin", 0, autologin).Err + err = userObject.Call("org.freedesktop.Accounts.User.SetAutomaticLogin", 0, autologin).Err if err != nil { return nil, status.Errorf(codes.Internal, "failed to set autologin: %s", err) } // Set the hostname - hostnameObject := conn.Object(hostnameDBusName, dbus.ObjectPath(hostnameDBusPath)) - err = hostnameObject.CallWithContext(ctx, "org.freedesktop.hostname1.SetStaticHostname", 0, hostname, false).Err + err = s.hostname.Call("org.freedesktop.hostname1.SetStaticHostname", 0, hostname, false).Err if err != nil { return nil, status.Errorf(codes.Internal, "failed to set hostname: %s", err) } From cb4b58dca84dc20686e221f06072c593c550e812 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 9 Jan 2024 16:55:42 +0200 Subject: [PATCH 055/106] feat: create mocks for testing --- provd/internal/services/user/export_test.go | 121 ++++++++++++++++++++ provd/internal/testutils/dbus.go | 102 ----------------- 2 files changed, 121 insertions(+), 102 deletions(-) create mode 100644 provd/internal/services/user/export_test.go delete mode 100644 provd/internal/testutils/dbus.go diff --git a/provd/internal/services/user/export_test.go b/provd/internal/services/user/export_test.go new file mode 100644 index 000000000..f0979378a --- /dev/null +++ b/provd/internal/services/user/export_test.go @@ -0,0 +1,121 @@ +package user + +import ( + "errors" + + "github.com/godbus/dbus/v5" +) + +// WithAccounts overrides the accounts caller for the service. +func WithAccounts(accounts Caller) option { + return func(o *options) { + o.accounts = accounts + } +} + +// WithHostname overrides the hostname caller for the service. +func WithHostname(hostname Caller) option { + return func(o *options) { + o.hostname = hostname + } +} + +// WithUserFactory overrides the userObjectFactory for the service. +func WithUserFactory(userFactory UserObjectFactory) option { + return func(o *options) { + o.userFactory = userFactory + } +} + +type AccountsObjectMock struct { + WantError bool + UserObjectPath dbus.ObjectPath +} + +type HostnameObjectMock struct { + WantError bool + Hostname string +} + +type UserObjectMock struct { + WantError bool + Properties map[string]interface{} +} + +func (a *AccountsObjectMock) Call(method string, flags dbus.Flags, args ...interface{}) *dbus.Call { + var err error + + if a.WantError { + err = errors.New("Accounts object error") + } + + var callResult interface{} + + switch method { + case "org.freedesktop.Accounts.CreateUser": + callResult = a.UserObjectPath + default: + err = errors.New("Unsupported method") + } + + return &dbus.Call{ + Err: err, + Body: []interface{}{callResult}, + } +} + +func (h *HostnameObjectMock) Call(method string, flags dbus.Flags, args ...interface{}) *dbus.Call { + var err error + + if h.WantError { + err = errors.New("Hostname object error") + } + + var callResult interface{} + + switch method { + case "org.freedesktop.hostname1.SetStaticHostname": + h.Hostname = args[0].(string) + callResult = []interface{}{} + default: + err = errors.New("Unsupported method") + } + + return &dbus.Call{ + Err: err, + Body: []interface{}{callResult}, + } +} + +func (u *UserObjectMock) Call(method string, flags dbus.Flags, args ...interface{}) *dbus.Call { + var err error + if u.WantError { + err = errors.New("User object error") + } + + callResult := dbus.ObjectPath("/org/freedesktop/Accounts/User1000") + + return &dbus.Call{ + Err: err, + Body: []interface{}{callResult}, + } +} + +func (u *UserObjectMock) GetProperty(prop string) (dbus.Variant, error) { + if u.WantError { + return dbus.Variant{}, errors.New("GetProperty error") + } + value, exists := u.Properties[prop] + if !exists { + return dbus.Variant{}, errors.New("Property not found") + } + return dbus.MakeVariant(value), nil +} + +type UserObjectFactoryMock struct { + UserObject *UserObjectMock +} + +func (f UserObjectFactoryMock) GetUserObject(userObjectPath dbus.ObjectPath) Caller { + return f.UserObject +} diff --git a/provd/internal/testutils/dbus.go b/provd/internal/testutils/dbus.go deleted file mode 100644 index 3843ad837..000000000 --- a/provd/internal/testutils/dbus.go +++ /dev/null @@ -1,102 +0,0 @@ -// Package testutils provides utility functions and behaviors for testing. -package testutils - -import ( - "context" - "errors" - "fmt" - "os" - "os/exec" - "path/filepath" - "testing" - "time" - - "github.com/godbus/dbus/v5" -) - -const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket" - -var systemBusMockCfg = ` - - system - - unix:path=%s - - - - - - - -` - -// StartSystemBusMock starts a mock dbus daemon and returns a cancel function to stop it. -// -// This function uses t.Setenv to set the DBUS_SYSTEM_BUS_ADDRESS environment, so it shouldn't be used in parallel tests -// that rely on the mentioned variable. -func StartSystemBusMock() (func(), error) { - if isRunning() { - return nil, errors.New("system bus mock is already running") - } - - tmp, err := os.MkdirTemp(os.TempDir(), "authd-system-bus-mock") - if err != nil { - return nil, err - } - - cfgPath := filepath.Join(tmp, "bus.conf") - listenPath := filepath.Join(tmp, "bus.sock") - - err = os.WriteFile(cfgPath, []byte(fmt.Sprintf(systemBusMockCfg, listenPath)), 0600) - if err != nil { - err = errors.Join(err, os.RemoveAll(tmp)) - return nil, err - } - - busCtx, busCancel := context.WithCancel(context.Background()) - //#nosec:G204 // This is a test helper and we are in control of the arguments. - cmd := exec.CommandContext(busCtx, "dbus-daemon", "--config-file="+cfgPath) - if err := cmd.Start(); err != nil { - busCancel() - err = errors.Join(err, os.RemoveAll(tmp)) - return nil, err - } - // Give some time for the daemon to start. - time.Sleep(500 * time.Millisecond) - - prev, set := os.LookupEnv("DBUS_SYSTEM_BUS_ADDRESS") - os.Setenv("DBUS_SYSTEM_BUS_ADDRESS", "unix:path="+listenPath) - - return func() { - busCancel() - _ = cmd.Wait() - _ = os.RemoveAll(tmp) - - if !set { - os.Unsetenv("DBUS_SYSTEM_BUS_ADDRESS") - } else { - os.Setenv("DBUS_SYSTEM_BUS_ADDRESS", prev) - } - }, nil -} - -// GetSystemBusConnection returns a connection to the system bus with a safety check to avoid mistakenly connecting to the -// actual system bus. -func GetSystemBusConnection(t *testing.T) (*dbus.Conn, error) { - t.Helper() - if !isRunning() { - return nil, errors.New("system bus mock is not running. If that's intended, manually connect to the system bus instead of using this function") - } - conn, err := dbus.ConnectSystemBus() - if err != nil { - return nil, err - } - return conn, nil -} - -// isRunning checks if the system bus mock is running. -func isRunning() bool { - busAddr := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS") - return !(busAddr == "" || busAddr == defaultSystemBusAddress) -} From e21efcc517327d9818d7729a4a209a1bc943f48b Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 9 Jan 2024 16:55:42 +0200 Subject: [PATCH 056/106] test: CreateUser tests --- provd/internal/services/user/user_test.go | 115 ++++++++++++++++------ 1 file changed, 83 insertions(+), 32 deletions(-) diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go index 071c0bab2..7bb6d8f71 100644 --- a/provd/internal/services/user/user_test.go +++ b/provd/internal/services/user/user_test.go @@ -2,8 +2,6 @@ package user_test import ( "context" - "flag" - "fmt" "net" "os" "path/filepath" @@ -12,6 +10,7 @@ import ( "github.com/canonical/ubuntu-desktop-provision/provd/internal/services/user" "github.com/canonical/ubuntu-desktop-provision/provd/internal/testutils" "github.com/canonical/ubuntu-desktop-provision/provd/proto" + "github.com/godbus/dbus/v5" "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -27,20 +26,90 @@ func TestCreateUser(t *testing.T) { hostname string autoLogin bool + accountsError bool + hostnameError bool + wantErr bool }{ - "Successfully creates a user": {realName: "Matt", username: "matt", password: "password", hostname: "ubuntu", autoLogin: true}, - "Error when realName is empty": {realName: "", username: "matt", password: "password", hostname: "ubuntu", autoLogin: true, wantErr: true}, - "Error when username is empty": {realName: "Matt", username: "", password: "password", hostname: "ubuntu", autoLogin: true, wantErr: true}, - "Error when hostname is empty": {realName: "Matt", username: "matt", password: "password", hostname: "", autoLogin: true, wantErr: true}, + "Successfully creates a user": { + realName: "Matt", + username: "matt", + password: "password", + hostname: "ubuntu", + autoLogin: true, + }, + "Error when realName is empty": { + realName: "", + username: "matt", + password: "password", + hostname: "ubuntu", + autoLogin: true, + wantErr: true, + }, + "Error when username is empty": { + realName: "Matt", + username: "", + password: "password", + hostname: "ubuntu", + autoLogin: true, + wantErr: true, + }, + "Error when hostname is empty": { + realName: "Matt", + username: "matt", + password: "password", + hostname: "", + autoLogin: true, + wantErr: true, + }, + "Error from Accounts service": { + realName: "Matt", + username: "matt", + password: "password", + hostname: "ubuntu", + autoLogin: true, + accountsError: true, + wantErr: true, + }, + "Error from Hostname service": { + realName: "Matt", + username: "matt", + password: "password", + hostname: "ubuntu", + autoLogin: true, + hostnameError: true, + wantErr: true, + }, } for name, tc := range tests { tc := tc t.Run(name, func(t *testing.T) { t.Parallel() + t.Cleanup(testutils.StartLocalSystemBus()) + + accountsMock := &user.AccountsObjectMock{ + UserObjectPath: dbus.ObjectPath("/org/freedesktop/Accounts/User1000"), + WantError: tc.accountsError, + } + + hostnameMock := &user.HostnameObjectMock{ + Hostname: tc.hostname, + WantError: tc.hostnameError, + } + + userFactoryMock := user.UserObjectFactoryMock{ + &user.UserObjectMock{ + Properties: map[string]interface{}{ + "RealName": tc.realName, + "UserName": tc.username, + "Password": tc.password, + "AutoLogin": tc.autoLogin, + }, + }, + } - client := newUserClient(t) + client := newUserClient(t, accountsMock, hostnameMock, userFactoryMock) userReq := &proto.CreateUserRequest{ User: &proto.User{ @@ -52,23 +121,18 @@ func TestCreateUser(t *testing.T) { }, } - userResp, err := client.CreateUser(context.Background(), userReq) + _, err := client.CreateUser(context.Background(), userReq) if tc.wantErr { require.Error(t, err, "CreateUser should return an error, but did not") return } require.NoError(t, err, "CreateUser should not return an error, but did") - - got := userResp - want := testutils.LoadWithUpdateFromGolden(t, got.String()) - require.Equal(t, want, got, "CreateUser returned an unexpected response") }) } } -func newUserClient(t *testing.T) (client proto.UserServiceClient) { +func newUserClient(t *testing.T, accountsMock user.Caller, hostnameMock user.Caller, userFactoryMock user.UserObjectFactory) proto.UserServiceClient { t.Helper() - // socket path is limited in length. tmpDir, err := os.MkdirTemp("", "hello-socket-dir") require.NoError(t, err, "Setup: could not setup temporary socket dir path") @@ -78,10 +142,13 @@ func newUserClient(t *testing.T) (client proto.UserServiceClient) { lis, err := net.Listen("unix", socketPath) require.NoError(t, err, "Setup: could not create unix socket") - service := user.Service{} + bus := testutils.NewDbusConn(t) + + // Create the service with the necessary mocks + service := user.New(bus, user.WithAccounts(accountsMock), user.WithHostname(hostnameMock), user.WithUserFactory(userFactoryMock)) grpcServer := grpc.NewServer() - proto.RegisterUserServiceServer(grpcServer, &service) + proto.RegisterUserServiceServer(grpcServer, service) done := make(chan struct{}) go func() { defer close(done) @@ -98,19 +165,3 @@ func newUserClient(t *testing.T) (client proto.UserServiceClient) { return proto.NewUserServiceClient(conn) } - -func TestMain(m *testing.M) { - testutils.InstallUpdateFlag() - flag.Parse() - - // Start system bus mock. - busCleanup, err := testutils.StartSystemBusMock() - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to start system bus mock: %v\n", err) - os.Exit(1) - } - defer busCleanup() - - // Run the tests. - os.Exit(m.Run()) -} From 7eff9469f7599db84d1cd9ab64930668dbe5c1eb Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 9 Jan 2024 16:57:16 +0200 Subject: [PATCH 057/106] test: validate user tests --- .../testdata/TestRegisterGRPCServices/golden | 5 +- provd/internal/services/user/export_test.go | 13 +++- .../golden/successfully_creates_a_user | 0 .../golden/existing_username | 1 + .../golden/valid_username | 1 + provd/internal/services/user/user.go | 25 ++++--- provd/internal/services/user/user_test.go | 65 +++++++++++++++++++ 7 files changed, 95 insertions(+), 15 deletions(-) delete mode 100644 provd/internal/services/user/testdata/TestCreateUser/golden/successfully_creates_a_user create mode 100644 provd/internal/services/user/testdata/TestValidateUsername/golden/existing_username create mode 100644 provd/internal/services/user/testdata/TestValidateUsername/golden/valid_username diff --git a/provd/internal/services/testdata/TestRegisterGRPCServices/golden b/provd/internal/services/testdata/TestRegisterGRPCServices/golden index 4136aa632..3f051b1f2 100644 --- a/provd/internal/services/testdata/TestRegisterGRPCServices/golden +++ b/provd/internal/services/testdata/TestRegisterGRPCServices/golden @@ -6,6 +6,9 @@ provd.HelloWorldService: metadata: provd.protos user.UserService: methods: + - name: GetUser + isclientstream: false + isserverstream: false - name: CreateUser isclientstream: false isserverstream: false @@ -15,4 +18,4 @@ user.UserService: - name: GetUser isclientstream: false isserverstream: false - metadata: protos/user.proto + diff --git a/provd/internal/services/user/export_test.go b/provd/internal/services/user/export_test.go index f0979378a..c47f8d400 100644 --- a/provd/internal/services/user/export_test.go +++ b/provd/internal/services/user/export_test.go @@ -46,7 +46,16 @@ func (a *AccountsObjectMock) Call(method string, flags dbus.Flags, args ...inter var err error if a.WantError { - err = errors.New("Accounts object error") + switch method { + case "org.freedesktop.Accounts.FindUserByName": + dbusError := dbus.Error{ + Name: "org.freedesktop.Accounts.Error.Failed", + Body: []interface{}{"specific error message"}, + } + err = dbusError + default: + err = errors.New("Accounts object error") + } } var callResult interface{} @@ -54,6 +63,8 @@ func (a *AccountsObjectMock) Call(method string, flags dbus.Flags, args ...inter switch method { case "org.freedesktop.Accounts.CreateUser": callResult = a.UserObjectPath + case "org.freedesktop.Accounts.FindUserByName": + callResult = a.UserObjectPath default: err = errors.New("Unsupported method") } diff --git a/provd/internal/services/user/testdata/TestCreateUser/golden/successfully_creates_a_user b/provd/internal/services/user/testdata/TestCreateUser/golden/successfully_creates_a_user deleted file mode 100644 index e69de29bb..000000000 diff --git a/provd/internal/services/user/testdata/TestValidateUsername/golden/existing_username b/provd/internal/services/user/testdata/TestValidateUsername/golden/existing_username new file mode 100644 index 000000000..02e4a84d6 --- /dev/null +++ b/provd/internal/services/user/testdata/TestValidateUsername/golden/existing_username @@ -0,0 +1 @@ +false \ No newline at end of file diff --git a/provd/internal/services/user/testdata/TestValidateUsername/golden/valid_username b/provd/internal/services/user/testdata/TestValidateUsername/golden/valid_username new file mode 100644 index 000000000..f32a5804e --- /dev/null +++ b/provd/internal/services/user/testdata/TestValidateUsername/golden/valid_username @@ -0,0 +1 @@ +true \ No newline at end of file diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index 6b4152e73..beec49946 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -184,26 +184,25 @@ func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsern return nil, status.Errorf(codes.InvalidArgument, "username cannot be empty") } - // Connect to dbus - conn, err := dbus.ConnectSystemBus() + err := s.accounts.Call("org.freedesktop.Accounts.FindUserByName", 0, username).Err if err != nil { - return nil, status.Errorf(codes.Internal, "Failed to connect to system bus: %s", err) - } - defer conn.Close() - accountObject := conn.Object(accountsDBusName, dbus.ObjectPath(accountsDBusPath)) - - // Try to find the user by username - err = accountObject.CallWithContext(ctx, "org.freedesktop.Accounts.FindUserByName", 0, username).Err - if err != nil { - if _, ok := err.(dbus.Error); ok { - // User not found - return &proto.ValidateUsernameResponse{Valid: true}, nil + // Check if the error is due to user not being found or a D-Bus error + if dbusError, ok := err.(dbus.Error); ok { + if dbusError.Name == "org.freedesktop.Accounts.Error.Failed" { + // User not found + return &proto.ValidateUsernameResponse{Valid: true}, nil + } + // Handle other dbus errors + return nil, status.Errorf(codes.Internal, "dbus error: %v", dbusError) } + // Unknown error + return nil, status.Errorf(codes.Internal, "unknown error: %v", err) } // User found return &proto.ValidateUsernameResponse{Valid: false}, nil + } func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*proto.GetUserResponse, error) { diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go index 7bb6d8f71..49113ac83 100644 --- a/provd/internal/services/user/user_test.go +++ b/provd/internal/services/user/user_test.go @@ -2,9 +2,11 @@ package user_test import ( "context" + "flag" "net" "os" "path/filepath" + "strconv" "testing" "github.com/canonical/ubuntu-desktop-provision/provd/internal/services/user" @@ -131,6 +133,63 @@ func TestCreateUser(t *testing.T) { } } +func TestValidateUsername(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + username string + accountsError bool + wantErr bool + }{ + "Valid username": { + username: "newuser", + accountsError: true, + wantErr: false, + }, + "Existing username": { + username: "existinguser", + accountsError: false, + wantErr: false, + }, + "Empty username": { + username: "", + wantErr: true, + }, + } + + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + t.Cleanup(testutils.StartLocalSystemBus()) + + accountsMock := &user.AccountsObjectMock{ + WantError: tc.accountsError, + } + + hostnameMock := &user.HostnameObjectMock{} + userFactoryMock := user.UserObjectFactoryMock{} + + client := newUserClient(t, accountsMock, hostnameMock, userFactoryMock) + + validateReq := &proto.ValidateUsernameRequest{ + Username: tc.username, + } + + resp, err := client.ValidateUsername(context.Background(), validateReq) + if tc.wantErr { + require.Error(t, err, "ValidateUsername should return an error, but did not") + return + } + require.NoError(t, err, "ValidateUsername should not return an error, but did") + + got := strconv.FormatBool(resp.GetValid()) + want := testutils.LoadWithUpdateFromGolden(t, got) + require.Equal(t, want, got, "ValidateUsername returned an unexpected response") + }) + } +} + func newUserClient(t *testing.T, accountsMock user.Caller, hostnameMock user.Caller, userFactoryMock user.UserObjectFactory) proto.UserServiceClient { t.Helper() // socket path is limited in length. @@ -165,3 +224,9 @@ func newUserClient(t *testing.T, accountsMock user.Caller, hostnameMock user.Cal return proto.NewUserServiceClient(conn) } + +func TestMain(m *testing.M) { + testutils.InstallUpdateFlag() + flag.Parse() + os.Exit(m.Run()) +} From e19c6712c5785b66d6e0cfda34d47b8ac486c416 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 9 Jan 2024 16:57:16 +0200 Subject: [PATCH 058/106] test: GetUser tests --- provd/internal/services/user/export_test.go | 37 ++++- .../golden/successfully_gets_a_user | 1 + provd/internal/services/user/user.go | 21 +-- provd/internal/services/user/user_test.go | 144 +++++++++++++++--- 4 files changed, 163 insertions(+), 40 deletions(-) create mode 100644 provd/internal/services/user/testdata/TestGetUser/golden/successfully_gets_a_user diff --git a/provd/internal/services/user/export_test.go b/provd/internal/services/user/export_test.go index c47f8d400..732adef3f 100644 --- a/provd/internal/services/user/export_test.go +++ b/provd/internal/services/user/export_test.go @@ -30,11 +30,12 @@ func WithUserFactory(userFactory UserObjectFactory) option { type AccountsObjectMock struct { WantError bool UserObjectPath dbus.ObjectPath + Properties map[string]interface{} } type HostnameObjectMock struct { - WantError bool - Hostname string + WantError bool + Properties map[string]interface{} } type UserObjectMock struct { @@ -65,8 +66,10 @@ func (a *AccountsObjectMock) Call(method string, flags dbus.Flags, args ...inter callResult = a.UserObjectPath case "org.freedesktop.Accounts.FindUserByName": callResult = a.UserObjectPath + case "org.freedesktop.Accounts.FindUserById": + callResult = a.UserObjectPath default: - err = errors.New("Unsupported method") + err = errors.New("Unsupported accounts method") } return &dbus.Call{ @@ -86,10 +89,9 @@ func (h *HostnameObjectMock) Call(method string, flags dbus.Flags, args ...inter switch method { case "org.freedesktop.hostname1.SetStaticHostname": - h.Hostname = args[0].(string) callResult = []interface{}{} default: - err = errors.New("Unsupported method") + err = errors.New("Unsupported hostname method") } return &dbus.Call{ @@ -118,7 +120,30 @@ func (u *UserObjectMock) GetProperty(prop string) (dbus.Variant, error) { } value, exists := u.Properties[prop] if !exists { - return dbus.Variant{}, errors.New("Property not found") + + return dbus.Variant{}, errors.New("User property not found: " + prop) + } + return dbus.MakeVariant(value), nil +} + +func (u *AccountsObjectMock) GetProperty(prop string) (dbus.Variant, error) { + if u.WantError { + return dbus.Variant{}, errors.New("GetProperty error") + } + value, exists := u.Properties[prop] + if !exists { + return dbus.Variant{}, errors.New("Accounts property not found") + } + return dbus.MakeVariant(value), nil +} + +func (u *HostnameObjectMock) GetProperty(prop string) (dbus.Variant, error) { + if u.WantError { + return dbus.Variant{}, errors.New("GetProperty error") + } + value, exists := u.Properties[prop] + if !exists { + return dbus.Variant{}, errors.New("Hostname1 property not found") } return dbus.MakeVariant(value), nil } diff --git a/provd/internal/services/user/testdata/TestGetUser/golden/successfully_gets_a_user b/provd/internal/services/user/testdata/TestGetUser/golden/successfully_gets_a_user new file mode 100644 index 000000000..57780b531 --- /dev/null +++ b/provd/internal/services/user/testdata/TestGetUser/golden/successfully_gets_a_user @@ -0,0 +1 @@ +realName:"Matt" hostname:"ubuntu" username:"matt" autoLogin:true \ No newline at end of file diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index beec49946..c007d58ca 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -28,6 +28,7 @@ const ( type Caller interface { Call(method string, flags dbus.Flags, args ...interface{}) *dbus.Call + GetProperty(p string) (dbus.Variant, error) } type UserObjectFactory interface { @@ -216,33 +217,23 @@ func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*prot return nil, status.Errorf(codes.InvalidArgument, "uid cannot be empty") } - // Connect to dbus - conn, err := dbus.ConnectSystemBus() - if err != nil { - return nil, status.Errorf(codes.Internal, "Failed to connect to session bus: %s", err) - } - defer conn.Close() - - accountObject := conn.Object(accountsDBusName, dbus.ObjectPath(accountsDBusPath)) - var userObjectPath dbus.ObjectPath - uidInt, err := strconv.ParseInt(uid, 10, 64) if err != nil { // handle error: uid is not a valid integer return nil, status.Errorf(codes.Internal, "int convert: %s", err) } + var userObjectPath dbus.ObjectPath + err = s.accounts.Call("org.freedesktop.Accounts.FindUserById", 0, uidInt).Store(&userObjectPath) - err = accountObject.CallWithContext(ctx, "org.freedesktop.Accounts.FindUserById", 0, uidInt).Store(&userObjectPath) if err != nil { return nil, err } - userObject := conn.Object(accountsDBusName, userObjectPath) - hostnameObject := conn.Object(hostnameDBusName, dbus.ObjectPath(hostnameDBusPath)) + userObject := s.userFactory.GetUserObject(userObjectPath) // Get the username via dbus - response, err := userObject.GetProperty("org.freedesktop.Accounts.User.UserName") + response, err := userObject.GetProperty("org.freedesktop.Accounts.User." + "UserName") if err != nil { return nil, status.Errorf(codes.Internal, "failed to get username: %s", err) } @@ -281,7 +272,7 @@ func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*prot } // Get the hostname via dbus - response, err = hostnameObject.GetProperty("org.freedesktop.hostname1." + "Hostname") + response, err = s.hostname.GetProperty("org.freedesktop.hostname1." + "Hostname") if err != nil { return nil, status.Errorf(codes.Internal, "failed to get hostname: %s", err) } diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go index 49113ac83..627d6bc2f 100644 --- a/provd/internal/services/user/user_test.go +++ b/provd/internal/services/user/user_test.go @@ -34,22 +34,22 @@ func TestCreateUser(t *testing.T) { wantErr bool }{ "Successfully creates a user": { - realName: "Matt", - username: "matt", + realName: "Ubuntu", + username: "ubuntu", password: "password", hostname: "ubuntu", autoLogin: true, }, "Error when realName is empty": { realName: "", - username: "matt", + username: "ubuntu", password: "password", hostname: "ubuntu", autoLogin: true, wantErr: true, }, "Error when username is empty": { - realName: "Matt", + realName: "Ubuntu", username: "", password: "password", hostname: "ubuntu", @@ -57,16 +57,16 @@ func TestCreateUser(t *testing.T) { wantErr: true, }, "Error when hostname is empty": { - realName: "Matt", - username: "matt", + realName: "Ubuntu", + username: "ubuntu", password: "password", hostname: "", autoLogin: true, wantErr: true, }, "Error from Accounts service": { - realName: "Matt", - username: "matt", + realName: "Ubuntu", + username: "ubuntu", password: "password", hostname: "ubuntu", autoLogin: true, @@ -74,8 +74,8 @@ func TestCreateUser(t *testing.T) { wantErr: true, }, "Error from Hostname service": { - realName: "Matt", - username: "matt", + realName: "Ubuntu", + username: "ubuntu", password: "password", hostname: "ubuntu", autoLogin: true, @@ -96,19 +96,11 @@ func TestCreateUser(t *testing.T) { } hostnameMock := &user.HostnameObjectMock{ - Hostname: tc.hostname, WantError: tc.hostnameError, } userFactoryMock := user.UserObjectFactoryMock{ - &user.UserObjectMock{ - Properties: map[string]interface{}{ - "RealName": tc.realName, - "UserName": tc.username, - "Password": tc.password, - "AutoLogin": tc.autoLogin, - }, - }, + &user.UserObjectMock{}, } client := newUserClient(t, accountsMock, hostnameMock, userFactoryMock) @@ -133,6 +125,120 @@ func TestCreateUser(t *testing.T) { } } +func TestGetUser(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + uid string + + realName string + username string + password string + hostname string + autoLogin bool + + accountsError bool + hostnameError bool + + wantErr bool + }{ + "Successfully gets a user": { + uid: "1000", + realName: "Ubuntu", + username: "ubuntu", + password: "password", + hostname: "ubuntu", + autoLogin: true, + }, + "Error when uid is empty": { + uid: "", + realName: "Ubuntu", + username: "ubuntu", + password: "password", + hostname: "ubuntu", + autoLogin: true, + wantErr: true, + }, + "Error when uid is not a number": { + uid: "notanumber", + realName: "Ubuntu", + username: "ubuntu", + password: "password", + hostname: "ubuntu", + autoLogin: true, + wantErr: true, + }, + "Error when accounts returns an error": { + uid: "9999", + realName: "Ubuntu", + username: "ubuntu", + password: "password", + hostname: "ubuntu", + autoLogin: true, + wantErr: true, + accountsError: true, + }, + "Error when hostname returns an error": { + uid: "1000", + realName: "Ubuntu", + username: "ubuntu", + password: "password", + hostname: "ubuntu", + autoLogin: true, + wantErr: true, + hostnameError: true, + }, + } + + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + t.Cleanup(testutils.StartLocalSystemBus()) + + accountsMock := &user.AccountsObjectMock{ + WantError: tc.accountsError, + } + + hostnameMock := &user.HostnameObjectMock{ + WantError: tc.hostnameError, + Properties: map[string]interface{}{ + "org.freedesktop.hostname1.Hostname": tc.hostname, + }, + } + userFactoryMock := user.UserObjectFactoryMock{ + &user.UserObjectMock{ + Properties: map[string]interface{}{ + "org.freedesktop.Accounts.User.RealName": tc.realName, + "org.freedesktop.Accounts.User.UserName": tc.username, + "org.freedesktop.Accounts.User.Password": tc.password, + "org.freedesktop.Accounts.User.AutomaticLogin": tc.autoLogin, + }, + }, + } + + client := newUserClient(t, accountsMock, hostnameMock, userFactoryMock) + + getReq := &proto.GetUserRequest{ + UserId: tc.uid, + } + + resp, err := client.GetUser(context.Background(), getReq) + if tc.wantErr { + require.Error(t, err, "ValidateUsername should return an error, but did not") + return + } + require.NoError(t, err, "ValidateUsername should not return an error, but did") + + got := resp.GetUser().String() + want := testutils.LoadWithUpdateFromGolden(t, got) + require.Equal(t, want, got, "ValidateUsername returned an unexpected response") + + }) + + } +} + func TestValidateUsername(t *testing.T) { t.Parallel() From 240e85dfb2d21cb7c50d53e451103ca650773f57 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 9 Jan 2024 16:57:28 +0200 Subject: [PATCH 059/106] test: lp runner requirements --- provd/cmd/provd/daemon/daemon_test.go | 4 ++++ provd/go.mod | 2 +- provd/internal/services/manager.go | 2 -- provd/internal/services/manager_test.go | 2 ++ provd/internal/services/user/user.go | 5 ----- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/provd/cmd/provd/daemon/daemon_test.go b/provd/cmd/provd/daemon/daemon_test.go index a84769115..1a4feb5e0 100644 --- a/provd/cmd/provd/daemon/daemon_test.go +++ b/provd/cmd/provd/daemon/daemon_test.go @@ -11,6 +11,8 @@ import ( "time" "github.com/canonical/ubuntu-desktop-provision/provd/cmd/provd/daemon" + "github.com/canonical/ubuntu-desktop-provision/provd/internal/testutils" + // "github.com/canonical/ubuntu-desktop-provision/provd/internal/consts" "github.com/stretchr/testify/require" ) @@ -244,6 +246,7 @@ func TestConfigLoad(t *testing.T) { } func TestAutoDetectConfig(t *testing.T) { + t.Cleanup(testutils.StartLocalSystemBus()) customizedSocketPath := filepath.Join(t.TempDir(), "mysocket") var config daemon.DaemonConfig config.Paths.Socket = customizedSocketPath @@ -327,6 +330,7 @@ func requireGoroutineStarted(t *testing.T, f func()) { // to wait for the daemon to stop. func startDaemon(t *testing.T, conf *daemon.DaemonConfig) (app *daemon.App, done func()) { t.Helper() + t.Cleanup(testutils.StartLocalSystemBus()) a := daemon.NewForTests(t, conf) diff --git a/provd/go.mod b/provd/go.mod index 37b935817..790e746a5 100644 --- a/provd/go.mod +++ b/provd/go.mod @@ -1,6 +1,6 @@ module github.com/canonical/ubuntu-desktop-provision/provd -go 1.21.5 +go 1.21.1 require ( github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf diff --git a/provd/internal/services/manager.go b/provd/internal/services/manager.go index b36675380..f916b6486 100644 --- a/provd/internal/services/manager.go +++ b/provd/internal/services/manager.go @@ -43,8 +43,6 @@ func NewManager(ctx context.Context) (m Manager, err error) { return Manager{}, status.Errorf(codes.Internal, "Failed to connect to system bus: %s", err) } - slog.Debug("Building provd object") - helloService := hello.Service{} userService := user.New(bus) diff --git a/provd/internal/services/manager_test.go b/provd/internal/services/manager_test.go index 14ac15e6b..0839046f9 100644 --- a/provd/internal/services/manager_test.go +++ b/provd/internal/services/manager_test.go @@ -18,6 +18,7 @@ func TestNewManager(t *testing.T) { "Successfully creates the manager": {}, } for name, tc := range tests { + t.Cleanup(testutils.StartLocalSystemBus()) tc := tc t.Run(name, func(t *testing.T) { _, err := services.NewManager(context.Background()) @@ -34,6 +35,7 @@ func TestNewManager(t *testing.T) { func TestRegisterGRPCServices(t *testing.T) { t.Parallel() + t.Cleanup(testutils.StartLocalSystemBus()) m, err := services.NewManager(context.Background()) require.NoError(t, err, "Setup: could not create manager for the test") diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index c007d58ca..82f5ad8d8 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -7,7 +7,6 @@ import ( "crypto/sha512" "encoding/base64" "fmt" - "log/slog" "strconv" "github.com/canonical/ubuntu-desktop-provision/provd/proto" @@ -138,10 +137,6 @@ func (s *Service) CreateUser(ctx context.Context, req *proto.CreateUserRequest) var userObjectPath dbus.ObjectPath call := s.accounts.Call("org.freedesktop.Accounts.CreateUser", 0, username, realName, accountType) - // Log the response (assuming you have a logger set up) - - slog.Info(fmt.Sprintf("%v", call.Body)) - err := call.Store(&userObjectPath) if err != nil { return nil, status.Errorf(codes.Internal, "failed to create user: %s", err) From 7e0544d466623573e758e7b286c09e6f24c43429 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 9 Jan 2024 16:57:44 +0200 Subject: [PATCH 060/106] style(provd): cleanup consts --- provd/example.config.yaml | 2 +- provd/internal/services/manager.go | 9 ----- .../testdata/TestRegisterGRPCServices/golden | 5 +-- provd/internal/services/user/user.go | 36 +++++++++---------- provd/internal/services/user/user_test.go | 2 +- 5 files changed, 19 insertions(+), 35 deletions(-) diff --git a/provd/example.config.yaml b/provd/example.config.yaml index c9d9a0519..e2fd1c2cd 100644 --- a/provd/example.config.yaml +++ b/provd/example.config.yaml @@ -1,2 +1,2 @@ Paths: - Socket: "/home/ubuntu/sock" + Socket: "/run/test.sock" diff --git a/provd/internal/services/manager.go b/provd/internal/services/manager.go index f916b6486..1cc29843f 100644 --- a/provd/internal/services/manager.go +++ b/provd/internal/services/manager.go @@ -17,15 +17,6 @@ import ( "google.golang.org/grpc/status" ) -const ( - accountsDBusName string = "org.freedesktop.Accounts" - accountsDBusPath string = "/org/freedesktop/Accounts" - hostnameDBusName string = "org.freedesktop.hostname1" - hostnameDBusPath string = "/org/freedesktop/hostname1" - accountsUserIface string = "org.freedesktop.Accounts.User" - hostnameIface string = "org.freedesktop.hostname1" -) - // Manager mediates the whole business logic of the application. type Manager struct { helloService hello.Service diff --git a/provd/internal/services/testdata/TestRegisterGRPCServices/golden b/provd/internal/services/testdata/TestRegisterGRPCServices/golden index 3f051b1f2..49bc27d61 100644 --- a/provd/internal/services/testdata/TestRegisterGRPCServices/golden +++ b/provd/internal/services/testdata/TestRegisterGRPCServices/golden @@ -6,9 +6,6 @@ provd.HelloWorldService: metadata: provd.protos user.UserService: methods: - - name: GetUser - isclientstream: false - isserverstream: false - name: CreateUser isclientstream: false isserverstream: false @@ -18,4 +15,4 @@ user.UserService: - name: GetUser isclientstream: false isserverstream: false - + metadata: proto/user.proto diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index 82f5ad8d8..201389807 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -17,12 +17,8 @@ import ( ) const ( - accountsDBusName string = "org.freedesktop.Accounts" - accountsDBusPath string = "/org/freedesktop/Accounts" - hostnameDBusName string = "org.freedesktop.hostname1" - hostnameDBusPath string = "/org/freedesktop/hostname1" - accountsUserIface string = "org.freedesktop.Accounts.User" - hostnameIface string = "org.freedesktop.hostname1" + dbusAccountsPrefix = "org.freedesktop.Accounts" + dbusHostnamePrefix = "org.freedesktop.hostname1" ) type Caller interface { @@ -39,7 +35,7 @@ type userObjectFactoryImpl struct { } func (f *userObjectFactoryImpl) GetUserObject(userObjectPath dbus.ObjectPath) Caller { - userObject := f.conn.Object(accountsDBusName, userObjectPath) + userObject := f.conn.Object(dbusAccountsPrefix, userObjectPath) return userObject } @@ -61,8 +57,8 @@ type Service struct { // New retuns a new instance of the User service. func New(bus *dbus.Conn, args ...option) *Service { - accounts := bus.Object("org.freedesktop.Accounts", "/org/freedesktop/Accounts") - hostname := bus.Object("org.freedesktop.hostname1", "/org/freedesktop/hostname1") + accounts := bus.Object(dbusAccountsPrefix, "/org/freedesktop/Accounts") + hostname := bus.Object(dbusHostnamePrefix, "/org/freedesktop/hostname1") userFactory := &userObjectFactoryImpl{conn: bus} opts := options{ @@ -135,7 +131,7 @@ func (s *Service) CreateUser(ctx context.Context, req *proto.CreateUserRequest) } // Create the user var userObjectPath dbus.ObjectPath - call := s.accounts.Call("org.freedesktop.Accounts.CreateUser", 0, username, realName, accountType) + call := s.accounts.Call(dbusAccountsPrefix+".CreateUser", 0, username, realName, accountType) err := call.Store(&userObjectPath) if err != nil { @@ -150,19 +146,19 @@ func (s *Service) CreateUser(ctx context.Context, req *proto.CreateUserRequest) // Set the password for the user userObject := s.userFactory.GetUserObject(userObjectPath) fmt.Printf(hashed) - err = userObject.Call("org.freedesktop.Accounts.User.SetPassword", 0, hashed, "").Err + err = userObject.Call(dbusAccountsPrefix+".User.SetPassword", 0, hashed, "").Err if err != nil { return nil, status.Errorf(codes.Internal, "failed to set password: %s", err) } // Set autologin for the user - err = userObject.Call("org.freedesktop.Accounts.User.SetAutomaticLogin", 0, autologin).Err + err = userObject.Call(dbusAccountsPrefix+".User.SetAutomaticLogin", 0, autologin).Err if err != nil { return nil, status.Errorf(codes.Internal, "failed to set autologin: %s", err) } // Set the hostname - err = s.hostname.Call("org.freedesktop.hostname1.SetStaticHostname", 0, hostname, false).Err + err = s.hostname.Call(dbusHostnamePrefix+".SetStaticHostname", 0, hostname, false).Err if err != nil { return nil, status.Errorf(codes.Internal, "failed to set hostname: %s", err) } @@ -180,12 +176,12 @@ func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsern return nil, status.Errorf(codes.InvalidArgument, "username cannot be empty") } - err := s.accounts.Call("org.freedesktop.Accounts.FindUserByName", 0, username).Err + err := s.accounts.Call(dbusAccountsPrefix+".FindUserByName", 0, username).Err if err != nil { // Check if the error is due to user not being found or a D-Bus error if dbusError, ok := err.(dbus.Error); ok { - if dbusError.Name == "org.freedesktop.Accounts.Error.Failed" { + if dbusError.Name == dbusAccountsPrefix+".Error.Failed" { // User not found return &proto.ValidateUsernameResponse{Valid: true}, nil } @@ -219,7 +215,7 @@ func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*prot } var userObjectPath dbus.ObjectPath - err = s.accounts.Call("org.freedesktop.Accounts.FindUserById", 0, uidInt).Store(&userObjectPath) + err = s.accounts.Call(dbusAccountsPrefix+".FindUserById", 0, uidInt).Store(&userObjectPath) if err != nil { return nil, err @@ -228,7 +224,7 @@ func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*prot userObject := s.userFactory.GetUserObject(userObjectPath) // Get the username via dbus - response, err := userObject.GetProperty("org.freedesktop.Accounts.User." + "UserName") + response, err := userObject.GetProperty(dbusAccountsPrefix + ".User.UserName") if err != nil { return nil, status.Errorf(codes.Internal, "failed to get username: %s", err) } @@ -241,7 +237,7 @@ func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*prot } // Get the realName via dbus - response, err = userObject.GetProperty("org.freedesktop.Accounts.User." + "RealName") + response, err = userObject.GetProperty(dbusAccountsPrefix + ".User.RealName") if err != nil { return nil, status.Errorf(codes.Internal, "failed to get realName: %s", err) } @@ -254,7 +250,7 @@ func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*prot } // Get autoLogin via dbus - response, err = userObject.GetProperty("org.freedesktop.Accounts.User." + "AutomaticLogin") + response, err = userObject.GetProperty(dbusAccountsPrefix + ".User.AutomaticLogin") if err != nil { return nil, status.Errorf(codes.Internal, "failed to get autoLogin: %s", err) } @@ -267,7 +263,7 @@ func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*prot } // Get the hostname via dbus - response, err = s.hostname.GetProperty("org.freedesktop.hostname1." + "Hostname") + response, err = s.hostname.GetProperty(dbusHostnamePrefix + ".Hostname") if err != nil { return nil, status.Errorf(codes.Internal, "failed to get hostname: %s", err) } diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go index 627d6bc2f..daa3a3e6b 100644 --- a/provd/internal/services/user/user_test.go +++ b/provd/internal/services/user/user_test.go @@ -120,7 +120,7 @@ func TestCreateUser(t *testing.T) { require.Error(t, err, "CreateUser should return an error, but did not") return } - require.NoError(t, err, "CreateUser should not return an error, but did") + require.NoError(t, err, "CreateUser should not return an error, but did not") }) } } From f317e1fc5009ffcc60a5ac1ae013ea1e35afc66b Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 9 Jan 2024 16:57:44 +0200 Subject: [PATCH 061/106] fix(provd): invalid salt length & chars --- .../testdata/TestRegisterGRPCServices/golden | 6 +-- .../golden/successfully_gets_a_user | 2 +- .../TestHashPassword/golden/valid_password | 1 + provd/internal/services/user/user.go | 44 ++++++++++++++----- provd/internal/services/user/user_test.go | 36 +++++++++++++++ 5 files changed, 75 insertions(+), 14 deletions(-) create mode 100644 provd/internal/services/user/testdata/TestHashPassword/golden/valid_password diff --git a/provd/internal/services/testdata/TestRegisterGRPCServices/golden b/provd/internal/services/testdata/TestRegisterGRPCServices/golden index 49bc27d61..4606dd57a 100644 --- a/provd/internal/services/testdata/TestRegisterGRPCServices/golden +++ b/provd/internal/services/testdata/TestRegisterGRPCServices/golden @@ -6,13 +6,13 @@ provd.HelloWorldService: metadata: provd.protos user.UserService: methods: - - name: CreateUser + - name: GetUser isclientstream: false isserverstream: false - - name: ValidateUsername + - name: CreateUser isclientstream: false isserverstream: false - - name: GetUser + - name: ValidateUsername isclientstream: false isserverstream: false metadata: proto/user.proto diff --git a/provd/internal/services/user/testdata/TestGetUser/golden/successfully_gets_a_user b/provd/internal/services/user/testdata/TestGetUser/golden/successfully_gets_a_user index 57780b531..4a58abcfa 100644 --- a/provd/internal/services/user/testdata/TestGetUser/golden/successfully_gets_a_user +++ b/provd/internal/services/user/testdata/TestGetUser/golden/successfully_gets_a_user @@ -1 +1 @@ -realName:"Matt" hostname:"ubuntu" username:"matt" autoLogin:true \ No newline at end of file +realName:"Ubuntu" hostname:"ubuntu" username:"ubuntu" autoLogin:true \ No newline at end of file diff --git a/provd/internal/services/user/testdata/TestHashPassword/golden/valid_password b/provd/internal/services/user/testdata/TestHashPassword/golden/valid_password new file mode 100644 index 000000000..e63545eff --- /dev/null +++ b/provd/internal/services/user/testdata/TestHashPassword/golden/valid_password @@ -0,0 +1 @@ +$6$pepper$WICKqYlM18NvutvLOKnT+munY4NnMKMj8CVlyP779Oh1elwSb3oEdrXa+iZnLZpqvA65Nwtdegp39Uv3blX8IA \ No newline at end of file diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index 201389807..e9bc18948 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -7,6 +7,7 @@ import ( "crypto/sha512" "encoding/base64" "fmt" + "math/big" "strconv" "github.com/canonical/ubuntu-desktop-provision/provd/proto" @@ -79,23 +80,46 @@ func New(bus *dbus.Conn, args ...option) *Service { } } -func hashPassword(password string) (string, error) { - // Generate a 16-byte salt - salt := make([]byte, 16) - if _, err := rand.Read(salt); err != nil { - return "", err +const validSalts = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./" + +// Generate a salt string of the specified length using validSalts characters. +func generateSalt(length int) (string, error) { + salt := make([]byte, length) + for i := range salt { + index, err := rand.Int(rand.Reader, big.NewInt(int64(len(validSalts)))) + if err != nil { + return "", err + } + salt[i] = validSalts[index.Int64()] + } + return string(salt), nil +} + +func HashPassword(password string, testSalt *string) (string, error) { + if password == "" { + return "", status.Errorf(codes.InvalidArgument, "recieved an empty password") + } + var salt string + var err error + + if testSalt != nil { + salt = *testSalt + } else { + // Generate a 16-byte salt + salt, err = generateSalt(16) + if err != nil { + return "", err + } } // Hash the password with the salt hasher := sha512.New() - hasher.Write(salt) - hasher.Write([]byte(password)) + hasher.Write([]byte(salt + password)) hashedBytes := hasher.Sum(nil) hashedPassword := base64.RawStdEncoding.EncodeToString(hashedBytes) // Format the hash in the SHA-512 crypt format - saltStr := base64.RawStdEncoding.EncodeToString(salt) - return fmt.Sprintf("$6$%s$%s", saltStr, hashedPassword), nil + return fmt.Sprintf("$6$%s$%s", salt, hashedPassword), nil } func (s *Service) CreateUser(ctx context.Context, req *proto.CreateUserRequest) (*proto.Empty, error) { @@ -138,7 +162,7 @@ func (s *Service) CreateUser(ctx context.Context, req *proto.CreateUserRequest) return nil, status.Errorf(codes.Internal, "failed to create user: %s", err) } - hashed, err := hashPassword(password) + hashed, err := HashPassword(password, nil) if err != nil { return nil, status.Errorf(codes.Internal, "failed to generate hashed password: %s", err) } diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go index daa3a3e6b..c03407d0c 100644 --- a/provd/internal/services/user/user_test.go +++ b/provd/internal/services/user/user_test.go @@ -18,6 +18,42 @@ import ( "google.golang.org/grpc/credentials/insecure" ) +func TestHashPassword(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + password string + wantErr bool + }{ + "Valid password": { + password: "securepassword123", + wantErr: false, + }, + "Empty password": { + password: "", + wantErr: true, + }, + } + + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + salt := "pepper" + got, err := user.HashPassword(tc.password, &salt) + + if tc.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err, "HashPassword should not return an error, but did") + + want := testutils.LoadWithUpdateFromGolden(t, got) + require.Equal(t, want, got, "HashPassword returned an unexpected response") + }) + } +} + func TestCreateUser(t *testing.T) { t.Parallel() From ac143d44e6eb3b2496ca762ace16208198f22bd5 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 9 Jan 2024 16:57:44 +0200 Subject: [PATCH 062/106] feat(provd): more robust username validation --- .../testdata/TestRegisterGRPCServices/golden | 6 +- .../internal/services/user/reserved-usernames | 123 ++++++++++++++++++ provd/internal/services/user/user.go | 58 ++++++--- provd/internal/services/user/user_test.go | 12 ++ 4 files changed, 179 insertions(+), 20 deletions(-) create mode 100644 provd/internal/services/user/reserved-usernames diff --git a/provd/internal/services/testdata/TestRegisterGRPCServices/golden b/provd/internal/services/testdata/TestRegisterGRPCServices/golden index 4606dd57a..83ef299a3 100644 --- a/provd/internal/services/testdata/TestRegisterGRPCServices/golden +++ b/provd/internal/services/testdata/TestRegisterGRPCServices/golden @@ -6,13 +6,13 @@ provd.HelloWorldService: metadata: provd.protos user.UserService: methods: - - name: GetUser + - name: ValidateUsername isclientstream: false isserverstream: false - - name: CreateUser + - name: GetUser isclientstream: false isserverstream: false - - name: ValidateUsername + - name: CreateUser isclientstream: false isserverstream: false metadata: proto/user.proto diff --git a/provd/internal/services/user/reserved-usernames b/provd/internal/services/user/reserved-usernames new file mode 100644 index 000000000..b654e05cb --- /dev/null +++ b/provd/internal/services/user/reserved-usernames @@ -0,0 +1,123 @@ +# Static users from base-passwd/passwd.master (3.5.41). +root +daemon +bin +sys +sync +games +man +lp +mail +news +uucp +proxy +www-data +backup +list +irc +gnats +nobody + +# Other static groups from base-passwd/group.master (3.5.41). +adm +tty +disk +kmem +dialout +fax +voice +cdrom +floppy +tape +sudo +audio +dip +operator +src +shadow +utmp +video +sasl +plugdev +staff +users +nogroup + +# Reserved usernames listed in base-passwd/README (3.5.41). +netplan +ftn +mysql +tac-plus +alias +qmail +qmaild +qmails +qmailr +qmailq +qmaill +qmailp +asterisk +vpopmail +vchkpw +slurm +hacluster +haclient +grsec-tpe +grsec-sock-all +grsec-sock-clt +grsec-sock-srv +grsec-proc +ceph +opensrf +libvirt-qemu + +# Ubuntu creates the admin group and adds the first user to it in order to +# grant them sudo privileges. +admin + +# Other miscellaneous system users/groups created by common packages. While +# it's useful to add things here that people might run into, it's not +# absolutely critical; the worst that will happen is that the installation +# will fail at some later point. +Debian-exim +bind +crontab +cupsys +dcc +dhcp +dictd +dnsmasq +dovecot +fetchmail +firebird +ftp +fuse +gdm +haldaemon +hplilp +identd +input +jwhois +klog +kvm +lpadmin +maas +messagebus +mythtv +netdev +powerdev +radvd +render +saned +sbuild +scanner +sgx +slocate +ssh +sshd +ssl-cert +sslwrap +statd +syslog +telnetd +tftpd \ No newline at end of file diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index e9bc18948..42ac141f9 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -8,7 +8,10 @@ import ( "encoding/base64" "fmt" "math/big" + "os" + "regexp" "strconv" + "strings" "github.com/canonical/ubuntu-desktop-provision/provd/proto" "google.golang.org/grpc/codes" @@ -18,8 +21,10 @@ import ( ) const ( - dbusAccountsPrefix = "org.freedesktop.Accounts" - dbusHostnamePrefix = "org.freedesktop.hostname1" + DbusAccountsPrefix = "org.freedesktop.Accounts" + DbusHostnamePrefix = "org.freedesktop.hostname1" + UsernameMaxLen = 32 + UsernameRegex = "^[a-z_][a-z0-9_-]*$" ) type Caller interface { @@ -36,7 +41,7 @@ type userObjectFactoryImpl struct { } func (f *userObjectFactoryImpl) GetUserObject(userObjectPath dbus.ObjectPath) Caller { - userObject := f.conn.Object(dbusAccountsPrefix, userObjectPath) + userObject := f.conn.Object(DbusAccountsPrefix, userObjectPath) return userObject } @@ -58,8 +63,8 @@ type Service struct { // New retuns a new instance of the User service. func New(bus *dbus.Conn, args ...option) *Service { - accounts := bus.Object(dbusAccountsPrefix, "/org/freedesktop/Accounts") - hostname := bus.Object(dbusHostnamePrefix, "/org/freedesktop/hostname1") + accounts := bus.Object(DbusAccountsPrefix, "/org/freedesktop/Accounts") + hostname := bus.Object(DbusHostnamePrefix, "/org/freedesktop/hostname1") userFactory := &userObjectFactoryImpl{conn: bus} opts := options{ @@ -155,7 +160,7 @@ func (s *Service) CreateUser(ctx context.Context, req *proto.CreateUserRequest) } // Create the user var userObjectPath dbus.ObjectPath - call := s.accounts.Call(dbusAccountsPrefix+".CreateUser", 0, username, realName, accountType) + call := s.accounts.Call(DbusAccountsPrefix+".CreateUser", 0, username, realName, accountType) err := call.Store(&userObjectPath) if err != nil { @@ -170,19 +175,19 @@ func (s *Service) CreateUser(ctx context.Context, req *proto.CreateUserRequest) // Set the password for the user userObject := s.userFactory.GetUserObject(userObjectPath) fmt.Printf(hashed) - err = userObject.Call(dbusAccountsPrefix+".User.SetPassword", 0, hashed, "").Err + err = userObject.Call(DbusAccountsPrefix+".User.SetPassword", 0, hashed, "").Err if err != nil { return nil, status.Errorf(codes.Internal, "failed to set password: %s", err) } // Set autologin for the user - err = userObject.Call(dbusAccountsPrefix+".User.SetAutomaticLogin", 0, autologin).Err + err = userObject.Call(DbusAccountsPrefix+".User.SetAutomaticLogin", 0, autologin).Err if err != nil { return nil, status.Errorf(codes.Internal, "failed to set autologin: %s", err) } // Set the hostname - err = s.hostname.Call(dbusHostnamePrefix+".SetStaticHostname", 0, hostname, false).Err + err = s.hostname.Call(DbusHostnamePrefix+".SetStaticHostname", 0, hostname, false).Err if err != nil { return nil, status.Errorf(codes.Internal, "failed to set hostname: %s", err) } @@ -200,12 +205,32 @@ func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsern return nil, status.Errorf(codes.InvalidArgument, "username cannot be empty") } - err := s.accounts.Call(dbusAccountsPrefix+".FindUserByName", 0, username).Err + // Regex check + matched, _ := regexp.MatchString(UsernameRegex, username) + if !matched { + return nil, status.Errorf(codes.InvalidArgument, "username does not match required pattern") + } + + // Length check + if len(username) > UsernameMaxLen { + return nil, status.Errorf(codes.InvalidArgument, "username exceeds maximum length") + } + + // Reserved username check + reservedUsernames, err := os.ReadFile("reserved-usernames") + if err != nil { + return nil, status.Errorf(codes.Internal, "error reading reserved usernames file: %v", err) + } + if strings.Contains(string(reservedUsernames), username) { + return nil, status.Errorf(codes.InvalidArgument, "username is reserved") + } + + err = s.accounts.Call(DbusAccountsPrefix+".FindUserByName", 0, username).Err if err != nil { // Check if the error is due to user not being found or a D-Bus error if dbusError, ok := err.(dbus.Error); ok { - if dbusError.Name == dbusAccountsPrefix+".Error.Failed" { + if dbusError.Name == DbusAccountsPrefix+".Error.Failed" { // User not found return &proto.ValidateUsernameResponse{Valid: true}, nil } @@ -218,7 +243,6 @@ func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsern // User found return &proto.ValidateUsernameResponse{Valid: false}, nil - } func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*proto.GetUserResponse, error) { @@ -239,7 +263,7 @@ func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*prot } var userObjectPath dbus.ObjectPath - err = s.accounts.Call(dbusAccountsPrefix+".FindUserById", 0, uidInt).Store(&userObjectPath) + err = s.accounts.Call(DbusAccountsPrefix+".FindUserById", 0, uidInt).Store(&userObjectPath) if err != nil { return nil, err @@ -248,7 +272,7 @@ func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*prot userObject := s.userFactory.GetUserObject(userObjectPath) // Get the username via dbus - response, err := userObject.GetProperty(dbusAccountsPrefix + ".User.UserName") + response, err := userObject.GetProperty(DbusAccountsPrefix + ".User.UserName") if err != nil { return nil, status.Errorf(codes.Internal, "failed to get username: %s", err) } @@ -261,7 +285,7 @@ func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*prot } // Get the realName via dbus - response, err = userObject.GetProperty(dbusAccountsPrefix + ".User.RealName") + response, err = userObject.GetProperty(DbusAccountsPrefix + ".User.RealName") if err != nil { return nil, status.Errorf(codes.Internal, "failed to get realName: %s", err) } @@ -274,7 +298,7 @@ func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*prot } // Get autoLogin via dbus - response, err = userObject.GetProperty(dbusAccountsPrefix + ".User.AutomaticLogin") + response, err = userObject.GetProperty(DbusAccountsPrefix + ".User.AutomaticLogin") if err != nil { return nil, status.Errorf(codes.Internal, "failed to get autoLogin: %s", err) } @@ -287,7 +311,7 @@ func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*prot } // Get the hostname via dbus - response, err = s.hostname.GetProperty(dbusHostnamePrefix + ".Hostname") + response, err = s.hostname.GetProperty(DbusHostnamePrefix + ".Hostname") if err != nil { return nil, status.Errorf(codes.Internal, "failed to get hostname: %s", err) } diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go index c03407d0c..fce34f0d6 100644 --- a/provd/internal/services/user/user_test.go +++ b/provd/internal/services/user/user_test.go @@ -297,6 +297,18 @@ func TestValidateUsername(t *testing.T) { username: "", wantErr: true, }, + "Reserved username": { + username: "root", + wantErr: true, + }, + "Username too long": { + username: "thisusernameiswaytoolong1234567890abcdefghijklmnopqrstuvwxyz", + wantErr: true, + }, + "Invalid characters in username": { + username: "invalid@username", + wantErr: true, + }, } for name, tc := range tests { From fba3d72a1b4baae2bb580497b267f7c873d3ac04 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 9 Jan 2024 16:57:44 +0200 Subject: [PATCH 063/106] fix(provd): scan reserved names, ignore #'s --- .../testdata/TestRegisterGRPCServices/golden | 6 ++--- provd/internal/services/user/user.go | 25 +++++++++++++++++-- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/provd/internal/services/testdata/TestRegisterGRPCServices/golden b/provd/internal/services/testdata/TestRegisterGRPCServices/golden index 83ef299a3..4606dd57a 100644 --- a/provd/internal/services/testdata/TestRegisterGRPCServices/golden +++ b/provd/internal/services/testdata/TestRegisterGRPCServices/golden @@ -6,13 +6,13 @@ provd.HelloWorldService: metadata: provd.protos user.UserService: methods: - - name: ValidateUsername - isclientstream: false - isserverstream: false - name: GetUser isclientstream: false isserverstream: false - name: CreateUser isclientstream: false isserverstream: false + - name: ValidateUsername + isclientstream: false + isserverstream: false metadata: proto/user.proto diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index 42ac141f9..7eff62bf0 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -2,6 +2,7 @@ package user import ( + "bufio" "context" "crypto/rand" "crypto/sha512" @@ -217,11 +218,31 @@ func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsern } // Reserved username check - reservedUsernames, err := os.ReadFile("reserved-usernames") + file, err := os.Open("reserved-usernames") if err != nil { + return nil, status.Errorf(codes.Internal, "error opening reserved usernames file: %v", err) + } + defer file.Close() + + isReserved := false + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + // Ignore comment lines + if strings.HasPrefix(line, "#") { + continue + } + if line == username { + isReserved = true + break + } + } + + if err := scanner.Err(); err != nil { return nil, status.Errorf(codes.Internal, "error reading reserved usernames file: %v", err) } - if strings.Contains(string(reservedUsernames), username) { + + if isReserved { return nil, status.Errorf(codes.InvalidArgument, "username is reserved") } From c0d4e56c7dcf921dc00e165b01e41db746271603 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 9 Jan 2024 16:57:44 +0200 Subject: [PATCH 064/106] feat(provd): validate username reponse enums --- .../testdata/TestRegisterGRPCServices/golden | 6 +- .../golden/successfully_gets_a_user | 2 +- .../golden/empty_username | 1 + .../golden/existing_username | 2 +- .../golden/invalid_characters_in_username | 1 + .../golden/reserved_username | 1 + .../golden/username_too_long | 1 + .../golden/valid_username | 2 +- provd/internal/services/user/user.go | 12 +- provd/internal/services/user/user_test.go | 16 +- provd/protos/user.pb.go | 212 ++++++++++++------ provd/protos/user.proto | 15 +- 12 files changed, 177 insertions(+), 94 deletions(-) create mode 100644 provd/internal/services/user/testdata/TestValidateUsername/golden/empty_username create mode 100644 provd/internal/services/user/testdata/TestValidateUsername/golden/invalid_characters_in_username create mode 100644 provd/internal/services/user/testdata/TestValidateUsername/golden/reserved_username create mode 100644 provd/internal/services/user/testdata/TestValidateUsername/golden/username_too_long diff --git a/provd/internal/services/testdata/TestRegisterGRPCServices/golden b/provd/internal/services/testdata/TestRegisterGRPCServices/golden index 4606dd57a..49bc27d61 100644 --- a/provd/internal/services/testdata/TestRegisterGRPCServices/golden +++ b/provd/internal/services/testdata/TestRegisterGRPCServices/golden @@ -6,13 +6,13 @@ provd.HelloWorldService: metadata: provd.protos user.UserService: methods: - - name: GetUser - isclientstream: false - isserverstream: false - name: CreateUser isclientstream: false isserverstream: false - name: ValidateUsername isclientstream: false isserverstream: false + - name: GetUser + isclientstream: false + isserverstream: false metadata: proto/user.proto diff --git a/provd/internal/services/user/testdata/TestGetUser/golden/successfully_gets_a_user b/provd/internal/services/user/testdata/TestGetUser/golden/successfully_gets_a_user index 4a58abcfa..c066eae11 100644 --- a/provd/internal/services/user/testdata/TestGetUser/golden/successfully_gets_a_user +++ b/provd/internal/services/user/testdata/TestGetUser/golden/successfully_gets_a_user @@ -1 +1 @@ -realName:"Ubuntu" hostname:"ubuntu" username:"ubuntu" autoLogin:true \ No newline at end of file +real_name:"Ubuntu" hostname:"ubuntu" username:"ubuntu" auto_login:true \ No newline at end of file diff --git a/provd/internal/services/user/testdata/TestValidateUsername/golden/empty_username b/provd/internal/services/user/testdata/TestValidateUsername/golden/empty_username new file mode 100644 index 000000000..1ec6b8a65 --- /dev/null +++ b/provd/internal/services/user/testdata/TestValidateUsername/golden/empty_username @@ -0,0 +1 @@ +EMPTY \ No newline at end of file diff --git a/provd/internal/services/user/testdata/TestValidateUsername/golden/existing_username b/provd/internal/services/user/testdata/TestValidateUsername/golden/existing_username index 02e4a84d6..ae9b57ad8 100644 --- a/provd/internal/services/user/testdata/TestValidateUsername/golden/existing_username +++ b/provd/internal/services/user/testdata/TestValidateUsername/golden/existing_username @@ -1 +1 @@ -false \ No newline at end of file +ALREADY_IN_USE \ No newline at end of file diff --git a/provd/internal/services/user/testdata/TestValidateUsername/golden/invalid_characters_in_username b/provd/internal/services/user/testdata/TestValidateUsername/golden/invalid_characters_in_username new file mode 100644 index 000000000..ff791a49d --- /dev/null +++ b/provd/internal/services/user/testdata/TestValidateUsername/golden/invalid_characters_in_username @@ -0,0 +1 @@ +INVALID_CHARS \ No newline at end of file diff --git a/provd/internal/services/user/testdata/TestValidateUsername/golden/reserved_username b/provd/internal/services/user/testdata/TestValidateUsername/golden/reserved_username new file mode 100644 index 000000000..0fa0a1a6c --- /dev/null +++ b/provd/internal/services/user/testdata/TestValidateUsername/golden/reserved_username @@ -0,0 +1 @@ +SYSTEM_RESERVED \ No newline at end of file diff --git a/provd/internal/services/user/testdata/TestValidateUsername/golden/username_too_long b/provd/internal/services/user/testdata/TestValidateUsername/golden/username_too_long new file mode 100644 index 000000000..279dfbf45 --- /dev/null +++ b/provd/internal/services/user/testdata/TestValidateUsername/golden/username_too_long @@ -0,0 +1 @@ +TOO_LONG \ No newline at end of file diff --git a/provd/internal/services/user/testdata/TestValidateUsername/golden/valid_username b/provd/internal/services/user/testdata/TestValidateUsername/golden/valid_username index f32a5804e..a0aba9318 100644 --- a/provd/internal/services/user/testdata/TestValidateUsername/golden/valid_username +++ b/provd/internal/services/user/testdata/TestValidateUsername/golden/valid_username @@ -1 +1 @@ -true \ No newline at end of file +OK \ No newline at end of file diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index 7eff62bf0..97555dd9f 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -203,18 +203,18 @@ func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsern } username := req.GetUsername() if username == "" { - return nil, status.Errorf(codes.InvalidArgument, "username cannot be empty") + return &proto.ValidateUsernameResponse{UsernameValidation: proto.UsernameValidation_EMPTY}, nil } // Regex check matched, _ := regexp.MatchString(UsernameRegex, username) if !matched { - return nil, status.Errorf(codes.InvalidArgument, "username does not match required pattern") + return &proto.ValidateUsernameResponse{UsernameValidation: proto.UsernameValidation_INVALID_CHARS}, nil } // Length check if len(username) > UsernameMaxLen { - return nil, status.Errorf(codes.InvalidArgument, "username exceeds maximum length") + return &proto.ValidateUsernameResponse{UsernameValidation: proto.UsernameValidation_TOO_LONG}, nil } // Reserved username check @@ -243,7 +243,7 @@ func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsern } if isReserved { - return nil, status.Errorf(codes.InvalidArgument, "username is reserved") + return &proto.ValidateUsernameResponse{UsernameValidation: proto.UsernameValidation_SYSTEM_RESERVED}, nil } err = s.accounts.Call(DbusAccountsPrefix+".FindUserByName", 0, username).Err @@ -253,7 +253,7 @@ func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsern if dbusError, ok := err.(dbus.Error); ok { if dbusError.Name == DbusAccountsPrefix+".Error.Failed" { // User not found - return &proto.ValidateUsernameResponse{Valid: true}, nil + return &proto.ValidateUsernameResponse{UsernameValidation: proto.UsernameValidation_OK}, nil } // Handle other dbus errors return nil, status.Errorf(codes.Internal, "dbus error: %v", dbusError) @@ -263,7 +263,7 @@ func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsern } // User found - return &proto.ValidateUsernameResponse{Valid: false}, nil + return &proto.ValidateUsernameResponse{UsernameValidation: proto.UsernameValidation_ALREADY_IN_USE}, nil } func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*proto.GetUserResponse, error) { diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go index fce34f0d6..2a4db8051 100644 --- a/provd/internal/services/user/user_test.go +++ b/provd/internal/services/user/user_test.go @@ -6,7 +6,6 @@ import ( "net" "os" "path/filepath" - "strconv" "testing" "github.com/canonical/ubuntu-desktop-provision/provd/internal/services/user" @@ -289,25 +288,24 @@ func TestValidateUsername(t *testing.T) { wantErr: false, }, "Existing username": { - username: "existinguser", - accountsError: false, - wantErr: false, + username: "existinguser", + wantErr: false, }, "Empty username": { username: "", - wantErr: true, + wantErr: false, }, "Reserved username": { username: "root", - wantErr: true, + wantErr: false, }, "Username too long": { username: "thisusernameiswaytoolong1234567890abcdefghijklmnopqrstuvwxyz", - wantErr: true, + wantErr: false, }, "Invalid characters in username": { username: "invalid@username", - wantErr: true, + wantErr: false, }, } @@ -337,7 +335,7 @@ func TestValidateUsername(t *testing.T) { } require.NoError(t, err, "ValidateUsername should not return an error, but did") - got := strconv.FormatBool(resp.GetValid()) + got := resp.UsernameValidation.String() want := testutils.LoadWithUpdateFromGolden(t, got) require.Equal(t, want, got, "ValidateUsername returned an unexpected response") }) diff --git a/provd/protos/user.pb.go b/provd/protos/user.pb.go index 4c60e5771..d8ffb3b57 100644 --- a/provd/protos/user.pb.go +++ b/provd/protos/user.pb.go @@ -20,6 +20,64 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type UsernameValidation int32 + +const ( + UsernameValidation_OK UsernameValidation = 0 + UsernameValidation_ALREADY_IN_USE UsernameValidation = 1 + UsernameValidation_SYSTEM_RESERVED UsernameValidation = 2 + UsernameValidation_INVALID_CHARS UsernameValidation = 3 + UsernameValidation_TOO_LONG UsernameValidation = 4 + UsernameValidation_EMPTY UsernameValidation = 5 +) + +// Enum value maps for UsernameValidation. +var ( + UsernameValidation_name = map[int32]string{ + 0: "OK", + 1: "ALREADY_IN_USE", + 2: "SYSTEM_RESERVED", + 3: "INVALID_CHARS", + 4: "TOO_LONG", + 5: "EMPTY", + } + UsernameValidation_value = map[string]int32{ + "OK": 0, + "ALREADY_IN_USE": 1, + "SYSTEM_RESERVED": 2, + "INVALID_CHARS": 3, + "TOO_LONG": 4, + "EMPTY": 5, + } +) + +func (x UsernameValidation) Enum() *UsernameValidation { + p := new(UsernameValidation) + *p = x + return p +} + +func (x UsernameValidation) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (UsernameValidation) Descriptor() protoreflect.EnumDescriptor { + return file_proto_user_proto_enumTypes[0].Descriptor() +} + +func (UsernameValidation) Type() protoreflect.EnumType { + return &file_proto_user_proto_enumTypes[0] +} + +func (x UsernameValidation) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use UsernameValidation.Descriptor instead. +func (UsernameValidation) EnumDescriptor() ([]byte, []int) { + return file_proto_user_proto_rawDescGZIP(), []int{0} +} + type Empty struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -110,11 +168,11 @@ type User struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RealName string `protobuf:"bytes,1,opt,name=realName,proto3" json:"realName,omitempty"` + RealName string `protobuf:"bytes,1,opt,name=real_name,json=realName,proto3" json:"real_name,omitempty"` Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"` Username string `protobuf:"bytes,3,opt,name=username,proto3" json:"username,omitempty"` Password string `protobuf:"bytes,4,opt,name=password,proto3" json:"password,omitempty"` - AutoLogin bool `protobuf:"varint,5,opt,name=autoLogin,proto3" json:"autoLogin,omitempty"` + AutoLogin bool `protobuf:"varint,5,opt,name=auto_login,json=autoLogin,proto3" json:"auto_login,omitempty"` } func (x *User) Reset() { @@ -338,7 +396,7 @@ type ValidateUsernameResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Valid bool `protobuf:"varint,1,opt,name=valid,proto3" json:"valid,omitempty"` + UsernameValidation UsernameValidation `protobuf:"varint,1,opt,name=username_validation,json=usernameValidation,proto3,enum=user.UsernameValidation" json:"username_validation,omitempty"` } func (x *ValidateUsernameResponse) Reset() { @@ -373,11 +431,11 @@ func (*ValidateUsernameResponse) Descriptor() ([]byte, []int) { return file_proto_user_proto_rawDescGZIP(), []int{6} } -func (x *ValidateUsernameResponse) GetValid() bool { +func (x *ValidateUsernameResponse) GetUsernameValidation() UsernameValidation { if x != nil { - return x.Valid + return x.UsernameValidation } - return false + return UsernameValidation_OK } var File_proto_user_proto protoreflect.FileDescriptor @@ -387,49 +445,59 @@ var file_proto_user_proto_rawDesc = []byte{ 0x74, 0x6f, 0x12, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x29, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x94, 0x01, 0x0a, - 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x4e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, - 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, - 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x67, - 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x75, 0x74, 0x6f, 0x4c, 0x6f, - 0x67, 0x69, 0x6e, 0x22, 0x31, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, - 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x4e, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x04, 0x75, - 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x75, 0x73, 0x65, 0x72, - 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x69, - 0x73, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, - 0x73, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x22, 0x35, 0x0a, 0x17, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x30, 0x0a, - 0x18, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x32, - 0xcc, 0x01, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x36, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x14, 0x2e, 0x75, 0x73, 0x65, - 0x72, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x15, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b, - 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x51, 0x0a, 0x10, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x1d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, - 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, - 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, - 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3b, - 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x61, 0x6e, - 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x75, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x2d, 0x64, 0x65, - 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x2d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2f, - 0x70, 0x72, 0x6f, 0x76, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x96, 0x01, 0x0a, + 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x6c, + 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x75, 0x74, 0x6f, + 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x22, 0x31, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x55, 0x73, + 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x4e, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, + 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x75, 0x73, + 0x65, 0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x19, 0x0a, + 0x08, 0x69, 0x73, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x69, 0x73, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x22, 0x35, 0x0a, 0x17, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, + 0x65, 0x0a, 0x18, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, + 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x13, 0x75, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, + 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x12, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x71, 0x0a, 0x12, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, + 0x6d, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x06, 0x0a, 0x02, + 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x41, 0x4c, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f, + 0x49, 0x4e, 0x5f, 0x55, 0x53, 0x45, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x59, 0x53, 0x54, + 0x45, 0x4d, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x45, 0x44, 0x10, 0x02, 0x12, 0x11, 0x0a, + 0x0d, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x43, 0x48, 0x41, 0x52, 0x53, 0x10, 0x03, + 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x4f, 0x4f, 0x5f, 0x4c, 0x4f, 0x4e, 0x47, 0x10, 0x04, 0x12, 0x09, + 0x0a, 0x05, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x10, 0x05, 0x32, 0xcc, 0x01, 0x0a, 0x0b, 0x55, 0x73, + 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x36, 0x0a, 0x07, 0x47, 0x65, 0x74, + 0x55, 0x73, 0x65, 0x72, 0x12, 0x14, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x75, 0x73, 0x65, + 0x72, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x32, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, + 0x17, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x51, 0x0a, 0x10, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x2e, 0x75, 0x73, 0x65, 0x72, + 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, + 0x2f, 0x75, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x2d, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x2d, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x64, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -444,30 +512,33 @@ func file_proto_user_proto_rawDescGZIP() []byte { return file_proto_user_proto_rawDescData } +var file_proto_user_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_proto_user_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_proto_user_proto_goTypes = []interface{}{ - (*Empty)(nil), // 0: user.Empty - (*GetUserRequest)(nil), // 1: user.GetUserRequest - (*User)(nil), // 2: user.User - (*GetUserResponse)(nil), // 3: user.GetUserResponse - (*CreateUserRequest)(nil), // 4: user.CreateUserRequest - (*ValidateUsernameRequest)(nil), // 5: user.ValidateUsernameRequest - (*ValidateUsernameResponse)(nil), // 6: user.ValidateUsernameResponse + (UsernameValidation)(0), // 0: user.UsernameValidation + (*Empty)(nil), // 1: user.Empty + (*GetUserRequest)(nil), // 2: user.GetUserRequest + (*User)(nil), // 3: user.User + (*GetUserResponse)(nil), // 4: user.GetUserResponse + (*CreateUserRequest)(nil), // 5: user.CreateUserRequest + (*ValidateUsernameRequest)(nil), // 6: user.ValidateUsernameRequest + (*ValidateUsernameResponse)(nil), // 7: user.ValidateUsernameResponse } var file_proto_user_proto_depIdxs = []int32{ - 2, // 0: user.GetUserResponse.user:type_name -> user.User - 2, // 1: user.CreateUserRequest.user:type_name -> user.User - 1, // 2: user.UserService.GetUser:input_type -> user.GetUserRequest - 4, // 3: user.UserService.CreateUser:input_type -> user.CreateUserRequest - 5, // 4: user.UserService.ValidateUsername:input_type -> user.ValidateUsernameRequest - 3, // 5: user.UserService.GetUser:output_type -> user.GetUserResponse - 0, // 6: user.UserService.CreateUser:output_type -> user.Empty - 6, // 7: user.UserService.ValidateUsername:output_type -> user.ValidateUsernameResponse - 5, // [5:8] is the sub-list for method output_type - 2, // [2:5] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 3, // 0: user.GetUserResponse.user:type_name -> user.User + 3, // 1: user.CreateUserRequest.user:type_name -> user.User + 0, // 2: user.ValidateUsernameResponse.username_validation:type_name -> user.UsernameValidation + 2, // 3: user.UserService.GetUser:input_type -> user.GetUserRequest + 5, // 4: user.UserService.CreateUser:input_type -> user.CreateUserRequest + 6, // 5: user.UserService.ValidateUsername:input_type -> user.ValidateUsernameRequest + 4, // 6: user.UserService.GetUser:output_type -> user.GetUserResponse + 1, // 7: user.UserService.CreateUser:output_type -> user.Empty + 7, // 8: user.UserService.ValidateUsername:output_type -> user.ValidateUsernameResponse + 6, // [6:9] is the sub-list for method output_type + 3, // [3:6] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_proto_user_proto_init() } @@ -566,13 +637,14 @@ func file_proto_user_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_user_proto_rawDesc, - NumEnums: 0, + NumEnums: 1, NumMessages: 7, NumExtensions: 0, NumServices: 1, }, GoTypes: file_proto_user_proto_goTypes, DependencyIndexes: file_proto_user_proto_depIdxs, + EnumInfos: file_proto_user_proto_enumTypes, MessageInfos: file_proto_user_proto_msgTypes, }.Build() File_proto_user_proto = out.File diff --git a/provd/protos/user.proto b/provd/protos/user.proto index c73d08fce..5a71de5e8 100644 --- a/provd/protos/user.proto +++ b/provd/protos/user.proto @@ -17,11 +17,11 @@ message GetUserRequest { } message User { - string realName = 1; + string real_name = 1; string hostname = 2; string username = 3; string password = 4; - bool autoLogin = 5; + bool auto_login = 5; } message GetUserResponse { @@ -38,5 +38,14 @@ message ValidateUsernameRequest { } message ValidateUsernameResponse { - bool valid = 1; + UsernameValidation username_validation = 1; } + +enum UsernameValidation { + OK = 0; + ALREADY_IN_USE = 1; + SYSTEM_RESERVED = 2; + INVALID_CHARS = 3; + TOO_LONG = 4; + EMPTY = 5; +} \ No newline at end of file From b13c17991420ad122e61c2a0a3fc7892b947f68e Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 9 Jan 2024 16:57:44 +0200 Subject: [PATCH 065/106] docs(provd): better comments --- provd/internal/services/manager.go | 14 +++--- provd/internal/services/user/export_test.go | 17 ++++++-- provd/internal/services/user/user.go | 48 +++++++++++++-------- provd/internal/services/user/user_test.go | 5 ++- 4 files changed, 57 insertions(+), 27 deletions(-) diff --git a/provd/internal/services/manager.go b/provd/internal/services/manager.go index 1cc29843f..74a455185 100644 --- a/provd/internal/services/manager.go +++ b/provd/internal/services/manager.go @@ -3,6 +3,7 @@ package services import ( "context" + "fmt" "log/slog" "github.com/canonical/ubuntu-desktop-provision/provd" @@ -25,20 +26,23 @@ type Manager struct { } // NewManager returns a new manager after creating all necessary items for our business logic. -func NewManager(ctx context.Context) (m Manager, err error) { +func NewManager(ctx context.Context) (m *Manager, err error) { defer decorate.OnError(&err, "can't create provd object") // Connect to dbus - bus, err := dbus.ConnectSystemBus() + bus, err := dbus.ConnectSystemBus( + dbus.WithIncomingInterceptor(func(msg *dbus.Message) { + slog.Debug(fmt.Sprintf("DBUS: %s", msg)) + })) if err != nil { - return Manager{}, status.Errorf(codes.Internal, "Failed to connect to system bus: %s", err) + return nil, status.Errorf(codes.Internal, "Failed to connect to system bus: %s", err) } helloService := hello.Service{} userService := user.New(bus) - return Manager{ + return &Manager{ helloService: helloService, userService: *userService, bus: bus, @@ -56,7 +60,7 @@ func (m Manager) RegisterGRPCServices(ctx context.Context) *grpc.Server { return grpcServer } -// stop closes the dbus connection. +// Stop closes the dbus connection. func (m *Manager) Stop() error { slog.Debug("Closing grpc manager and dbus connection") diff --git a/provd/internal/services/user/export_test.go b/provd/internal/services/user/export_test.go index 732adef3f..4bc5fcd86 100644 --- a/provd/internal/services/user/export_test.go +++ b/provd/internal/services/user/export_test.go @@ -7,14 +7,14 @@ import ( ) // WithAccounts overrides the accounts caller for the service. -func WithAccounts(accounts Caller) option { +func WithAccounts(accounts DbusObject) option { return func(o *options) { o.accounts = accounts } } // WithHostname overrides the hostname caller for the service. -func WithHostname(hostname Caller) option { +func WithHostname(hostname DbusObject) option { return func(o *options) { o.hostname = hostname } @@ -27,22 +27,26 @@ func WithUserFactory(userFactory UserObjectFactory) option { } } +// AccountsObjectMock is a mock AccountsObject. type AccountsObjectMock struct { WantError bool UserObjectPath dbus.ObjectPath Properties map[string]interface{} } +// HostnameObjectMock is a mock HostnameObject. type HostnameObjectMock struct { WantError bool Properties map[string]interface{} } +// UserObjectMock is a mock UserObject. type UserObjectMock struct { WantError bool Properties map[string]interface{} } +// Call is a mock implementation of the dbus Call method for the AccountsObjectMock. func (a *AccountsObjectMock) Call(method string, flags dbus.Flags, args ...interface{}) *dbus.Call { var err error @@ -78,6 +82,7 @@ func (a *AccountsObjectMock) Call(method string, flags dbus.Flags, args ...inter } } +// Call is a mock implementation of the dbus Call method for the HostnameObjectMock. func (h *HostnameObjectMock) Call(method string, flags dbus.Flags, args ...interface{}) *dbus.Call { var err error @@ -100,6 +105,7 @@ func (h *HostnameObjectMock) Call(method string, flags dbus.Flags, args ...inter } } +// Call is a mock implementation of the dbus Call method for the UserObjectMock. func (u *UserObjectMock) Call(method string, flags dbus.Flags, args ...interface{}) *dbus.Call { var err error if u.WantError { @@ -114,6 +120,7 @@ func (u *UserObjectMock) Call(method string, flags dbus.Flags, args ...interface } } +// GetProperty is a mock implementation of the dbus GetProperty method for the UserObjectMock. func (u *UserObjectMock) GetProperty(prop string) (dbus.Variant, error) { if u.WantError { return dbus.Variant{}, errors.New("GetProperty error") @@ -126,6 +133,7 @@ func (u *UserObjectMock) GetProperty(prop string) (dbus.Variant, error) { return dbus.MakeVariant(value), nil } +// GetProperty is a mock implementation of the dbus GetProperty method for the AccountsObjectMock. func (u *AccountsObjectMock) GetProperty(prop string) (dbus.Variant, error) { if u.WantError { return dbus.Variant{}, errors.New("GetProperty error") @@ -137,6 +145,7 @@ func (u *AccountsObjectMock) GetProperty(prop string) (dbus.Variant, error) { return dbus.MakeVariant(value), nil } +// GetProperty is a mock implementation of the dbus GetProperty method for the HostnameObjectMock. func (u *HostnameObjectMock) GetProperty(prop string) (dbus.Variant, error) { if u.WantError { return dbus.Variant{}, errors.New("GetProperty error") @@ -148,10 +157,12 @@ func (u *HostnameObjectMock) GetProperty(prop string) (dbus.Variant, error) { return dbus.MakeVariant(value), nil } +// UserObjectFactoryMock is a mock UserObjectFactory. type UserObjectFactoryMock struct { UserObject *UserObjectMock } -func (f UserObjectFactoryMock) GetUserObject(userObjectPath dbus.ObjectPath) Caller { +// GetUserObject returns a mock UserObject. +func (f UserObjectFactoryMock) GetUserObject(userObjectPath dbus.ObjectPath) DbusObject { return f.UserObject } diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index 97555dd9f..50e8eb91d 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -22,33 +22,40 @@ import ( ) const ( + // DbusAccountsPrefix is the prefix for the Accounts D-Bus interface. DbusAccountsPrefix = "org.freedesktop.Accounts" + // DbusHostnamePrefix is the prefix for the Hostname D-Bus interface. DbusHostnamePrefix = "org.freedesktop.hostname1" UsernameMaxLen = 32 UsernameRegex = "^[a-z_][a-z0-9_-]*$" ) -type Caller interface { +// DbusObject is an abstraction of a dbus object. +type DbusObject interface { Call(method string, flags dbus.Flags, args ...interface{}) *dbus.Call GetProperty(p string) (dbus.Variant, error) } +// UserObjectFactory is a factory for creating DbusObjects for users. +// This is used to allow mocking of the DbusObjects for testing. type UserObjectFactory interface { - GetUserObject(userObjectPath dbus.ObjectPath) Caller + GetUserObject(userObjectPath dbus.ObjectPath) DbusObject } +// userObjectFactoryImpl is the default implementation of UserObjectFactory. type userObjectFactoryImpl struct { conn *dbus.Conn } -func (f *userObjectFactoryImpl) GetUserObject(userObjectPath dbus.ObjectPath) Caller { +// Wraps the dbus call for getting a user object. +func (f *userObjectFactoryImpl) GetUserObject(userObjectPath dbus.ObjectPath) DbusObject { userObject := f.conn.Object(DbusAccountsPrefix, userObjectPath) return userObject } type options struct { - accounts Caller - hostname Caller + accounts DbusObject + hostname DbusObject userFactory UserObjectFactory } @@ -57,8 +64,8 @@ type option func(*options) // Service is the implementation of the User module service. type Service struct { proto.UnimplementedUserServiceServer - accounts Caller - hostname Caller + accounts DbusObject + hostname DbusObject userFactory UserObjectFactory } @@ -86,21 +93,23 @@ func New(bus *dbus.Conn, args ...option) *Service { } } -const validSalts = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./" +const validSaltChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./" -// Generate a salt string of the specified length using validSalts characters. +// generateSalt generates a salt string of the specified length using validSalts characters. func generateSalt(length int) (string, error) { salt := make([]byte, length) for i := range salt { - index, err := rand.Int(rand.Reader, big.NewInt(int64(len(validSalts)))) + index, err := rand.Int(rand.Reader, big.NewInt(int64(len(validSaltChars)))) if err != nil { return "", err } - salt[i] = validSalts[index.Int64()] + salt[i] = validSaltChars[index.Int64()] } return string(salt), nil } +// HashPassword hashes the given password, returning in the SHA-512 crypt format. +// A salt can be provided for testing purposes. func HashPassword(password string, testSalt *string) (string, error) { if password == "" { return "", status.Errorf(codes.InvalidArgument, "recieved an empty password") @@ -111,23 +120,20 @@ func HashPassword(password string, testSalt *string) (string, error) { if testSalt != nil { salt = *testSalt } else { - // Generate a 16-byte salt salt, err = generateSalt(16) if err != nil { return "", err } } - - // Hash the password with the salt hasher := sha512.New() hasher.Write([]byte(salt + password)) hashedBytes := hasher.Sum(nil) hashedPassword := base64.RawStdEncoding.EncodeToString(hashedBytes) - // Format the hash in the SHA-512 crypt format return fmt.Sprintf("$6$%s$%s", salt, hashedPassword), nil } +// CreateUser creates a new user on the system. func (s *Service) CreateUser(ctx context.Context, req *proto.CreateUserRequest) (*proto.Empty, error) { // Validate requtest @@ -165,6 +171,7 @@ func (s *Service) CreateUser(ctx context.Context, req *proto.CreateUserRequest) err := call.Store(&userObjectPath) if err != nil { + return nil, status.Errorf(codes.Internal, "failed to create user: %s", err) } @@ -196,6 +203,8 @@ func (s *Service) CreateUser(ctx context.Context, req *proto.CreateUserRequest) return &proto.Empty{}, nil } +// ValidateUsername validates the given username. Returns an enum value indicating +// the result of the validation. func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsernameRequest) (*proto.ValidateUsernameResponse, error) { // Validate request if req == nil { @@ -206,18 +215,19 @@ func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsern return &proto.ValidateUsernameResponse{UsernameValidation: proto.UsernameValidation_EMPTY}, nil } - // Regex check + // Check if username uses valid characters matched, _ := regexp.MatchString(UsernameRegex, username) if !matched { return &proto.ValidateUsernameResponse{UsernameValidation: proto.UsernameValidation_INVALID_CHARS}, nil } - // Length check + // Check if username is too long if len(username) > UsernameMaxLen { return &proto.ValidateUsernameResponse{UsernameValidation: proto.UsernameValidation_TOO_LONG}, nil } - // Reserved username check + // Check if username is in reserved list + // Read line by line to avoid loading the whole file into memory file, err := os.Open("reserved-usernames") if err != nil { return nil, status.Errorf(codes.Internal, "error opening reserved usernames file: %v", err) @@ -246,6 +256,7 @@ func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsern return &proto.ValidateUsernameResponse{UsernameValidation: proto.UsernameValidation_SYSTEM_RESERVED}, nil } + // Check if username is already in use err = s.accounts.Call(DbusAccountsPrefix+".FindUserByName", 0, username).Err if err != nil { @@ -266,6 +277,7 @@ func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsern return &proto.ValidateUsernameResponse{UsernameValidation: proto.UsernameValidation_ALREADY_IN_USE}, nil } +// GetUser returns the user information for the given uid. func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*proto.GetUserResponse, error) { // Validate request diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go index 2a4db8051..ec6882d98 100644 --- a/provd/internal/services/user/user_test.go +++ b/provd/internal/services/user/user_test.go @@ -241,6 +241,8 @@ func TestGetUser(t *testing.T) { "org.freedesktop.hostname1.Hostname": tc.hostname, }, } + + // userFactoryMock is used to return a mock user object with the properties we want to test. userFactoryMock := user.UserObjectFactoryMock{ &user.UserObjectMock{ Properties: map[string]interface{}{ @@ -342,7 +344,8 @@ func TestValidateUsername(t *testing.T) { } } -func newUserClient(t *testing.T, accountsMock user.Caller, hostnameMock user.Caller, userFactoryMock user.UserObjectFactory) proto.UserServiceClient { +// newUserClient creates a new user client for testing, with a temp unix socket and mock Dbus connection. +func newUserClient(t *testing.T, accountsMock user.DbusObject, hostnameMock user.DbusObject, userFactoryMock user.UserObjectFactory) proto.UserServiceClient { t.Helper() // socket path is limited in length. tmpDir, err := os.MkdirTemp("", "hello-socket-dir") From e8c764baa7ecd0d2b03865eace6cdda92ad100db Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 9 Jan 2024 17:06:50 +0200 Subject: [PATCH 066/106] style(provd): pb for protobuff --- provd/go.sum | 2 +- provd/internal/services/manager.go | 4 ++-- provd/internal/services/user/user.go | 28 +++++++++++------------ provd/internal/services/user/user_test.go | 16 ++++++------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/provd/go.sum b/provd/go.sum index c87140b48..594e599f2 100644 --- a/provd/go.sum +++ b/provd/go.sum @@ -3,7 +3,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:z60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= diff --git a/provd/internal/services/manager.go b/provd/internal/services/manager.go index 74a455185..c5612d44b 100644 --- a/provd/internal/services/manager.go +++ b/provd/internal/services/manager.go @@ -9,7 +9,7 @@ import ( "github.com/canonical/ubuntu-desktop-provision/provd" "github.com/canonical/ubuntu-desktop-provision/provd/internal/services/hello" "github.com/canonical/ubuntu-desktop-provision/provd/internal/services/user" - proto "github.com/canonical/ubuntu-desktop-provision/provd/proto" + pb "github.com/canonical/ubuntu-desktop-provision/provd/protos" "github.com/godbus/dbus/v5" "github.com/ubuntu/decorate" @@ -56,7 +56,7 @@ func (m Manager) RegisterGRPCServices(ctx context.Context) *grpc.Server { grpcServer := grpc.NewServer() provd.RegisterHelloWorldServiceServer(grpcServer, &m.helloService) - proto.RegisterUserServiceServer(grpcServer, &m.userService) + pb.RegisterUserServiceServer(grpcServer, &m.userService) return grpcServer } diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index 50e8eb91d..5a9681ec4 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -14,7 +14,7 @@ import ( "strconv" "strings" - "github.com/canonical/ubuntu-desktop-provision/provd/proto" + pb "github.com/canonical/ubuntu-desktop-provision/provd/protos" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -63,7 +63,7 @@ type option func(*options) // Service is the implementation of the User module service. type Service struct { - proto.UnimplementedUserServiceServer + pb.UnimplementedUserServiceServer accounts DbusObject hostname DbusObject userFactory UserObjectFactory @@ -134,7 +134,7 @@ func HashPassword(password string, testSalt *string) (string, error) { } // CreateUser creates a new user on the system. -func (s *Service) CreateUser(ctx context.Context, req *proto.CreateUserRequest) (*proto.Empty, error) { +func (s *Service) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.Empty, error) { // Validate requtest if req == nil { @@ -200,30 +200,30 @@ func (s *Service) CreateUser(ctx context.Context, req *proto.CreateUserRequest) return nil, status.Errorf(codes.Internal, "failed to set hostname: %s", err) } - return &proto.Empty{}, nil + return &pb.Empty{}, nil } // ValidateUsername validates the given username. Returns an enum value indicating // the result of the validation. -func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsernameRequest) (*proto.ValidateUsernameResponse, error) { +func (s *Service) ValidateUsername(ctx context.Context, req *pb.ValidateUsernameRequest) (*pb.ValidateUsernameResponse, error) { // Validate request if req == nil { return nil, status.Errorf(codes.InvalidArgument, "received a nil request") } username := req.GetUsername() if username == "" { - return &proto.ValidateUsernameResponse{UsernameValidation: proto.UsernameValidation_EMPTY}, nil + return &pb.ValidateUsernameResponse{UsernameValidation: pb.UsernameValidation_EMPTY}, nil } // Check if username uses valid characters matched, _ := regexp.MatchString(UsernameRegex, username) if !matched { - return &proto.ValidateUsernameResponse{UsernameValidation: proto.UsernameValidation_INVALID_CHARS}, nil + return &pb.ValidateUsernameResponse{UsernameValidation: pb.UsernameValidation_INVALID_CHARS}, nil } // Check if username is too long if len(username) > UsernameMaxLen { - return &proto.ValidateUsernameResponse{UsernameValidation: proto.UsernameValidation_TOO_LONG}, nil + return &pb.ValidateUsernameResponse{UsernameValidation: pb.UsernameValidation_TOO_LONG}, nil } // Check if username is in reserved list @@ -253,7 +253,7 @@ func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsern } if isReserved { - return &proto.ValidateUsernameResponse{UsernameValidation: proto.UsernameValidation_SYSTEM_RESERVED}, nil + return &pb.ValidateUsernameResponse{UsernameValidation: pb.UsernameValidation_SYSTEM_RESERVED}, nil } // Check if username is already in use @@ -264,7 +264,7 @@ func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsern if dbusError, ok := err.(dbus.Error); ok { if dbusError.Name == DbusAccountsPrefix+".Error.Failed" { // User not found - return &proto.ValidateUsernameResponse{UsernameValidation: proto.UsernameValidation_OK}, nil + return &pb.ValidateUsernameResponse{UsernameValidation: pb.UsernameValidation_OK}, nil } // Handle other dbus errors return nil, status.Errorf(codes.Internal, "dbus error: %v", dbusError) @@ -274,11 +274,11 @@ func (s *Service) ValidateUsername(ctx context.Context, req *proto.ValidateUsern } // User found - return &proto.ValidateUsernameResponse{UsernameValidation: proto.UsernameValidation_ALREADY_IN_USE}, nil + return &pb.ValidateUsernameResponse{UsernameValidation: pb.UsernameValidation_ALREADY_IN_USE}, nil } // GetUser returns the user information for the given uid. -func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*proto.GetUserResponse, error) { +func (s *Service) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) { // Validate request if req == nil { @@ -356,14 +356,14 @@ func (s *Service) GetUser(ctx context.Context, req *proto.GetUserRequest) (*prot return nil, status.Errorf(codes.Internal, "unexpected type returned for hostname: %s", err) } - user := &proto.User{ + user := &pb.User{ RealName: realName, Hostname: hostname, Username: username, AutoLogin: autoLogin, } - return &proto.GetUserResponse{ + return &pb.GetUserResponse{ User: user, }, nil diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go index ec6882d98..f14546fd9 100644 --- a/provd/internal/services/user/user_test.go +++ b/provd/internal/services/user/user_test.go @@ -10,7 +10,7 @@ import ( "github.com/canonical/ubuntu-desktop-provision/provd/internal/services/user" "github.com/canonical/ubuntu-desktop-provision/provd/internal/testutils" - "github.com/canonical/ubuntu-desktop-provision/provd/proto" + pb "github.com/canonical/ubuntu-desktop-provision/provd/protos" "github.com/godbus/dbus/v5" "github.com/stretchr/testify/require" "google.golang.org/grpc" @@ -140,8 +140,8 @@ func TestCreateUser(t *testing.T) { client := newUserClient(t, accountsMock, hostnameMock, userFactoryMock) - userReq := &proto.CreateUserRequest{ - User: &proto.User{ + userReq := &pb.CreateUserRequest{ + User: &pb.User{ RealName: tc.realName, Username: tc.username, Password: tc.password, @@ -256,7 +256,7 @@ func TestGetUser(t *testing.T) { client := newUserClient(t, accountsMock, hostnameMock, userFactoryMock) - getReq := &proto.GetUserRequest{ + getReq := &pb.GetUserRequest{ UserId: tc.uid, } @@ -326,7 +326,7 @@ func TestValidateUsername(t *testing.T) { client := newUserClient(t, accountsMock, hostnameMock, userFactoryMock) - validateReq := &proto.ValidateUsernameRequest{ + validateReq := &pb.ValidateUsernameRequest{ Username: tc.username, } @@ -345,7 +345,7 @@ func TestValidateUsername(t *testing.T) { } // newUserClient creates a new user client for testing, with a temp unix socket and mock Dbus connection. -func newUserClient(t *testing.T, accountsMock user.DbusObject, hostnameMock user.DbusObject, userFactoryMock user.UserObjectFactory) proto.UserServiceClient { +func newUserClient(t *testing.T, accountsMock user.DbusObject, hostnameMock user.DbusObject, userFactoryMock user.UserObjectFactory) pb.UserServiceClient { t.Helper() // socket path is limited in length. tmpDir, err := os.MkdirTemp("", "hello-socket-dir") @@ -362,7 +362,7 @@ func newUserClient(t *testing.T, accountsMock user.DbusObject, hostnameMock user service := user.New(bus, user.WithAccounts(accountsMock), user.WithHostname(hostnameMock), user.WithUserFactory(userFactoryMock)) grpcServer := grpc.NewServer() - proto.RegisterUserServiceServer(grpcServer, service) + pb.RegisterUserServiceServer(grpcServer, service) done := make(chan struct{}) go func() { defer close(done) @@ -377,7 +377,7 @@ func newUserClient(t *testing.T, accountsMock user.DbusObject, hostnameMock user require.NoError(t, err, "Setup: Could not connect to GRPC server") t.Cleanup(func() { _ = conn.Close() }) - return proto.NewUserServiceClient(conn) + return pb.NewUserServiceClient(conn) } func TestMain(m *testing.M) { From efde8c10fa249cc43704717e220282ca3878ef37 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 9 Jan 2024 17:18:05 +0200 Subject: [PATCH 067/106] chore(provd): remove GetUser endponit --- provd/generate.go | 2 +- .../testdata/TestRegisterGRPCServices/golden | 7 +- provd/internal/services/user/export_test.go | 2 - .../golden/successfully_gets_a_user | 1 - provd/internal/services/user/user.go | 93 ----- provd/internal/services/user/user_test.go | 116 ------ provd/protos/user.pb.go | 362 ++++++------------ provd/protos/user.proto | 9 - provd/protos/user_grpc.pb.go | 41 +- 9 files changed, 120 insertions(+), 513 deletions(-) delete mode 100644 provd/internal/services/user/testdata/TestGetUser/golden/successfully_gets_a_user diff --git a/provd/generate.go b/provd/generate.go index 7de14dc03..9842feb30 100644 --- a/provd/generate.go +++ b/provd/generate.go @@ -5,4 +5,4 @@ package provd // previously described on: https://github.com/protocolbuffers/protobuf/blob/main/docs/implementing_proto3_presence.md. // // Should it become default, remove the --experimental_allow_proto3_optional flag from the go generate command below. -//go:generate sh -c "PATH=\"$PATH:`go env GOPATH`/bin\" protoc --proto_path=. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./proto/user.proto --experimental_allow_proto3_optional" +//go:generate sh -c "PATH=\"$PATH:`go env GOPATH`/bin\" protoc --proto_path=. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./protos/user.proto --experimental_allow_proto3_optional" diff --git a/provd/internal/services/testdata/TestRegisterGRPCServices/golden b/provd/internal/services/testdata/TestRegisterGRPCServices/golden index 49bc27d61..004cdf251 100644 --- a/provd/internal/services/testdata/TestRegisterGRPCServices/golden +++ b/provd/internal/services/testdata/TestRegisterGRPCServices/golden @@ -6,13 +6,10 @@ provd.HelloWorldService: metadata: provd.protos user.UserService: methods: - - name: CreateUser - isclientstream: false - isserverstream: false - name: ValidateUsername isclientstream: false isserverstream: false - - name: GetUser + - name: CreateUser isclientstream: false isserverstream: false - metadata: proto/user.proto + metadata: protos/user.proto diff --git a/provd/internal/services/user/export_test.go b/provd/internal/services/user/export_test.go index 4bc5fcd86..02c71af87 100644 --- a/provd/internal/services/user/export_test.go +++ b/provd/internal/services/user/export_test.go @@ -70,8 +70,6 @@ func (a *AccountsObjectMock) Call(method string, flags dbus.Flags, args ...inter callResult = a.UserObjectPath case "org.freedesktop.Accounts.FindUserByName": callResult = a.UserObjectPath - case "org.freedesktop.Accounts.FindUserById": - callResult = a.UserObjectPath default: err = errors.New("Unsupported accounts method") } diff --git a/provd/internal/services/user/testdata/TestGetUser/golden/successfully_gets_a_user b/provd/internal/services/user/testdata/TestGetUser/golden/successfully_gets_a_user deleted file mode 100644 index c066eae11..000000000 --- a/provd/internal/services/user/testdata/TestGetUser/golden/successfully_gets_a_user +++ /dev/null @@ -1 +0,0 @@ -real_name:"Ubuntu" hostname:"ubuntu" username:"ubuntu" auto_login:true \ No newline at end of file diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index 5a9681ec4..ad3f75ee8 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -11,7 +11,6 @@ import ( "math/big" "os" "regexp" - "strconv" "strings" pb "github.com/canonical/ubuntu-desktop-provision/provd/protos" @@ -276,95 +275,3 @@ func (s *Service) ValidateUsername(ctx context.Context, req *pb.ValidateUsername // User found return &pb.ValidateUsernameResponse{UsernameValidation: pb.UsernameValidation_ALREADY_IN_USE}, nil } - -// GetUser returns the user information for the given uid. -func (s *Service) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) { - - // Validate request - if req == nil { - return nil, status.Errorf(codes.InvalidArgument, "received a nil request") - } - uid := req.GetUserId() - if uid == "" { - return nil, status.Errorf(codes.InvalidArgument, "uid cannot be empty") - } - - uidInt, err := strconv.ParseInt(uid, 10, 64) - if err != nil { - // handle error: uid is not a valid integer - return nil, status.Errorf(codes.Internal, "int convert: %s", err) - - } - var userObjectPath dbus.ObjectPath - err = s.accounts.Call(DbusAccountsPrefix+".FindUserById", 0, uidInt).Store(&userObjectPath) - - if err != nil { - return nil, err - } - - userObject := s.userFactory.GetUserObject(userObjectPath) - - // Get the username via dbus - response, err := userObject.GetProperty(DbusAccountsPrefix + ".User.UserName") - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get username: %s", err) - } - if response.Value() == nil { - return nil, status.Errorf(codes.NotFound, "could not find username for given uid") - } - username, ok := response.Value().(string) - if !ok { - return nil, status.Errorf(codes.Internal, "unexpected type returned for username: %s", err) - } - - // Get the realName via dbus - response, err = userObject.GetProperty(DbusAccountsPrefix + ".User.RealName") - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get realName: %s", err) - } - if response.Value() == nil { - return nil, status.Errorf(codes.NotFound, "could not find realName for given uid") - } - realName, ok := response.Value().(string) - if !ok { - return nil, status.Errorf(codes.Internal, "unexpected type returned for realName: %s", err) - } - - // Get autoLogin via dbus - response, err = userObject.GetProperty(DbusAccountsPrefix + ".User.AutomaticLogin") - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get autoLogin: %s", err) - } - if response.Value() == nil { - return nil, status.Errorf(codes.NotFound, "could not find autoLogin for given uid") - } - autoLogin, ok := response.Value().(bool) - if !ok { - return nil, status.Errorf(codes.Internal, "unexpected type returned for autoLogin: %s", err) - } - - // Get the hostname via dbus - response, err = s.hostname.GetProperty(DbusHostnamePrefix + ".Hostname") - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get hostname: %s", err) - } - if response.Value() == nil { - return nil, status.Errorf(codes.NotFound, "could not find hostname") - } - hostname, ok := response.Value().(string) - if !ok { - return nil, status.Errorf(codes.Internal, "unexpected type returned for hostname: %s", err) - } - - user := &pb.User{ - RealName: realName, - Hostname: hostname, - Username: username, - AutoLogin: autoLogin, - } - - return &pb.GetUserResponse{ - User: user, - }, nil - -} diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go index f14546fd9..cc794aea1 100644 --- a/provd/internal/services/user/user_test.go +++ b/provd/internal/services/user/user_test.go @@ -160,122 +160,6 @@ func TestCreateUser(t *testing.T) { } } -func TestGetUser(t *testing.T) { - t.Parallel() - - tests := map[string]struct { - uid string - - realName string - username string - password string - hostname string - autoLogin bool - - accountsError bool - hostnameError bool - - wantErr bool - }{ - "Successfully gets a user": { - uid: "1000", - realName: "Ubuntu", - username: "ubuntu", - password: "password", - hostname: "ubuntu", - autoLogin: true, - }, - "Error when uid is empty": { - uid: "", - realName: "Ubuntu", - username: "ubuntu", - password: "password", - hostname: "ubuntu", - autoLogin: true, - wantErr: true, - }, - "Error when uid is not a number": { - uid: "notanumber", - realName: "Ubuntu", - username: "ubuntu", - password: "password", - hostname: "ubuntu", - autoLogin: true, - wantErr: true, - }, - "Error when accounts returns an error": { - uid: "9999", - realName: "Ubuntu", - username: "ubuntu", - password: "password", - hostname: "ubuntu", - autoLogin: true, - wantErr: true, - accountsError: true, - }, - "Error when hostname returns an error": { - uid: "1000", - realName: "Ubuntu", - username: "ubuntu", - password: "password", - hostname: "ubuntu", - autoLogin: true, - wantErr: true, - hostnameError: true, - }, - } - - for name, tc := range tests { - tc := tc - t.Run(name, func(t *testing.T) { - t.Parallel() - t.Cleanup(testutils.StartLocalSystemBus()) - - accountsMock := &user.AccountsObjectMock{ - WantError: tc.accountsError, - } - - hostnameMock := &user.HostnameObjectMock{ - WantError: tc.hostnameError, - Properties: map[string]interface{}{ - "org.freedesktop.hostname1.Hostname": tc.hostname, - }, - } - - // userFactoryMock is used to return a mock user object with the properties we want to test. - userFactoryMock := user.UserObjectFactoryMock{ - &user.UserObjectMock{ - Properties: map[string]interface{}{ - "org.freedesktop.Accounts.User.RealName": tc.realName, - "org.freedesktop.Accounts.User.UserName": tc.username, - "org.freedesktop.Accounts.User.Password": tc.password, - "org.freedesktop.Accounts.User.AutomaticLogin": tc.autoLogin, - }, - }, - } - - client := newUserClient(t, accountsMock, hostnameMock, userFactoryMock) - - getReq := &pb.GetUserRequest{ - UserId: tc.uid, - } - - resp, err := client.GetUser(context.Background(), getReq) - if tc.wantErr { - require.Error(t, err, "ValidateUsername should return an error, but did not") - return - } - require.NoError(t, err, "ValidateUsername should not return an error, but did") - - got := resp.GetUser().String() - want := testutils.LoadWithUpdateFromGolden(t, got) - require.Equal(t, want, got, "ValidateUsername returned an unexpected response") - - }) - - } -} - func TestValidateUsername(t *testing.T) { t.Parallel() diff --git a/provd/protos/user.pb.go b/provd/protos/user.pb.go index d8ffb3b57..34b753628 100644 --- a/provd/protos/user.pb.go +++ b/provd/protos/user.pb.go @@ -2,7 +2,7 @@ // versions: // protoc-gen-go v1.31.0 // protoc v3.20.3 -// source: proto/user.proto +// source: protos/user.proto package proto @@ -62,11 +62,11 @@ func (x UsernameValidation) String() string { } func (UsernameValidation) Descriptor() protoreflect.EnumDescriptor { - return file_proto_user_proto_enumTypes[0].Descriptor() + return file_protos_user_proto_enumTypes[0].Descriptor() } func (UsernameValidation) Type() protoreflect.EnumType { - return &file_proto_user_proto_enumTypes[0] + return &file_protos_user_proto_enumTypes[0] } func (x UsernameValidation) Number() protoreflect.EnumNumber { @@ -75,7 +75,7 @@ func (x UsernameValidation) Number() protoreflect.EnumNumber { // Deprecated: Use UsernameValidation.Descriptor instead. func (UsernameValidation) EnumDescriptor() ([]byte, []int) { - return file_proto_user_proto_rawDescGZIP(), []int{0} + return file_protos_user_proto_rawDescGZIP(), []int{0} } type Empty struct { @@ -87,7 +87,7 @@ type Empty struct { func (x *Empty) Reset() { *x = Empty{} if protoimpl.UnsafeEnabled { - mi := &file_proto_user_proto_msgTypes[0] + mi := &file_protos_user_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -100,7 +100,7 @@ func (x *Empty) String() string { func (*Empty) ProtoMessage() {} func (x *Empty) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_proto_msgTypes[0] + mi := &file_protos_user_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -113,54 +113,7 @@ func (x *Empty) ProtoReflect() protoreflect.Message { // Deprecated: Use Empty.ProtoReflect.Descriptor instead. func (*Empty) Descriptor() ([]byte, []int) { - return file_proto_user_proto_rawDescGZIP(), []int{0} -} - -type GetUserRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` -} - -func (x *GetUserRequest) Reset() { - *x = GetUserRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_proto_user_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetUserRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetUserRequest) ProtoMessage() {} - -func (x *GetUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetUserRequest.ProtoReflect.Descriptor instead. -func (*GetUserRequest) Descriptor() ([]byte, []int) { - return file_proto_user_proto_rawDescGZIP(), []int{1} -} - -func (x *GetUserRequest) GetUserId() string { - if x != nil { - return x.UserId - } - return "" + return file_protos_user_proto_rawDescGZIP(), []int{0} } type User struct { @@ -178,7 +131,7 @@ type User struct { func (x *User) Reset() { *x = User{} if protoimpl.UnsafeEnabled { - mi := &file_proto_user_proto_msgTypes[2] + mi := &file_protos_user_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -191,7 +144,7 @@ func (x *User) String() string { func (*User) ProtoMessage() {} func (x *User) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_proto_msgTypes[2] + mi := &file_protos_user_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -204,7 +157,7 @@ func (x *User) ProtoReflect() protoreflect.Message { // Deprecated: Use User.ProtoReflect.Descriptor instead. func (*User) Descriptor() ([]byte, []int) { - return file_proto_user_proto_rawDescGZIP(), []int{2} + return file_protos_user_proto_rawDescGZIP(), []int{1} } func (x *User) GetRealName() string { @@ -242,53 +195,6 @@ func (x *User) GetAutoLogin() bool { return false } -type GetUserResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` -} - -func (x *GetUserResponse) Reset() { - *x = GetUserResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_proto_user_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetUserResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetUserResponse) ProtoMessage() {} - -func (x *GetUserResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetUserResponse.ProtoReflect.Descriptor instead. -func (*GetUserResponse) Descriptor() ([]byte, []int) { - return file_proto_user_proto_rawDescGZIP(), []int{3} -} - -func (x *GetUserResponse) GetUser() *User { - if x != nil { - return x.User - } - return nil -} - type CreateUserRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -301,7 +207,7 @@ type CreateUserRequest struct { func (x *CreateUserRequest) Reset() { *x = CreateUserRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_user_proto_msgTypes[4] + mi := &file_protos_user_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -314,7 +220,7 @@ func (x *CreateUserRequest) String() string { func (*CreateUserRequest) ProtoMessage() {} func (x *CreateUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_proto_msgTypes[4] + mi := &file_protos_user_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -327,7 +233,7 @@ func (x *CreateUserRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateUserRequest.ProtoReflect.Descriptor instead. func (*CreateUserRequest) Descriptor() ([]byte, []int) { - return file_proto_user_proto_rawDescGZIP(), []int{4} + return file_protos_user_proto_rawDescGZIP(), []int{2} } func (x *CreateUserRequest) GetUser() *User { @@ -355,7 +261,7 @@ type ValidateUsernameRequest struct { func (x *ValidateUsernameRequest) Reset() { *x = ValidateUsernameRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_user_proto_msgTypes[5] + mi := &file_protos_user_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -368,7 +274,7 @@ func (x *ValidateUsernameRequest) String() string { func (*ValidateUsernameRequest) ProtoMessage() {} func (x *ValidateUsernameRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_proto_msgTypes[5] + mi := &file_protos_user_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -381,7 +287,7 @@ func (x *ValidateUsernameRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ValidateUsernameRequest.ProtoReflect.Descriptor instead. func (*ValidateUsernameRequest) Descriptor() ([]byte, []int) { - return file_proto_user_proto_rawDescGZIP(), []int{5} + return file_protos_user_proto_rawDescGZIP(), []int{3} } func (x *ValidateUsernameRequest) GetUsername() string { @@ -402,7 +308,7 @@ type ValidateUsernameResponse struct { func (x *ValidateUsernameResponse) Reset() { *x = ValidateUsernameResponse{} if protoimpl.UnsafeEnabled { - mi := &file_proto_user_proto_msgTypes[6] + mi := &file_protos_user_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -415,7 +321,7 @@ func (x *ValidateUsernameResponse) String() string { func (*ValidateUsernameResponse) ProtoMessage() {} func (x *ValidateUsernameResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_proto_msgTypes[6] + mi := &file_protos_user_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -428,7 +334,7 @@ func (x *ValidateUsernameResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ValidateUsernameResponse.ProtoReflect.Descriptor instead. func (*ValidateUsernameResponse) Descriptor() ([]byte, []int) { - return file_proto_user_proto_rawDescGZIP(), []int{6} + return file_protos_user_proto_rawDescGZIP(), []int{4} } func (x *ValidateUsernameResponse) GetUsernameValidation() UsernameValidation { @@ -438,116 +344,102 @@ func (x *ValidateUsernameResponse) GetUsernameValidation() UsernameValidation { return UsernameValidation_OK } -var File_proto_user_proto protoreflect.FileDescriptor - -var file_proto_user_proto_rawDesc = []byte{ - 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x12, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x22, 0x29, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x96, 0x01, 0x0a, - 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, - 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, - 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, - 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x6c, - 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x75, 0x74, 0x6f, - 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x22, 0x31, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x55, 0x73, - 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x4e, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, - 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x75, 0x73, - 0x65, 0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x19, 0x0a, - 0x08, 0x69, 0x73, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x69, 0x73, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x22, 0x35, 0x0a, 0x17, 0x56, 0x61, 0x6c, 0x69, +var File_protos_user_proto protoreflect.FileDescriptor + +var file_protos_user_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x96, 0x01, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x72, + 0x65, 0x61, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x72, 0x65, 0x61, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1d, 0x0a, 0x0a, + 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x61, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x22, 0x4e, 0x0a, 0x11, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1e, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, + 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x22, 0x35, 0x0a, 0x17, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, + 0x6d, 0x65, 0x22, 0x65, 0x0a, 0x18, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, + 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, + 0x0a, 0x13, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x75, 0x73, + 0x65, 0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x12, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x71, 0x0a, 0x12, 0x55, 0x73, 0x65, + 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x41, 0x4c, 0x52, 0x45, 0x41, + 0x44, 0x59, 0x5f, 0x49, 0x4e, 0x5f, 0x55, 0x53, 0x45, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x53, + 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x45, 0x44, 0x10, 0x02, + 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x43, 0x48, 0x41, 0x52, + 0x53, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x4f, 0x4f, 0x5f, 0x4c, 0x4f, 0x4e, 0x47, 0x10, + 0x04, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x10, 0x05, 0x32, 0x94, 0x01, 0x0a, + 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x32, 0x0a, 0x0a, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x75, 0x73, 0x65, + 0x72, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x12, 0x51, 0x0a, 0x10, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, - 0x65, 0x0a, 0x18, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, - 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x13, 0x75, - 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, - 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x12, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x71, 0x0a, 0x12, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, - 0x6d, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x06, 0x0a, 0x02, - 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x41, 0x4c, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f, - 0x49, 0x4e, 0x5f, 0x55, 0x53, 0x45, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x59, 0x53, 0x54, - 0x45, 0x4d, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x45, 0x44, 0x10, 0x02, 0x12, 0x11, 0x0a, - 0x0d, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x43, 0x48, 0x41, 0x52, 0x53, 0x10, 0x03, - 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x4f, 0x4f, 0x5f, 0x4c, 0x4f, 0x4e, 0x47, 0x10, 0x04, 0x12, 0x09, - 0x0a, 0x05, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x10, 0x05, 0x32, 0xcc, 0x01, 0x0a, 0x0b, 0x55, 0x73, - 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x36, 0x0a, 0x07, 0x47, 0x65, 0x74, - 0x55, 0x73, 0x65, 0x72, 0x12, 0x14, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x55, - 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x75, 0x73, 0x65, - 0x72, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x32, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, - 0x17, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x51, 0x0a, 0x10, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x2e, 0x75, 0x73, 0x65, 0x72, - 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, - 0x2f, 0x75, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x2d, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x2d, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x64, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x75, 0x62, 0x75, 0x6e, + 0x74, 0x75, 0x2d, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x2d, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( - file_proto_user_proto_rawDescOnce sync.Once - file_proto_user_proto_rawDescData = file_proto_user_proto_rawDesc + file_protos_user_proto_rawDescOnce sync.Once + file_protos_user_proto_rawDescData = file_protos_user_proto_rawDesc ) -func file_proto_user_proto_rawDescGZIP() []byte { - file_proto_user_proto_rawDescOnce.Do(func() { - file_proto_user_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_user_proto_rawDescData) +func file_protos_user_proto_rawDescGZIP() []byte { + file_protos_user_proto_rawDescOnce.Do(func() { + file_protos_user_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_user_proto_rawDescData) }) - return file_proto_user_proto_rawDescData + return file_protos_user_proto_rawDescData } -var file_proto_user_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_proto_user_proto_msgTypes = make([]protoimpl.MessageInfo, 7) -var file_proto_user_proto_goTypes = []interface{}{ +var file_protos_user_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_protos_user_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_protos_user_proto_goTypes = []interface{}{ (UsernameValidation)(0), // 0: user.UsernameValidation (*Empty)(nil), // 1: user.Empty - (*GetUserRequest)(nil), // 2: user.GetUserRequest - (*User)(nil), // 3: user.User - (*GetUserResponse)(nil), // 4: user.GetUserResponse - (*CreateUserRequest)(nil), // 5: user.CreateUserRequest - (*ValidateUsernameRequest)(nil), // 6: user.ValidateUsernameRequest - (*ValidateUsernameResponse)(nil), // 7: user.ValidateUsernameResponse -} -var file_proto_user_proto_depIdxs = []int32{ - 3, // 0: user.GetUserResponse.user:type_name -> user.User - 3, // 1: user.CreateUserRequest.user:type_name -> user.User - 0, // 2: user.ValidateUsernameResponse.username_validation:type_name -> user.UsernameValidation - 2, // 3: user.UserService.GetUser:input_type -> user.GetUserRequest - 5, // 4: user.UserService.CreateUser:input_type -> user.CreateUserRequest - 6, // 5: user.UserService.ValidateUsername:input_type -> user.ValidateUsernameRequest - 4, // 6: user.UserService.GetUser:output_type -> user.GetUserResponse - 1, // 7: user.UserService.CreateUser:output_type -> user.Empty - 7, // 8: user.UserService.ValidateUsername:output_type -> user.ValidateUsernameResponse - 6, // [6:9] is the sub-list for method output_type - 3, // [3:6] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name -} - -func init() { file_proto_user_proto_init() } -func file_proto_user_proto_init() { - if File_proto_user_proto != nil { + (*User)(nil), // 2: user.User + (*CreateUserRequest)(nil), // 3: user.CreateUserRequest + (*ValidateUsernameRequest)(nil), // 4: user.ValidateUsernameRequest + (*ValidateUsernameResponse)(nil), // 5: user.ValidateUsernameResponse +} +var file_protos_user_proto_depIdxs = []int32{ + 2, // 0: user.CreateUserRequest.user:type_name -> user.User + 0, // 1: user.ValidateUsernameResponse.username_validation:type_name -> user.UsernameValidation + 3, // 2: user.UserService.CreateUser:input_type -> user.CreateUserRequest + 4, // 3: user.UserService.ValidateUsername:input_type -> user.ValidateUsernameRequest + 1, // 4: user.UserService.CreateUser:output_type -> user.Empty + 5, // 5: user.UserService.ValidateUsername:output_type -> user.ValidateUsernameResponse + 4, // [4:6] is the sub-list for method output_type + 2, // [2:4] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_protos_user_proto_init() } +func file_protos_user_proto_init() { + if File_protos_user_proto != nil { return } if !protoimpl.UnsafeEnabled { - file_proto_user_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_protos_user_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Empty); i { case 0: return &v.state @@ -559,19 +451,7 @@ func file_proto_user_proto_init() { return nil } } - file_proto_user_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetUserRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_proto_user_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_protos_user_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*User); i { case 0: return &v.state @@ -583,19 +463,7 @@ func file_proto_user_proto_init() { return nil } } - file_proto_user_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetUserResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_proto_user_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_protos_user_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateUserRequest); i { case 0: return &v.state @@ -607,7 +475,7 @@ func file_proto_user_proto_init() { return nil } } - file_proto_user_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_protos_user_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateUsernameRequest); i { case 0: return &v.state @@ -619,7 +487,7 @@ func file_proto_user_proto_init() { return nil } } - file_proto_user_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + file_protos_user_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateUsernameResponse); i { case 0: return &v.state @@ -636,19 +504,19 @@ func file_proto_user_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_proto_user_proto_rawDesc, + RawDescriptor: file_protos_user_proto_rawDesc, NumEnums: 1, - NumMessages: 7, + NumMessages: 5, NumExtensions: 0, NumServices: 1, }, - GoTypes: file_proto_user_proto_goTypes, - DependencyIndexes: file_proto_user_proto_depIdxs, - EnumInfos: file_proto_user_proto_enumTypes, - MessageInfos: file_proto_user_proto_msgTypes, + GoTypes: file_protos_user_proto_goTypes, + DependencyIndexes: file_protos_user_proto_depIdxs, + EnumInfos: file_protos_user_proto_enumTypes, + MessageInfos: file_protos_user_proto_msgTypes, }.Build() - File_proto_user_proto = out.File - file_proto_user_proto_rawDesc = nil - file_proto_user_proto_goTypes = nil - file_proto_user_proto_depIdxs = nil + File_protos_user_proto = out.File + file_protos_user_proto_rawDesc = nil + file_protos_user_proto_goTypes = nil + file_protos_user_proto_depIdxs = nil } diff --git a/provd/protos/user.proto b/provd/protos/user.proto index 5a71de5e8..3a36fca22 100644 --- a/provd/protos/user.proto +++ b/provd/protos/user.proto @@ -5,17 +5,12 @@ package user; option go_package = "github.com/canonical/ubuntu-desktop-provision/provd/proto"; service UserService { - rpc GetUser(GetUserRequest) returns (GetUserResponse); rpc CreateUser(CreateUserRequest) returns (Empty); rpc ValidateUsername(ValidateUsernameRequest) returns (ValidateUsernameResponse); } message Empty {} -message GetUserRequest { - string user_id = 1; -} - message User { string real_name = 1; string hostname = 2; @@ -24,10 +19,6 @@ message User { bool auto_login = 5; } -message GetUserResponse { - User user = 1; -} - message CreateUserRequest { User user = 1; bool is_admin = 2; diff --git a/provd/protos/user_grpc.pb.go b/provd/protos/user_grpc.pb.go index f01f962c0..2b7edc35d 100644 --- a/provd/protos/user_grpc.pb.go +++ b/provd/protos/user_grpc.pb.go @@ -2,7 +2,7 @@ // versions: // - protoc-gen-go-grpc v1.3.0 // - protoc v3.20.3 -// source: proto/user.proto +// source: protos/user.proto package proto @@ -19,7 +19,6 @@ import ( const _ = grpc.SupportPackageIsVersion7 const ( - UserService_GetUser_FullMethodName = "/user.UserService/GetUser" UserService_CreateUser_FullMethodName = "/user.UserService/CreateUser" UserService_ValidateUsername_FullMethodName = "/user.UserService/ValidateUsername" ) @@ -28,7 +27,6 @@ const ( // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type UserServiceClient interface { - GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error) CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*Empty, error) ValidateUsername(ctx context.Context, in *ValidateUsernameRequest, opts ...grpc.CallOption) (*ValidateUsernameResponse, error) } @@ -41,15 +39,6 @@ func NewUserServiceClient(cc grpc.ClientConnInterface) UserServiceClient { return &userServiceClient{cc} } -func (c *userServiceClient) GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error) { - out := new(GetUserResponse) - err := c.cc.Invoke(ctx, UserService_GetUser_FullMethodName, in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *userServiceClient) CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) err := c.cc.Invoke(ctx, UserService_CreateUser_FullMethodName, in, out, opts...) @@ -72,7 +61,6 @@ func (c *userServiceClient) ValidateUsername(ctx context.Context, in *ValidateUs // All implementations must embed UnimplementedUserServiceServer // for forward compatibility type UserServiceServer interface { - GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) CreateUser(context.Context, *CreateUserRequest) (*Empty, error) ValidateUsername(context.Context, *ValidateUsernameRequest) (*ValidateUsernameResponse, error) mustEmbedUnimplementedUserServiceServer() @@ -82,9 +70,6 @@ type UserServiceServer interface { type UnimplementedUserServiceServer struct { } -func (UnimplementedUserServiceServer) GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetUser not implemented") -} func (UnimplementedUserServiceServer) CreateUser(context.Context, *CreateUserRequest) (*Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateUser not implemented") } @@ -104,24 +89,6 @@ func RegisterUserServiceServer(s grpc.ServiceRegistrar, srv UserServiceServer) { s.RegisterService(&UserService_ServiceDesc, srv) } -func _UserService_GetUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetUserRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(UserServiceServer).GetUser(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: UserService_GetUser_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(UserServiceServer).GetUser(ctx, req.(*GetUserRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _UserService_CreateUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CreateUserRequest) if err := dec(in); err != nil { @@ -165,10 +132,6 @@ var UserService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "user.UserService", HandlerType: (*UserServiceServer)(nil), Methods: []grpc.MethodDesc{ - { - MethodName: "GetUser", - Handler: _UserService_GetUser_Handler, - }, { MethodName: "CreateUser", Handler: _UserService_CreateUser_Handler, @@ -179,5 +142,5 @@ var UserService_ServiceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "proto/user.proto", + Metadata: "protos/user.proto", } From d1a1f89c5ecb3b97e155612b147dadcd4e1b0d4a Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Wed, 10 Jan 2024 09:00:39 +0200 Subject: [PATCH 068/106] chore(provd): Add toolchain version --- provd/go.mod | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/provd/go.mod b/provd/go.mod index 790e746a5..524f57d79 100644 --- a/provd/go.mod +++ b/provd/go.mod @@ -1,6 +1,8 @@ module github.com/canonical/ubuntu-desktop-provision/provd -go 1.21.1 +go 1.21.0 + +toolchain go1.21.5 require ( github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf From c9d257f14fdd16abc2d1729f32db383b38d3924f Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Wed, 10 Jan 2024 14:38:10 +0200 Subject: [PATCH 069/106] chore(provd): use google.protobuf.Empty --- provd/internal/services/user/user.go | 5 +- provd/protos/accessibility.proto | 127 +++++++++-------- provd/protos/ad.proto | 14 +- provd/protos/eula.proto | 6 +- provd/protos/keyboard.proto | 8 +- provd/protos/locale.proto | 8 +- provd/protos/privacy.proto | 10 +- provd/protos/pro_enrolment.proto | 6 +- provd/protos/theme.proto | 12 +- provd/protos/user.pb.go | 201 ++++++++++----------------- provd/protos/user.proto | 6 +- provd/protos/user_grpc.pb.go | 11 +- 12 files changed, 184 insertions(+), 230 deletions(-) diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index ad3f75ee8..28bdd045c 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -16,6 +16,7 @@ import ( pb "github.com/canonical/ubuntu-desktop-provision/provd/protos" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/emptypb" "github.com/godbus/dbus/v5" ) @@ -133,7 +134,7 @@ func HashPassword(password string, testSalt *string) (string, error) { } // CreateUser creates a new user on the system. -func (s *Service) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.Empty, error) { +func (s *Service) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*emptypb.Empty, error) { // Validate requtest if req == nil { @@ -199,7 +200,7 @@ func (s *Service) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*p return nil, status.Errorf(codes.Internal, "failed to set hostname: %s", err) } - return &pb.Empty{}, nil + return &emptypb.Empty{}, nil } // ValidateUsername validates the given username. Returns an enum value indicating diff --git a/provd/protos/accessibility.proto b/provd/protos/accessibility.proto index 8a2f14c46..f954579a9 100644 --- a/provd/protos/accessibility.proto +++ b/provd/protos/accessibility.proto @@ -2,98 +2,97 @@ syntax = "proto3"; package accessibility; -service AccessibilityService { +import "google/protobuf/empty.proto"; +service AccessibilityService { // Seeing - rpc GetHighContrast(Empty) returns (GetHighContrastResponse) {} - rpc EnableHighContrast(Empty) returns (Empty) {} - rpc DisableHighContrast(Empty) returns (Empty) {} + rpc GetHighContrast(google.protobuf.Empty) returns (GetHighContrastResponse) {} + rpc EnableHighContrast(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableHighContrast(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetReducedMotion(Empty) returns (GetReducedMotionResponse) {} - rpc EnableReducedMotion(Empty) returns (Empty) {} - rpc DisableReducedMotion(Empty) returns (Empty) {} + rpc GetReducedMotion(google.protobuf.Empty) returns (GetReducedMotionResponse) {} + rpc EnableReducedMotion(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableReducedMotion(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetLargeText(Empty) returns (GetLargeTextResponse) {} - rpc EnableLargeText(Empty) returns (Empty) {} - rpc DisableLargeText(Empty) returns (Empty) {} + rpc GetLargeText(google.protobuf.Empty) returns (GetLargeTextResponse) {} + rpc EnableLargeText(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableLargeText(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetCursorSize(Empty) returns (GetCursorSizeResponse) {} - rpc SetCursorSize(SetCursorSizeRequest) returns (Empty) {} + rpc GetCursorSize(google.protobuf.Empty) returns (GetCursorSizeResponse) {} + rpc SetCursorSize(SetCursorSizeRequest) returns (google.protobuf.Empty) {} - rpc GetSoundKeys(Empty) returns (GetSoundKeysResponse) {} - rpc EnableSoundKeys(Empty) returns (Empty) {} - rpc DisableSoundKeys(Empty) returns (Empty) {} + rpc GetSoundKeys(google.protobuf.Empty) returns (GetSoundKeysResponse) {} + rpc EnableSoundKeys(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableSoundKeys(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetOverlayScrollbars(Empty) returns (GetOverlayScrollbarsResponse) {} - rpc EnableOverlayScrollbars(Empty) returns (Empty) {} - rpc DisableOverlayScrollbars(Empty) returns (Empty) {} + rpc GetOverlayScrollbars(google.protobuf.Empty) returns (GetOverlayScrollbarsResponse) {} + rpc EnableOverlayScrollbars(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableOverlayScrollbars(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetScreenReader(Empty) returns (GetScreenReaderResponse) {} - rpc EnableScreenReader(Empty) returns (Empty) {} - rpc DisableScreenReader(Empty) returns (Empty) {} + rpc GetScreenReader(google.protobuf.Empty) returns (GetScreenReaderResponse) {} + rpc EnableScreenReader(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableScreenReader(google.protobuf.Empty) returns (google.protobuf.Empty) {} // Hearing - rpc GetOveramplification(Empty) returns (GetOveramplificationResponse) {} - rpc EnableOveramplification(Empty) returns (Empty) {} - rpc DisableOveramplification(Empty) returns (Empty) {} + rpc GetOveramplification(google.protobuf.Empty) returns (GetOveramplificationResponse) {} + rpc EnableOveramplification(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableOveramplification(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetVisualAlerts(Empty) returns (GetVisualAlertsResponse) {} - rpc EnableVisualAlerts(EnableVisualAlertsRequest) returns (Empty) {} - rpc DisableVisualAlerts(Empty) returns (Empty) {} + rpc GetVisualAlerts(google.protobuf.Empty) returns (GetVisualAlertsResponse) {} + rpc EnableVisualAlerts(EnableVisualAlertsRequest) returns (google.protobuf.Empty) {} + rpc DisableVisualAlerts(google.protobuf.Empty) returns (google.protobuf.Empty) {} // Typing - rpc GetScreenKeyboard(Empty) returns (GetScreenKeyboardResponse) {} - rpc EnableScreenKeyboard(Empty) returns (Empty) {} - rpc DisableScreenKeyboard(Empty) returns (Empty) {} + rpc GetScreenKeyboard(google.protobuf.Empty) returns (GetScreenKeyboardResponse) {} + rpc EnableScreenKeyboard(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableScreenKeyboard(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetEnableByKeyboard(Empty) returns (GetEnableByKeyboardResponse) {} - rpc EnableEnableByKeyboard(Empty) returns (Empty) {} - rpc DisableEnableByKeyboard(Empty) returns (Empty) {} + rpc GetEnableByKeyboard(google.protobuf.Empty) returns (GetEnableByKeyboardResponse) {} + rpc EnableEnableByKeyboard(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableEnableByKeyboard(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetCursorBlinking(Empty) returns (GetCursorBlinkingResponse) {} - rpc EnableCursorBlinking(EnableCursorBlinkingRequest) returns (Empty) {} - rpc DisableCursorBlinking(Empty) returns (Empty) {} + rpc GetCursorBlinking(google.protobuf.Empty) returns (GetCursorBlinkingResponse) {} + rpc EnableCursorBlinking(EnableCursorBlinkingRequest) returns (google.protobuf.Empty) {} + rpc DisableCursorBlinking(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetRepeatedKeys(Empty) returns (GetRepeatedKeysResponse) {} - rpc EnableRepeatedKeys(Empty) returns (Empty) {} - rpc DisableRepeatedKeys(Empty) returns (Empty) {} + rpc GetRepeatedKeys(google.protobuf.Empty) returns (GetRepeatedKeysResponse) {} + rpc EnableRepeatedKeys(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableRepeatedKeys(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetStickyKeys(Empty) returns (GetStickyKeysResponse) {} - rpc EnableStickyKeys(Empty) returns (Empty) {} - rpc DisableStickyKeys(Empty) returns (Empty) {} + rpc GetStickyKeys(google.protobuf.Empty) returns (GetStickyKeysResponse) {} + rpc EnableStickyKeys(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableStickyKeys(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetSlowKeys(Empty) returns (GetSlowKeysResponse) {} - rpc EnableSlowKeys(Empty) returns (Empty) {} - rpc DisableSlowKeys(Empty) returns (Empty) {} + rpc GetSlowKeys(google.protobuf.Empty) returns (GetSlowKeysResponse) {} + rpc EnableSlowKeys(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableSlowKeys(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetBounceKeys(Empty) returns (GetBounceKeysResponse) {} - rpc EnableBounceKeys(Empty) returns (Empty) {} - rpc DisableBounceKeys(Empty) returns (Empty) {} + rpc GetBounceKeys(google.protobuf.Empty) returns (GetBounceKeysResponse) {} + rpc EnableBounceKeys(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableBounceKeys(google.protobuf.Empty) returns (google.protobuf.Empty) {} // Pointing & Clicking - rpc GetMouseKeys(Empty) returns (GetMouseKeysResponse) {} - rpc EnableMouseKeys(Empty) returns (Empty) {} - rpc DisableMouseKeys(Empty) returns (Empty) {} + rpc GetMouseKeys(google.protobuf.Empty) returns (GetMouseKeysResponse) {} + rpc EnableMouseKeys(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableMouseKeys(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetLocationPointer(Empty) returns (GetLocationPointerResponse) {} - rpc EnableLocationPointer(Empty) returns (Empty) {} - rpc DisableLocationPointer(Empty) returns (Empty) {} + rpc GetLocationPointer(google.protobuf.Empty) returns (GetLocationPointerResponse) {} + rpc EnableLocationPointer(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableLocationPointer(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetDoubleClickDelay(Empty) returns (GetDoubleClickDelayResponse) {} - rpc SetDoubleClickDelay(SetDoubleClickDelayRequest) returns (Empty) {} + rpc GetDoubleClickDelay(google.protobuf.Empty) returns (GetDoubleClickDelayResponse) {} + rpc SetDoubleClickDelay(SetDoubleClickDelayRequest) returns (google.protobuf.Empty) {} - rpc GetSimulatedSecondaryClicks(Empty) returns (GetSimulatedSecondaryClicksResponse) {} - rpc EnableSimulatedSecondaryClicks(Empty) returns (Empty) {} - rpc DisableSimulatedSecondaryClicks(Empty) returns (Empty) {} + rpc GetSimulatedSecondaryClicks(google.protobuf.Empty) returns (GetSimulatedSecondaryClicksResponse) {} + rpc EnableSimulatedSecondaryClicks(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableSimulatedSecondaryClicks(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetHoverClicks(Empty) returns (GetHoverClicksResponse) {} - rpc EnableHoverClicks(Empty) returns (Empty) {} - rpc DisableHoverClicks(Empty) returns (Empty) {} + rpc GetHoverClicks(google.protobuf.Empty) returns (GetHoverClicksResponse) {} + rpc EnableHoverClicks(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableHoverClicks(google.protobuf.Empty) returns (google.protobuf.Empty) {} } -message Empty {} - message GetDoubleClickDelayResponse { float delay = 1; } diff --git a/provd/protos/ad.proto b/provd/protos/ad.proto index d45be9fc1..a62b390c7 100644 --- a/provd/protos/ad.proto +++ b/provd/protos/ad.proto @@ -2,12 +2,14 @@ syntax = "proto3"; package active_directory; +import "google/protobuf/empty.proto"; + service ActiveDirectoryService { - rpc HasSupport(Empty) returns (HasSupportResponse); - rpc IsUsed(Empty) returns (IsUsedResponse); - rpc SetUsed(SetUsedRequest) returns (Empty); - rpc GetConnectionInfo(Empty) returns (GetConnectionInfoResponse); - rpc SetConnectionInfo(SetConnectionInfoRequest) returns (Empty); + rpc HasSupport(google.protobuf.Empty) returns (HasSupportResponse); + rpc IsUsed(google.protobuf.Empty) returns (IsUsedResponse); + rpc SetUsed(SetUsedRequest) returns (google.protobuf.Empty); + rpc GetConnectionInfo(google.protobuf.Empty) returns (GetConnectionInfoResponse); + rpc SetConnectionInfo(SetConnectionInfoRequest) returns (google.protobuf.Empty); rpc CheckDomainName(CheckDomainNameRequest) returns (CheckDomainNameResponse); rpc CheckAdminName(CheckAdminNameRequest) returns (CheckAdminNameResponse); rpc CheckPassword(CheckPasswordRequest) returns (CheckPasswordResponse); @@ -15,8 +17,6 @@ service ActiveDirectoryService { rpc GetJoinResult(GetJoinResultRequest) returns (GetJoinResultResponse); } -message Empty {} - message HasSupportResponse { bool has_support = 1; } diff --git a/provd/protos/eula.proto b/provd/protos/eula.proto index 23e980b5f..a6957abb2 100644 --- a/provd/protos/eula.proto +++ b/provd/protos/eula.proto @@ -2,9 +2,9 @@ syntax = "proto3"; package eula; +import "google/protobuf/empty.proto"; + // Just a placeholder until we have designed the EULA service EulaService { - rpc AcceptEula(Empty) returns (Empty) {} + rpc AcceptEula(google.protobuf.Empty) returns (google.protobuf.Empty) {} } - -message Empty {} diff --git a/provd/protos/keyboard.proto b/provd/protos/keyboard.proto index b1dc65791..67f1ea087 100644 --- a/provd/protos/keyboard.proto +++ b/provd/protos/keyboard.proto @@ -2,13 +2,13 @@ syntax = "proto3"; package keyboard; +import "google/protobuf/empty.proto"; + service KeyboardService { - rpc SetKeyboard(SetKeyboardRequest) returns (Empty) {} - rpc SetInputSource(SetInputSourceRequest) returns (Empty) {} + rpc SetKeyboard(SetKeyboardRequest) returns (google.protobuf.Empty) {} + rpc SetInputSource(SetInputSourceRequest) returns (google.protobuf.Empty) {} } -message Empty {} - message KeyboardSettings { string layout = 1; string variant = 2; diff --git a/provd/protos/locale.proto b/provd/protos/locale.proto index 2b107a657..5ea24991a 100644 --- a/provd/protos/locale.proto +++ b/provd/protos/locale.proto @@ -2,13 +2,13 @@ syntax = "proto3"; package locale; +import "google/protobuf/empty.proto"; + service LocaleService { - rpc SetLocale (SetLocaleRequest) returns (Empty) {}; - rpc GetLocale (Empty) returns (GetLocaleResponse) {}; + rpc SetLocale (SetLocaleRequest) returns (google.protobuf.Empty) {}; + rpc GetLocale (google.protobuf.Empty) returns (GetLocaleResponse) {}; } -message Empty {} - message SetLocaleRequest { string locale = 1; } diff --git a/provd/protos/privacy.proto b/provd/protos/privacy.proto index 2ca9c499b..fb65e8097 100644 --- a/provd/protos/privacy.proto +++ b/provd/protos/privacy.proto @@ -2,14 +2,14 @@ syntax = "proto3"; package privacy; +import "google/protobuf/empty.proto"; + service PrivacyService { - rpc GetLocationServices(Empty) returns (GetLocationServicesResponse) {} - rpc EnableLocationServices(Empty) returns (Empty) {} - rpc DisableLocationServices(Empty) returns (Empty) {} + rpc GetLocationServices(google.protobuf.Empty) returns (GetLocationServicesResponse) {} + rpc EnableLocationServices(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc DisableLocationServices(google.protobuf.Empty) returns (google.protobuf.Empty) {} } -message Empty {} - message GetLocationServicesResponse { bool enabled = 1; } diff --git a/provd/protos/pro_enrolment.proto b/provd/protos/pro_enrolment.proto index 64e2623ab..de34d9dc1 100644 --- a/provd/protos/pro_enrolment.proto +++ b/provd/protos/pro_enrolment.proto @@ -2,13 +2,13 @@ syntax = "proto3"; package pro_enrollment; +import "google/protobuf/empty.proto"; + service ProEnrolmentService { rpc GenerateEnrolmentCode(GenerateEnrolmentCodeRequest) returns (GenerateEnrolmentCodeResponse); - rpc SetTokenManually(SetTokenManuallyRequest) returns (Empty); + rpc SetTokenManually(SetTokenManuallyRequest) returns (google.protobuf.Empty); } -message Empty {} - message GenerateEnrolmentCodeRequest { string token = 1; } diff --git a/provd/protos/theme.proto b/provd/protos/theme.proto index 3150486cf..2f4456d63 100644 --- a/provd/protos/theme.proto +++ b/provd/protos/theme.proto @@ -2,15 +2,15 @@ syntax = "proto3"; package theme; +import "google/protobuf/empty.proto"; + service ThemeService { - rpc SetTheme(SetThemeRequest) returns (Empty); - rpc GetTheme(Empty) returns (GetThemeResponse); - rpc SetAccent(SetAccentRequest) returns (Empty); - rpc GetAccent(Empty) returns (GetAccentResponse); + rpc SetTheme(SetThemeRequest) returns (google.protobuf.Empty); + rpc GetTheme(google.protobuf.Empty) returns (GetThemeResponse); + rpc SetAccent(SetAccentRequest) returns (google.protobuf.Empty); + rpc GetAccent(google.protobuf.Empty) returns (GetAccentResponse); } -message Empty {} - enum Theme { LIGHT = 0; DARK = 1; diff --git a/provd/protos/user.pb.go b/provd/protos/user.pb.go index 34b753628..e23f243c1 100644 --- a/provd/protos/user.pb.go +++ b/provd/protos/user.pb.go @@ -9,6 +9,7 @@ package proto import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" reflect "reflect" sync "sync" ) @@ -78,44 +79,6 @@ func (UsernameValidation) EnumDescriptor() ([]byte, []int) { return file_protos_user_proto_rawDescGZIP(), []int{0} } -type Empty struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *Empty) Reset() { - *x = Empty{} - if protoimpl.UnsafeEnabled { - mi := &file_protos_user_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Empty) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Empty) ProtoMessage() {} - -func (x *Empty) ProtoReflect() protoreflect.Message { - mi := &file_protos_user_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Empty.ProtoReflect.Descriptor instead. -func (*Empty) Descriptor() ([]byte, []int) { - return file_protos_user_proto_rawDescGZIP(), []int{0} -} - type User struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -131,7 +94,7 @@ type User struct { func (x *User) Reset() { *x = User{} if protoimpl.UnsafeEnabled { - mi := &file_protos_user_proto_msgTypes[1] + mi := &file_protos_user_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -144,7 +107,7 @@ func (x *User) String() string { func (*User) ProtoMessage() {} func (x *User) ProtoReflect() protoreflect.Message { - mi := &file_protos_user_proto_msgTypes[1] + mi := &file_protos_user_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -157,7 +120,7 @@ func (x *User) ProtoReflect() protoreflect.Message { // Deprecated: Use User.ProtoReflect.Descriptor instead. func (*User) Descriptor() ([]byte, []int) { - return file_protos_user_proto_rawDescGZIP(), []int{1} + return file_protos_user_proto_rawDescGZIP(), []int{0} } func (x *User) GetRealName() string { @@ -207,7 +170,7 @@ type CreateUserRequest struct { func (x *CreateUserRequest) Reset() { *x = CreateUserRequest{} if protoimpl.UnsafeEnabled { - mi := &file_protos_user_proto_msgTypes[2] + mi := &file_protos_user_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -220,7 +183,7 @@ func (x *CreateUserRequest) String() string { func (*CreateUserRequest) ProtoMessage() {} func (x *CreateUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_protos_user_proto_msgTypes[2] + mi := &file_protos_user_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -233,7 +196,7 @@ func (x *CreateUserRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateUserRequest.ProtoReflect.Descriptor instead. func (*CreateUserRequest) Descriptor() ([]byte, []int) { - return file_protos_user_proto_rawDescGZIP(), []int{2} + return file_protos_user_proto_rawDescGZIP(), []int{1} } func (x *CreateUserRequest) GetUser() *User { @@ -261,7 +224,7 @@ type ValidateUsernameRequest struct { func (x *ValidateUsernameRequest) Reset() { *x = ValidateUsernameRequest{} if protoimpl.UnsafeEnabled { - mi := &file_protos_user_proto_msgTypes[3] + mi := &file_protos_user_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -274,7 +237,7 @@ func (x *ValidateUsernameRequest) String() string { func (*ValidateUsernameRequest) ProtoMessage() {} func (x *ValidateUsernameRequest) ProtoReflect() protoreflect.Message { - mi := &file_protos_user_proto_msgTypes[3] + mi := &file_protos_user_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -287,7 +250,7 @@ func (x *ValidateUsernameRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ValidateUsernameRequest.ProtoReflect.Descriptor instead. func (*ValidateUsernameRequest) Descriptor() ([]byte, []int) { - return file_protos_user_proto_rawDescGZIP(), []int{3} + return file_protos_user_proto_rawDescGZIP(), []int{2} } func (x *ValidateUsernameRequest) GetUsername() string { @@ -308,7 +271,7 @@ type ValidateUsernameResponse struct { func (x *ValidateUsernameResponse) Reset() { *x = ValidateUsernameResponse{} if protoimpl.UnsafeEnabled { - mi := &file_protos_user_proto_msgTypes[4] + mi := &file_protos_user_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -321,7 +284,7 @@ func (x *ValidateUsernameResponse) String() string { func (*ValidateUsernameResponse) ProtoMessage() {} func (x *ValidateUsernameResponse) ProtoReflect() protoreflect.Message { - mi := &file_protos_user_proto_msgTypes[4] + mi := &file_protos_user_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -334,7 +297,7 @@ func (x *ValidateUsernameResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ValidateUsernameResponse.ProtoReflect.Descriptor instead. func (*ValidateUsernameResponse) Descriptor() ([]byte, []int) { - return file_protos_user_proto_rawDescGZIP(), []int{4} + return file_protos_user_proto_rawDescGZIP(), []int{3} } func (x *ValidateUsernameResponse) GetUsernameValidation() UsernameValidation { @@ -348,53 +311,55 @@ var File_protos_user_proto protoreflect.FileDescriptor var file_protos_user_proto_rawDesc = []byte{ 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x22, 0x96, 0x01, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x72, - 0x65, 0x61, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x72, 0x65, 0x61, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1d, 0x0a, 0x0a, - 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x09, 0x61, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x22, 0x4e, 0x0a, 0x11, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1e, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, - 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, - 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x22, 0x35, 0x0a, 0x17, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, - 0x6d, 0x65, 0x22, 0x65, 0x0a, 0x18, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, - 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, - 0x0a, 0x13, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x75, 0x73, - 0x65, 0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x12, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x71, 0x0a, 0x12, 0x55, 0x73, 0x65, - 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x41, 0x4c, 0x52, 0x45, 0x41, - 0x44, 0x59, 0x5f, 0x49, 0x4e, 0x5f, 0x55, 0x53, 0x45, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x53, - 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x45, 0x44, 0x10, 0x02, - 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x43, 0x48, 0x41, 0x52, - 0x53, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x4f, 0x4f, 0x5f, 0x4c, 0x4f, 0x4e, 0x47, 0x10, - 0x04, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x10, 0x05, 0x32, 0x94, 0x01, 0x0a, - 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x32, 0x0a, 0x0a, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x75, 0x73, 0x65, - 0x72, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x12, 0x51, 0x0a, 0x10, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x75, 0x62, 0x75, 0x6e, - 0x74, 0x75, 0x2d, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x2d, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x75, 0x73, 0x65, 0x72, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x96, 0x01, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, + 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, + 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x22, + 0x4e, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, + 0x75, 0x73, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x22, + 0x35, 0x0a, 0x17, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, + 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, + 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, + 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x65, 0x0a, 0x18, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x49, 0x0a, 0x13, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x18, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x12, 0x75, 0x73, 0x65, 0x72, 0x6e, + 0x61, 0x6d, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x71, 0x0a, + 0x12, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x41, + 0x4c, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f, 0x49, 0x4e, 0x5f, 0x55, 0x53, 0x45, 0x10, 0x01, 0x12, + 0x13, 0x0a, 0x0f, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, + 0x45, 0x44, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, + 0x43, 0x48, 0x41, 0x52, 0x53, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x4f, 0x4f, 0x5f, 0x4c, + 0x4f, 0x4e, 0x47, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x10, 0x05, + 0x32, 0x9f, 0x01, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x12, 0x3d, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x17, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, + 0x51, 0x0a, 0x10, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x75, 0x62, 0x75, 0x6e, 0x74, + 0x75, 0x2d, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x2d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -410,22 +375,22 @@ func file_protos_user_proto_rawDescGZIP() []byte { } var file_protos_user_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_protos_user_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_protos_user_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_protos_user_proto_goTypes = []interface{}{ (UsernameValidation)(0), // 0: user.UsernameValidation - (*Empty)(nil), // 1: user.Empty - (*User)(nil), // 2: user.User - (*CreateUserRequest)(nil), // 3: user.CreateUserRequest - (*ValidateUsernameRequest)(nil), // 4: user.ValidateUsernameRequest - (*ValidateUsernameResponse)(nil), // 5: user.ValidateUsernameResponse + (*User)(nil), // 1: user.User + (*CreateUserRequest)(nil), // 2: user.CreateUserRequest + (*ValidateUsernameRequest)(nil), // 3: user.ValidateUsernameRequest + (*ValidateUsernameResponse)(nil), // 4: user.ValidateUsernameResponse + (*emptypb.Empty)(nil), // 5: google.protobuf.Empty } var file_protos_user_proto_depIdxs = []int32{ - 2, // 0: user.CreateUserRequest.user:type_name -> user.User + 1, // 0: user.CreateUserRequest.user:type_name -> user.User 0, // 1: user.ValidateUsernameResponse.username_validation:type_name -> user.UsernameValidation - 3, // 2: user.UserService.CreateUser:input_type -> user.CreateUserRequest - 4, // 3: user.UserService.ValidateUsername:input_type -> user.ValidateUsernameRequest - 1, // 4: user.UserService.CreateUser:output_type -> user.Empty - 5, // 5: user.UserService.ValidateUsername:output_type -> user.ValidateUsernameResponse + 2, // 2: user.UserService.CreateUser:input_type -> user.CreateUserRequest + 3, // 3: user.UserService.ValidateUsername:input_type -> user.ValidateUsernameRequest + 5, // 4: user.UserService.CreateUser:output_type -> google.protobuf.Empty + 4, // 5: user.UserService.ValidateUsername:output_type -> user.ValidateUsernameResponse 4, // [4:6] is the sub-list for method output_type 2, // [2:4] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name @@ -440,18 +405,6 @@ func file_protos_user_proto_init() { } if !protoimpl.UnsafeEnabled { file_protos_user_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Empty); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_protos_user_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*User); i { case 0: return &v.state @@ -463,7 +416,7 @@ func file_protos_user_proto_init() { return nil } } - file_protos_user_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_protos_user_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateUserRequest); i { case 0: return &v.state @@ -475,7 +428,7 @@ func file_protos_user_proto_init() { return nil } } - file_protos_user_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_protos_user_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateUsernameRequest); i { case 0: return &v.state @@ -487,7 +440,7 @@ func file_protos_user_proto_init() { return nil } } - file_protos_user_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_protos_user_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateUsernameResponse); i { case 0: return &v.state @@ -506,7 +459,7 @@ func file_protos_user_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_protos_user_proto_rawDesc, NumEnums: 1, - NumMessages: 5, + NumMessages: 4, NumExtensions: 0, NumServices: 1, }, diff --git a/provd/protos/user.proto b/provd/protos/user.proto index 3a36fca22..cce43da53 100644 --- a/provd/protos/user.proto +++ b/provd/protos/user.proto @@ -4,13 +4,13 @@ package user; option go_package = "github.com/canonical/ubuntu-desktop-provision/provd/proto"; +import "google/protobuf/empty.proto"; + service UserService { - rpc CreateUser(CreateUserRequest) returns (Empty); + rpc CreateUser(CreateUserRequest) returns (google.protobuf.Empty); rpc ValidateUsername(ValidateUsernameRequest) returns (ValidateUsernameResponse); } -message Empty {} - message User { string real_name = 1; string hostname = 2; diff --git a/provd/protos/user_grpc.pb.go b/provd/protos/user_grpc.pb.go index 2b7edc35d..a457d4222 100644 --- a/provd/protos/user_grpc.pb.go +++ b/provd/protos/user_grpc.pb.go @@ -11,6 +11,7 @@ import ( grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" ) // This is a compile-time assertion to ensure that this generated file @@ -27,7 +28,7 @@ const ( // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type UserServiceClient interface { - CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*Empty, error) + CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) ValidateUsername(ctx context.Context, in *ValidateUsernameRequest, opts ...grpc.CallOption) (*ValidateUsernameResponse, error) } @@ -39,8 +40,8 @@ func NewUserServiceClient(cc grpc.ClientConnInterface) UserServiceClient { return &userServiceClient{cc} } -func (c *userServiceClient) CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*Empty, error) { - out := new(Empty) +func (c *userServiceClient) CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) err := c.cc.Invoke(ctx, UserService_CreateUser_FullMethodName, in, out, opts...) if err != nil { return nil, err @@ -61,7 +62,7 @@ func (c *userServiceClient) ValidateUsername(ctx context.Context, in *ValidateUs // All implementations must embed UnimplementedUserServiceServer // for forward compatibility type UserServiceServer interface { - CreateUser(context.Context, *CreateUserRequest) (*Empty, error) + CreateUser(context.Context, *CreateUserRequest) (*emptypb.Empty, error) ValidateUsername(context.Context, *ValidateUsernameRequest) (*ValidateUsernameResponse, error) mustEmbedUnimplementedUserServiceServer() } @@ -70,7 +71,7 @@ type UserServiceServer interface { type UnimplementedUserServiceServer struct { } -func (UnimplementedUserServiceServer) CreateUser(context.Context, *CreateUserRequest) (*Empty, error) { +func (UnimplementedUserServiceServer) CreateUser(context.Context, *CreateUserRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateUser not implemented") } func (UnimplementedUserServiceServer) ValidateUsername(context.Context, *ValidateUsernameRequest) (*ValidateUsernameResponse, error) { From e5e519c9c9263876187608c29c8cb3bcba1ac7a8 Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Thu, 11 Jan 2024 16:36:52 +0100 Subject: [PATCH 070/106] feat(init): add gdm service --- .../lib/src/services/gdm_service.dart | 105 ++ .../test/services/gdm_service_test.dart | 111 +++ .../test/services/gdm_service_test.mocks.dart | 902 ++++++++++++++++++ 3 files changed, 1118 insertions(+) create mode 100644 packages/ubuntu_init/lib/src/services/gdm_service.dart create mode 100644 packages/ubuntu_init/test/services/gdm_service_test.dart create mode 100644 packages/ubuntu_init/test/services/gdm_service_test.mocks.dart diff --git a/packages/ubuntu_init/lib/src/services/gdm_service.dart b/packages/ubuntu_init/lib/src/services/gdm_service.dart new file mode 100644 index 000000000..fc8cb7c6e --- /dev/null +++ b/packages/ubuntu_init/lib/src/services/gdm_service.dart @@ -0,0 +1,105 @@ +import 'dart:async'; + +import 'package:dbus/dbus.dart'; +import 'package:meta/meta.dart'; +import 'package:ubuntu_logger/ubuntu_logger.dart'; + +final _log = Logger('gdm_service'); + +class GdmService { + GdmService({ + DBusClient? bus, + @visibleForTesting Map? signalStreams, + }) : _sessionClient = bus, + _signalStreams = signalStreams; + DBusClient? _sessionClient; + final Map? _signalStreams; + + Future init() async { + if (_sessionClient != null) return; + + final gdmObject = DBusRemoteObject( + DBusClient.system(), + name: 'org.gnome.DisplayManager', + path: DBusObjectPath('/org/gnome/DisplayManager/Manager'), + ); + _sessionClient = await gdmObject + .callMethod('org.gnome.DisplayManager.Manager', 'OpenSession', [], + replySignature: DBusSignature.string) + .then((response) => DBusClient( + DBusAddress(response.values.first.asString()), + messageBus: false, + )); + } + + Future launchSession(String username, String password) async { + if (_sessionClient == null) { + return; + } + + final userVerifier = DBusRemoteObject( + _sessionClient!, + name: 'org.gnome.DisplayManager.UserVerifier', + path: DBusObjectPath('/org/gnome/DisplayManager/Session'), + ); + final greeter = DBusRemoteObject( + _sessionClient!, + name: 'org.gnome.DisplayManager.Greeter', + path: DBusObjectPath('/org/gnome/DisplayManager/Session'), + ); + + final infoSignal = _signalStreams?['info'] ?? + DBusRemoteObjectSignalStream( + object: userVerifier, + interface: 'org.gnome.DisplayManager.UserVerifier', + name: 'Info'); + final infoQuerySignal = _signalStreams?['infoQuery'] ?? + DBusRemoteObjectSignalStream( + object: userVerifier, + interface: 'org.gnome.DisplayManager.UserVerifier', + name: 'InfoQuery'); + final secretInfoQuerySignal = _signalStreams?['secretInfoQuery'] ?? + DBusRemoteObjectSignalStream( + object: userVerifier, + interface: 'org.gnome.DisplayManager.UserVerifier', + name: 'SecretInfoQuery'); + final problemSignal = _signalStreams?['problem'] ?? + DBusRemoteObjectSignalStream( + object: userVerifier, + interface: 'org.gnome.DisplayManager.UserVerifier', + name: 'Problem'); + final sessionOpenedSignal = _signalStreams?['sessionOpened'] ?? + DBusRemoteObjectSignalStream( + object: greeter, + interface: 'org.gnome.DisplayManager.Greeter', + name: 'SessionOpened'); + + final completer = Completer(); + + infoSignal.listen((signal) => _log.debug('Info signal received $signal')); + infoQuerySignal + .listen((signal) => _log.debug('InfoQuery signal received $signal')); + problemSignal + .listen((signal) => _log.error('Problem signal received $signal')); + secretInfoQuerySignal.listen((signal) { + _log.debug('SecretInfoQuery signal received $signal'); + userVerifier.callMethod('org.gnome.DisplayManager.UserVerifier', + 'AnswerQuery', [signal.values.first, DBusString(password)]); + }); + sessionOpenedSignal.listen((signal) async { + _log.debug('SessionOpened signal received $signal'); + await greeter.callMethod( + 'org.gnome.DisplayManager.Greeter', + 'StartSessionWhenReady', + [signal.values.first, const DBusBoolean(true)]); + completer.complete(); + }); + + _log.debug('Starting user verification'); + await userVerifier.callMethod( + 'org.gnome.DisplayManager.UserVerifier', + 'BeginVerificationForUser', + [const DBusString('gdm-password'), DBusString(username)]); + await completer.future; + } +} diff --git a/packages/ubuntu_init/test/services/gdm_service_test.dart b/packages/ubuntu_init/test/services/gdm_service_test.dart new file mode 100644 index 000000000..66fa46027 --- /dev/null +++ b/packages/ubuntu_init/test/services/gdm_service_test.dart @@ -0,0 +1,111 @@ +import 'dart:async'; + +import 'package:dbus/dbus.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:ubuntu_init/ubuntu_init.dart'; + +import 'gdm_service_test.mocks.dart'; + +@GenerateMocks([DBusClient, DBusRemoteObjectSignalStream]) +void main() { + test('launch desktop session', () async { + final client = createMockSessionClient(); + final secretInfoQueryController = StreamController(); + final sessionOpenedController = StreamController(); + + final service = GdmService(bus: client, signalStreams: { + 'info': createMockDBusRemoteObjectSignalStream(const Stream.empty()), + 'infoQuery': createMockDBusRemoteObjectSignalStream(const Stream.empty()), + 'secretInfoQuery': createMockDBusRemoteObjectSignalStream( + secretInfoQueryController.stream), + 'problem': createMockDBusRemoteObjectSignalStream(const Stream.empty()), + 'sessionOpened': createMockDBusRemoteObjectSignalStream( + sessionOpenedController.stream), + }); + + final launchSessionFuture = service.launchSession('username', 'password'); + verify(client.callMethod( + destination: 'org.gnome.DisplayManager.UserVerifier', + path: DBusObjectPath('/org/gnome/DisplayManager/Session'), + interface: 'org.gnome.DisplayManager.UserVerifier', + name: 'BeginVerificationForUser', + values: const [DBusString('gdm-password'), DBusString('username')], + )).called(1); + + secretInfoQueryController.add( + DBusSignal( + name: 'SecretInfoQuery', + path: DBusObjectPath('/org/gnome/DisplayManager/Session'), + interface: 'org.gnome.DisplayManager.UserVerifier', + sender: 'org.gnome.DisplayManager.UserVerifier', + values: [const DBusString('foo')], + ), + ); + sessionOpenedController.add( + DBusSignal( + name: 'SessionOpened', + path: DBusObjectPath('/org/gnome/DisplayManager/Session'), + interface: 'org.gnome.DisplayManager.Greeter', + sender: 'org.gnome.DisplayManager.Greeter', + values: [const DBusString('bar')], + ), + ); + + await launchSessionFuture; + + verify(client.callMethod( + destination: 'org.gnome.DisplayManager.UserVerifier', + path: DBusObjectPath('/org/gnome/DisplayManager/Session'), + interface: 'org.gnome.DisplayManager.UserVerifier', + name: 'AnswerQuery', + values: const [DBusString('foo'), DBusString('password')], + )).called(1); + verify(client.callMethod( + destination: 'org.gnome.DisplayManager.Greeter', + path: DBusObjectPath('/org/gnome/DisplayManager/Session'), + interface: 'org.gnome.DisplayManager.Greeter', + name: 'StartSessionWhenReady', + values: const [DBusString('bar'), DBusBoolean(true)], + )).called(1); + + await secretInfoQueryController.close(); + await sessionOpenedController.close(); + }); +} + +DBusClient createMockSessionClient() { + final client = MockDBusClient(); + when(client.callMethod( + destination: 'org.gnome.DisplayManager.UserVerifier', + path: DBusObjectPath('/org/gnome/DisplayManager/Session'), + interface: 'org.gnome.DisplayManager.UserVerifier', + name: 'BeginVerificationForUser', + values: anyNamed('values'), + )).thenAnswer((_) async => DBusMethodSuccessResponse()); + when(client.callMethod( + destination: 'org.gnome.DisplayManager.UserVerifier', + path: DBusObjectPath('/org/gnome/DisplayManager/Session'), + interface: 'org.gnome.DisplayManager.UserVerifier', + name: 'AnswerQuery', + values: anyNamed('values'), + )).thenAnswer((_) async => DBusMethodSuccessResponse()); + when(client.callMethod( + destination: 'org.gnome.DisplayManager.Greeter', + path: DBusObjectPath('/org/gnome/DisplayManager/Session'), + interface: 'org.gnome.DisplayManager.Greeter', + name: 'StartSessionWhenReady', + values: anyNamed('values'), + )).thenAnswer((_) async => DBusMethodSuccessResponse()); + + return client; +} + +DBusRemoteObjectSignalStream createMockDBusRemoteObjectSignalStream( + Stream signals) { + final stream = MockDBusRemoteObjectSignalStream(); + when(stream.listen(any)).thenAnswer((i) => + signals.listen(i.positionalArguments.first as void Function(DBusSignal))); + return stream; +} diff --git a/packages/ubuntu_init/test/services/gdm_service_test.mocks.dart b/packages/ubuntu_init/test/services/gdm_service_test.mocks.dart new file mode 100644 index 000000000..a68e90f84 --- /dev/null +++ b/packages/ubuntu_init/test/services/gdm_service_test.mocks.dart @@ -0,0 +1,902 @@ +// Mocks generated by Mockito 5.4.3 from annotations +// in ubuntu_init/test/services/gdm_service_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i5; + +import 'package:dbus/src/dbus_client.dart' as _i2; +import 'package:dbus/src/dbus_method_response.dart' as _i3; +import 'package:dbus/src/dbus_object.dart' as _i8; +import 'package:dbus/src/dbus_remote_object.dart' as _i9; +import 'package:dbus/src/dbus_signal.dart' as _i4; +import 'package:dbus/src/dbus_value.dart' as _i7; +import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i6; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeDBusProcessCredentials_0 extends _i1.SmartFake + implements _i2.DBusProcessCredentials { + _FakeDBusProcessCredentials_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeDBusMethodSuccessResponse_1 extends _i1.SmartFake + implements _i3.DBusMethodSuccessResponse { + _FakeDBusMethodSuccessResponse_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeDBusSignal_2 extends _i1.SmartFake implements _i4.DBusSignal { + _FakeDBusSignal_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeStreamSubscription_3 extends _i1.SmartFake + implements _i5.StreamSubscription { + _FakeStreamSubscription_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeFuture_4 extends _i1.SmartFake implements _i5.Future { + _FakeFuture_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [DBusClient]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockDBusClient extends _i1.Mock implements _i2.DBusClient { + MockDBusClient() { + _i1.throwOnMissingStub(this); + } + + @override + bool get introspectable => (super.noSuchMethod( + Invocation.getter(#introspectable), + returnValue: false, + ) as bool); + + @override + String get uniqueName => (super.noSuchMethod( + Invocation.getter(#uniqueName), + returnValue: _i6.dummyValue( + this, + Invocation.getter(#uniqueName), + ), + ) as String); + + @override + Iterable get ownedNames => (super.noSuchMethod( + Invocation.getter(#ownedNames), + returnValue: [], + ) as Iterable); + + @override + _i5.Stream get nameAcquired => (super.noSuchMethod( + Invocation.getter(#nameAcquired), + returnValue: _i5.Stream.empty(), + ) as _i5.Stream); + + @override + _i5.Stream get nameLost => (super.noSuchMethod( + Invocation.getter(#nameLost), + returnValue: _i5.Stream.empty(), + ) as _i5.Stream); + + @override + _i5.Stream<_i2.DBusNameOwnerChangedEvent> get nameOwnerChanged => + (super.noSuchMethod( + Invocation.getter(#nameOwnerChanged), + returnValue: _i5.Stream<_i2.DBusNameOwnerChangedEvent>.empty(), + ) as _i5.Stream<_i2.DBusNameOwnerChangedEvent>); + + @override + _i5.Future close() => (super.noSuchMethod( + Invocation.method( + #close, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future<_i2.DBusRequestNameReply> requestName( + String? name, { + Set<_i2.DBusRequestNameFlag>? flags = const {}, + }) => + (super.noSuchMethod( + Invocation.method( + #requestName, + [name], + {#flags: flags}, + ), + returnValue: _i5.Future<_i2.DBusRequestNameReply>.value( + _i2.DBusRequestNameReply.primaryOwner), + ) as _i5.Future<_i2.DBusRequestNameReply>); + + @override + _i5.Future<_i2.DBusReleaseNameReply> releaseName(String? name) => + (super.noSuchMethod( + Invocation.method( + #releaseName, + [name], + ), + returnValue: _i5.Future<_i2.DBusReleaseNameReply>.value( + _i2.DBusReleaseNameReply.released), + ) as _i5.Future<_i2.DBusReleaseNameReply>); + + @override + _i5.Future> listQueuedOwners(String? name) => + (super.noSuchMethod( + Invocation.method( + #listQueuedOwners, + [name], + ), + returnValue: _i5.Future>.value([]), + ) as _i5.Future>); + + @override + _i5.Future> listNames() => (super.noSuchMethod( + Invocation.method( + #listNames, + [], + ), + returnValue: _i5.Future>.value([]), + ) as _i5.Future>); + + @override + _i5.Future> listActivatableNames() => (super.noSuchMethod( + Invocation.method( + #listActivatableNames, + [], + ), + returnValue: _i5.Future>.value([]), + ) as _i5.Future>); + + @override + _i5.Future<_i2.DBusStartServiceByNameReply> startServiceByName( + String? name) => + (super.noSuchMethod( + Invocation.method( + #startServiceByName, + [name], + ), + returnValue: _i5.Future<_i2.DBusStartServiceByNameReply>.value( + _i2.DBusStartServiceByNameReply.success), + ) as _i5.Future<_i2.DBusStartServiceByNameReply>); + + @override + _i5.Future nameHasOwner(String? name) => (super.noSuchMethod( + Invocation.method( + #nameHasOwner, + [name], + ), + returnValue: _i5.Future.value(false), + ) as _i5.Future); + + @override + _i5.Future getNameOwner(String? name) => (super.noSuchMethod( + Invocation.method( + #getNameOwner, + [name], + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future getConnectionUnixUser(String? name) => (super.noSuchMethod( + Invocation.method( + #getConnectionUnixUser, + [name], + ), + returnValue: _i5.Future.value(0), + ) as _i5.Future); + + @override + _i5.Future getConnectionUnixProcessId(String? name) => + (super.noSuchMethod( + Invocation.method( + #getConnectionUnixProcessId, + [name], + ), + returnValue: _i5.Future.value(0), + ) as _i5.Future); + + @override + _i5.Future<_i2.DBusProcessCredentials> getConnectionCredentials( + String? name) => + (super.noSuchMethod( + Invocation.method( + #getConnectionCredentials, + [name], + ), + returnValue: _i5.Future<_i2.DBusProcessCredentials>.value( + _FakeDBusProcessCredentials_0( + this, + Invocation.method( + #getConnectionCredentials, + [name], + ), + )), + ) as _i5.Future<_i2.DBusProcessCredentials>); + + @override + _i5.Future getId() => (super.noSuchMethod( + Invocation.method( + #getId, + [], + ), + returnValue: _i5.Future.value(_i6.dummyValue( + this, + Invocation.method( + #getId, + [], + ), + )), + ) as _i5.Future); + + @override + _i5.Future ping([String? destination = r'org.freedesktop.DBus']) => + (super.noSuchMethod( + Invocation.method( + #ping, + [destination], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future getMachineId( + [String? destination = r'org.freedesktop.DBus']) => + (super.noSuchMethod( + Invocation.method( + #getMachineId, + [destination], + ), + returnValue: _i5.Future.value(_i6.dummyValue( + this, + Invocation.method( + #getMachineId, + [destination], + ), + )), + ) as _i5.Future); + + @override + _i5.Future<_i3.DBusMethodSuccessResponse> callMethod({ + String? destination, + required _i7.DBusObjectPath? path, + String? interface, + required String? name, + Iterable<_i7.DBusValue>? values = const [], + _i7.DBusSignature? replySignature, + bool? noReplyExpected = false, + bool? noAutoStart = false, + bool? allowInteractiveAuthorization = false, + }) => + (super.noSuchMethod( + Invocation.method( + #callMethod, + [], + { + #destination: destination, + #path: path, + #interface: interface, + #name: name, + #values: values, + #replySignature: replySignature, + #noReplyExpected: noReplyExpected, + #noAutoStart: noAutoStart, + #allowInteractiveAuthorization: allowInteractiveAuthorization, + }, + ), + returnValue: _i5.Future<_i3.DBusMethodSuccessResponse>.value( + _FakeDBusMethodSuccessResponse_1( + this, + Invocation.method( + #callMethod, + [], + { + #destination: destination, + #path: path, + #interface: interface, + #name: name, + #values: values, + #replySignature: replySignature, + #noReplyExpected: noReplyExpected, + #noAutoStart: noAutoStart, + #allowInteractiveAuthorization: allowInteractiveAuthorization, + }, + ), + )), + ) as _i5.Future<_i3.DBusMethodSuccessResponse>); + + @override + _i5.Future emitSignal({ + String? destination, + required _i7.DBusObjectPath? path, + required String? interface, + required String? name, + Iterable<_i7.DBusValue>? values = const [], + }) => + (super.noSuchMethod( + Invocation.method( + #emitSignal, + [], + { + #destination: destination, + #path: path, + #interface: interface, + #name: name, + #values: values, + }, + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future registerObject(_i8.DBusObject? object) => + (super.noSuchMethod( + Invocation.method( + #registerObject, + [object], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future unregisterObject(_i8.DBusObject? object) => + (super.noSuchMethod( + Invocation.method( + #unregisterObject, + [object], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); +} + +/// A class which mocks [DBusRemoteObjectSignalStream]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockDBusRemoteObjectSignalStream extends _i1.Mock + implements _i9.DBusRemoteObjectSignalStream { + MockDBusRemoteObjectSignalStream() { + _i1.throwOnMissingStub(this); + } + + @override + bool get isBroadcast => (super.noSuchMethod( + Invocation.getter(#isBroadcast), + returnValue: false, + ) as bool); + + @override + _i5.Future get length => (super.noSuchMethod( + Invocation.getter(#length), + returnValue: _i5.Future.value(0), + ) as _i5.Future); + + @override + _i5.Future get isEmpty => (super.noSuchMethod( + Invocation.getter(#isEmpty), + returnValue: _i5.Future.value(false), + ) as _i5.Future); + + @override + _i5.Future<_i4.DBusSignal> get first => (super.noSuchMethod( + Invocation.getter(#first), + returnValue: _i5.Future<_i4.DBusSignal>.value(_FakeDBusSignal_2( + this, + Invocation.getter(#first), + )), + ) as _i5.Future<_i4.DBusSignal>); + + @override + _i5.Future<_i4.DBusSignal> get last => (super.noSuchMethod( + Invocation.getter(#last), + returnValue: _i5.Future<_i4.DBusSignal>.value(_FakeDBusSignal_2( + this, + Invocation.getter(#last), + )), + ) as _i5.Future<_i4.DBusSignal>); + + @override + _i5.Future<_i4.DBusSignal> get single => (super.noSuchMethod( + Invocation.getter(#single), + returnValue: _i5.Future<_i4.DBusSignal>.value(_FakeDBusSignal_2( + this, + Invocation.getter(#single), + )), + ) as _i5.Future<_i4.DBusSignal>); + + @override + _i5.StreamSubscription<_i4.DBusSignal> listen( + void Function(_i4.DBusSignal)? onData, { + Function? onError, + void Function()? onDone, + bool? cancelOnError, + }) => + (super.noSuchMethod( + Invocation.method( + #listen, + [onData], + { + #onError: onError, + #onDone: onDone, + #cancelOnError: cancelOnError, + }, + ), + returnValue: _FakeStreamSubscription_3<_i4.DBusSignal>( + this, + Invocation.method( + #listen, + [onData], + { + #onError: onError, + #onDone: onDone, + #cancelOnError: cancelOnError, + }, + ), + ), + ) as _i5.StreamSubscription<_i4.DBusSignal>); + + @override + _i5.Stream<_i4.DBusSignal> asBroadcastStream({ + void Function(_i5.StreamSubscription<_i4.DBusSignal>)? onListen, + void Function(_i5.StreamSubscription<_i4.DBusSignal>)? onCancel, + }) => + (super.noSuchMethod( + Invocation.method( + #asBroadcastStream, + [], + { + #onListen: onListen, + #onCancel: onCancel, + }, + ), + returnValue: _i5.Stream<_i4.DBusSignal>.empty(), + ) as _i5.Stream<_i4.DBusSignal>); + + @override + _i5.Stream<_i4.DBusSignal> where(bool Function(_i4.DBusSignal)? test) => + (super.noSuchMethod( + Invocation.method( + #where, + [test], + ), + returnValue: _i5.Stream<_i4.DBusSignal>.empty(), + ) as _i5.Stream<_i4.DBusSignal>); + + @override + _i5.Stream map(S Function(_i4.DBusSignal)? convert) => + (super.noSuchMethod( + Invocation.method( + #map, + [convert], + ), + returnValue: _i5.Stream.empty(), + ) as _i5.Stream); + + @override + _i5.Stream asyncMap( + _i5.FutureOr Function(_i4.DBusSignal)? convert) => + (super.noSuchMethod( + Invocation.method( + #asyncMap, + [convert], + ), + returnValue: _i5.Stream.empty(), + ) as _i5.Stream); + + @override + _i5.Stream asyncExpand( + _i5.Stream? Function(_i4.DBusSignal)? convert) => + (super.noSuchMethod( + Invocation.method( + #asyncExpand, + [convert], + ), + returnValue: _i5.Stream.empty(), + ) as _i5.Stream); + + @override + _i5.Stream<_i4.DBusSignal> handleError( + Function? onError, { + bool Function(dynamic)? test, + }) => + (super.noSuchMethod( + Invocation.method( + #handleError, + [onError], + {#test: test}, + ), + returnValue: _i5.Stream<_i4.DBusSignal>.empty(), + ) as _i5.Stream<_i4.DBusSignal>); + + @override + _i5.Stream expand(Iterable Function(_i4.DBusSignal)? convert) => + (super.noSuchMethod( + Invocation.method( + #expand, + [convert], + ), + returnValue: _i5.Stream.empty(), + ) as _i5.Stream); + + @override + _i5.Future pipe( + _i5.StreamConsumer<_i4.DBusSignal>? streamConsumer) => + (super.noSuchMethod( + Invocation.method( + #pipe, + [streamConsumer], + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Stream transform( + _i5.StreamTransformer<_i4.DBusSignal, S>? streamTransformer) => + (super.noSuchMethod( + Invocation.method( + #transform, + [streamTransformer], + ), + returnValue: _i5.Stream.empty(), + ) as _i5.Stream); + + @override + _i5.Future<_i4.DBusSignal> reduce( + _i4.DBusSignal Function( + _i4.DBusSignal, + _i4.DBusSignal, + )? combine) => + (super.noSuchMethod( + Invocation.method( + #reduce, + [combine], + ), + returnValue: _i5.Future<_i4.DBusSignal>.value(_FakeDBusSignal_2( + this, + Invocation.method( + #reduce, + [combine], + ), + )), + ) as _i5.Future<_i4.DBusSignal>); + + @override + _i5.Future fold( + S? initialValue, + S Function( + S, + _i4.DBusSignal, + )? combine, + ) => + (super.noSuchMethod( + Invocation.method( + #fold, + [ + initialValue, + combine, + ], + ), + returnValue: _i6.ifNotNull( + _i6.dummyValueOrNull( + this, + Invocation.method( + #fold, + [ + initialValue, + combine, + ], + ), + ), + (S v) => _i5.Future.value(v), + ) ?? + _FakeFuture_4( + this, + Invocation.method( + #fold, + [ + initialValue, + combine, + ], + ), + ), + ) as _i5.Future); + + @override + _i5.Future join([String? separator = r'']) => (super.noSuchMethod( + Invocation.method( + #join, + [separator], + ), + returnValue: _i5.Future.value(_i6.dummyValue( + this, + Invocation.method( + #join, + [separator], + ), + )), + ) as _i5.Future); + + @override + _i5.Future contains(Object? needle) => (super.noSuchMethod( + Invocation.method( + #contains, + [needle], + ), + returnValue: _i5.Future.value(false), + ) as _i5.Future); + + @override + _i5.Future forEach(void Function(_i4.DBusSignal)? action) => + (super.noSuchMethod( + Invocation.method( + #forEach, + [action], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future every(bool Function(_i4.DBusSignal)? test) => + (super.noSuchMethod( + Invocation.method( + #every, + [test], + ), + returnValue: _i5.Future.value(false), + ) as _i5.Future); + + @override + _i5.Future any(bool Function(_i4.DBusSignal)? test) => + (super.noSuchMethod( + Invocation.method( + #any, + [test], + ), + returnValue: _i5.Future.value(false), + ) as _i5.Future); + + @override + _i5.Stream cast() => (super.noSuchMethod( + Invocation.method( + #cast, + [], + ), + returnValue: _i5.Stream.empty(), + ) as _i5.Stream); + + @override + _i5.Future> toList() => (super.noSuchMethod( + Invocation.method( + #toList, + [], + ), + returnValue: _i5.Future>.value(<_i4.DBusSignal>[]), + ) as _i5.Future>); + + @override + _i5.Future> toSet() => (super.noSuchMethod( + Invocation.method( + #toSet, + [], + ), + returnValue: _i5.Future>.value(<_i4.DBusSignal>{}), + ) as _i5.Future>); + + @override + _i5.Future drain([E? futureValue]) => (super.noSuchMethod( + Invocation.method( + #drain, + [futureValue], + ), + returnValue: _i6.ifNotNull( + _i6.dummyValueOrNull( + this, + Invocation.method( + #drain, + [futureValue], + ), + ), + (E v) => _i5.Future.value(v), + ) ?? + _FakeFuture_4( + this, + Invocation.method( + #drain, + [futureValue], + ), + ), + ) as _i5.Future); + + @override + _i5.Stream<_i4.DBusSignal> take(int? count) => (super.noSuchMethod( + Invocation.method( + #take, + [count], + ), + returnValue: _i5.Stream<_i4.DBusSignal>.empty(), + ) as _i5.Stream<_i4.DBusSignal>); + + @override + _i5.Stream<_i4.DBusSignal> takeWhile(bool Function(_i4.DBusSignal)? test) => + (super.noSuchMethod( + Invocation.method( + #takeWhile, + [test], + ), + returnValue: _i5.Stream<_i4.DBusSignal>.empty(), + ) as _i5.Stream<_i4.DBusSignal>); + + @override + _i5.Stream<_i4.DBusSignal> skip(int? count) => (super.noSuchMethod( + Invocation.method( + #skip, + [count], + ), + returnValue: _i5.Stream<_i4.DBusSignal>.empty(), + ) as _i5.Stream<_i4.DBusSignal>); + + @override + _i5.Stream<_i4.DBusSignal> skipWhile(bool Function(_i4.DBusSignal)? test) => + (super.noSuchMethod( + Invocation.method( + #skipWhile, + [test], + ), + returnValue: _i5.Stream<_i4.DBusSignal>.empty(), + ) as _i5.Stream<_i4.DBusSignal>); + + @override + _i5.Stream<_i4.DBusSignal> distinct( + [bool Function( + _i4.DBusSignal, + _i4.DBusSignal, + )? equals]) => + (super.noSuchMethod( + Invocation.method( + #distinct, + [equals], + ), + returnValue: _i5.Stream<_i4.DBusSignal>.empty(), + ) as _i5.Stream<_i4.DBusSignal>); + + @override + _i5.Future<_i4.DBusSignal> firstWhere( + bool Function(_i4.DBusSignal)? test, { + _i4.DBusSignal Function()? orElse, + }) => + (super.noSuchMethod( + Invocation.method( + #firstWhere, + [test], + {#orElse: orElse}, + ), + returnValue: _i5.Future<_i4.DBusSignal>.value(_FakeDBusSignal_2( + this, + Invocation.method( + #firstWhere, + [test], + {#orElse: orElse}, + ), + )), + ) as _i5.Future<_i4.DBusSignal>); + + @override + _i5.Future<_i4.DBusSignal> lastWhere( + bool Function(_i4.DBusSignal)? test, { + _i4.DBusSignal Function()? orElse, + }) => + (super.noSuchMethod( + Invocation.method( + #lastWhere, + [test], + {#orElse: orElse}, + ), + returnValue: _i5.Future<_i4.DBusSignal>.value(_FakeDBusSignal_2( + this, + Invocation.method( + #lastWhere, + [test], + {#orElse: orElse}, + ), + )), + ) as _i5.Future<_i4.DBusSignal>); + + @override + _i5.Future<_i4.DBusSignal> singleWhere( + bool Function(_i4.DBusSignal)? test, { + _i4.DBusSignal Function()? orElse, + }) => + (super.noSuchMethod( + Invocation.method( + #singleWhere, + [test], + {#orElse: orElse}, + ), + returnValue: _i5.Future<_i4.DBusSignal>.value(_FakeDBusSignal_2( + this, + Invocation.method( + #singleWhere, + [test], + {#orElse: orElse}, + ), + )), + ) as _i5.Future<_i4.DBusSignal>); + + @override + _i5.Future<_i4.DBusSignal> elementAt(int? index) => (super.noSuchMethod( + Invocation.method( + #elementAt, + [index], + ), + returnValue: _i5.Future<_i4.DBusSignal>.value(_FakeDBusSignal_2( + this, + Invocation.method( + #elementAt, + [index], + ), + )), + ) as _i5.Future<_i4.DBusSignal>); + + @override + _i5.Stream<_i4.DBusSignal> timeout( + Duration? timeLimit, { + void Function(_i5.EventSink<_i4.DBusSignal>)? onTimeout, + }) => + (super.noSuchMethod( + Invocation.method( + #timeout, + [timeLimit], + {#onTimeout: onTimeout}, + ), + returnValue: _i5.Stream<_i4.DBusSignal>.empty(), + ) as _i5.Stream<_i4.DBusSignal>); +} From 432dc4bd5f1208e75fa06b9589dc188b78e7e4cf Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Thu, 11 Jan 2024 16:43:25 +0100 Subject: [PATCH 071/106] feat(init): add launchDesktopSession to InitModel --- packages/ubuntu_init/lib/src/init_model.dart | 25 ++++- .../ubuntu_init/lib/src/init_services.dart | 3 + packages/ubuntu_init/lib/src/init_wizard.dart | 1 + .../ubuntu_init/test/init_model_test.dart | 30 ++++- .../test/init_model_test.mocks.dart | 106 ++++++++++++++++-- .../ubuntu_init/test/test_utils.mocks.dart | 10 ++ 6 files changed, 164 insertions(+), 11 deletions(-) diff --git a/packages/ubuntu_init/lib/src/init_model.dart b/packages/ubuntu_init/lib/src/init_model.dart index 9f6e8420c..fe15a3dfa 100644 --- a/packages/ubuntu_init/lib/src/init_model.dart +++ b/packages/ubuntu_init/lib/src/init_model.dart @@ -10,20 +10,41 @@ final initModelProvider = Provider( (_) => InitModel( pageConfig: tryGetService(), args: tryGetService(), + identityService: tryGetService(), + gdmService: tryGetService(), ), ); class InitModel { - InitModel({PageConfigService? pageConfig, ArgResults? args}) - : _pageConfig = pageConfig; + InitModel({ + PageConfigService? pageConfig, + ArgResults? args, + IdentityService? identityService, + GdmService? gdmService, + }) : _gdmService = gdmService, + _identityService = identityService, + _pageConfig = pageConfig; final PageConfigService? _pageConfig; + final IdentityService? _identityService; + final GdmService? _gdmService; List? _excludedPages; Future init() async { _excludedPages = _pageConfig?.excludedPages; } + Future launchDesktopSession() async { + final identity = await _identityService?.getIdentity(); + if (identity == null) return; + + await _gdmService?.init(); + await _gdmService?.launchSession( + identity.username, + identity.password, + ); + } + bool hasRoute(String route) { return !(_excludedPages?.contains(route.removePrefix('/')) ?? false); } diff --git a/packages/ubuntu_init/lib/src/init_services.dart b/packages/ubuntu_init/lib/src/init_services.dart index 327bec145..d7c79992a 100644 --- a/packages/ubuntu_init/lib/src/init_services.dart +++ b/packages/ubuntu_init/lib/src/init_services.dart @@ -2,6 +2,7 @@ import 'package:args/args.dart'; import 'package:gsettings/gsettings.dart'; import 'package:sysmetrics/sysmetrics.dart'; import 'package:timezone_map/timezone_map.dart'; +import 'package:ubuntu_init/src/services/gdm_service.dart'; import 'package:ubuntu_init/src/services/privacy_service.dart'; import 'package:ubuntu_init/src/services/realmd_active_directory_service.dart'; import 'package:ubuntu_init/src/services/xdg_identity_service.dart'; @@ -17,6 +18,7 @@ export 'package:args/args.dart' show ArgResults; export 'package:sysmetrics/sysmetrics.dart' show Sysmetrics; export 'package:timezone_map/timezone_map.dart' show GeoService; +export 'services/gdm_service.dart'; export 'services/privacy_service.dart'; export 'services/realmd_active_directory_service.dart'; export 'services/xdg_identity_service.dart'; @@ -38,6 +40,7 @@ Future registerInitServices(List args) { tryRegisterService(RealmdActiveDirectoryService.new); tryRegisterService( () => ConfigService(path: options!['config'] as String?)); + tryRegisterService(GdmService.new); tryRegisterServiceFactory(GSettings.new); tryRegisterService(XdgIdentityService.new); tryRegisterService(XdgKeyboardService.new); diff --git a/packages/ubuntu_init/lib/src/init_wizard.dart b/packages/ubuntu_init/lib/src/init_wizard.dart index bf362fad2..821eebb8a 100644 --- a/packages/ubuntu_init/lib/src/init_wizard.dart +++ b/packages/ubuntu_init/lib/src/init_wizard.dart @@ -43,6 +43,7 @@ class InitWizard extends ConsumerWidget { onNext: (_) async { final window = YaruWindow.of(context); await _onDone?.call(); + await ref.read(initModelProvider).launchDesktopSession(); await window.close(); return Routes.initial; }, diff --git a/packages/ubuntu_init/test/init_model_test.dart b/packages/ubuntu_init/test/init_model_test.dart index 768f7e572..f354e7f3b 100644 --- a/packages/ubuntu_init/test/init_model_test.dart +++ b/packages/ubuntu_init/test/init_model_test.dart @@ -1,13 +1,13 @@ -import 'package:args/args.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:ubuntu_init/src/init_model.dart'; +import 'package:ubuntu_init/src/init_services.dart'; import 'package:ubuntu_provision/services.dart'; import 'init_model_test.mocks.dart'; -@GenerateMocks([ArgResults, PageConfigService]) +@GenerateMocks([ArgResults, PageConfigService, IdentityService, GdmService]) void main() { test('configured page array', () async { final config = MockPageConfigService(); @@ -25,4 +25,30 @@ void main() { expect(model.hasRoute('c'), isFalse); expect(model.hasRoute('/c'), isFalse); }); + + test('launch desktop session', () async { + final config = MockPageConfigService(); + when(config.excludedPages).thenReturn([]); + + final identity = MockIdentityService(); + when(identity.getIdentity()).thenAnswer( + (_) async => const Identity( + username: 'username', + password: 'password', + ), + ); + + final gdm = MockGdmService(); + + final model = InitModel( + pageConfig: config, + identityService: identity, + gdmService: gdm, + ); + await model.init(); + await model.launchDesktopSession(); + + verify(gdm.init()).called(1); + verify(gdm.launchSession('username', 'password')).called(1); + }); } diff --git a/packages/ubuntu_init/test/init_model_test.mocks.dart b/packages/ubuntu_init/test/init_model_test.mocks.dart index 211d1e360..e3196d1a1 100644 --- a/packages/ubuntu_init/test/init_model_test.mocks.dart +++ b/packages/ubuntu_init/test/init_model_test.mocks.dart @@ -5,9 +5,10 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i4; -import 'package:args/src/arg_results.dart' as _i2; +import 'package:args/src/arg_results.dart' as _i3; import 'package:mockito/mockito.dart' as _i1; -import 'package:ubuntu_provision/services.dart' as _i3; +import 'package:ubuntu_init/src/services/gdm_service.dart' as _i5; +import 'package:ubuntu_provision/services.dart' as _i2; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -22,10 +23,20 @@ import 'package:ubuntu_provision/services.dart' as _i3; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +class _FakeIdentity_0 extends _i1.SmartFake implements _i2.Identity { + _FakeIdentity_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + /// A class which mocks [ArgResults]. /// /// See the documentation for Mockito's code generation for more information. -class MockArgResults extends _i1.Mock implements _i2.ArgResults { +class MockArgResults extends _i1.Mock implements _i3.ArgResults { MockArgResults() { _i1.throwOnMissingStub(this); } @@ -67,16 +78,16 @@ class MockArgResults extends _i1.Mock implements _i2.ArgResults { /// A class which mocks [PageConfigService]. /// /// See the documentation for Mockito's code generation for more information. -class MockPageConfigService extends _i1.Mock implements _i3.PageConfigService { +class MockPageConfigService extends _i1.Mock implements _i2.PageConfigService { MockPageConfigService() { _i1.throwOnMissingStub(this); } @override - Map get pages => (super.noSuchMethod( + Map get pages => (super.noSuchMethod( Invocation.getter(#pages), - returnValue: {}, - ) as Map); + returnValue: {}, + ) as Map); @override bool get includeWelcome => (super.noSuchMethod( @@ -106,3 +117,84 @@ class MockPageConfigService extends _i1.Mock implements _i3.PageConfigService { returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); } + +/// A class which mocks [IdentityService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockIdentityService extends _i1.Mock implements _i2.IdentityService { + MockIdentityService() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future<_i2.Identity> getIdentity() => (super.noSuchMethod( + Invocation.method( + #getIdentity, + [], + ), + returnValue: _i4.Future<_i2.Identity>.value(_FakeIdentity_0( + this, + Invocation.method( + #getIdentity, + [], + ), + )), + ) as _i4.Future<_i2.Identity>); + + @override + _i4.Future setIdentity(_i2.Identity? identity) => (super.noSuchMethod( + Invocation.method( + #setIdentity, + [identity], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future<_i2.UsernameValidation> validateUsername(String? username) => + (super.noSuchMethod( + Invocation.method( + #validateUsername, + [username], + ), + returnValue: + _i4.Future<_i2.UsernameValidation>.value(_i2.UsernameValidation.OK), + ) as _i4.Future<_i2.UsernameValidation>); +} + +/// A class which mocks [GdmService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockGdmService extends _i1.Mock implements _i5.GdmService { + MockGdmService() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future init() => (super.noSuchMethod( + Invocation.method( + #init, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future launchSession( + String? username, + String? password, + ) => + (super.noSuchMethod( + Invocation.method( + #launchSession, + [ + username, + password, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); +} diff --git a/packages/ubuntu_init/test/test_utils.mocks.dart b/packages/ubuntu_init/test/test_utils.mocks.dart index cbfb29ce1..c82a864c8 100644 --- a/packages/ubuntu_init/test/test_utils.mocks.dart +++ b/packages/ubuntu_init/test/test_utils.mocks.dart @@ -247,6 +247,16 @@ class MockInitModel extends _i1.Mock implements _i3.InitModel { returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override + _i4.Future launchDesktopSession() => (super.noSuchMethod( + Invocation.method( + #launchDesktopSession, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + @override bool hasRoute(String? route) => (super.noSuchMethod( Invocation.method( From 4f93987c16b2795bec538a15832a92ca0c655065 Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Thu, 11 Jan 2024 17:32:07 +0100 Subject: [PATCH 072/106] test(init): add fake GdmService --- .../lib/src/fake_init.dart | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/packages/ubuntu_provision_test/lib/src/fake_init.dart b/packages/ubuntu_provision_test/lib/src/fake_init.dart index 6f2f90670..3b2b63a61 100644 --- a/packages/ubuntu_provision_test/lib/src/fake_init.dart +++ b/packages/ubuntu_provision_test/lib/src/fake_init.dart @@ -32,6 +32,7 @@ Future registerFakeInitServices({ final keyfile = GSettingsKeyfileBackend(file: File('${tempDir.path}/gsettings.ini')); + registerService(() => GdmService(bus: client)); registerServiceFactory( (s) => GSettings(s, backend: keyfile)); @@ -62,6 +63,7 @@ class FakeDBusServer extends DBusServer { _locale = _FakeXdgLocaleObject(this); _network = _FakeXdgNetworkManagerObject(this); _timedate = _FakeXdgTimedateObject(this); + _gdmSession = _FakeGdmSessionObject(this); } late final DBusClient _client; @@ -72,6 +74,7 @@ class FakeDBusServer extends DBusServer { late final _FakeXdgLocaleObject _locale; late final _FakeXdgNetworkManagerObject _network; late final _FakeXdgTimedateObject _timedate; + late final _FakeGdmSessionObject _gdmSession; Identity identity; KeyboardSetting keyboard; @@ -108,6 +111,10 @@ class FakeDBusServer extends DBusServer { await _client.requestName('org.freedesktop.timedate1'); await _client.registerObject(_timedate); + await _client.requestName('org.gnome.DisplayManager.UserVerifier'); + await _client.requestName('org.gnome.DisplayManager.Greeter'); + await _client.registerObject(_gdmSession); + return address; } @@ -342,6 +349,34 @@ class _FakeXdgTimedateObject extends DBusObject { } } +class _FakeGdmSessionObject extends DBusObject { + _FakeGdmSessionObject(this.server) + : super(DBusObjectPath('/org/gnome/DisplayManager/Session')); + + final FakeDBusServer server; + + @override + Future handleMethodCall(DBusMethodCall methodCall) async { + switch (methodCall.name) { + case 'AnswerQuery': + assert(methodCall.interface == 'org.gnome.DisplayManager.UserVerifier'); + await emitSignal('org.gnome.DisplayManager.Greeter', 'SessionOpened', + const [DBusString('gdm-password')]); + return DBusMethodSuccessResponse(); + case 'BeginVerificationForUser': + assert(methodCall.interface == 'org.gnome.DisplayManager.UserVerifier'); + await emitSignal('org.gnome.DisplayManager.UserVerifier', + 'SecretInfoQuery', const [DBusString('gdm-password')]); + return DBusMethodSuccessResponse(); + case 'StartSessionWhenReady': + assert(methodCall.interface == 'org.gnome.DisplayManager.Greeter'); + return DBusMethodSuccessResponse(); + default: + return DBusMethodErrorResponse.unknownMethod(); + } + } +} + class _FakeSysmetrics implements Sysmetrics { @override Future collect() async => null; From 3f633d902079751f6fe8f986f967dc2783683168 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Thu, 11 Jan 2024 10:05:52 +0200 Subject: [PATCH 073/106] fix(provd): version bump go --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e97aba24..2bdc6def3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -155,7 +155,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Go code sanity check - uses: canonical/desktop-engineering/gh-actions/go/code-sanity@main + uses: matthew-hagemann/desktop-engineering/gh-actions/go/code-sanity@main with: golangci-lint-configfile: ".golangci.yaml" tools-directory: "./provd/tools" From a8d2c821e75ec2c1e306f69eb826ce8567648b33 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Thu, 11 Jan 2024 22:13:47 +0200 Subject: [PATCH 074/106] style(provd): run the linter --- provd/cmd/provd/daemon/daemon_test.go | 2 - provd/internal/services/manager.go | 1 - provd/internal/services/user/export_test.go | 19 ++++---- provd/internal/services/user/user.go | 50 ++++++++++----------- provd/internal/services/user/user_test.go | 3 +- 5 files changed, 36 insertions(+), 39 deletions(-) diff --git a/provd/cmd/provd/daemon/daemon_test.go b/provd/cmd/provd/daemon/daemon_test.go index 1a4feb5e0..ef389d43e 100644 --- a/provd/cmd/provd/daemon/daemon_test.go +++ b/provd/cmd/provd/daemon/daemon_test.go @@ -12,8 +12,6 @@ import ( "github.com/canonical/ubuntu-desktop-provision/provd/cmd/provd/daemon" "github.com/canonical/ubuntu-desktop-provision/provd/internal/testutils" - - // "github.com/canonical/ubuntu-desktop-provision/provd/internal/consts" "github.com/stretchr/testify/require" ) diff --git a/provd/internal/services/manager.go b/provd/internal/services/manager.go index c5612d44b..ab35166e9 100644 --- a/provd/internal/services/manager.go +++ b/provd/internal/services/manager.go @@ -11,7 +11,6 @@ import ( "github.com/canonical/ubuntu-desktop-provision/provd/internal/services/user" pb "github.com/canonical/ubuntu-desktop-provision/provd/protos" "github.com/godbus/dbus/v5" - "github.com/ubuntu/decorate" "google.golang.org/grpc" "google.golang.org/grpc/codes" diff --git a/provd/internal/services/user/export_test.go b/provd/internal/services/user/export_test.go index 02c71af87..71a64adb4 100644 --- a/provd/internal/services/user/export_test.go +++ b/provd/internal/services/user/export_test.go @@ -7,21 +7,21 @@ import ( ) // WithAccounts overrides the accounts caller for the service. -func WithAccounts(accounts DbusObject) option { +func WithAccounts(accounts DbusObject) Option { return func(o *options) { o.accounts = accounts } } // WithHostname overrides the hostname caller for the service. -func WithHostname(hostname DbusObject) option { +func WithHostname(hostname DbusObject) Option { return func(o *options) { o.hostname = hostname } } // WithUserFactory overrides the userObjectFactory for the service. -func WithUserFactory(userFactory UserObjectFactory) option { +func WithUserFactory(userFactory ObjectFactory) Option { return func(o *options) { o.userFactory = userFactory } @@ -125,18 +125,17 @@ func (u *UserObjectMock) GetProperty(prop string) (dbus.Variant, error) { } value, exists := u.Properties[prop] if !exists { - return dbus.Variant{}, errors.New("User property not found: " + prop) } return dbus.MakeVariant(value), nil } // GetProperty is a mock implementation of the dbus GetProperty method for the AccountsObjectMock. -func (u *AccountsObjectMock) GetProperty(prop string) (dbus.Variant, error) { - if u.WantError { +func (a *AccountsObjectMock) GetProperty(prop string) (dbus.Variant, error) { + if a.WantError { return dbus.Variant{}, errors.New("GetProperty error") } - value, exists := u.Properties[prop] + value, exists := a.Properties[prop] if !exists { return dbus.Variant{}, errors.New("Accounts property not found") } @@ -144,11 +143,11 @@ func (u *AccountsObjectMock) GetProperty(prop string) (dbus.Variant, error) { } // GetProperty is a mock implementation of the dbus GetProperty method for the HostnameObjectMock. -func (u *HostnameObjectMock) GetProperty(prop string) (dbus.Variant, error) { - if u.WantError { +func (h *HostnameObjectMock) GetProperty(prop string) (dbus.Variant, error) { + if h.WantError { return dbus.Variant{}, errors.New("GetProperty error") } - value, exists := u.Properties[prop] + value, exists := h.Properties[prop] if !exists { return dbus.Variant{}, errors.New("Hostname1 property not found") } diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index 28bdd045c..073f0875e 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -7,6 +7,7 @@ import ( "crypto/rand" "crypto/sha512" "encoding/base64" + "errors" "fmt" "math/big" "os" @@ -14,11 +15,10 @@ import ( "strings" pb "github.com/canonical/ubuntu-desktop-provision/provd/protos" + "github.com/godbus/dbus/v5" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" - - "github.com/godbus/dbus/v5" ) const ( @@ -26,8 +26,10 @@ const ( DbusAccountsPrefix = "org.freedesktop.Accounts" // DbusHostnamePrefix is the prefix for the Hostname D-Bus interface. DbusHostnamePrefix = "org.freedesktop.hostname1" - UsernameMaxLen = 32 - UsernameRegex = "^[a-z_][a-z0-9_-]*$" + // UsernameMaxLen is the maximum length of a username. + UsernameMaxLen = 32 + // UsernameRegex is the regex for a valid username. + UsernameRegex = "^[a-z_][a-z0-9_-]*$" ) // DbusObject is an abstraction of a dbus object. @@ -36,44 +38,46 @@ type DbusObject interface { GetProperty(p string) (dbus.Variant, error) } -// UserObjectFactory is a factory for creating DbusObjects for users. +// ObjectFactory is a factory for creating DbusObjects for users. // This is used to allow mocking of the DbusObjects for testing. -type UserObjectFactory interface { +type ObjectFactory interface { GetUserObject(userObjectPath dbus.ObjectPath) DbusObject } -// userObjectFactoryImpl is the default implementation of UserObjectFactory. -type userObjectFactoryImpl struct { +// objectFactoryImpl is the default implementation of UserObjectFactory. +type objectFactoryImpl struct { conn *dbus.Conn } // Wraps the dbus call for getting a user object. -func (f *userObjectFactoryImpl) GetUserObject(userObjectPath dbus.ObjectPath) DbusObject { +func (f *objectFactoryImpl) GetUserObject(userObjectPath dbus.ObjectPath) DbusObject { userObject := f.conn.Object(DbusAccountsPrefix, userObjectPath) return userObject } +// options are the configurable functional options of the User service. type options struct { accounts DbusObject hostname DbusObject - userFactory UserObjectFactory + userFactory ObjectFactory } -type option func(*options) +// Option type exported for tests. +type Option func(*options) // Service is the implementation of the User module service. type Service struct { pb.UnimplementedUserServiceServer accounts DbusObject hostname DbusObject - userFactory UserObjectFactory + userFactory ObjectFactory } -// New retuns a new instance of the User service. -func New(bus *dbus.Conn, args ...option) *Service { +// New returns a new instance of the User service. +func New(bus *dbus.Conn, args ...Option) *Service { accounts := bus.Object(DbusAccountsPrefix, "/org/freedesktop/Accounts") hostname := bus.Object(DbusHostnamePrefix, "/org/freedesktop/hostname1") - userFactory := &userObjectFactoryImpl{conn: bus} + userFactory := &objectFactoryImpl{conn: bus} opts := options{ accounts: accounts, @@ -112,7 +116,7 @@ func generateSalt(length int) (string, error) { // A salt can be provided for testing purposes. func HashPassword(password string, testSalt *string) (string, error) { if password == "" { - return "", status.Errorf(codes.InvalidArgument, "recieved an empty password") + return "", status.Errorf(codes.InvalidArgument, "received an empty password") } var salt string var err error @@ -135,7 +139,6 @@ func HashPassword(password string, testSalt *string) (string, error) { // CreateUser creates a new user on the system. func (s *Service) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*emptypb.Empty, error) { - // Validate requtest if req == nil { return nil, status.Errorf(codes.InvalidArgument, "received a nil request") @@ -146,11 +149,11 @@ func (s *Service) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*e } username := user.GetUsername() if username == "" { - return nil, status.Errorf(codes.InvalidArgument, "recieved an empty username") + return nil, status.Errorf(codes.InvalidArgument, "received an empty username") } realName := user.GetRealName() if realName == "" { - return nil, status.Errorf(codes.InvalidArgument, "recieved an empty realName") + return nil, status.Errorf(codes.InvalidArgument, "received an empty realName") } isAdmin := req.GetIsAdmin() var accountType int32 @@ -163,7 +166,7 @@ func (s *Service) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*e autologin := user.GetAutoLogin() hostname := user.GetHostname() if hostname == "" { - return nil, status.Errorf(codes.InvalidArgument, "recieved an empty hostname") + return nil, status.Errorf(codes.InvalidArgument, "received an empty hostname") } // Create the user var userObjectPath dbus.ObjectPath @@ -171,7 +174,6 @@ func (s *Service) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*e err := call.Store(&userObjectPath) if err != nil { - return nil, status.Errorf(codes.Internal, "failed to create user: %s", err) } @@ -182,7 +184,6 @@ func (s *Service) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*e // Set the password for the user userObject := s.userFactory.GetUserObject(userObjectPath) - fmt.Printf(hashed) err = userObject.Call(DbusAccountsPrefix+".User.SetPassword", 0, hashed, "").Err if err != nil { return nil, status.Errorf(codes.Internal, "failed to set password: %s", err) @@ -259,9 +260,8 @@ func (s *Service) ValidateUsername(ctx context.Context, req *pb.ValidateUsername // Check if username is already in use err = s.accounts.Call(DbusAccountsPrefix+".FindUserByName", 0, username).Err if err != nil { - - // Check if the error is due to user not being found or a D-Bus error - if dbusError, ok := err.(dbus.Error); ok { + var dbusError dbus.Error + if errors.As(err, &dbusError) { if dbusError.Name == DbusAccountsPrefix+".Error.Failed" { // User not found return &pb.ValidateUsernameResponse{UsernameValidation: pb.UsernameValidation_OK}, nil diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go index cc794aea1..2f456d237 100644 --- a/provd/internal/services/user/user_test.go +++ b/provd/internal/services/user/user_test.go @@ -37,6 +37,7 @@ func TestHashPassword(t *testing.T) { for name, tc := range tests { tc := tc t.Run(name, func(t *testing.T) { + t.Parallel() salt := "pepper" got, err := user.HashPassword(tc.password, &salt) @@ -229,7 +230,7 @@ func TestValidateUsername(t *testing.T) { } // newUserClient creates a new user client for testing, with a temp unix socket and mock Dbus connection. -func newUserClient(t *testing.T, accountsMock user.DbusObject, hostnameMock user.DbusObject, userFactoryMock user.UserObjectFactory) pb.UserServiceClient { +func newUserClient(t *testing.T, accountsMock user.DbusObject, hostnameMock user.DbusObject, userFactoryMock user.ObjectFactory) pb.UserServiceClient { t.Helper() // socket path is limited in length. tmpDir, err := os.MkdirTemp("", "hello-socket-dir") From b0f60f6c730f3c40314e6463a434468a4002ac62 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Fri, 12 Jan 2024 09:44:54 +0200 Subject: [PATCH 075/106] test(provd): increasing codecov --- provd/internal/services/user/user_test.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go index 2f456d237..aa0b9e3ed 100644 --- a/provd/internal/services/user/user_test.go +++ b/provd/internal/services/user/user_test.go @@ -54,6 +54,26 @@ func TestHashPassword(t *testing.T) { } } +func TestEmptyCreateUserRequest(t *testing.T) { + t.Parallel() + + client := newUserClient(t, nil, nil, nil) + + userResp, err := client.CreateUser(context.Background(), nil) + require.Error(t, err, "CreateUser should return an error for nil request") + require.Empty(t, userResp, "CreateUser should return a nil response for a nil request") +} + +func TestEmptyValidateUsernameRequest(t *testing.T) { + t.Parallel() + + client := newUserClient(t, nil, nil, nil) + + userResp, err := client.ValidateUsername(context.Background(), nil) + require.Error(t, err, "ValidateUsername should return an error for nil request") + require.Empty(t, userResp, "ValidateUsername should return a nil response for a nil request") +} + func TestCreateUser(t *testing.T) { t.Parallel() @@ -63,6 +83,7 @@ func TestCreateUser(t *testing.T) { password string hostname string autoLogin bool + isAdmin bool accountsError bool hostnameError bool @@ -74,6 +95,7 @@ func TestCreateUser(t *testing.T) { username: "ubuntu", password: "password", hostname: "ubuntu", + isAdmin: true, autoLogin: true, }, "Error when realName is empty": { @@ -149,6 +171,7 @@ func TestCreateUser(t *testing.T) { Hostname: tc.hostname, AutoLogin: tc.autoLogin, }, + IsAdmin: tc.isAdmin, } _, err := client.CreateUser(context.Background(), userReq) From 1f390807da0a49b660798ad03e7b16d26d9efb29 Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Fri, 12 Jan 2024 11:40:22 +0100 Subject: [PATCH 076/106] chore: remove generated files from coverage --- codecov.yaml | 5 ----- melos.yaml | 13 ++++++++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/codecov.yaml b/codecov.yaml index 2745926ce..1445bf2be 100644 --- a/codecov.yaml +++ b/codecov.yaml @@ -1,9 +1,4 @@ comment: false -ignore: - - "**/*.freezed.dart" - - "**/*.g.dart" - - "**/*.mocks.dart" - - "**/l10n/*.dart" coverage: precision: 2 diff --git a/melos.yaml b/melos.yaml index 29ba350ef..5446c8a48 100644 --- a/melos.yaml +++ b/melos.yaml @@ -86,16 +86,23 @@ scripts: # cleanup generated files from coverage coverage:cleanup: > melos exec --file-exists=coverage/lcov.info -- \ - lcov --remove coverage/lcov.info '**/*.g.dart' '**/*.freezed.dart' -o coverage/lcov.info + lcov --remove coverage/lcov.info \ + '**/*.freezed.dart' \ + '**/*.g.dart' \ + '**/*.mocks.dart' \ + '**/l10n/*.dart' \ + '**/*.pb*.dart' \ + -o coverage/lcov.info # format all packages format: > melos exec -c 1 -- \ "find $MELOS_PACKAGE_PATH -name '*.dart' \ - ! -name '*.g.dart' \ ! -name '*.freezed.dart' \ - ! -name '*.pb*.dart' \ + ! -name '*.g.dart' \ + ! -name '*.mocks.dart' \ ! -path '*/l10n/*' \ + ! -name '*.pb*.dart' \ ! -path '*/.*/*' \ | xargs dart format --set-exit-if-changed" From 940588e9e0a4c66fef4af7b0982f2d3edd5bd077 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Fri, 12 Jan 2024 14:57:18 +0200 Subject: [PATCH 077/106] feat: fewer public definitions --- provd/internal/services/user/user.go | 37 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index 073f0875e..4722141b2 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -22,14 +22,14 @@ import ( ) const ( - // DbusAccountsPrefix is the prefix for the Accounts D-Bus interface. - DbusAccountsPrefix = "org.freedesktop.Accounts" - // DbusHostnamePrefix is the prefix for the Hostname D-Bus interface. - DbusHostnamePrefix = "org.freedesktop.hostname1" - // UsernameMaxLen is the maximum length of a username. - UsernameMaxLen = 32 - // UsernameRegex is the regex for a valid username. - UsernameRegex = "^[a-z_][a-z0-9_-]*$" + // dbusAccountsPrefix is the prefix for the Accounts D-Bus interface. + dbusAccountsPrefix = "org.freedesktop.Accounts" + // dbusHostnamePrefix is the prefix for the Hostname D-Bus interface. + dbusHostnamePrefix = "org.freedesktop.hostname1" + // usernameMaxLen is the maximum length of a username. + usernameMaxLen = 32 + // usernameRegex is the regex for a valid username. + usernameRegex = "^[a-z_][a-z0-9_-]*$" ) // DbusObject is an abstraction of a dbus object. @@ -112,9 +112,9 @@ func generateSalt(length int) (string, error) { return string(salt), nil } -// HashPassword hashes the given password, returning in the SHA-512 crypt format. +// hashPassword hashes the given password, returning in the SHA-512 crypt format. // A salt can be provided for testing purposes. -func HashPassword(password string, testSalt *string) (string, error) { +func hashPassword(password string, testSalt *string) (string, error) { if password == "" { return "", status.Errorf(codes.InvalidArgument, "received an empty password") } @@ -170,21 +170,20 @@ func (s *Service) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*e } // Create the user var userObjectPath dbus.ObjectPath - call := s.accounts.Call(DbusAccountsPrefix+".CreateUser", 0, username, realName, accountType) - + call := s.accounts.Call(dbusAccountsPrefix+".CreateUser", 0, username, realName, accountType) err := call.Store(&userObjectPath) if err != nil { return nil, status.Errorf(codes.Internal, "failed to create user: %s", err) } - hashed, err := HashPassword(password, nil) + hashed, err := hashPassword(password, nil) if err != nil { return nil, status.Errorf(codes.Internal, "failed to generate hashed password: %s", err) } // Set the password for the user userObject := s.userFactory.GetUserObject(userObjectPath) - err = userObject.Call(DbusAccountsPrefix+".User.SetPassword", 0, hashed, "").Err + err = userObject.Call(dbusAccountsPrefix+".User.SetPassword", 0, hashed, "").Err if err != nil { return nil, status.Errorf(codes.Internal, "failed to set password: %s", err) } @@ -196,7 +195,7 @@ func (s *Service) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*e } // Set the hostname - err = s.hostname.Call(DbusHostnamePrefix+".SetStaticHostname", 0, hostname, false).Err + err = s.hostname.Call(dbusHostnamePrefix+".SetStaticHostname", 0, hostname, false).Err if err != nil { return nil, status.Errorf(codes.Internal, "failed to set hostname: %s", err) } @@ -217,13 +216,13 @@ func (s *Service) ValidateUsername(ctx context.Context, req *pb.ValidateUsername } // Check if username uses valid characters - matched, _ := regexp.MatchString(UsernameRegex, username) + matched, _ := regexp.MatchString(usernameRegex, username) if !matched { return &pb.ValidateUsernameResponse{UsernameValidation: pb.UsernameValidation_INVALID_CHARS}, nil } // Check if username is too long - if len(username) > UsernameMaxLen { + if len(username) > usernameMaxLen { return &pb.ValidateUsernameResponse{UsernameValidation: pb.UsernameValidation_TOO_LONG}, nil } @@ -258,11 +257,11 @@ func (s *Service) ValidateUsername(ctx context.Context, req *pb.ValidateUsername } // Check if username is already in use - err = s.accounts.Call(DbusAccountsPrefix+".FindUserByName", 0, username).Err + err = s.accounts.Call(dbusAccountsPrefix+".FindUserByName", 0, username).Err if err != nil { var dbusError dbus.Error if errors.As(err, &dbusError) { - if dbusError.Name == DbusAccountsPrefix+".Error.Failed" { + if dbusError.Name == dbusAccountsPrefix+".Error.Failed" { // User not found return &pb.ValidateUsernameResponse{UsernameValidation: pb.UsernameValidation_OK}, nil } From ff931245acc48b63c6a8a79f51bb48bfe3c65518 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Fri, 12 Jan 2024 14:57:50 +0200 Subject: [PATCH 078/106] test: split out unit tests from module tests --- provd/internal/services/user/internal_test.go | 45 +++++++++++++++++++ provd/internal/services/user/user_test.go | 37 --------------- 2 files changed, 45 insertions(+), 37 deletions(-) create mode 100644 provd/internal/services/user/internal_test.go diff --git a/provd/internal/services/user/internal_test.go b/provd/internal/services/user/internal_test.go new file mode 100644 index 000000000..4ad89b336 --- /dev/null +++ b/provd/internal/services/user/internal_test.go @@ -0,0 +1,45 @@ +package user + +import ( + "testing" + + "github.com/canonical/ubuntu-desktop-provision/provd/internal/testutils" + "github.com/stretchr/testify/require" +) + +func TestHashPassword(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + password string + wantErr bool + }{ + "Valid password": { + password: "securepassword123", + wantErr: false, + }, + "Empty password": { + password: "", + wantErr: true, + }, + } + + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + salt := "pepper" + got, err := hashPassword(tc.password, &salt) + + if tc.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err, "HashPassword should not return an error, but did") + + want := testutils.LoadWithUpdateFromGolden(t, got) + require.Equal(t, want, got, "HashPassword returned an unexpected response") + }) + } +} diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go index aa0b9e3ed..f4885d785 100644 --- a/provd/internal/services/user/user_test.go +++ b/provd/internal/services/user/user_test.go @@ -17,43 +17,6 @@ import ( "google.golang.org/grpc/credentials/insecure" ) -func TestHashPassword(t *testing.T) { - t.Parallel() - - tests := map[string]struct { - password string - wantErr bool - }{ - "Valid password": { - password: "securepassword123", - wantErr: false, - }, - "Empty password": { - password: "", - wantErr: true, - }, - } - - for name, tc := range tests { - tc := tc - t.Run(name, func(t *testing.T) { - t.Parallel() - salt := "pepper" - got, err := user.HashPassword(tc.password, &salt) - - if tc.wantErr { - require.Error(t, err) - return - } - - require.NoError(t, err, "HashPassword should not return an error, but did") - - want := testutils.LoadWithUpdateFromGolden(t, got) - require.Equal(t, want, got, "HashPassword returned an unexpected response") - }) - } -} - func TestEmptyCreateUserRequest(t *testing.T) { t.Parallel() From d08d94ff03f7018ffbb91ed71b25a02d72e8bd5b Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Fri, 12 Jan 2024 14:58:06 +0200 Subject: [PATCH 079/106] refactor: remove factory --- provd/internal/services/manager.go | 11 ++++- provd/internal/services/user/user.go | 65 ++++++---------------------- 2 files changed, 23 insertions(+), 53 deletions(-) diff --git a/provd/internal/services/manager.go b/provd/internal/services/manager.go index ab35166e9..973dea95b 100644 --- a/provd/internal/services/manager.go +++ b/provd/internal/services/manager.go @@ -24,11 +24,18 @@ type Manager struct { bus *dbus.Conn } +type dbusConnectionAdapter struct { + *dbus.Conn +} + +func (bus dbusConnectionAdapter) Object(iface string, path dbus.ObjectPath) user.DbusObject { + return bus.Object(iface, path) +} + // NewManager returns a new manager after creating all necessary items for our business logic. func NewManager(ctx context.Context) (m *Manager, err error) { defer decorate.OnError(&err, "can't create provd object") - // Connect to dbus bus, err := dbus.ConnectSystemBus( dbus.WithIncomingInterceptor(func(msg *dbus.Message) { slog.Debug(fmt.Sprintf("DBUS: %s", msg)) @@ -39,7 +46,7 @@ func NewManager(ctx context.Context) (m *Manager, err error) { helloService := hello.Service{} - userService := user.New(bus) + userService := user.New(dbusConnectionAdapter{bus}) return &Manager{ helloService: helloService, diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index 4722141b2..465392967 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -38,62 +38,25 @@ type DbusObject interface { GetProperty(p string) (dbus.Variant, error) } -// ObjectFactory is a factory for creating DbusObjects for users. -// This is used to allow mocking of the DbusObjects for testing. -type ObjectFactory interface { - GetUserObject(userObjectPath dbus.ObjectPath) DbusObject +// DbusConnector is the interface to the dbus connection which allow easy mocking. +type DbusConnector interface { + Object(dest string, path dbus.ObjectPath) DbusObject } -// objectFactoryImpl is the default implementation of UserObjectFactory. -type objectFactoryImpl struct { - conn *dbus.Conn -} - -// Wraps the dbus call for getting a user object. -func (f *objectFactoryImpl) GetUserObject(userObjectPath dbus.ObjectPath) DbusObject { - userObject := f.conn.Object(DbusAccountsPrefix, userObjectPath) - return userObject -} - -// options are the configurable functional options of the User service. -type options struct { - accounts DbusObject - hostname DbusObject - userFactory ObjectFactory -} - -// Option type exported for tests. -type Option func(*options) - // Service is the implementation of the User module service. type Service struct { pb.UnimplementedUserServiceServer - accounts DbusObject - hostname DbusObject - userFactory ObjectFactory + conn DbusConnector + accounts DbusObject + hostname DbusObject } // New returns a new instance of the User service. -func New(bus *dbus.Conn, args ...Option) *Service { - accounts := bus.Object(DbusAccountsPrefix, "/org/freedesktop/Accounts") - hostname := bus.Object(DbusHostnamePrefix, "/org/freedesktop/hostname1") - userFactory := &objectFactoryImpl{conn: bus} - - opts := options{ - accounts: accounts, - hostname: hostname, - userFactory: userFactory, - } - - // Apply given options - for _, f := range args { - f(&opts) - } - +func New(conn DbusConnector) *Service { return &Service{ - accounts: opts.accounts, - hostname: opts.hostname, - userFactory: opts.userFactory, + conn: conn, + accounts: conn.Object(dbusAccountsPrefix, "/org/freedesktop/Accounts"), + hostname: conn.Object(dbusHostnamePrefix, "/org/freedesktop/hostname1"), } } @@ -139,7 +102,7 @@ func hashPassword(password string, testSalt *string) (string, error) { // CreateUser creates a new user on the system. func (s *Service) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*emptypb.Empty, error) { - // Validate requtest + // Validate request if req == nil { return nil, status.Errorf(codes.InvalidArgument, "received a nil request") } @@ -182,14 +145,14 @@ func (s *Service) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*e } // Set the password for the user - userObject := s.userFactory.GetUserObject(userObjectPath) + userObject := s.conn.Object(dbusAccountsPrefix, userObjectPath) err = userObject.Call(dbusAccountsPrefix+".User.SetPassword", 0, hashed, "").Err if err != nil { return nil, status.Errorf(codes.Internal, "failed to set password: %s", err) } // Set autologin for the user - err = userObject.Call(DbusAccountsPrefix+".User.SetAutomaticLogin", 0, autologin).Err + err = userObject.Call(dbusAccountsPrefix+".User.SetAutomaticLogin", 0, autologin).Err if err != nil { return nil, status.Errorf(codes.Internal, "failed to set autologin: %s", err) } @@ -239,7 +202,7 @@ func (s *Service) ValidateUsername(ctx context.Context, req *pb.ValidateUsername for scanner.Scan() { line := scanner.Text() // Ignore comment lines - if strings.HasPrefix(line, "#") { + if strings.HasPrefix(line, "#") { // trim the lines / spaces (stinrgs) continue } if line == username { From 91150fe3d22e67b39011be4eb36fcfc0fe98862d Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Fri, 12 Jan 2024 15:54:58 +0100 Subject: [PATCH 080/106] fix(init): load images from config file --- packages/ubuntu_init/lib/src/init_wizard.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/ubuntu_init/lib/src/init_wizard.dart b/packages/ubuntu_init/lib/src/init_wizard.dart index 821eebb8a..7fb756ccb 100644 --- a/packages/ubuntu_init/lib/src/init_wizard.dart +++ b/packages/ubuntu_init/lib/src/init_wizard.dart @@ -30,8 +30,10 @@ class InitWizard extends ConsumerWidget { routes: { Routes.initial: WizardRoute( builder: (_) => const SizedBox.shrink(), - onReplace: (_) => - ref.read(initModelProvider).init().then((_) => null), + onReplace: (_) async { + await ref.read(pageImagesProvider).preCache(context); + return ref.read(initModelProvider).init().then((_) => null); + }, ), ...routes, Routes.theme: WizardRoute( From c20801830b87df62c0f1866bfc8ff093e346ab69 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Tue, 28 Nov 2023 13:31:50 +0100 Subject: [PATCH 081/106] feat: Create new HorizontalPage layout for wizard --- .../assets/icons/accessibility.svg | 14 ++ .../ubuntu_bootstrap/assets/icons/animal.svg | 9 ++ .../ubuntu_bootstrap/assets/icons/disk.svg | 16 ++ .../assets/icons/keyboard.svg | 19 +++ .../assets/icons/logo-dark.svg | 16 ++ .../assets/icons/logo-light.svg | 16 ++ .../ubuntu_bootstrap/assets/icons/mascot.svg | 77 +++++++++ .../assets/icons/proprietary.svg | 34 ++++ .../assets/icons/security_key.svg | 29 ++++ .../ubuntu_bootstrap/assets/icons/summary.svg | 24 +++ .../assets/icons/ubuntu_certified.svg | 9 ++ .../ubuntu_bootstrap/assets/icons/wifi.svg | 19 +++ .../lib/pages/rst/rst_page.dart | 4 +- .../lib/pages/source/source_page.dart | 46 +++--- .../lib/pages/storage/storage_page.dart | 36 ++--- .../lib/slides/default_slides.dart | 4 +- .../lib/slides/layouts/test_slide_layout.dart | 33 ++++ .../lib/slides/slide_layouts.dart | 1 + packages/ubuntu_bootstrap/pubspec.yaml | 1 + .../lib/src/keyboard/keyboard_page.dart | 149 ++++++++---------- .../lib/src/locale/locale_page.dart | 72 +++------ .../lib/src/network/network_page.dart | 14 +- .../lib/src/build_context_extension.dart | 9 ++ packages/ubuntu_utils/lib/ubuntu_utils.dart | 1 + .../lib/src/layouts/horizontal_page.dart | 116 ++++++++++++++ packages/ubuntu_wizard/lib/ubuntu_wizard.dart | 1 + 26 files changed, 578 insertions(+), 191 deletions(-) create mode 100644 packages/ubuntu_bootstrap/assets/icons/accessibility.svg create mode 100644 packages/ubuntu_bootstrap/assets/icons/animal.svg create mode 100644 packages/ubuntu_bootstrap/assets/icons/disk.svg create mode 100644 packages/ubuntu_bootstrap/assets/icons/keyboard.svg create mode 100644 packages/ubuntu_bootstrap/assets/icons/logo-dark.svg create mode 100644 packages/ubuntu_bootstrap/assets/icons/logo-light.svg create mode 100644 packages/ubuntu_bootstrap/assets/icons/mascot.svg create mode 100644 packages/ubuntu_bootstrap/assets/icons/proprietary.svg create mode 100644 packages/ubuntu_bootstrap/assets/icons/security_key.svg create mode 100644 packages/ubuntu_bootstrap/assets/icons/summary.svg create mode 100644 packages/ubuntu_bootstrap/assets/icons/ubuntu_certified.svg create mode 100644 packages/ubuntu_bootstrap/assets/icons/wifi.svg create mode 100644 packages/ubuntu_bootstrap/lib/slides/layouts/test_slide_layout.dart create mode 100644 packages/ubuntu_utils/lib/src/build_context_extension.dart create mode 100644 packages/ubuntu_wizard/lib/src/layouts/horizontal_page.dart diff --git a/packages/ubuntu_bootstrap/assets/icons/accessibility.svg b/packages/ubuntu_bootstrap/assets/icons/accessibility.svg new file mode 100644 index 000000000..92ce2c5cd --- /dev/null +++ b/packages/ubuntu_bootstrap/assets/icons/accessibility.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/packages/ubuntu_bootstrap/assets/icons/animal.svg b/packages/ubuntu_bootstrap/assets/icons/animal.svg new file mode 100644 index 000000000..2851111e7 --- /dev/null +++ b/packages/ubuntu_bootstrap/assets/icons/animal.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/ubuntu_bootstrap/assets/icons/disk.svg b/packages/ubuntu_bootstrap/assets/icons/disk.svg new file mode 100644 index 000000000..438d66601 --- /dev/null +++ b/packages/ubuntu_bootstrap/assets/icons/disk.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/packages/ubuntu_bootstrap/assets/icons/keyboard.svg b/packages/ubuntu_bootstrap/assets/icons/keyboard.svg new file mode 100644 index 000000000..074bff5d7 --- /dev/null +++ b/packages/ubuntu_bootstrap/assets/icons/keyboard.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/packages/ubuntu_bootstrap/assets/icons/logo-dark.svg b/packages/ubuntu_bootstrap/assets/icons/logo-dark.svg new file mode 100644 index 000000000..143b4086f --- /dev/null +++ b/packages/ubuntu_bootstrap/assets/icons/logo-dark.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/packages/ubuntu_bootstrap/assets/icons/logo-light.svg b/packages/ubuntu_bootstrap/assets/icons/logo-light.svg new file mode 100644 index 000000000..32260d8bc --- /dev/null +++ b/packages/ubuntu_bootstrap/assets/icons/logo-light.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/packages/ubuntu_bootstrap/assets/icons/mascot.svg b/packages/ubuntu_bootstrap/assets/icons/mascot.svg new file mode 100644 index 000000000..de689afcb --- /dev/null +++ b/packages/ubuntu_bootstrap/assets/icons/mascot.svg @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ubuntu_bootstrap/assets/icons/proprietary.svg b/packages/ubuntu_bootstrap/assets/icons/proprietary.svg new file mode 100644 index 000000000..b95a95694 --- /dev/null +++ b/packages/ubuntu_bootstrap/assets/icons/proprietary.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ubuntu_bootstrap/assets/icons/security_key.svg b/packages/ubuntu_bootstrap/assets/icons/security_key.svg new file mode 100644 index 000000000..297debfc6 --- /dev/null +++ b/packages/ubuntu_bootstrap/assets/icons/security_key.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ubuntu_bootstrap/assets/icons/summary.svg b/packages/ubuntu_bootstrap/assets/icons/summary.svg new file mode 100644 index 000000000..af824954b --- /dev/null +++ b/packages/ubuntu_bootstrap/assets/icons/summary.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ubuntu_bootstrap/assets/icons/ubuntu_certified.svg b/packages/ubuntu_bootstrap/assets/icons/ubuntu_certified.svg new file mode 100644 index 000000000..b6cb73159 --- /dev/null +++ b/packages/ubuntu_bootstrap/assets/icons/ubuntu_certified.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/ubuntu_bootstrap/assets/icons/wifi.svg b/packages/ubuntu_bootstrap/assets/icons/wifi.svg new file mode 100644 index 000000000..96f727732 --- /dev/null +++ b/packages/ubuntu_bootstrap/assets/icons/wifi.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/packages/ubuntu_bootstrap/lib/pages/rst/rst_page.dart b/packages/ubuntu_bootstrap/lib/pages/rst/rst_page.dart index d2dba4a05..d515ede01 100644 --- a/packages/ubuntu_bootstrap/lib/pages/rst/rst_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/rst/rst_page.dart @@ -42,9 +42,7 @@ class RstPage extends ConsumerWidget with ProvisioningPage { child: SvgPicture.asset( 'assets/storage/rst.svg', colorFilter: ColorFilter.mode( - Theme.of(context).brightness == Brightness.light - ? Colors.black - : Colors.white, + context.isDarkMode ? Colors.white : Colors.black, BlendMode.srcIn, ), ), diff --git a/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart b/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart index 15336a6f8..95f7c917f 100644 --- a/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart @@ -1,11 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:subiquity_client/subiquity_client.dart'; import 'package:ubuntu_bootstrap/l10n.dart'; import 'package:ubuntu_bootstrap/pages/source/source_model.dart'; import 'package:ubuntu_bootstrap/services.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; +import 'package:ubuntu_utils/ubuntu_utils.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru/yaru.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; @@ -27,18 +29,18 @@ class SourcePage extends ConsumerWidget with ProvisioningPage { final model = ref.watch(sourceModelProvider); final lang = UbuntuBootstrapLocalizations.of(context); - return WizardPage( - title: YaruWindowTitleBar( - title: Text(lang.updatesOtherSoftwarePageTitle), + return HorizontalPage( + windowTitle: lang.updatesOtherSoftwarePageTitle, + title: lang.updatesOtherSoftwarePageDescription, + icon: SvgPicture.asset( + 'assets/icons/mascot.svg', + colorFilter: ColorFilter.mode( + context.isDarkMode ? Colors.white : Colors.black, + BlendMode.srcIn, + ), ), - headerPadding: EdgeInsets.zero, - contentPadding: EdgeInsets.zero, content: ListView( children: [ - Padding( - padding: const EdgeInsets.all(kYaruPagePadding), - child: Text(lang.updatesOtherSoftwarePageDescription), - ), ...model.sources .map((source) => Align( alignment: AlignmentDirectional.centerStart, @@ -105,23 +107,15 @@ class SourcePage extends ConsumerWidget with ProvisioningPage { showCloseIcon: true, ) : null, - bottomBar: WizardBar( - leading: WizardButton.previous(context), - trailing: [ - WizardButton.next( - context, - enabled: model.sourceId != null, - onNext: () async { - final telemetry = tryGetService(); - await telemetry?.addMetrics({ - 'Minimal': model.sourceId?.contains('minimal') ?? false, - 'RestrictedAddons': model.installCodecs, - }); - await model.save(); - }, - ), - ], - ), + isNextEnabled: model.sourceId != null, + onNext: () async { + final telemetry = tryGetService(); + await telemetry?.addMetrics({ + 'Minimal': model.sourceId?.contains('minimal') ?? false, + 'RestrictedAddons': model.installCodecs, + }); + await model.save(); + }, ); } } diff --git a/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart b/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart index 65ea8b703..96dac72d3 100644 --- a/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:subiquity_client/subiquity_client.dart'; import 'package:ubuntu_bootstrap/l10n.dart'; import 'package:ubuntu_bootstrap/pages/storage/storage_dialogs.dart'; @@ -60,11 +61,10 @@ class StoragePage extends ConsumerWidget with ProvisioningPage { final model = ref.watch(storageModelProvider); final lang = UbuntuBootstrapLocalizations.of(context); final flavor = ref.watch(flavorProvider); - return WizardPage( - title: YaruWindowTitleBar( - title: Text(lang.installationTypeTitle), - ), - header: Text(_formatHeader(context, model.existingOS ?? [])), + return HorizontalPage( + windowTitle: lang.installationTypeTitle, + title: _formatHeader(context, model.existingOS ?? []), + icon: SvgPicture.asset('assets/icons/disk.svg'), content: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -124,23 +124,15 @@ class StoragePage extends ConsumerWidget with ProvisioningPage { ), ], ), - bottomBar: WizardBar( - leading: WizardButton.previous(context), - trailing: [ - WizardButton.next( - context, - enabled: model.canEraseDisk || - model.canInstallAlongside || - model.canManualPartition, - arguments: model.type, - onNext: model.save, - // If the user returns back to select another installation type, the - // previously configured storage must be reset to make all guided - // partitioning targets available. - onBack: model.resetStorage, - ), - ], - ), + isNextEnabled: model.canEraseDisk || + model.canInstallAlongside || + model.canManualPartition, + nextArguments: model.type, + onNext: model.save, + // If the user returns back to select another installation type, the + // previously configured storage must be reset to make all guided + // partitioning targets available. + onBack: model.resetStorage, ); } } diff --git a/packages/ubuntu_bootstrap/lib/slides/default_slides.dart b/packages/ubuntu_bootstrap/lib/slides/default_slides.dart index addb59319..3ca1ad923 100644 --- a/packages/ubuntu_bootstrap/lib/slides/default_slides.dart +++ b/packages/ubuntu_bootstrap/lib/slides/default_slides.dart @@ -25,8 +25,8 @@ final defaultSlides = [ Widget _buildWelcomeSlide(BuildContext context) { final lang = UbuntuBootstrapLocalizations.of(context); final product = getService(); - return IntroSlideLayout( - title: Text(lang.installationSlidesWelcomeTitle), + return TestSlideLayout( + //title: Text(lang.installationSlidesWelcomeTitle), body: SlideColumn( children: [ Consumer(builder: (context, ref, child) { diff --git a/packages/ubuntu_bootstrap/lib/slides/layouts/test_slide_layout.dart b/packages/ubuntu_bootstrap/lib/slides/layouts/test_slide_layout.dart new file mode 100644 index 000000000..87985af6c --- /dev/null +++ b/packages/ubuntu_bootstrap/lib/slides/layouts/test_slide_layout.dart @@ -0,0 +1,33 @@ +import 'package:flutter/widgets.dart'; + +class TestSlideLayout extends StatelessWidget { + const TestSlideLayout({required this.body, required this.image, super.key}); + + final Widget body; + final Widget image; + + @override + Widget build(BuildContext context) { + return ListView( + padding: const EdgeInsets.symmetric(vertical: 8), + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Spacer(flex: 10), + Expanded( + flex: 30, + child: image, + ), + const Spacer(flex: 10), + Expanded( + flex: 40, + child: body, + ), + const Spacer(flex: 10), + ], + ), + ], + ); + } +} diff --git a/packages/ubuntu_bootstrap/lib/slides/slide_layouts.dart b/packages/ubuntu_bootstrap/lib/slides/slide_layouts.dart index 3b8a72339..032f4f6d3 100644 --- a/packages/ubuntu_bootstrap/lib/slides/slide_layouts.dart +++ b/packages/ubuntu_bootstrap/lib/slides/slide_layouts.dart @@ -3,3 +3,4 @@ export 'layouts/intro_slide_layout.dart'; export 'layouts/landscape_slide_layout.dart'; export 'layouts/outro_slide_layout.dart'; export 'layouts/portrait_slide_layout.dart'; +export 'layouts/test_slide_layout.dart'; diff --git a/packages/ubuntu_bootstrap/pubspec.yaml b/packages/ubuntu_bootstrap/pubspec.yaml index 78264ab0a..7aa840dca 100644 --- a/packages/ubuntu_bootstrap/pubspec.yaml +++ b/packages/ubuntu_bootstrap/pubspec.yaml @@ -73,6 +73,7 @@ flutter: uses-material-design: true assets: - assets/ + - assets/icons/ - assets/slides/ - assets/slides/icons/ - assets/slides/icons/dark/ diff --git a/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart b/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart index 1db71a699..f2548b345 100644 --- a/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart +++ b/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_provision/src/keyboard/keyboard_dialogs.dart'; import 'package:ubuntu_provision/src/keyboard/keyboard_l10n.dart'; import 'package:ubuntu_provision/src/keyboard/keyboard_model.dart'; import 'package:ubuntu_widgets/ubuntu_widgets.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; -import 'package:yaru_widgets/yaru_widgets.dart'; class KeyboardPage extends ConsumerWidget with ProvisioningPage { const KeyboardPage({super.key}); @@ -24,99 +24,84 @@ class KeyboardPage extends ConsumerWidget with ProvisioningPage { Widget build(BuildContext context, WidgetRef ref) { final model = ref.watch(keyboardModelProvider); final lang = KeyboardLocalizations.of(context); - return WizardPage( - title: YaruWindowTitleBar( - title: Text(lang.keyboardTitle), + return HorizontalPage( + windowTitle: lang.keyboardTitle, + title: lang.keyboardHeader, + trailingTitleWidget: model.canDetectLayout + ? OutlinedButton( + child: Text(lang.keyboardDetectButton), + onPressed: () async { + final result = await showDetectKeyboardDialog(context); + if (result != null) { + await model.trySelectLayoutVariant( + result.layout, result.variant); + } + }, + ) + : null, + padding: const EdgeInsets.symmetric( + horizontal: HorizontalPage.defaultContentPadding, + vertical: 40, // TODO(Lukas): Move to correct place ), - content: FractionallySizedBox( - widthFactor: 0.5, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Row( - children: [ - Expanded(child: Text(lang.keyboardHeader)), - if (model.canDetectLayout) ...[ - const SizedBox(width: kWizardSpacing), - OutlinedButton( - child: Text(lang.keyboardDetectButton), - onPressed: () async { - final result = await showDetectKeyboardDialog(context); - if (result != null) { - await model.trySelectLayoutVariant( - result.layout, result.variant); - } - }, - ), - ], - ], - ), - const SizedBox(height: kWizardSpacing), - Expanded( - child: Row( - children: [ - Expanded( - child: ListWidget.builder( - selectedIndex: model.selectedLayoutIndex, - itemCount: model.layoutCount, - itemBuilder: (context, index) => ListTile( - key: ValueKey(index), - title: Text(model.layoutName(index)), - selected: index == model.selectedLayoutIndex, - onTap: () => model.selectLayout(index), - ), - onKeySearch: (value) { - final index = model.searchLayout(value); - if (index != -1) { - model.selectLayout(index); - } - }, - ), - ), - ], - ), - ), - const SizedBox(height: kWizardSpacing), - Row( - children: [ - Text(lang.keyboardVariantLabel), - const SizedBox(width: kWizardSpacing), + icon: SvgPicture.asset('assets/icons/keyboard.svg'), + content: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + child: Row( + children: [ Expanded( - child: MenuButtonBuilder( - selected: model.selectedVariantIndex, - values: List.generate(model.variantCount, (index) => index), - itemBuilder: (context, index, child) { - return index < 0 || index >= model.variantCount - ? const SizedBox.shrink() - : Text(model.variantName(index)); + child: ListWidget.builder( + selectedIndex: model.selectedLayoutIndex, + itemCount: model.layoutCount, + itemBuilder: (context, index) => ListTile( + key: ValueKey(index), + title: Text(model.layoutName(index)), + selected: index == model.selectedLayoutIndex, + onTap: () => model.selectLayout(index), + ), + onKeySearch: (value) { + final index = model.searchLayout(value); + if (index != -1) { + model.selectLayout(index); + } }, - onSelected: model.selectVariant, ), ), ], ), - const SizedBox(height: kWizardSpacing), - const Divider(height: 1), - const SizedBox(height: kWizardSpacing), - TextField( - decoration: InputDecoration( - hintText: lang.keyboardTestHint, + ), + const SizedBox(height: kWizardSpacing), + Row( + children: [ + Text(lang.keyboardVariantLabel), + const SizedBox(width: kWizardSpacing), + Expanded( + child: MenuButtonBuilder( + selected: model.selectedVariantIndex, + values: List.generate(model.variantCount, (index) => index), + itemBuilder: (context, index, child) { + return index < 0 || index >= model.variantCount + ? const SizedBox.shrink() + : Text(model.variantName(index)); + }, + onSelected: model.selectVariant, + ), ), + ], + ), + const SizedBox(height: kWizardSpacing), + const Divider(height: 1), + const SizedBox(height: kWizardSpacing), + TextField( + decoration: InputDecoration( + hintText: lang.keyboardTestHint, ), - const SizedBox(height: kWizardSpacing * 2), - ], - ), - ), - bottomBar: WizardBar( - leading: WizardButton.previous(context), - trailing: [ - WizardButton.next( - context, - enabled: model.isValid, - onNext: model.save, ), ], ), + onNext: model.save, + isNextEnabled: model.isValid, ); } } diff --git a/packages/ubuntu_provision/lib/src/locale/locale_page.dart b/packages/ubuntu_provision/lib/src/locale/locale_page.dart index 5a19be9db..00f8e540d 100644 --- a/packages/ubuntu_provision/lib/src/locale/locale_page.dart +++ b/packages/ubuntu_provision/lib/src/locale/locale_page.dart @@ -1,10 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:ubuntu_provision/ubuntu_provision.dart'; -import 'package:ubuntu_service/ubuntu_service.dart'; +import 'package:ubuntu_provision/interfaces.dart'; +import 'package:ubuntu_provision/providers.dart'; import 'package:ubuntu_widgets/ubuntu_widgets.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; -import 'package:yaru_widgets/yaru_widgets.dart'; class LocalePage extends ConsumerWidget with ProvisioningPage { const LocalePage({super.key}); @@ -22,54 +21,27 @@ class LocalePage extends ConsumerWidget with ProvisioningPage { final lang = LocaleLocalizations.of(context); final pageImages = ref.watch(pageImagesProvider); - return WizardPage( - title: YaruWindowTitleBar( - title: Text(lang.localePageTitle(flavor.name)), - ), - content: FractionallySizedBox( - widthFactor: 0.5, - child: Column( - children: [ - const SizedBox(height: kWizardSpacing / 2), - pageImages.get('locale'), - const SizedBox(height: kWizardSpacing), - Text(lang.localeHeader), - const SizedBox(height: kWizardSpacing / 2), - Expanded( - child: ListWidget.builder( - selectedIndex: model.selectedIndex, - itemCount: model.languageCount, - itemBuilder: (context, index) => ListTile( - key: ValueKey(index), - title: Text(model.language(index)), - selected: index == model.selectedIndex, - onTap: () => model.selectLanguage(index), - ), - onKeySearch: (value) { - final index = model.searchLanguage(value); - if (index != -1) { - model.selectLanguage(index); - } - }, - ), - ), - const SizedBox(height: kWizardSpacing), - ], - ), - ), - bottomBar: WizardBar( - leading: WizardButton.previous(context), - trailing: [ - WizardButton.next( - context, - onNext: () async { - final locale = model.locale(model.selectedIndex); - await model.applyLocale(locale); - await tryGetService() - ?.addMetric('Language', locale.languageCode); - }, + return HorizontalPage( + windowTitle: lang.localePageTitle(flavor.name), + title: lang.localeHeader, + icon: pageImages.get('locale'), + onNext: () => model.selectLanguage(model.selectedIndex), + content: Expanded( + child: ListWidget.builder( + selectedIndex: model.selectedIndex, + itemCount: model.languageCount, + itemBuilder: (context, index) => ListTile( + key: ValueKey(index), + title: Text(model.language(index)), + selected: index == model.selectedIndex, ), - ], + onKeySearch: (value) { + final index = model.searchLanguage(value); + if (index != -1) { + model.selectLanguage(index); + } + }, + ), ), ); } diff --git a/packages/ubuntu_provision/lib/src/network/network_page.dart b/packages/ubuntu_provision/lib/src/network/network_page.dart index 2a6feb9dc..89506d66f 100644 --- a/packages/ubuntu_provision/lib/src/network/network_page.dart +++ b/packages/ubuntu_provision/lib/src/network/network_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:ubuntu_localizations/ubuntu_localizations.dart'; import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_provision/src/network/connect_model.dart'; @@ -13,7 +14,6 @@ import 'package:ubuntu_provision/src/network/network_model.dart'; import 'package:ubuntu_provision/src/network/wifi_model.dart'; import 'package:ubuntu_provision/src/network/wifi_view.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; -import 'package:yaru_widgets/yaru_widgets.dart'; export 'connect_model.dart' show ConnectMode; @@ -38,14 +38,16 @@ class NetworkPage extends ConsumerWidget with ProvisioningPage { Widget build(BuildContext context, WidgetRef ref) { final model = ref.watch(networkModelProvider); final lang = NetworkLocalizations.of(context); - return WizardPage( - title: YaruWindowTitleBar( - title: Text(lang.networkPageTitle), - ), - header: Text(lang.networkPageHeader), + return HorizontalPage( + windowTitle: lang.networkPageTitle, + title: 'Connect to the Internet?', // TODO(Lukas): Add to localization + padding: const EdgeInsets.all(kWizardSpacing), + icon: SvgPicture.asset('assets/icons/wifi.svg'), content: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text(lang.networkPageHeader), + const SizedBox(height: kWizardSpacing), EthernetRadioButton( value: model.connectMode, onChanged: (_) => model.selectConnectMode(ConnectMode.ethernet), diff --git a/packages/ubuntu_utils/lib/src/build_context_extension.dart b/packages/ubuntu_utils/lib/src/build_context_extension.dart new file mode 100644 index 000000000..b15bcd559 --- /dev/null +++ b/packages/ubuntu_utils/lib/src/build_context_extension.dart @@ -0,0 +1,9 @@ +import 'dart:ui'; +import 'package:flutter/widgets.dart'; + +extension DarkMode on BuildContext { + bool get isDarkMode { + final brightness = MediaQuery.of(this).platformBrightness; + return brightness == Brightness.dark; + } +} diff --git a/packages/ubuntu_utils/lib/ubuntu_utils.dart b/packages/ubuntu_utils/lib/ubuntu_utils.dart index f7f5f7ec7..dca8abfbf 100644 --- a/packages/ubuntu_utils/lib/ubuntu_utils.dart +++ b/packages/ubuntu_utils/lib/ubuntu_utils.dart @@ -1,3 +1,4 @@ +export 'src/build_context_extension.dart'; export 'src/command_line.dart'; export 'src/data_size.dart'; export 'src/property_stream_notifier.dart'; diff --git a/packages/ubuntu_wizard/lib/src/layouts/horizontal_page.dart b/packages/ubuntu_wizard/lib/src/layouts/horizontal_page.dart new file mode 100644 index 000000000..3195f4aab --- /dev/null +++ b/packages/ubuntu_wizard/lib/src/layouts/horizontal_page.dart @@ -0,0 +1,116 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:ubuntu_wizard/ubuntu_wizard.dart'; +import 'package:yaru_widgets/widgets.dart'; + +class HorizontalPage extends StatelessWidget { + const HorizontalPage({ + required this.windowTitle, + required this.title, + required this.icon, + required this.content, + this.onNext, + this.onBack, + this.nextArguments, + this.trailingTitleWidget, + this.isNextEnabled = true, + this.padding = const EdgeInsets.all(defaultContentPadding), + this.bottomBar, + this.snackBar, + super.key, + }); + + /// The title for the title bar. + final String windowTitle; + + /// The title that is displayed on top of the content column. + final String title; + + /// A widget shown after the [title]. + final Widget? trailingTitleWidget; + + /// The left, smaller, column that shows something visual, usually an image. + final Widget icon; + + /// The right, larger, column with the content that should be focus on. + final Widget content; + + /// A callback for when the user presses the "Next" button. + final FutureOr Function()? onNext; + + /// A callback for when the user presses the "Back" button. + final FutureOr Function()? onBack; + + final Object? nextArguments; + + /// Whether the next button should be enabled or not. + final bool isNextEnabled; + + /// The padding applied around the area storing the icon and the content. + final EdgeInsets padding; + + /// Override of the default bottom bar. + final Widget? bottomBar; + + /// The snack bar to use (default is none). + final SnackBar? snackBar; + + // TODO(Lukas): Move these to a proper place. + static const defaultContentPadding = 100.0; + static const _contentSpacing = 60.0; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return WizardPage( + title: YaruWindowTitleBar(title: Text(windowTitle)), + content: Padding( + padding: padding, + child: Row( + children: [ + icon, + const SizedBox(width: _contentSpacing), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox( + width: 400, // TODO(Lukas): Do this properly + child: Text( + title, + style: theme.textTheme.headlineSmall, + ), + ), + if (trailingTitleWidget != null) trailingTitleWidget!, + ], + ), + const SizedBox(height: kWizardSpacing), + Expanded(child: content), + ], + ), + ), + const SizedBox(width: kWizardSpacing), + ], + ), + ), + snackBar: snackBar, + bottomBar: bottomBar ?? + WizardBar( + leading: WizardButton.previous(context), + trailing: [ + WizardButton.next( + context, + onNext: onNext, + enabled: isNextEnabled, + arguments: nextArguments, + ), + ], + ), + ); + } +} diff --git a/packages/ubuntu_wizard/lib/ubuntu_wizard.dart b/packages/ubuntu_wizard/lib/ubuntu_wizard.dart index 01ba8e85d..28e495bbc 100644 --- a/packages/ubuntu_wizard/lib/ubuntu_wizard.dart +++ b/packages/ubuntu_wizard/lib/ubuntu_wizard.dart @@ -2,6 +2,7 @@ library ubuntu_wizard; export 'package:wizard_router/wizard_router.dart'; +export 'src/layouts/horizontal_page.dart'; export 'src/wizard_app.dart'; export 'src/wizard_bar.dart'; export 'src/wizard_builder.dart'; From 472e96da43a3a345e717f3661000ec17df17b202 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Thu, 11 Jan 2024 10:59:43 +0100 Subject: [PATCH 082/106] Update layout of pages to use HorizontalPage --- .../assets/ubuntu-provision.yml | 6 +- .../lib/pages/source/source_page.dart | 15 ++-- .../lib/pages/storage/storage_page.dart | 2 +- .../test/storage/storage_dialogs_test.dart | 68 +++++++------------ .../test/storage/storage_page_test.dart | 8 ++- .../lib/src/keyboard/keyboard_page.dart | 3 +- .../lib/src/locale/locale_page.dart | 41 ++++++----- .../lib/src/network/network_page.dart | 15 ++-- .../lib/src/network/wifi_view.dart | 10 +-- .../lib/src/providers/page_images.dart | 23 ++++++- .../lib/src/widgets}/horizontal_page.dart | 36 ++++++---- packages/ubuntu_provision/lib/widgets.dart | 1 + .../test/keyboard/keyboard_page_test.dart | 7 +- .../test/locale/locale_page_test.dart | 13 ++-- .../test/locale/test_locale.dart | 3 + .../test/network/network_page_test.dart | 4 ++ .../test/network/test_network.dart | 5 ++ .../test/providers/page_images_test.dart | 8 ++- .../ubuntu_wizard/lib/src/wizard_page.dart | 4 ++ packages/ubuntu_wizard/lib/ubuntu_wizard.dart | 1 - 20 files changed, 158 insertions(+), 115 deletions(-) rename packages/{ubuntu_wizard/lib/src/layouts => ubuntu_provision/lib/src/widgets}/horizontal_page.dart (74%) diff --git a/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml b/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml index 2c4ccdedb..d27dce627 100644 --- a/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml +++ b/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml @@ -1,7 +1,7 @@ pages: locale: - image: "packages/ubuntu_provision/assets/mascot.png" title: "Welcome" + image: "packages/ubuntu_provision/assets/mascot.png" visible: true welcome: title: "Welcome to Ubuntu" @@ -12,15 +12,18 @@ pages: visible: true keyboard: title: "Keyboard layout" + image: "assets/icons/keyboard.svg" visible: true network: title: "Connect to a network" + image: "assets/icons/wifi.svg" visible: true refresh: title: "Update available" visible: true source: title: "Applications and updates" + image: "assets/icons/mascot.svg" visible: true notEnoughDiskSpace: title: "Not enough disk space" @@ -30,6 +33,7 @@ pages: visible: true storage: title: "Type of installation" + image: "assets/icons/disk.svg" visible: true confirm: title: "Ready to install" diff --git a/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart b/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart index 95f7c917f..ab226afb8 100644 --- a/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart @@ -30,15 +30,16 @@ class SourcePage extends ConsumerWidget with ProvisioningPage { final lang = UbuntuBootstrapLocalizations.of(context); return HorizontalPage( + name: 'source', windowTitle: lang.updatesOtherSoftwarePageTitle, title: lang.updatesOtherSoftwarePageDescription, - icon: SvgPicture.asset( - 'assets/icons/mascot.svg', - colorFilter: ColorFilter.mode( - context.isDarkMode ? Colors.white : Colors.black, - BlendMode.srcIn, - ), - ), + //icon: SvgPicture.asset( + // 'assets/icons/mascot.svg', + // colorFilter: ColorFilter.mode( + // context.isDarkMode ? Colors.white : Colors.black, + // BlendMode.srcIn, + // ), + //), content: ListView( children: [ ...model.sources diff --git a/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart b/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart index 96dac72d3..f2ebbc393 100644 --- a/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart @@ -62,9 +62,9 @@ class StoragePage extends ConsumerWidget with ProvisioningPage { final lang = UbuntuBootstrapLocalizations.of(context); final flavor = ref.watch(flavorProvider); return HorizontalPage( + name: 'storage', windowTitle: lang.installationTypeTitle, title: _formatHeader(context, model.existingOS ?? []), - icon: SvgPicture.asset('assets/icons/disk.svg'), content: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/packages/ubuntu_bootstrap/test/storage/storage_dialogs_test.dart b/packages/ubuntu_bootstrap/test/storage/storage_dialogs_test.dart index c9fa96be0..a648fddf0 100644 --- a/packages/ubuntu_bootstrap/test/storage/storage_dialogs_test.dart +++ b/packages/ubuntu_bootstrap/test/storage/storage_dialogs_test.dart @@ -1,3 +1,6 @@ +import 'dart:ui'; + +import 'package:flutter/widgets.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; @@ -6,21 +9,28 @@ import 'package:ubuntu_bootstrap/l10n.dart'; import 'package:ubuntu_bootstrap/pages/storage/storage_dialogs.dart'; import 'package:ubuntu_bootstrap/pages/storage/storage_model.dart'; import 'package:ubuntu_bootstrap/pages/storage/storage_page.dart'; +import 'package:ubuntu_provision/providers.dart'; import 'package:ubuntu_test/ubuntu_test.dart'; import 'package:yaru_test/yaru_test.dart'; import 'test_storage.dart'; void main() { + Widget buildPage(WidgetTester tester, StorageModel model) { + final pageImages = PageImages(MockPageConfigService()); + return ProviderScope( + overrides: [ + storageModelProvider.overrideWith((_) => model), + pageImagesProvider.overrideWith((_) => pageImages), + ], + child: tester.buildApp((_) => const StoragePage()), + ); + } + testWidgets('select zfs', (tester) async { final model = buildStorageModel(); - - await tester.pumpWidget( - ProviderScope( - overrides: [storageModelProvider.overrideWith((_) => model)], - child: tester.buildApp((_) => const StoragePage()), - ), - ); + await tester.binding.setSurfaceSize(const Size(900, 900)); + await tester.pumpWidget(buildPage(tester, model)); final result = showAdvancedFeaturesDialog( tester.element(find.byType(StoragePage)), model); @@ -37,13 +47,7 @@ void main() { testWidgets('select lvm', (tester) async { final model = buildStorageModel(); - - await tester.pumpWidget( - ProviderScope( - overrides: [storageModelProvider.overrideWith((_) => model)], - child: tester.buildApp((_) => const StoragePage()), - ), - ); + await tester.pumpWidget(buildPage(tester, model)); final result = showAdvancedFeaturesDialog( tester.element(find.byType(StoragePage)), model); @@ -60,13 +64,7 @@ void main() { testWidgets('select encrypted lvm', (tester) async { final model = buildStorageModel(); - - await tester.pumpWidget( - ProviderScope( - overrides: [storageModelProvider.overrideWith((_) => model)], - child: tester.buildApp((_) => const StoragePage()), - ), - ); + await tester.pumpWidget(buildPage(tester, model)); final context = tester.element(find.byType(StoragePage)); final l10n = UbuntuBootstrapLocalizations.of(context); @@ -96,12 +94,7 @@ void main() { scenario: SecureBootScenarios.supported, ); - await tester.pumpWidget( - ProviderScope( - overrides: [storageModelProvider.overrideWith((_) => model)], - child: tester.buildApp((_) => const StoragePage()), - ), - ); + await tester.pumpWidget(buildPage(tester, model)); final result = showAdvancedFeaturesDialog( tester.element(find.byType(StoragePage)), model); @@ -127,12 +120,7 @@ void main() { hasTpm: true, scenario: SecureBootScenarios.bios, ); - await tester.pumpWidget( - ProviderScope( - overrides: [storageModelProvider.overrideWith((_) => model)], - child: tester.buildApp((_) => const StoragePage()), - ), - ); + await tester.pumpWidget(buildPage(tester, model)); final result = showAdvancedFeaturesDialog( tester.element(find.byType(StoragePage)), model); @@ -154,12 +142,7 @@ void main() { hasTpm: false, scenario: SecureBootScenarios.noTpm, ); - await tester.pumpWidget( - ProviderScope( - overrides: [storageModelProvider.overrideWith((_) => model)], - child: tester.buildApp((_) => const StoragePage()), - ), - ); + await tester.pumpWidget(buildPage(tester, model)); final result = showAdvancedFeaturesDialog( tester.element(find.byType(StoragePage)), model); @@ -181,12 +164,7 @@ void main() { hasTpm: true, scenario: SecureBootScenarios.thirdPartyDrivers, ); - await tester.pumpWidget( - ProviderScope( - overrides: [storageModelProvider.overrideWith((_) => model)], - child: tester.buildApp((_) => const StoragePage()), - ), - ); + await tester.pumpWidget(buildPage(tester, model)); final result = showAdvancedFeaturesDialog( tester.element(find.byType(StoragePage)), model); diff --git a/packages/ubuntu_bootstrap/test/storage/storage_page_test.dart b/packages/ubuntu_bootstrap/test/storage/storage_page_test.dart index ca546cf3f..cb7f2231a 100644 --- a/packages/ubuntu_bootstrap/test/storage/storage_page_test.dart +++ b/packages/ubuntu_bootstrap/test/storage/storage_page_test.dart @@ -6,6 +6,7 @@ import 'package:subiquity_client/subiquity_client.dart'; import 'package:ubuntu_bootstrap/l10n.dart'; import 'package:ubuntu_bootstrap/pages/storage/storage_model.dart'; import 'package:ubuntu_bootstrap/pages/storage/storage_page.dart'; +import 'package:ubuntu_provision/providers.dart'; import 'package:ubuntu_provision/services.dart'; import 'package:ubuntu_test/ubuntu_test.dart'; import 'package:yaru_test/yaru_test.dart'; @@ -14,13 +15,18 @@ import 'test_storage.dart'; void main() { Widget buildPage(StorageModel model) { + final pageImages = PageImages(MockPageConfigService()); return ProviderScope( - overrides: [storageModelProvider.overrideWith((_) => model)], + overrides: [ + storageModelProvider.overrideWith((_) => model), + pageImagesProvider.overrideWith((_) => pageImages), + ], child: const StoragePage(), ); } testWidgets('no existing OS', (tester) async { + await tester.binding.setSurfaceSize(const Size(1200, 1000)); await tester.pumpApp((_) => buildPage(buildStorageModel())); final context = tester.element(find.byType(StoragePage)); diff --git a/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart b/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart index f2548b345..3d2edf90e 100644 --- a/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart +++ b/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart @@ -5,6 +5,7 @@ import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_provision/src/keyboard/keyboard_dialogs.dart'; import 'package:ubuntu_provision/src/keyboard/keyboard_l10n.dart'; import 'package:ubuntu_provision/src/keyboard/keyboard_model.dart'; +import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_widgets/ubuntu_widgets.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; @@ -25,6 +26,7 @@ class KeyboardPage extends ConsumerWidget with ProvisioningPage { final model = ref.watch(keyboardModelProvider); final lang = KeyboardLocalizations.of(context); return HorizontalPage( + name: 'keyboard', windowTitle: lang.keyboardTitle, title: lang.keyboardHeader, trailingTitleWidget: model.canDetectLayout @@ -43,7 +45,6 @@ class KeyboardPage extends ConsumerWidget with ProvisioningPage { horizontal: HorizontalPage.defaultContentPadding, vertical: 40, // TODO(Lukas): Move to correct place ), - icon: SvgPicture.asset('assets/icons/keyboard.svg'), content: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ diff --git a/packages/ubuntu_provision/lib/src/locale/locale_page.dart b/packages/ubuntu_provision/lib/src/locale/locale_page.dart index 00f8e540d..6a6a31753 100644 --- a/packages/ubuntu_provision/lib/src/locale/locale_page.dart +++ b/packages/ubuntu_provision/lib/src/locale/locale_page.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_provision/providers.dart'; +import 'package:ubuntu_provision/ubuntu_provision.dart'; +import 'package:ubuntu_service/ubuntu_service.dart'; import 'package:ubuntu_widgets/ubuntu_widgets.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; @@ -19,29 +21,32 @@ class LocalePage extends ConsumerWidget with ProvisioningPage { final flavor = ref.watch(flavorProvider); final model = ref.watch(localeModelProvider); final lang = LocaleLocalizations.of(context); - final pageImages = ref.watch(pageImagesProvider); return HorizontalPage( + name: 'locale', windowTitle: lang.localePageTitle(flavor.name), title: lang.localeHeader, - icon: pageImages.get('locale'), - onNext: () => model.selectLanguage(model.selectedIndex), - content: Expanded( - child: ListWidget.builder( - selectedIndex: model.selectedIndex, - itemCount: model.languageCount, - itemBuilder: (context, index) => ListTile( - key: ValueKey(index), - title: Text(model.language(index)), - selected: index == model.selectedIndex, - ), - onKeySearch: (value) { - final index = model.searchLanguage(value); - if (index != -1) { - model.selectLanguage(index); - } - }, + onNext: () async { + final locale = model.locale(model.selectedIndex); + await model.applyLocale(locale); + await tryGetService() + ?.addMetric('Language', locale.languageCode); + }, + content: ListWidget.builder( + selectedIndex: model.selectedIndex, + itemCount: model.languageCount, + itemBuilder: (context, index) => ListTile( + key: ValueKey(index), + title: Text(model.language(index)), + selected: index == model.selectedIndex, + onTap: () => model.selectLanguage(index), ), + onKeySearch: (value) { + final index = model.searchLanguage(value); + if (index != -1) { + model.selectLanguage(index); + } + }, ), ); } diff --git a/packages/ubuntu_provision/lib/src/network/network_page.dart b/packages/ubuntu_provision/lib/src/network/network_page.dart index 89506d66f..c70c2f0c2 100644 --- a/packages/ubuntu_provision/lib/src/network/network_page.dart +++ b/packages/ubuntu_provision/lib/src/network/network_page.dart @@ -1,18 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_svg/svg.dart'; import 'package:ubuntu_localizations/ubuntu_localizations.dart'; -import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_provision/src/network/connect_model.dart'; import 'package:ubuntu_provision/src/network/connect_view.dart'; -import 'package:ubuntu_provision/src/network/ethernet_model.dart'; import 'package:ubuntu_provision/src/network/ethernet_view.dart'; -import 'package:ubuntu_provision/src/network/hidden_wifi_model.dart'; import 'package:ubuntu_provision/src/network/hidden_wifi_view.dart'; -import 'package:ubuntu_provision/src/network/network_l10n.dart'; -import 'package:ubuntu_provision/src/network/network_model.dart'; -import 'package:ubuntu_provision/src/network/wifi_model.dart'; import 'package:ubuntu_provision/src/network/wifi_view.dart'; +import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; export 'connect_model.dart' show ConnectMode; @@ -39,10 +33,13 @@ class NetworkPage extends ConsumerWidget with ProvisioningPage { final model = ref.watch(networkModelProvider); final lang = NetworkLocalizations.of(context); return HorizontalPage( + name: 'network', windowTitle: lang.networkPageTitle, title: 'Connect to the Internet?', // TODO(Lukas): Add to localization - padding: const EdgeInsets.all(kWizardSpacing), - icon: SvgPicture.asset('assets/icons/wifi.svg'), + padding: const EdgeInsets.symmetric( + horizontal: HorizontalPage.defaultContentPadding, + vertical: kWizardSpacing, + ), content: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/packages/ubuntu_provision/lib/src/network/wifi_view.dart b/packages/ubuntu_provision/lib/src/network/wifi_view.dart index d32d0a30b..ca8a111cf 100644 --- a/packages/ubuntu_provision/lib/src/network/wifi_view.dart +++ b/packages/ubuntu_provision/lib/src/network/wifi_view.dart @@ -99,13 +99,9 @@ class _WifiViewState extends ConsumerState { return AnimatedExpanded( expanded: widget.expanded, - child: FractionallySizedBox( - alignment: Alignment.centerLeft, - widthFactor: kWizardWidthFraction, - child: Padding( - padding: kWizardIndentation, - child: WifiListView(onSelected: widget.onSelected), - ), + child: Padding( + padding: kWizardIndentedPadding, + child: WifiListView(onSelected: widget.onSelected), ), ); } diff --git a/packages/ubuntu_provision/lib/src/providers/page_images.dart b/packages/ubuntu_provision/lib/src/providers/page_images.dart index 9bf8364cc..87b69f55a 100644 --- a/packages/ubuntu_provision/lib/src/providers/page_images.dart +++ b/packages/ubuntu_provision/lib/src/providers/page_images.dart @@ -7,6 +7,7 @@ import 'package:path/path.dart' as path; import 'package:ubuntu_logger/ubuntu_logger.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_service/ubuntu_service.dart'; +import 'package:ubuntu_utils/ubuntu_utils.dart'; final pageImagesProvider = Provider((ref) => PageImages(getService())); @@ -29,8 +30,26 @@ class PageImages { @visibleForTesting final Map images = {}; - Widget get(String pageName) => - images[pageName] ?? const Text('PLACEHOLDER, REPLACE WITH DEFAULT'); + /// Since extension methods can't be mocked this is a workaround to make + /// [isDarkMode] mockable. + @visibleForTesting + bool Function(BuildContext context) isDarkMode = + (context) => context.isDarkMode; + + Widget? get(String pageName, BuildContext context) { + final image = images[pageName]; + if (image == null) { + return null; + } + // TODO(Lukas): How should we handle dark mode? + return ColorFiltered( + colorFilter: ColorFilter.mode( + isDarkMode(context) ? Colors.white : Colors.black, + BlendMode.srcIn, + ), + child: images[pageName], + ); + } Future preCache(BuildContext context) async { final loadFutures = >[]; diff --git a/packages/ubuntu_wizard/lib/src/layouts/horizontal_page.dart b/packages/ubuntu_provision/lib/src/widgets/horizontal_page.dart similarity index 74% rename from packages/ubuntu_wizard/lib/src/layouts/horizontal_page.dart rename to packages/ubuntu_provision/lib/src/widgets/horizontal_page.dart index 3195f4aab..70c76ba57 100644 --- a/packages/ubuntu_wizard/lib/src/layouts/horizontal_page.dart +++ b/packages/ubuntu_provision/lib/src/widgets/horizontal_page.dart @@ -1,14 +1,17 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:ubuntu_provision/providers.dart'; +import 'package:ubuntu_provision/services.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru_widgets/widgets.dart'; -class HorizontalPage extends StatelessWidget { +class HorizontalPage extends ConsumerWidget { const HorizontalPage({ + required this.name, required this.windowTitle, required this.title, - required this.icon, required this.content, this.onNext, this.onBack, @@ -21,6 +24,9 @@ class HorizontalPage extends StatelessWidget { super.key, }); + /// The name of the page that is used to get settings from the [PageConfigService]. + final String name; + /// The title for the title bar. final String windowTitle; @@ -30,9 +36,6 @@ class HorizontalPage extends StatelessWidget { /// A widget shown after the [title]. final Widget? trailingTitleWidget; - /// The left, smaller, column that shows something visual, usually an image. - final Widget icon; - /// The right, larger, column with the content that should be focus on. final Widget content; @@ -61,29 +64,37 @@ class HorizontalPage extends StatelessWidget { static const _contentSpacing = 60.0; @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); + final icon = ref.watch(pageImagesProvider).get(name, context); return WizardPage( title: YaruWindowTitleBar(title: Text(windowTitle)), content: Padding( - padding: padding, + padding: + padding, // TODO(Lukas): Make padding smaller when the size of the window is small. child: Row( children: [ - icon, - const SizedBox(width: _contentSpacing), + if (icon != null) ...[ + Expanded( + flex: 2, + child: icon, + ), + const SizedBox(width: _contentSpacing), + ], Expanded( + flex: 5, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - SizedBox( - width: 400, // TODO(Lukas): Do this properly + Expanded( child: Text( title, - style: theme.textTheme.headlineSmall, + style: theme.textTheme.headlineSmall + ?.copyWith(fontSize: 20), // TODO: Move to theme ), ), if (trailingTitleWidget != null) trailingTitleWidget!, @@ -94,7 +105,6 @@ class HorizontalPage extends StatelessWidget { ], ), ), - const SizedBox(width: kWizardSpacing), ], ), ), diff --git a/packages/ubuntu_provision/lib/widgets.dart b/packages/ubuntu_provision/lib/widgets.dart index 6a0054251..d7256dab8 100644 --- a/packages/ubuntu_provision/lib/widgets.dart +++ b/packages/ubuntu_provision/lib/widgets.dart @@ -1 +1,2 @@ export 'src/widgets/mascot_avatar.dart'; +export 'src/widgets/horizontal_page.dart'; diff --git a/packages/ubuntu_provision/test/keyboard/keyboard_page_test.dart b/packages/ubuntu_provision/test/keyboard/keyboard_page_test.dart index 211deecdb..4e7c2040c 100644 --- a/packages/ubuntu_provision/test/keyboard/keyboard_page_test.dart +++ b/packages/ubuntu_provision/test/keyboard/keyboard_page_test.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; +import 'package:ubuntu_provision/providers.dart'; import 'package:ubuntu_provision/src/keyboard/keyboard_l10n.dart'; import 'package:ubuntu_provision/src/keyboard/keyboard_model.dart'; import 'package:ubuntu_provision/src/keyboard/keyboard_page.dart'; @@ -20,9 +21,13 @@ Widget buildKeyboardPage(KeyboardModel model) { when(service.getKeyboardStep(any)).thenAnswer( (_) async => const AnyStep.stepPressKey(keycodes: {}, symbols: [])); registerMockService(service); + final pageImages = PageImages(MockPageConfigService()); return ProviderScope( - overrides: [keyboardModelProvider.overrideWith((_) => model)], + overrides: [ + keyboardModelProvider.overrideWith((_) => model), + pageImagesProvider.overrideWith((_) => pageImages), + ], child: const KeyboardPage(), ); } diff --git a/packages/ubuntu_provision/test/locale/locale_page_test.dart b/packages/ubuntu_provision/test/locale/locale_page_test.dart index 034d2d78e..03171cab6 100644 --- a/packages/ubuntu_provision/test/locale/locale_page_test.dart +++ b/packages/ubuntu_provision/test/locale/locale_page_test.dart @@ -45,7 +45,7 @@ void main() { await tester.pump(); await tester.tap(itemItalian); await tester.pump(); - expect((itemItalian.evaluate().single.widget as ListTile).selected, true); + expect((itemItalian.evaluate().single.widget as ListTile).selected, isTrue); expect(model.selectedLocale?.languageCode, 'it'); // scroll backward to French @@ -53,7 +53,7 @@ void main() { await tester.pump(); await tester.tap(itemFrench); await tester.pump(); - expect((itemFrench.evaluate().single.widget as ListTile).selected, true); + expect((itemFrench.evaluate().single.widget as ListTile).selected, isTrue); expect(model.selectedLocale?.languageCode, 'fr'); // scroll forward to Galego @@ -62,9 +62,12 @@ void main() { await tester.tap(itemGalego); await tester.pump(); - expect((itemItalian.evaluate().single.widget as ListTile).selected, false); - expect((itemFrench.evaluate().single.widget as ListTile).selected, false); - expect((itemGalego.evaluate().single.widget as ListTile).selected, true); + expect( + (itemItalian.evaluate().single.widget as ListTile).selected, + isFalse, + ); + expect((itemFrench.evaluate().single.widget as ListTile).selected, isFalse); + expect((itemGalego.evaluate().single.widget as ListTile).selected, isTrue); expect(model.selectedLocale?.languageCode, 'gl'); }); diff --git a/packages/ubuntu_provision/test/locale/test_locale.dart b/packages/ubuntu_provision/test/locale/test_locale.dart index fcb4ae811..0750705ab 100644 --- a/packages/ubuntu_provision/test/locale/test_locale.dart +++ b/packages/ubuntu_provision/test/locale/test_locale.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mockito/mockito.dart'; +import 'package:ubuntu_provision/providers.dart'; import 'package:ubuntu_provision/services.dart'; import 'package:ubuntu_provision/src/locale/locale_model.dart'; import 'package:ubuntu_provision/src/locale/locale_page.dart'; @@ -21,10 +22,12 @@ LocaleModel buildLocaleModel() { Widget buildLocalePage(LocaleModel model) { registerMockService(MockTelemetryService()); + final pageImages = PageImages(MockPageConfigService()); return ProviderScope( overrides: [ localeModelProvider.overrideWith((_) => model), + pageImagesProvider.overrideWith((_) => pageImages), ], child: const LocalePage(), ); diff --git a/packages/ubuntu_provision/test/network/network_page_test.dart b/packages/ubuntu_provision/test/network/network_page_test.dart index fd61c0333..e272262f0 100644 --- a/packages/ubuntu_provision/test/network/network_page_test.dart +++ b/packages/ubuntu_provision/test/network/network_page_test.dart @@ -1,3 +1,5 @@ +import 'dart:ui'; + import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:ubuntu_provision/src/network/ethernet_view.dart'; @@ -6,6 +8,7 @@ import 'package:ubuntu_provision/src/network/network_l10n.dart'; import 'package:ubuntu_provision/src/network/network_model.dart'; import 'package:ubuntu_provision/src/network/network_page.dart'; import 'package:ubuntu_provision/src/network/wifi_view.dart'; +import 'package:ubuntu_provision/src/providers/page_images.dart'; import 'package:ubuntu_test/ubuntu_test.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru_test/yaru_test.dart'; @@ -72,6 +75,7 @@ void main() { }); testWidgets('selects wifi', (tester) async { + await tester.binding.setSurfaceSize(const Size(800, 800)); final model = NetworkModel(MockNetworkService()); await tester.pumpApp( (_) => buildNetworkPage(model: model, ethernet: false, wifi: true), diff --git a/packages/ubuntu_provision/test/network/test_network.dart b/packages/ubuntu_provision/test/network/test_network.dart index 72bb457c4..2be8266d1 100644 --- a/packages/ubuntu_provision/test/network/test_network.dart +++ b/packages/ubuntu_provision/test/network/test_network.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; +import 'package:ubuntu_provision/providers.dart'; import 'package:ubuntu_provision/src/network/connect_model.dart'; import 'package:ubuntu_provision/src/network/ethernet_model.dart'; import 'package:ubuntu_provision/src/network/hidden_wifi_model.dart'; @@ -9,6 +10,7 @@ import 'package:ubuntu_provision/src/network/network_model.dart'; import 'package:ubuntu_provision/src/network/network_page.dart'; import 'package:ubuntu_provision/src/network/wifi_model.dart'; +import '../test_utils.mocks.dart'; import 'test_network.mocks.dart'; export '../test_utils.dart'; export 'test_network.mocks.dart'; @@ -85,6 +87,8 @@ Widget buildNetworkPage({ when(hiddenWifiModel.onAvailabilityChanged) .thenAnswer((_) => const Stream.empty()); + final pageImages = PageImages(MockPageConfigService()); + return ProviderScope( overrides: [ networkModelProvider.overrideWith((_) => model), @@ -92,6 +96,7 @@ Widget buildNetworkPage({ wifiModelProvider.overrideWith((_) => wifiModel), hiddenWifiModelProvider.overrideWith((_) => hiddenWifiModel), noConnectModelProvider.overrideWith((_) => NoConnectModel()), + pageImagesProvider.overrideWith((_) => pageImages), ], child: Consumer(builder: (context, ref, child) { return FutureBuilder( diff --git a/packages/ubuntu_provision/test/providers/page_images_test.dart b/packages/ubuntu_provision/test/providers/page_images_test.dart index 3ebf8b821..b09f33284 100644 --- a/packages/ubuntu_provision/test/providers/page_images_test.dart +++ b/packages/ubuntu_provision/test/providers/page_images_test.dart @@ -19,11 +19,13 @@ void main() { final image = MockImage(); pageImages.images['testPage'] = image; + pageImages.isDarkMode = (_) => false; - final result = pageImages.get('testPage'); + final result = pageImages.get('testPage', MockBuildContext()); - expect(result, isInstanceOf()); - expect(result, equals(image)); + expect(result, isInstanceOf()); + expect((result as ColorFiltered).child, isInstanceOf()); + expect(result.child, equals(image)); }); test('preCache correctly loads images from assets and files', () async { diff --git a/packages/ubuntu_wizard/lib/src/wizard_page.dart b/packages/ubuntu_wizard/lib/src/wizard_page.dart index 1d1cf891e..497e7d38b 100644 --- a/packages/ubuntu_wizard/lib/src/wizard_page.dart +++ b/packages/ubuntu_wizard/lib/src/wizard_page.dart @@ -14,6 +14,10 @@ const kWizardWidthFraction = 0.7; const kWizardIndentation = EdgeInsetsDirectional.only(start: kWizardSpacing * 2); +/// The indentation to align with radio indicators etc. +const kWizardIndentedPadding = + EdgeInsetsDirectional.symmetric(horizontal: kWizardSpacing * 2); + /// The recommended width of wizard dialogs. const kWizardDialogWidth = 600.0; diff --git a/packages/ubuntu_wizard/lib/ubuntu_wizard.dart b/packages/ubuntu_wizard/lib/ubuntu_wizard.dart index 28e495bbc..01ba8e85d 100644 --- a/packages/ubuntu_wizard/lib/ubuntu_wizard.dart +++ b/packages/ubuntu_wizard/lib/ubuntu_wizard.dart @@ -2,7 +2,6 @@ library ubuntu_wizard; export 'package:wizard_router/wizard_router.dart'; -export 'src/layouts/horizontal_page.dart'; export 'src/wizard_app.dart'; export 'src/wizard_bar.dart'; export 'src/wizard_builder.dart'; From 5ebc20ebaf17dd14ec9716ad5f7b1409379ed32a Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Thu, 11 Jan 2024 15:18:01 +0100 Subject: [PATCH 083/106] Fix tests to conform to new layout --- .../ubuntu_bootstrap/lib/pages/source/source_page.dart | 7 ------- packages/ubuntu_bootstrap/test/installer_wizard_test.dart | 2 ++ .../test/source/not_enough_disk_space_test.dart | 2 ++ .../ubuntu_bootstrap/test/source/source_page_test.dart | 2 ++ packages/ubuntu_bootstrap/test/source/test_source.dart | 6 +++++- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart b/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart index ab226afb8..117ce17c5 100644 --- a/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart @@ -33,13 +33,6 @@ class SourcePage extends ConsumerWidget with ProvisioningPage { name: 'source', windowTitle: lang.updatesOtherSoftwarePageTitle, title: lang.updatesOtherSoftwarePageDescription, - //icon: SvgPicture.asset( - // 'assets/icons/mascot.svg', - // colorFilter: ColorFilter.mode( - // context.isDarkMode ? Colors.white : Colors.black, - // BlendMode.srcIn, - // ), - //), content: ListView( children: [ ...model.sources diff --git a/packages/ubuntu_bootstrap/test/installer_wizard_test.dart b/packages/ubuntu_bootstrap/test/installer_wizard_test.dart index 74b542431..6f32c69e0 100644 --- a/packages/ubuntu_bootstrap/test/installer_wizard_test.dart +++ b/packages/ubuntu_bootstrap/test/installer_wizard_test.dart @@ -88,6 +88,7 @@ void main() { }); testWidgets('guided reformat', (tester) async { + await tester.binding.setSurfaceSize(const Size(800, 800)); final loadingModel = buildLoadingModel(); final localeModel = buildLocaleModel(); final welcomeModel = buildWelcomeModel(option: Option.welcomeInstallOption); @@ -250,6 +251,7 @@ void main() { }); testWidgets('bitlocker', (tester) async { + await tester.binding.setSurfaceSize(const Size(800, 800)); final localeModel = buildLocaleModel(); final storageModel = buildStorageModel(type: StorageType.alongside); final bitlockerModel = buildBitLockerModel(hasBitLocker: true); diff --git a/packages/ubuntu_bootstrap/test/source/not_enough_disk_space_test.dart b/packages/ubuntu_bootstrap/test/source/not_enough_disk_space_test.dart index 49d829716..9aa305b6a 100644 --- a/packages/ubuntu_bootstrap/test/source/not_enough_disk_space_test.dart +++ b/packages/ubuntu_bootstrap/test/source/not_enough_disk_space_test.dart @@ -107,9 +107,11 @@ extension on WidgetTester { final telemetry = MockTelemetryService(); registerMockService(telemetry); + final pageImages = PageImages(MockPageConfigService()); return pumpWidget( ProviderScope( + overrides: [pageImagesProvider.overrideWith((_) => pageImages)], child: MaterialApp( localizationsDelegates: GlobalUbuntuBootstrapLocalizations.delegates, home: Consumer( diff --git a/packages/ubuntu_bootstrap/test/source/source_page_test.dart b/packages/ubuntu_bootstrap/test/source/source_page_test.dart index 7b1325369..5381c6756 100644 --- a/packages/ubuntu_bootstrap/test/source/source_page_test.dart +++ b/packages/ubuntu_bootstrap/test/source/source_page_test.dart @@ -45,6 +45,7 @@ void main() { }); testWidgets('install codecs', (tester) async { + await tester.binding.setSurfaceSize(const Size(800, 800)); final model = buildSourceModel(installCodecs: true); await tester.pumpApp((_) => buildSourcePage(model)); @@ -87,6 +88,7 @@ void main() { }); testWidgets('offline', (tester) async { + await tester.binding.setSurfaceSize(const Size(800, 800)); final model = buildSourceModel(isOnline: false); await tester.pumpApp((_) => buildSourcePage(model)); diff --git a/packages/ubuntu_bootstrap/test/source/test_source.dart b/packages/ubuntu_bootstrap/test/source/test_source.dart index 3ccc8d1f2..8b6d82a88 100644 --- a/packages/ubuntu_bootstrap/test/source/test_source.dart +++ b/packages/ubuntu_bootstrap/test/source/test_source.dart @@ -53,9 +53,13 @@ SourceModel buildSourceModel({ Widget buildSourcePage(SourceModel model) { registerMockService(MockSubiquityClient()); registerMockService(MockTelemetryService()); + final pageImages = PageImages(MockPageConfigService()); return ProviderScope( - overrides: [sourceModelProvider.overrideWith((_) => model)], + overrides: [ + sourceModelProvider.overrideWith((_) => model), + pageImagesProvider.overrideWith((_) => pageImages), + ], child: const SourcePage(), ); } From 61e50a96d2a5fa16bea58dba8b88a8c23a5aca82 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Thu, 11 Jan 2024 23:56:25 +0100 Subject: [PATCH 084/106] Add localization for init loading --- .../assets/ubuntu-provision.yml | 10 -- .../lib/pages/source/source_page.dart | 2 - .../lib/pages/storage/storage_page.dart | 1 - .../test/storage/storage_dialogs_test.dart | 2 - .../ubuntu_init/assets/ubuntu-provision.yml | 10 +- packages/ubuntu_init/lib/src/init_wizard.dart | 14 +-- .../lib/src/l10n/ubuntu_init_en.arb | 8 ++ .../src/l10n/ubuntu_init_localizations.dart | 12 +++ .../l10n/ubuntu_init_localizations_am.dart | 10 ++ .../l10n/ubuntu_init_localizations_ar.dart | 10 ++ .../l10n/ubuntu_init_localizations_be.dart | 10 ++ .../l10n/ubuntu_init_localizations_bg.dart | 10 ++ .../l10n/ubuntu_init_localizations_bn.dart | 10 ++ .../l10n/ubuntu_init_localizations_bo.dart | 10 ++ .../l10n/ubuntu_init_localizations_bs.dart | 10 ++ .../l10n/ubuntu_init_localizations_ca.dart | 10 ++ .../l10n/ubuntu_init_localizations_cs.dart | 10 ++ .../l10n/ubuntu_init_localizations_cy.dart | 10 ++ .../l10n/ubuntu_init_localizations_da.dart | 10 ++ .../l10n/ubuntu_init_localizations_de.dart | 10 ++ .../l10n/ubuntu_init_localizations_dz.dart | 10 ++ .../l10n/ubuntu_init_localizations_el.dart | 10 ++ .../l10n/ubuntu_init_localizations_en.dart | 10 ++ .../l10n/ubuntu_init_localizations_eo.dart | 10 ++ .../l10n/ubuntu_init_localizations_es.dart | 10 ++ .../l10n/ubuntu_init_localizations_et.dart | 10 ++ .../l10n/ubuntu_init_localizations_eu.dart | 10 ++ .../l10n/ubuntu_init_localizations_fa.dart | 10 ++ .../l10n/ubuntu_init_localizations_fi.dart | 10 ++ .../l10n/ubuntu_init_localizations_fr.dart | 10 ++ .../l10n/ubuntu_init_localizations_ga.dart | 10 ++ .../l10n/ubuntu_init_localizations_gl.dart | 10 ++ .../l10n/ubuntu_init_localizations_gu.dart | 10 ++ .../l10n/ubuntu_init_localizations_he.dart | 10 ++ .../l10n/ubuntu_init_localizations_hi.dart | 10 ++ .../l10n/ubuntu_init_localizations_hr.dart | 10 ++ .../l10n/ubuntu_init_localizations_hu.dart | 10 ++ .../l10n/ubuntu_init_localizations_id.dart | 10 ++ .../l10n/ubuntu_init_localizations_is.dart | 10 ++ .../l10n/ubuntu_init_localizations_it.dart | 10 ++ .../l10n/ubuntu_init_localizations_ja.dart | 10 ++ .../l10n/ubuntu_init_localizations_ka.dart | 10 ++ .../l10n/ubuntu_init_localizations_kk.dart | 10 ++ .../l10n/ubuntu_init_localizations_km.dart | 10 ++ .../l10n/ubuntu_init_localizations_kn.dart | 10 ++ .../l10n/ubuntu_init_localizations_ko.dart | 10 ++ .../l10n/ubuntu_init_localizations_ku.dart | 10 ++ .../l10n/ubuntu_init_localizations_lo.dart | 10 ++ .../l10n/ubuntu_init_localizations_lt.dart | 10 ++ .../l10n/ubuntu_init_localizations_lv.dart | 10 ++ .../l10n/ubuntu_init_localizations_mk.dart | 10 ++ .../l10n/ubuntu_init_localizations_ml.dart | 10 ++ .../l10n/ubuntu_init_localizations_mr.dart | 10 ++ .../l10n/ubuntu_init_localizations_my.dart | 10 ++ .../l10n/ubuntu_init_localizations_nb.dart | 10 ++ .../l10n/ubuntu_init_localizations_ne.dart | 10 ++ .../l10n/ubuntu_init_localizations_nl.dart | 10 ++ .../l10n/ubuntu_init_localizations_nn.dart | 10 ++ .../l10n/ubuntu_init_localizations_oc.dart | 10 ++ .../l10n/ubuntu_init_localizations_pa.dart | 10 ++ .../l10n/ubuntu_init_localizations_pl.dart | 10 ++ .../l10n/ubuntu_init_localizations_pt.dart | 10 ++ .../l10n/ubuntu_init_localizations_ro.dart | 10 ++ .../l10n/ubuntu_init_localizations_ru.dart | 10 ++ .../l10n/ubuntu_init_localizations_se.dart | 10 ++ .../l10n/ubuntu_init_localizations_si.dart | 10 ++ .../l10n/ubuntu_init_localizations_sk.dart | 10 ++ .../l10n/ubuntu_init_localizations_sl.dart | 10 ++ .../l10n/ubuntu_init_localizations_sq.dart | 10 ++ .../l10n/ubuntu_init_localizations_sr.dart | 10 ++ .../l10n/ubuntu_init_localizations_sv.dart | 10 ++ .../l10n/ubuntu_init_localizations_ta.dart | 10 ++ .../l10n/ubuntu_init_localizations_te.dart | 10 ++ .../l10n/ubuntu_init_localizations_tg.dart | 10 ++ .../l10n/ubuntu_init_localizations_th.dart | 10 ++ .../l10n/ubuntu_init_localizations_tl.dart | 10 ++ .../l10n/ubuntu_init_localizations_tr.dart | 10 ++ .../l10n/ubuntu_init_localizations_ug.dart | 10 ++ .../l10n/ubuntu_init_localizations_uk.dart | 10 ++ .../l10n/ubuntu_init_localizations_vi.dart | 10 ++ .../l10n/ubuntu_init_localizations_zh.dart | 10 ++ .../ubuntu_init/lib/src/loading_page.dart | 49 ++++++++++ packages/ubuntu_init/lib/src/routes.dart | 5 +- .../lib/src/welcome/welcome_page.dart | 95 ++++++++----------- packages/ubuntu_provision/lib/keyboard.dart | 1 + .../lib/src/keyboard/keyboard_page.dart | 5 - .../lib/src/locale/locale_page.dart | 3 - .../lib/src/widgets/horizontal_page.dart | 61 ++++++------ packages/ubuntu_provision/lib/widgets.dart | 2 +- .../test/locale/test_locale.dart | 2 - .../test/network/network_page_test.dart | 1 - 91 files changed, 887 insertions(+), 126 deletions(-) create mode 100644 packages/ubuntu_init/lib/src/loading_page.dart diff --git a/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml b/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml index d27dce627..28fc813fd 100644 --- a/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml +++ b/packages/ubuntu_bootstrap/assets/ubuntu-provision.yml @@ -2,39 +2,29 @@ pages: locale: title: "Welcome" image: "packages/ubuntu_provision/assets/mascot.png" - visible: true welcome: title: "Welcome to Ubuntu" visible: true rst: title: "RST is enabled" image: "assets/storage/rst.svg" - visible: true keyboard: title: "Keyboard layout" image: "assets/icons/keyboard.svg" - visible: true network: title: "Connect to a network" image: "assets/icons/wifi.svg" - visible: true refresh: title: "Update available" - visible: true source: title: "Applications and updates" image: "assets/icons/mascot.svg" - visible: true notEnoughDiskSpace: title: "Not enough disk space" - visible: true secureBoot: title: "Configure Secure Boot" - visible: true storage: title: "Type of installation" image: "assets/icons/disk.svg" - visible: true confirm: title: "Ready to install" - visible: true diff --git a/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart b/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart index 117ce17c5..998a8b5e0 100644 --- a/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart @@ -1,13 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_svg/svg.dart'; import 'package:subiquity_client/subiquity_client.dart'; import 'package:ubuntu_bootstrap/l10n.dart'; import 'package:ubuntu_bootstrap/pages/source/source_model.dart'; import 'package:ubuntu_bootstrap/services.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; -import 'package:ubuntu_utils/ubuntu_utils.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru/yaru.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; diff --git a/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart b/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart index f2ebbc393..483f3be3d 100644 --- a/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_svg/svg.dart'; import 'package:subiquity_client/subiquity_client.dart'; import 'package:ubuntu_bootstrap/l10n.dart'; import 'package:ubuntu_bootstrap/pages/storage/storage_dialogs.dart'; diff --git a/packages/ubuntu_bootstrap/test/storage/storage_dialogs_test.dart b/packages/ubuntu_bootstrap/test/storage/storage_dialogs_test.dart index a648fddf0..a262dce65 100644 --- a/packages/ubuntu_bootstrap/test/storage/storage_dialogs_test.dart +++ b/packages/ubuntu_bootstrap/test/storage/storage_dialogs_test.dart @@ -1,5 +1,3 @@ -import 'dart:ui'; - import 'package:flutter/widgets.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/ubuntu_init/assets/ubuntu-provision.yml b/packages/ubuntu_init/assets/ubuntu-provision.yml index 005b505c0..d3a30a1cd 100644 --- a/packages/ubuntu_init/assets/ubuntu-provision.yml +++ b/packages/ubuntu_init/assets/ubuntu-provision.yml @@ -1,32 +1,24 @@ pages: welcome: title: "Welcome" + image: "assets/ubuntu.svg" visible: true locale: title: "Welcome to Ubuntu" image: "packages/ubuntu_provision/assets/mascot.png" - visible: true keyboard: title: "Keyboard layout" - visible: true network: title: "Connect to a network" - visible: true identity: title: "Set up your account" - visible: true ubuntuPro: title: "Ubuntu Pro" - visible: true privacy: title: "Location services" - visible: true timezone: title: "Select your timezone" - visible: true telemetry: title: "Telemetry" - visible: true theme: title: "Choose your theme" - visible: true diff --git a/packages/ubuntu_init/lib/src/init_wizard.dart b/packages/ubuntu_init/lib/src/init_wizard.dart index 7fb756ccb..1db1a82eb 100644 --- a/packages/ubuntu_init/lib/src/init_wizard.dart +++ b/packages/ubuntu_init/lib/src/init_wizard.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:ubuntu_init/src/init_model.dart'; import 'package:ubuntu_init/src/init_step.dart'; +import 'package:ubuntu_init/src/loading_page.dart'; import 'package:ubuntu_init/src/routes.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; @@ -28,12 +29,13 @@ class InitWizard extends ConsumerWidget { return WizardBuilder( routes: { - Routes.initial: WizardRoute( - builder: (_) => const SizedBox.shrink(), - onReplace: (_) async { - await ref.read(pageImagesProvider).preCache(context); - return ref.read(initModelProvider).init().then((_) => null); - }, + Routes.loading: WizardRoute( + builder: (_) => const LoadingPage(), + userData: const WizardRouteData( + hasPrevious: false, + hasNext: false, + ), + onReplace: (_) => LoadingPage.init(context, ref).then((_) => null), ), ...routes, Routes.theme: WizardRoute( diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_en.arb b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_en.arb index d098bc712..f635c745f 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_en.arb +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_en.arb @@ -1,4 +1,12 @@ { + "loadingPageTitle": "Welcome to {DISTRO}", + "@loadingPageTitle": { + "type": "text", + "placeholders": { + "DISTRO": {} + } + }, + "loadingHeader": "Preparing {DISTRO}…", "welcomePageTitle": "Welcome", "@welcomePageTitle": {}, "welcomePageHeader": "Welcome to {distro}", diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations.dart index 5c083bf4f..f596f7add 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations.dart @@ -237,6 +237,18 @@ abstract class UbuntuInitLocalizations { Locale('zh', 'TW') ]; + /// No description provided for @loadingPageTitle. + /// + /// In en, this message translates to: + /// **'Welcome to {DISTRO}'** + String loadingPageTitle(Object DISTRO); + + /// No description provided for @loadingHeader. + /// + /// In en, this message translates to: + /// **'Preparing {DISTRO}…'** + String loadingHeader(Object DISTRO); + /// No description provided for @welcomePageTitle. /// /// In en, this message translates to: diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_am.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_am.dart index 8786a0349..d48d725a8 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_am.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_am.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsAm extends UbuntuInitLocalizations { UbuntuInitLocalizationsAm([String locale = 'am']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ar.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ar.dart index bd1ffe70b..735ec322c 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ar.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ar.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsAr extends UbuntuInitLocalizations { UbuntuInitLocalizationsAr([String locale = 'ar']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_be.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_be.dart index dd5f086fa..5d70bb269 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_be.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_be.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsBe extends UbuntuInitLocalizations { UbuntuInitLocalizationsBe([String locale = 'be']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bg.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bg.dart index 7e3e0e0ef..6777f485c 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bg.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bg.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsBg extends UbuntuInitLocalizations { UbuntuInitLocalizationsBg([String locale = 'bg']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bn.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bn.dart index 46c362e54..c9d560216 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bn.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bn.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsBn extends UbuntuInitLocalizations { UbuntuInitLocalizationsBn([String locale = 'bn']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bo.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bo.dart index 4cce5d975..fadeb034f 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bo.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bo.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsBo extends UbuntuInitLocalizations { UbuntuInitLocalizationsBo([String locale = 'bo']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bs.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bs.dart index bf57d3f65..a86bcaf80 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bs.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bs.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsBs extends UbuntuInitLocalizations { UbuntuInitLocalizationsBs([String locale = 'bs']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ca.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ca.dart index 5991b6edb..ae5adad9e 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ca.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ca.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsCa extends UbuntuInitLocalizations { UbuntuInitLocalizationsCa([String locale = 'ca']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_cs.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_cs.dart index 4835cb1bb..c8c2e2acd 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_cs.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_cs.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsCs extends UbuntuInitLocalizations { UbuntuInitLocalizationsCs([String locale = 'cs']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Vítejte'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_cy.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_cy.dart index 545ed257e..b788c3748 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_cy.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_cy.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsCy extends UbuntuInitLocalizations { UbuntuInitLocalizationsCy([String locale = 'cy']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_da.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_da.dart index 08580af1a..c523a186f 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_da.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_da.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsDa extends UbuntuInitLocalizations { UbuntuInitLocalizationsDa([String locale = 'da']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_de.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_de.dart index 255add93e..eefff67c9 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_de.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_de.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsDe extends UbuntuInitLocalizations { UbuntuInitLocalizationsDe([String locale = 'de']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Willkommen'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_dz.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_dz.dart index 6662bba5a..ee8cb33e3 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_dz.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_dz.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsDz extends UbuntuInitLocalizations { UbuntuInitLocalizationsDz([String locale = 'dz']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_el.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_el.dart index cb8fe8912..2a1d87308 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_el.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_el.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsEl extends UbuntuInitLocalizations { UbuntuInitLocalizationsEl([String locale = 'el']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_en.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_en.dart index b87fb9758..74f68158b 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_en.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_en.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsEn extends UbuntuInitLocalizations { UbuntuInitLocalizationsEn([String locale = 'en']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_eo.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_eo.dart index 9a7c44cdc..9d3ad1828 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_eo.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_eo.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsEo extends UbuntuInitLocalizations { UbuntuInitLocalizationsEo([String locale = 'eo']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Bonvenon'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_es.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_es.dart index cbd1113dd..f104506ca 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_es.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_es.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsEs extends UbuntuInitLocalizations { UbuntuInitLocalizationsEs([String locale = 'es']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Bienvenido'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_et.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_et.dart index 9d2ee9176..d503f95f1 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_et.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_et.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsEt extends UbuntuInitLocalizations { UbuntuInitLocalizationsEt([String locale = 'et']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_eu.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_eu.dart index 7c8bddbf5..15c726ae1 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_eu.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_eu.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsEu extends UbuntuInitLocalizations { UbuntuInitLocalizationsEu([String locale = 'eu']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fa.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fa.dart index 91612a9cb..438c353e2 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fa.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fa.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsFa extends UbuntuInitLocalizations { UbuntuInitLocalizationsFa([String locale = 'fa']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'خوش آمدید'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fi.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fi.dart index 9d434d031..1cb4a0607 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fi.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fi.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsFi extends UbuntuInitLocalizations { UbuntuInitLocalizationsFi([String locale = 'fi']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Tervetuloa'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fr.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fr.dart index 745d8cf20..5d96cf9f7 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fr.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fr.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsFr extends UbuntuInitLocalizations { UbuntuInitLocalizationsFr([String locale = 'fr']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Bienvenue'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ga.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ga.dart index 3e59a217f..a3fa5c8be 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ga.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ga.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsGa extends UbuntuInitLocalizations { UbuntuInitLocalizationsGa([String locale = 'ga']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_gl.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_gl.dart index f4f4adbcb..5583e8dbc 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_gl.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_gl.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsGl extends UbuntuInitLocalizations { UbuntuInitLocalizationsGl([String locale = 'gl']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_gu.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_gu.dart index def1d82ea..03f40766b 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_gu.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_gu.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsGu extends UbuntuInitLocalizations { UbuntuInitLocalizationsGu([String locale = 'gu']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_he.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_he.dart index 102d8c31a..7b806ddad 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_he.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_he.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsHe extends UbuntuInitLocalizations { UbuntuInitLocalizationsHe([String locale = 'he']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'ברוך בואך'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hi.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hi.dart index 009f593e7..0a074d681 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hi.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hi.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsHi extends UbuntuInitLocalizations { UbuntuInitLocalizationsHi([String locale = 'hi']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hr.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hr.dart index b0c169ca1..57f2cca2f 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hr.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hr.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsHr extends UbuntuInitLocalizations { UbuntuInitLocalizationsHr([String locale = 'hr']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hu.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hu.dart index 57d3b36a9..f5bf4cccb 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hu.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hu.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsHu extends UbuntuInitLocalizations { UbuntuInitLocalizationsHu([String locale = 'hu']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Üdvözöljük'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_id.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_id.dart index a8f22e5ef..2511d81a7 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_id.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_id.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsId extends UbuntuInitLocalizations { UbuntuInitLocalizationsId([String locale = 'id']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_is.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_is.dart index e5f28e4d1..63c7fad71 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_is.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_is.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsIs extends UbuntuInitLocalizations { UbuntuInitLocalizationsIs([String locale = 'is']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_it.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_it.dart index 4763516e2..465260966 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_it.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_it.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsIt extends UbuntuInitLocalizations { UbuntuInitLocalizationsIt([String locale = 'it']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Benvenuti'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ja.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ja.dart index 00fc34c9c..2e61cd65e 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ja.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ja.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsJa extends UbuntuInitLocalizations { UbuntuInitLocalizationsJa([String locale = 'ja']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ka.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ka.dart index a75362b59..dae6e5145 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ka.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ka.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsKa extends UbuntuInitLocalizations { UbuntuInitLocalizationsKa([String locale = 'ka']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'მოგესალმებით'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_kk.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_kk.dart index 24a6e3f9f..a6fd39f98 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_kk.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_kk.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsKk extends UbuntuInitLocalizations { UbuntuInitLocalizationsKk([String locale = 'kk']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_km.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_km.dart index 0ea8fbbf1..09ad9a1ca 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_km.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_km.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsKm extends UbuntuInitLocalizations { UbuntuInitLocalizationsKm([String locale = 'km']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_kn.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_kn.dart index 8489d6680..f3c6e4874 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_kn.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_kn.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsKn extends UbuntuInitLocalizations { UbuntuInitLocalizationsKn([String locale = 'kn']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ko.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ko.dart index 1d811d8dd..baba04957 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ko.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ko.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsKo extends UbuntuInitLocalizations { UbuntuInitLocalizationsKo([String locale = 'ko']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ku.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ku.dart index ddd17ec73..7a7d45169 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ku.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ku.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsKu extends UbuntuInitLocalizations { UbuntuInitLocalizationsKu([String locale = 'ku']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lo.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lo.dart index adfa49747..570430cbd 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lo.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lo.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsLo extends UbuntuInitLocalizations { UbuntuInitLocalizationsLo([String locale = 'lo']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lt.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lt.dart index 37d37d73e..1826ae579 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lt.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lt.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsLt extends UbuntuInitLocalizations { UbuntuInitLocalizationsLt([String locale = 'lt']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Sveiki!'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lv.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lv.dart index ef72d7bfd..22690d5ab 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lv.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lv.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsLv extends UbuntuInitLocalizations { UbuntuInitLocalizationsLv([String locale = 'lv']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_mk.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_mk.dart index e66ba65b8..a5b8804ac 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_mk.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_mk.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsMk extends UbuntuInitLocalizations { UbuntuInitLocalizationsMk([String locale = 'mk']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ml.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ml.dart index a342083b0..4ccd9e3cb 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ml.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ml.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsMl extends UbuntuInitLocalizations { UbuntuInitLocalizationsMl([String locale = 'ml']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_mr.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_mr.dart index 2735168ea..90ce9e8f2 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_mr.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_mr.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsMr extends UbuntuInitLocalizations { UbuntuInitLocalizationsMr([String locale = 'mr']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_my.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_my.dart index 49762c457..e5f4cdd9f 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_my.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_my.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsMy extends UbuntuInitLocalizations { UbuntuInitLocalizationsMy([String locale = 'my']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nb.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nb.dart index feb120e03..4942d1a50 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nb.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nb.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsNb extends UbuntuInitLocalizations { UbuntuInitLocalizationsNb([String locale = 'nb']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Velkommen'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ne.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ne.dart index 904f41694..6b89db33d 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ne.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ne.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsNe extends UbuntuInitLocalizations { UbuntuInitLocalizationsNe([String locale = 'ne']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nl.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nl.dart index 3c0b8adb7..4f964fe7b 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nl.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nl.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsNl extends UbuntuInitLocalizations { UbuntuInitLocalizationsNl([String locale = 'nl']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nn.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nn.dart index b41e161c2..f273378ee 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nn.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nn.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsNn extends UbuntuInitLocalizations { UbuntuInitLocalizationsNn([String locale = 'nn']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_oc.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_oc.dart index 03f41618b..663caee94 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_oc.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_oc.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsOc extends UbuntuInitLocalizations { UbuntuInitLocalizationsOc([String locale = 'oc']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Benvengut'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pa.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pa.dart index a86e3850c..0b1c334b2 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pa.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pa.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsPa extends UbuntuInitLocalizations { UbuntuInitLocalizationsPa([String locale = 'pa']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pl.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pl.dart index 127bf5177..40f7a0770 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pl.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pl.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsPl extends UbuntuInitLocalizations { UbuntuInitLocalizationsPl([String locale = 'pl']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Witamy'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pt.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pt.dart index 318b0d5d5..0330eecf6 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pt.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pt.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsPt extends UbuntuInitLocalizations { UbuntuInitLocalizationsPt([String locale = 'pt']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Bem-vindo'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ro.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ro.dart index fca52c9e8..9bc5d7105 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ro.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ro.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsRo extends UbuntuInitLocalizations { UbuntuInitLocalizationsRo([String locale = 'ro']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ru.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ru.dart index 9c6a1e1e9..27b0f8f39 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ru.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ru.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsRu extends UbuntuInitLocalizations { UbuntuInitLocalizationsRu([String locale = 'ru']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Добро пожаловать'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_se.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_se.dart index e5fde7512..7483aa5e7 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_se.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_se.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsSe extends UbuntuInitLocalizations { UbuntuInitLocalizationsSe([String locale = 'se']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_si.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_si.dart index 5af818aca..5e2e09a0e 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_si.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_si.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsSi extends UbuntuInitLocalizations { UbuntuInitLocalizationsSi([String locale = 'si']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sk.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sk.dart index 0951d1c38..a9bbdb4a7 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sk.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sk.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsSk extends UbuntuInitLocalizations { UbuntuInitLocalizationsSk([String locale = 'sk']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Vitajte'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sl.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sl.dart index 6f764bd3b..ecb819fd1 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sl.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sl.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsSl extends UbuntuInitLocalizations { UbuntuInitLocalizationsSl([String locale = 'sl']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sq.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sq.dart index 1cf58f160..ad573ba01 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sq.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sq.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsSq extends UbuntuInitLocalizations { UbuntuInitLocalizationsSq([String locale = 'sq']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sr.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sr.dart index 8397754d0..0c551323d 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sr.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sr.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsSr extends UbuntuInitLocalizations { UbuntuInitLocalizationsSr([String locale = 'sr']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sv.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sv.dart index 3cd3d717b..7c1c0ad2d 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sv.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sv.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsSv extends UbuntuInitLocalizations { UbuntuInitLocalizationsSv([String locale = 'sv']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Välkommen'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ta.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ta.dart index cd365d0f5..837807253 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ta.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ta.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsTa extends UbuntuInitLocalizations { UbuntuInitLocalizationsTa([String locale = 'ta']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_te.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_te.dart index 088e4573f..b52569833 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_te.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_te.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsTe extends UbuntuInitLocalizations { UbuntuInitLocalizationsTe([String locale = 'te']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tg.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tg.dart index 41b8ea7f8..59efb56f7 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tg.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tg.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsTg extends UbuntuInitLocalizations { UbuntuInitLocalizationsTg([String locale = 'tg']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_th.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_th.dart index f646e3f42..80af342fa 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_th.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_th.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsTh extends UbuntuInitLocalizations { UbuntuInitLocalizationsTh([String locale = 'th']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tl.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tl.dart index cc13a76d6..f77a5cf10 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tl.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tl.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsTl extends UbuntuInitLocalizations { UbuntuInitLocalizationsTl([String locale = 'tl']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tr.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tr.dart index 34244de76..8754d4d7f 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tr.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tr.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsTr extends UbuntuInitLocalizations { UbuntuInitLocalizationsTr([String locale = 'tr']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Hoş Geldiniz'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ug.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ug.dart index 9da92585b..572e0ddb2 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ug.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ug.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsUg extends UbuntuInitLocalizations { UbuntuInitLocalizationsUg([String locale = 'ug']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_uk.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_uk.dart index 4ad939885..619004d1d 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_uk.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_uk.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsUk extends UbuntuInitLocalizations { UbuntuInitLocalizationsUk([String locale = 'uk']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Ласкаво просимо!'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_vi.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_vi.dart index f81ea48f9..42fb5cf69 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_vi.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_vi.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsVi extends UbuntuInitLocalizations { UbuntuInitLocalizationsVi([String locale = 'vi']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Chào mừng'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_zh.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_zh.dart index 1a060b286..a0d124851 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_zh.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_zh.dart @@ -4,6 +4,16 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsZh extends UbuntuInitLocalizations { UbuntuInitLocalizationsZh([String locale = 'zh']) : super(locale); + @override + String loadingPageTitle(Object DISTRO) { + return 'Welcome to $DISTRO'; + } + + @override + String loadingHeader(Object DISTRO) { + return 'Preparing $DISTRO…'; + } + @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/loading_page.dart b/packages/ubuntu_init/lib/src/loading_page.dart new file mode 100644 index 000000000..5424d5a2e --- /dev/null +++ b/packages/ubuntu_init/lib/src/loading_page.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:ubuntu_init/ubuntu_init.dart'; +import 'package:ubuntu_provision/ubuntu_provision.dart'; +import 'package:ubuntu_wizard/ubuntu_wizard.dart'; +import 'package:yaru_widgets/yaru_widgets.dart'; + +class LoadingPage extends ConsumerStatefulWidget { + const LoadingPage({super.key}); + + static Future init(BuildContext context, WidgetRef ref) async { + await Future.wait([ + ref.read(pageImagesProvider).preCache(context), + ref.read(initModelProvider).init(), + ]); + } + + @override + ConsumerState createState() => _LoadingPageState(); +} + +class _LoadingPageState extends ConsumerState { + @override + Widget build(BuildContext context) { + final flavor = ref.watch(flavorProvider); + final lang = UbuntuInitLocalizations.of(context); + final style = Theme.of(context).textTheme.headlineSmall!; + return WizardPage( + title: YaruWindowTitleBar( + title: Text(lang.loadingPageTitle(flavor.name)), + ), + content: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox.square( + dimension: 72, + child: RepaintBoundary(child: YaruCircularProgressIndicator()), + ), + const SizedBox(height: kWizardSpacing * 2), + Text(lang.loadingHeader(flavor.name), style: style), + ], + ), + bottomBar: WizardBar( + leading: WizardButton.previous(context, enabled: false), + trailing: [WizardButton.next(context, enabled: false)], + ), + ); + } +} diff --git a/packages/ubuntu_init/lib/src/routes.dart b/packages/ubuntu_init/lib/src/routes.dart index fe2477738..c5092247e 100644 --- a/packages/ubuntu_init/lib/src/routes.dart +++ b/packages/ubuntu_init/lib/src/routes.dart @@ -1,6 +1,6 @@ sealed class Routes { static Map routeMap = { - 'initial': '/', + 'loading': '/loading', 'welcome': '/welcome', 'locale': '/locale', 'keyboard': '/keyboard', @@ -13,7 +13,8 @@ sealed class Routes { 'theme': '/theme', }; - static String get initial => routeMap['initial']!; + static String get initial => loading; + static String get loading => routeMap['loading']!; static String get welcome => routeMap['welcome']!; static String get locale => routeMap['locale']!; static String get keyboard => routeMap['keyboard']!; diff --git a/packages/ubuntu_init/lib/src/welcome/welcome_page.dart b/packages/ubuntu_init/lib/src/welcome/welcome_page.dart index b0fb19158..2fd287331 100644 --- a/packages/ubuntu_init/lib/src/welcome/welcome_page.dart +++ b/packages/ubuntu_init/lib/src/welcome/welcome_page.dart @@ -1,10 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:ubuntu_init/src/welcome/welcome_l10n.dart'; import 'package:ubuntu_init/src/welcome/welcome_model.dart'; -import 'package:ubuntu_provision/interfaces.dart'; +import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_utils/ubuntu_utils.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru/yaru.dart'; @@ -22,65 +21,51 @@ class WelcomePage extends ConsumerWidget with ProvisioningPage { final l10n = WelcomeLocalizations.of(context); final model = ref.watch(welcomeModelProvider); final theme = Theme.of(context); - return WizardPage( - title: YaruWindowTitleBar( - title: Text(l10n.welcomePageTitle), - ), - content: ListView( + + return HorizontalPage( + name: 'welcome', + windowTitle: l10n.welcomePageTitle, + title: l10n.welcomePageHeader(model.productInfo), + content: Column( children: [ - const SizedBox(height: kWizardSpacing * 2), - SvgPicture.asset('assets/ubuntu.svg', package: 'ubuntu_init'), + const Divider(height: 1), const SizedBox(height: kWizardSpacing), Text( - l10n.welcomePageHeader(model.productInfo), - style: theme.textTheme.titleLarge, - textAlign: TextAlign.center, + l10n.welcomeWhatsNew, + style: theme.textTheme.bodyMedium + ?.copyWith(fontWeight: FontWeight.bold), ), - const SizedBox(height: kWizardSpacing * 2), - Center( - child: IntrinsicWidth( - child: Column( - children: [ - const Divider(height: 1), - const SizedBox(height: kWizardSpacing), - Text( - l10n.welcomeWhatsNew, - style: theme.textTheme.bodyMedium - ?.copyWith(fontWeight: FontWeight.bold), - ), - const SizedBox(height: kWizardSpacing / 2), - YaruTile( - leading: Icon(YaruIcons.log_in, color: theme.primaryColor), - title: Text(l10n.welcomeStartTitle), - subtitle: Text(l10n.welcomeStartSubtitle), - ), - YaruTile( - leading: Icon(YaruIcons.application_bag, - color: theme.primaryColor), - title: Text(l10n.welcomeStoreTitle), - subtitle: Text(l10n.welcomeStoreSubtitle), - ), - YaruTile( - leading: Icon(YaruIcons.lock, color: theme.primaryColor), - title: Text(l10n.welcomeSecurityTitle), - subtitle: Text(l10n.welcomeSecuritySubtitle), - ), - const SizedBox(height: kWizardSpacing), - ], - ), - ), + const SizedBox(height: kWizardSpacing / 2), + YaruTile( + leading: Icon(YaruIcons.log_in, color: theme.primaryColor), + title: Text(l10n.welcomeStartTitle), + subtitle: Text(l10n.welcomeStartSubtitle), ), - Center( - child: Html( - data: l10n.welcomeChangelogLabel( - model.releaseNotesURL(Localizations.localeOf(context)), + YaruTile( + leading: Icon(YaruIcons.application_bag, color: theme.primaryColor), + title: Text(l10n.welcomeStoreTitle), + subtitle: Text(l10n.welcomeStoreSubtitle), + ), + YaruTile( + leading: Icon(YaruIcons.lock, color: theme.primaryColor), + title: Text(l10n.welcomeSecurityTitle), + subtitle: Text(l10n.welcomeSecuritySubtitle), + ), + const SizedBox(height: kWizardSpacing), + Container( + color: Colors.blue, + child: Center( + child: Html( + data: l10n.welcomeChangelogLabel( + model.releaseNotesURL(Localizations.localeOf(context)), + ), + style: { + 'body': Style(margin: Margins.zero), + 'a': Style(color: Theme.of(context).colorScheme.link), + }, + shrinkWrap: true, + onLinkTap: (url, _, __) => launchUrl(url!), ), - style: { - 'body': Style(margin: Margins.zero), - 'a': Style(color: Theme.of(context).colorScheme.link), - }, - shrinkWrap: true, - onLinkTap: (url, _, __) => launchUrl(url!), ), ), ], diff --git a/packages/ubuntu_provision/lib/keyboard.dart b/packages/ubuntu_provision/lib/keyboard.dart index a41050eca..9c5b00a2a 100644 --- a/packages/ubuntu_provision/lib/keyboard.dart +++ b/packages/ubuntu_provision/lib/keyboard.dart @@ -1,3 +1,4 @@ +export 'src/keyboard/keyboard_dialogs.dart'; export 'src/keyboard/keyboard_l10n.dart'; export 'src/keyboard/keyboard_model.dart'; export 'src/keyboard/keyboard_page.dart'; diff --git a/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart b/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart index 3d2edf90e..a5856d611 100644 --- a/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart +++ b/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart @@ -1,10 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:ubuntu_provision/interfaces.dart'; -import 'package:ubuntu_provision/src/keyboard/keyboard_dialogs.dart'; -import 'package:ubuntu_provision/src/keyboard/keyboard_l10n.dart'; -import 'package:ubuntu_provision/src/keyboard/keyboard_model.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_widgets/ubuntu_widgets.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; diff --git a/packages/ubuntu_provision/lib/src/locale/locale_page.dart b/packages/ubuntu_provision/lib/src/locale/locale_page.dart index 6a6a31753..12c48a1d5 100644 --- a/packages/ubuntu_provision/lib/src/locale/locale_page.dart +++ b/packages/ubuntu_provision/lib/src/locale/locale_page.dart @@ -1,11 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:ubuntu_provision/interfaces.dart'; -import 'package:ubuntu_provision/providers.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_service/ubuntu_service.dart'; import 'package:ubuntu_widgets/ubuntu_widgets.dart'; -import 'package:ubuntu_wizard/ubuntu_wizard.dart'; class LocalePage extends ConsumerWidget with ProvisioningPage { const LocalePage({super.key}); diff --git a/packages/ubuntu_provision/lib/src/widgets/horizontal_page.dart b/packages/ubuntu_provision/lib/src/widgets/horizontal_page.dart index 70c76ba57..c96a0b768 100644 --- a/packages/ubuntu_provision/lib/src/widgets/horizontal_page.dart +++ b/packages/ubuntu_provision/lib/src/widgets/horizontal_page.dart @@ -73,39 +73,46 @@ class HorizontalPage extends ConsumerWidget { content: Padding( padding: padding, // TODO(Lukas): Make padding smaller when the size of the window is small. - child: Row( - children: [ - if (icon != null) ...[ + child: Container( + color: Colors.amber, + child: Row( + children: [ + if (icon != null) ...[ + Expanded( + flex: 2, + child: icon, + ), + const SizedBox(width: _contentSpacing), + ], Expanded( - flex: 2, - child: icon, - ), - const SizedBox(width: _contentSpacing), - ], - Expanded( - flex: 5, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + flex: 5, + child: Container( + color: Colors.red, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ - Expanded( - child: Text( - title, - style: theme.textTheme.headlineSmall - ?.copyWith(fontSize: 20), // TODO: Move to theme - ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + title, + style: theme.textTheme.headlineSmall?.copyWith( + fontSize: 20, // TODO: Move to theme + ), + ), + ), + if (trailingTitleWidget != null) trailingTitleWidget!, + ], ), - if (trailingTitleWidget != null) trailingTitleWidget!, + const SizedBox(height: kWizardSpacing), + Expanded(child: content), ], ), - const SizedBox(height: kWizardSpacing), - Expanded(child: content), - ], + ), ), - ), - ], + ], + ), ), ), snackBar: snackBar, diff --git a/packages/ubuntu_provision/lib/widgets.dart b/packages/ubuntu_provision/lib/widgets.dart index d7256dab8..aef7371f7 100644 --- a/packages/ubuntu_provision/lib/widgets.dart +++ b/packages/ubuntu_provision/lib/widgets.dart @@ -1,2 +1,2 @@ -export 'src/widgets/mascot_avatar.dart'; export 'src/widgets/horizontal_page.dart'; +export 'src/widgets/mascot_avatar.dart'; diff --git a/packages/ubuntu_provision/test/locale/test_locale.dart b/packages/ubuntu_provision/test/locale/test_locale.dart index 0750705ab..282a3e10e 100644 --- a/packages/ubuntu_provision/test/locale/test_locale.dart +++ b/packages/ubuntu_provision/test/locale/test_locale.dart @@ -3,8 +3,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mockito/mockito.dart'; import 'package:ubuntu_provision/providers.dart'; import 'package:ubuntu_provision/services.dart'; -import 'package:ubuntu_provision/src/locale/locale_model.dart'; -import 'package:ubuntu_provision/src/locale/locale_page.dart'; import 'package:ubuntu_service/ubuntu_service.dart'; import '../test_utils.dart'; diff --git a/packages/ubuntu_provision/test/network/network_page_test.dart b/packages/ubuntu_provision/test/network/network_page_test.dart index e272262f0..6e6a106dc 100644 --- a/packages/ubuntu_provision/test/network/network_page_test.dart +++ b/packages/ubuntu_provision/test/network/network_page_test.dart @@ -8,7 +8,6 @@ import 'package:ubuntu_provision/src/network/network_l10n.dart'; import 'package:ubuntu_provision/src/network/network_model.dart'; import 'package:ubuntu_provision/src/network/network_page.dart'; import 'package:ubuntu_provision/src/network/wifi_view.dart'; -import 'package:ubuntu_provision/src/providers/page_images.dart'; import 'package:ubuntu_test/ubuntu_test.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru_test/yaru_test.dart'; From a7268eb14ce11fe1da1d442589cf99d649752e9d Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 12 Jan 2024 11:32:38 +0100 Subject: [PATCH 085/106] Fix bootstrap layout --- .../lib/pages/source/source_page.dart | 81 ++++++++++--------- .../lib/pages/storage/storage_page.dart | 9 ++- .../lib/src/keyboard/keyboard_page.dart | 1 + .../lib/src/locale/locale_page.dart | 1 + .../lib/src/network/network_page.dart | 5 +- .../lib/src/widgets/horizontal_page.dart | 70 ++++++++-------- 6 files changed, 87 insertions(+), 80 deletions(-) diff --git a/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart b/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart index 998a8b5e0..1f97620fa 100644 --- a/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/source/source_page.dart @@ -31,54 +31,55 @@ class SourcePage extends ConsumerWidget with ProvisioningPage { name: 'source', windowTitle: lang.updatesOtherSoftwarePageTitle, title: lang.updatesOtherSoftwarePageDescription, - content: ListView( - children: [ - ...model.sources - .map((source) => Align( - alignment: AlignmentDirectional.centerStart, - child: Visibility( - child: YaruRadioButton( - title: Text(source.localizeTitle(context)), - subtitle: Text(source.localizeSubtitle(context)), - contentPadding: kWizardPadding, - value: source.id, - groupValue: model.sourceId, - onChanged: model.setSourceId, + content: Center( + child: ListView( + shrinkWrap: true, + children: [ + ...model.sources + .map((source) => Align( + alignment: AlignmentDirectional.centerStart, + child: Visibility( + child: YaruRadioButton( + title: Text(source.localizeTitle(context)), + subtitle: Text(source.localizeSubtitle(context)), + contentPadding: kWizardPadding, + value: source.id, + groupValue: model.sourceId, + onChanged: model.setSourceId, + ), ), - ), - )) - .withSpacing(kWizardSpacing), - Visibility( - child: Padding( + )) + .withSpacing(kWizardSpacing), + Padding( padding: const EdgeInsets.all(kYaruPagePadding), child: Text(lang.otherOptions), ), - ), - Align( - alignment: AlignmentDirectional.centerStart, - child: YaruCheckButton( - title: Text(lang.installDriversTitle), - subtitle: Text(lang.installDriversSubtitle), - contentPadding: kWizardPadding, - value: model.installDrivers, - onChanged: model.setInstallDrivers, - ), - ), - const SizedBox(height: kWizardSpacing), - Align( - alignment: AlignmentDirectional.centerStart, - child: Tooltip( - message: !model.isOnline ? lang.offlineWarning : '', + Align( + alignment: AlignmentDirectional.centerStart, child: YaruCheckButton( - title: Text(lang.installCodecsTitle), - subtitle: Text(lang.installCodecsSubtitle), + title: Text(lang.installDriversTitle), + subtitle: Text(lang.installDriversSubtitle), contentPadding: kWizardPadding, - value: model.installCodecs && model.isOnline, - onChanged: model.isOnline ? model.setInstallCodecs : null, + value: model.installDrivers, + onChanged: model.setInstallDrivers, + ), + ), + const SizedBox(height: kWizardSpacing), + Align( + alignment: AlignmentDirectional.centerStart, + child: Tooltip( + message: !model.isOnline ? lang.offlineWarning : '', + child: YaruCheckButton( + title: Text(lang.installCodecsTitle), + subtitle: Text(lang.installCodecsSubtitle), + contentPadding: kWizardPadding, + value: model.installCodecs && model.isOnline, + onChanged: model.isOnline ? model.setInstallCodecs : null, + ), ), ), - ), - ], + ], + ), ), snackBar: model.onBattery ? SnackBar( diff --git a/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart b/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart index 483f3be3d..8a8baac6c 100644 --- a/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart +++ b/packages/ubuntu_bootstrap/lib/pages/storage/storage_page.dart @@ -71,8 +71,13 @@ class StoragePage extends ConsumerWidget with ProvisioningPage { Padding( padding: const EdgeInsets.only(bottom: kWizardSpacing), child: YaruRadioButton( - title: Text(_formatAlongside( - lang, model.productInfo, model.existingOS ?? [])), + title: Text( + _formatAlongside( + lang, + model.productInfo, + model.existingOS ?? [], + ), + ), subtitle: Text(lang.installationTypeAlongsideInfo), value: StorageType.alongside, groupValue: model.type, diff --git a/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart b/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart index a5856d611..fc98d8d63 100644 --- a/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart +++ b/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart @@ -40,6 +40,7 @@ class KeyboardPage extends ConsumerWidget with ProvisioningPage { horizontal: HorizontalPage.defaultContentPadding, vertical: 40, // TODO(Lukas): Move to correct place ), + expandContent: true, content: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ diff --git a/packages/ubuntu_provision/lib/src/locale/locale_page.dart b/packages/ubuntu_provision/lib/src/locale/locale_page.dart index 12c48a1d5..05d6b7ad4 100644 --- a/packages/ubuntu_provision/lib/src/locale/locale_page.dart +++ b/packages/ubuntu_provision/lib/src/locale/locale_page.dart @@ -29,6 +29,7 @@ class LocalePage extends ConsumerWidget with ProvisioningPage { await tryGetService() ?.addMetric('Language', locale.languageCode); }, + expandContent: true, content: ListWidget.builder( selectedIndex: model.selectedIndex, itemCount: model.languageCount, diff --git a/packages/ubuntu_provision/lib/src/network/network_page.dart b/packages/ubuntu_provision/lib/src/network/network_page.dart index c70c2f0c2..a0501cf7e 100644 --- a/packages/ubuntu_provision/lib/src/network/network_page.dart +++ b/packages/ubuntu_provision/lib/src/network/network_page.dart @@ -36,10 +36,7 @@ class NetworkPage extends ConsumerWidget with ProvisioningPage { name: 'network', windowTitle: lang.networkPageTitle, title: 'Connect to the Internet?', // TODO(Lukas): Add to localization - padding: const EdgeInsets.symmetric( - horizontal: HorizontalPage.defaultContentPadding, - vertical: kWizardSpacing, - ), + expandContent: true, content: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/packages/ubuntu_provision/lib/src/widgets/horizontal_page.dart b/packages/ubuntu_provision/lib/src/widgets/horizontal_page.dart index c96a0b768..bc41de55d 100644 --- a/packages/ubuntu_provision/lib/src/widgets/horizontal_page.dart +++ b/packages/ubuntu_provision/lib/src/widgets/horizontal_page.dart @@ -18,7 +18,11 @@ class HorizontalPage extends ConsumerWidget { this.nextArguments, this.trailingTitleWidget, this.isNextEnabled = true, - this.padding = const EdgeInsets.all(defaultContentPadding), + this.expandContent = false, + this.padding = const EdgeInsets.symmetric( + horizontal: defaultContentPadding, + vertical: _verticalContentPadding, + ), this.bottomBar, this.snackBar, super.key, @@ -50,6 +54,9 @@ class HorizontalPage extends ConsumerWidget { /// Whether the next button should be enabled or not. final bool isNextEnabled; + /// Whether the next button should be enabled or not. + final bool expandContent; + /// The padding applied around the area storing the icon and the content. final EdgeInsets padding; @@ -61,6 +68,7 @@ class HorizontalPage extends ConsumerWidget { // TODO(Lukas): Move these to a proper place. static const defaultContentPadding = 100.0; + static const _verticalContentPadding = 20.0; static const _contentSpacing = 60.0; @override @@ -73,46 +81,40 @@ class HorizontalPage extends ConsumerWidget { content: Padding( padding: padding, // TODO(Lukas): Make padding smaller when the size of the window is small. - child: Container( - color: Colors.amber, - child: Row( - children: [ - if (icon != null) ...[ - Expanded( - flex: 2, - child: icon, - ), - const SizedBox(width: _contentSpacing), - ], + child: Row( + children: [ + if (icon != null) ...[ Expanded( - flex: 5, - child: Container( - color: Colors.red, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, + flex: 2, + child: icon, + ), + const SizedBox(width: _contentSpacing), + ], + Expanded( + flex: 5, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - title, - style: theme.textTheme.headlineSmall?.copyWith( - fontSize: 20, // TODO: Move to theme - ), - ), + Expanded( + child: Text( + title, + style: theme.textTheme.headlineSmall?.copyWith( + fontSize: 20, // TODO: Move to theme ), - if (trailingTitleWidget != null) trailingTitleWidget!, - ], + ), ), - const SizedBox(height: kWizardSpacing), - Expanded(child: content), + if (trailingTitleWidget != null) trailingTitleWidget!, ], ), - ), + const SizedBox(height: kWizardSpacing), + expandContent ? Expanded(child: content) : content, + ], ), - ], - ), + ), + ], ), ), snackBar: snackBar, From 565bb4193382e0f990c46ef9ea4320a1f599857b Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 12 Jan 2024 11:36:27 +0100 Subject: [PATCH 086/106] Remove init changes --- .../ubuntu_init/assets/ubuntu-provision.yml | 10 +- packages/ubuntu_init/lib/src/init_wizard.dart | 12 +-- .../lib/src/l10n/ubuntu_init_en.arb | 8 -- .../src/l10n/ubuntu_init_localizations.dart | 12 --- .../l10n/ubuntu_init_localizations_am.dart | 10 -- .../l10n/ubuntu_init_localizations_ar.dart | 10 -- .../l10n/ubuntu_init_localizations_be.dart | 10 -- .../l10n/ubuntu_init_localizations_bg.dart | 10 -- .../l10n/ubuntu_init_localizations_bn.dart | 10 -- .../l10n/ubuntu_init_localizations_bo.dart | 10 -- .../l10n/ubuntu_init_localizations_bs.dart | 10 -- .../l10n/ubuntu_init_localizations_ca.dart | 10 -- .../l10n/ubuntu_init_localizations_cs.dart | 10 -- .../l10n/ubuntu_init_localizations_cy.dart | 10 -- .../l10n/ubuntu_init_localizations_da.dart | 10 -- .../l10n/ubuntu_init_localizations_de.dart | 10 -- .../l10n/ubuntu_init_localizations_dz.dart | 10 -- .../l10n/ubuntu_init_localizations_el.dart | 10 -- .../l10n/ubuntu_init_localizations_en.dart | 10 -- .../l10n/ubuntu_init_localizations_eo.dart | 10 -- .../l10n/ubuntu_init_localizations_es.dart | 10 -- .../l10n/ubuntu_init_localizations_et.dart | 10 -- .../l10n/ubuntu_init_localizations_eu.dart | 10 -- .../l10n/ubuntu_init_localizations_fa.dart | 10 -- .../l10n/ubuntu_init_localizations_fi.dart | 10 -- .../l10n/ubuntu_init_localizations_fr.dart | 10 -- .../l10n/ubuntu_init_localizations_ga.dart | 10 -- .../l10n/ubuntu_init_localizations_gl.dart | 10 -- .../l10n/ubuntu_init_localizations_gu.dart | 10 -- .../l10n/ubuntu_init_localizations_he.dart | 10 -- .../l10n/ubuntu_init_localizations_hi.dart | 10 -- .../l10n/ubuntu_init_localizations_hr.dart | 10 -- .../l10n/ubuntu_init_localizations_hu.dart | 10 -- .../l10n/ubuntu_init_localizations_id.dart | 10 -- .../l10n/ubuntu_init_localizations_is.dart | 10 -- .../l10n/ubuntu_init_localizations_it.dart | 10 -- .../l10n/ubuntu_init_localizations_ja.dart | 10 -- .../l10n/ubuntu_init_localizations_ka.dart | 10 -- .../l10n/ubuntu_init_localizations_kk.dart | 10 -- .../l10n/ubuntu_init_localizations_km.dart | 10 -- .../l10n/ubuntu_init_localizations_kn.dart | 10 -- .../l10n/ubuntu_init_localizations_ko.dart | 10 -- .../l10n/ubuntu_init_localizations_ku.dart | 10 -- .../l10n/ubuntu_init_localizations_lo.dart | 10 -- .../l10n/ubuntu_init_localizations_lt.dart | 10 -- .../l10n/ubuntu_init_localizations_lv.dart | 10 -- .../l10n/ubuntu_init_localizations_mk.dart | 10 -- .../l10n/ubuntu_init_localizations_ml.dart | 10 -- .../l10n/ubuntu_init_localizations_mr.dart | 10 -- .../l10n/ubuntu_init_localizations_my.dart | 10 -- .../l10n/ubuntu_init_localizations_nb.dart | 10 -- .../l10n/ubuntu_init_localizations_ne.dart | 10 -- .../l10n/ubuntu_init_localizations_nl.dart | 10 -- .../l10n/ubuntu_init_localizations_nn.dart | 10 -- .../l10n/ubuntu_init_localizations_oc.dart | 10 -- .../l10n/ubuntu_init_localizations_pa.dart | 10 -- .../l10n/ubuntu_init_localizations_pl.dart | 10 -- .../l10n/ubuntu_init_localizations_pt.dart | 10 -- .../l10n/ubuntu_init_localizations_ro.dart | 10 -- .../l10n/ubuntu_init_localizations_ru.dart | 10 -- .../l10n/ubuntu_init_localizations_se.dart | 10 -- .../l10n/ubuntu_init_localizations_si.dart | 10 -- .../l10n/ubuntu_init_localizations_sk.dart | 10 -- .../l10n/ubuntu_init_localizations_sl.dart | 10 -- .../l10n/ubuntu_init_localizations_sq.dart | 10 -- .../l10n/ubuntu_init_localizations_sr.dart | 10 -- .../l10n/ubuntu_init_localizations_sv.dart | 10 -- .../l10n/ubuntu_init_localizations_ta.dart | 10 -- .../l10n/ubuntu_init_localizations_te.dart | 10 -- .../l10n/ubuntu_init_localizations_tg.dart | 10 -- .../l10n/ubuntu_init_localizations_th.dart | 10 -- .../l10n/ubuntu_init_localizations_tl.dart | 10 -- .../l10n/ubuntu_init_localizations_tr.dart | 10 -- .../l10n/ubuntu_init_localizations_ug.dart | 10 -- .../l10n/ubuntu_init_localizations_uk.dart | 10 -- .../l10n/ubuntu_init_localizations_vi.dart | 10 -- .../l10n/ubuntu_init_localizations_zh.dart | 10 -- .../ubuntu_init/lib/src/loading_page.dart | 49 ---------- packages/ubuntu_init/lib/src/routes.dart | 5 +- .../lib/src/welcome/welcome_page.dart | 95 +++++++++++-------- 80 files changed, 70 insertions(+), 851 deletions(-) delete mode 100644 packages/ubuntu_init/lib/src/loading_page.dart diff --git a/packages/ubuntu_init/assets/ubuntu-provision.yml b/packages/ubuntu_init/assets/ubuntu-provision.yml index d3a30a1cd..005b505c0 100644 --- a/packages/ubuntu_init/assets/ubuntu-provision.yml +++ b/packages/ubuntu_init/assets/ubuntu-provision.yml @@ -1,24 +1,32 @@ pages: welcome: title: "Welcome" - image: "assets/ubuntu.svg" visible: true locale: title: "Welcome to Ubuntu" image: "packages/ubuntu_provision/assets/mascot.png" + visible: true keyboard: title: "Keyboard layout" + visible: true network: title: "Connect to a network" + visible: true identity: title: "Set up your account" + visible: true ubuntuPro: title: "Ubuntu Pro" + visible: true privacy: title: "Location services" + visible: true timezone: title: "Select your timezone" + visible: true telemetry: title: "Telemetry" + visible: true theme: title: "Choose your theme" + visible: true diff --git a/packages/ubuntu_init/lib/src/init_wizard.dart b/packages/ubuntu_init/lib/src/init_wizard.dart index 1db1a82eb..821eebb8a 100644 --- a/packages/ubuntu_init/lib/src/init_wizard.dart +++ b/packages/ubuntu_init/lib/src/init_wizard.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:ubuntu_init/src/init_model.dart'; import 'package:ubuntu_init/src/init_step.dart'; -import 'package:ubuntu_init/src/loading_page.dart'; import 'package:ubuntu_init/src/routes.dart'; import 'package:ubuntu_provision/ubuntu_provision.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; @@ -29,13 +28,10 @@ class InitWizard extends ConsumerWidget { return WizardBuilder( routes: { - Routes.loading: WizardRoute( - builder: (_) => const LoadingPage(), - userData: const WizardRouteData( - hasPrevious: false, - hasNext: false, - ), - onReplace: (_) => LoadingPage.init(context, ref).then((_) => null), + Routes.initial: WizardRoute( + builder: (_) => const SizedBox.shrink(), + onReplace: (_) => + ref.read(initModelProvider).init().then((_) => null), ), ...routes, Routes.theme: WizardRoute( diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_en.arb b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_en.arb index f635c745f..d098bc712 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_en.arb +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_en.arb @@ -1,12 +1,4 @@ { - "loadingPageTitle": "Welcome to {DISTRO}", - "@loadingPageTitle": { - "type": "text", - "placeholders": { - "DISTRO": {} - } - }, - "loadingHeader": "Preparing {DISTRO}…", "welcomePageTitle": "Welcome", "@welcomePageTitle": {}, "welcomePageHeader": "Welcome to {distro}", diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations.dart index f596f7add..5c083bf4f 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations.dart @@ -237,18 +237,6 @@ abstract class UbuntuInitLocalizations { Locale('zh', 'TW') ]; - /// No description provided for @loadingPageTitle. - /// - /// In en, this message translates to: - /// **'Welcome to {DISTRO}'** - String loadingPageTitle(Object DISTRO); - - /// No description provided for @loadingHeader. - /// - /// In en, this message translates to: - /// **'Preparing {DISTRO}…'** - String loadingHeader(Object DISTRO); - /// No description provided for @welcomePageTitle. /// /// In en, this message translates to: diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_am.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_am.dart index d48d725a8..8786a0349 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_am.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_am.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsAm extends UbuntuInitLocalizations { UbuntuInitLocalizationsAm([String locale = 'am']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ar.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ar.dart index 735ec322c..bd1ffe70b 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ar.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ar.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsAr extends UbuntuInitLocalizations { UbuntuInitLocalizationsAr([String locale = 'ar']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_be.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_be.dart index 5d70bb269..dd5f086fa 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_be.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_be.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsBe extends UbuntuInitLocalizations { UbuntuInitLocalizationsBe([String locale = 'be']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bg.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bg.dart index 6777f485c..7e3e0e0ef 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bg.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bg.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsBg extends UbuntuInitLocalizations { UbuntuInitLocalizationsBg([String locale = 'bg']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bn.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bn.dart index c9d560216..46c362e54 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bn.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bn.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsBn extends UbuntuInitLocalizations { UbuntuInitLocalizationsBn([String locale = 'bn']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bo.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bo.dart index fadeb034f..4cce5d975 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bo.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bo.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsBo extends UbuntuInitLocalizations { UbuntuInitLocalizationsBo([String locale = 'bo']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bs.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bs.dart index a86bcaf80..bf57d3f65 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bs.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_bs.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsBs extends UbuntuInitLocalizations { UbuntuInitLocalizationsBs([String locale = 'bs']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ca.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ca.dart index ae5adad9e..5991b6edb 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ca.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ca.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsCa extends UbuntuInitLocalizations { UbuntuInitLocalizationsCa([String locale = 'ca']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_cs.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_cs.dart index c8c2e2acd..4835cb1bb 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_cs.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_cs.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsCs extends UbuntuInitLocalizations { UbuntuInitLocalizationsCs([String locale = 'cs']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Vítejte'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_cy.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_cy.dart index b788c3748..545ed257e 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_cy.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_cy.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsCy extends UbuntuInitLocalizations { UbuntuInitLocalizationsCy([String locale = 'cy']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_da.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_da.dart index c523a186f..08580af1a 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_da.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_da.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsDa extends UbuntuInitLocalizations { UbuntuInitLocalizationsDa([String locale = 'da']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_de.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_de.dart index eefff67c9..255add93e 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_de.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_de.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsDe extends UbuntuInitLocalizations { UbuntuInitLocalizationsDe([String locale = 'de']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Willkommen'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_dz.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_dz.dart index ee8cb33e3..6662bba5a 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_dz.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_dz.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsDz extends UbuntuInitLocalizations { UbuntuInitLocalizationsDz([String locale = 'dz']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_el.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_el.dart index 2a1d87308..cb8fe8912 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_el.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_el.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsEl extends UbuntuInitLocalizations { UbuntuInitLocalizationsEl([String locale = 'el']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_en.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_en.dart index 74f68158b..b87fb9758 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_en.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_en.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsEn extends UbuntuInitLocalizations { UbuntuInitLocalizationsEn([String locale = 'en']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_eo.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_eo.dart index 9d3ad1828..9a7c44cdc 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_eo.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_eo.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsEo extends UbuntuInitLocalizations { UbuntuInitLocalizationsEo([String locale = 'eo']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Bonvenon'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_es.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_es.dart index f104506ca..cbd1113dd 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_es.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_es.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsEs extends UbuntuInitLocalizations { UbuntuInitLocalizationsEs([String locale = 'es']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Bienvenido'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_et.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_et.dart index d503f95f1..9d2ee9176 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_et.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_et.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsEt extends UbuntuInitLocalizations { UbuntuInitLocalizationsEt([String locale = 'et']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_eu.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_eu.dart index 15c726ae1..7c8bddbf5 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_eu.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_eu.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsEu extends UbuntuInitLocalizations { UbuntuInitLocalizationsEu([String locale = 'eu']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fa.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fa.dart index 438c353e2..91612a9cb 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fa.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fa.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsFa extends UbuntuInitLocalizations { UbuntuInitLocalizationsFa([String locale = 'fa']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'خوش آمدید'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fi.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fi.dart index 1cb4a0607..9d434d031 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fi.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fi.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsFi extends UbuntuInitLocalizations { UbuntuInitLocalizationsFi([String locale = 'fi']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Tervetuloa'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fr.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fr.dart index 5d96cf9f7..745d8cf20 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fr.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_fr.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsFr extends UbuntuInitLocalizations { UbuntuInitLocalizationsFr([String locale = 'fr']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Bienvenue'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ga.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ga.dart index a3fa5c8be..3e59a217f 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ga.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ga.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsGa extends UbuntuInitLocalizations { UbuntuInitLocalizationsGa([String locale = 'ga']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_gl.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_gl.dart index 5583e8dbc..f4f4adbcb 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_gl.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_gl.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsGl extends UbuntuInitLocalizations { UbuntuInitLocalizationsGl([String locale = 'gl']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_gu.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_gu.dart index 03f40766b..def1d82ea 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_gu.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_gu.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsGu extends UbuntuInitLocalizations { UbuntuInitLocalizationsGu([String locale = 'gu']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_he.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_he.dart index 7b806ddad..102d8c31a 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_he.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_he.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsHe extends UbuntuInitLocalizations { UbuntuInitLocalizationsHe([String locale = 'he']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'ברוך בואך'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hi.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hi.dart index 0a074d681..009f593e7 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hi.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hi.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsHi extends UbuntuInitLocalizations { UbuntuInitLocalizationsHi([String locale = 'hi']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hr.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hr.dart index 57f2cca2f..b0c169ca1 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hr.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hr.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsHr extends UbuntuInitLocalizations { UbuntuInitLocalizationsHr([String locale = 'hr']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hu.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hu.dart index f5bf4cccb..57d3b36a9 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hu.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_hu.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsHu extends UbuntuInitLocalizations { UbuntuInitLocalizationsHu([String locale = 'hu']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Üdvözöljük'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_id.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_id.dart index 2511d81a7..a8f22e5ef 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_id.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_id.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsId extends UbuntuInitLocalizations { UbuntuInitLocalizationsId([String locale = 'id']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_is.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_is.dart index 63c7fad71..e5f28e4d1 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_is.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_is.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsIs extends UbuntuInitLocalizations { UbuntuInitLocalizationsIs([String locale = 'is']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_it.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_it.dart index 465260966..4763516e2 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_it.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_it.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsIt extends UbuntuInitLocalizations { UbuntuInitLocalizationsIt([String locale = 'it']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Benvenuti'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ja.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ja.dart index 2e61cd65e..00fc34c9c 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ja.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ja.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsJa extends UbuntuInitLocalizations { UbuntuInitLocalizationsJa([String locale = 'ja']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ka.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ka.dart index dae6e5145..a75362b59 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ka.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ka.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsKa extends UbuntuInitLocalizations { UbuntuInitLocalizationsKa([String locale = 'ka']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'მოგესალმებით'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_kk.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_kk.dart index a6fd39f98..24a6e3f9f 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_kk.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_kk.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsKk extends UbuntuInitLocalizations { UbuntuInitLocalizationsKk([String locale = 'kk']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_km.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_km.dart index 09ad9a1ca..0ea8fbbf1 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_km.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_km.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsKm extends UbuntuInitLocalizations { UbuntuInitLocalizationsKm([String locale = 'km']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_kn.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_kn.dart index f3c6e4874..8489d6680 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_kn.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_kn.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsKn extends UbuntuInitLocalizations { UbuntuInitLocalizationsKn([String locale = 'kn']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ko.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ko.dart index baba04957..1d811d8dd 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ko.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ko.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsKo extends UbuntuInitLocalizations { UbuntuInitLocalizationsKo([String locale = 'ko']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ku.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ku.dart index 7a7d45169..ddd17ec73 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ku.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ku.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsKu extends UbuntuInitLocalizations { UbuntuInitLocalizationsKu([String locale = 'ku']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lo.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lo.dart index 570430cbd..adfa49747 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lo.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lo.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsLo extends UbuntuInitLocalizations { UbuntuInitLocalizationsLo([String locale = 'lo']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lt.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lt.dart index 1826ae579..37d37d73e 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lt.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lt.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsLt extends UbuntuInitLocalizations { UbuntuInitLocalizationsLt([String locale = 'lt']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Sveiki!'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lv.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lv.dart index 22690d5ab..ef72d7bfd 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lv.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_lv.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsLv extends UbuntuInitLocalizations { UbuntuInitLocalizationsLv([String locale = 'lv']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_mk.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_mk.dart index a5b8804ac..e66ba65b8 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_mk.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_mk.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsMk extends UbuntuInitLocalizations { UbuntuInitLocalizationsMk([String locale = 'mk']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ml.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ml.dart index 4ccd9e3cb..a342083b0 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ml.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ml.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsMl extends UbuntuInitLocalizations { UbuntuInitLocalizationsMl([String locale = 'ml']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_mr.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_mr.dart index 90ce9e8f2..2735168ea 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_mr.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_mr.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsMr extends UbuntuInitLocalizations { UbuntuInitLocalizationsMr([String locale = 'mr']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_my.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_my.dart index e5f4cdd9f..49762c457 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_my.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_my.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsMy extends UbuntuInitLocalizations { UbuntuInitLocalizationsMy([String locale = 'my']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nb.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nb.dart index 4942d1a50..feb120e03 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nb.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nb.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsNb extends UbuntuInitLocalizations { UbuntuInitLocalizationsNb([String locale = 'nb']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Velkommen'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ne.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ne.dart index 6b89db33d..904f41694 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ne.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ne.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsNe extends UbuntuInitLocalizations { UbuntuInitLocalizationsNe([String locale = 'ne']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nl.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nl.dart index 4f964fe7b..3c0b8adb7 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nl.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nl.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsNl extends UbuntuInitLocalizations { UbuntuInitLocalizationsNl([String locale = 'nl']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nn.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nn.dart index f273378ee..b41e161c2 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nn.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_nn.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsNn extends UbuntuInitLocalizations { UbuntuInitLocalizationsNn([String locale = 'nn']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_oc.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_oc.dart index 663caee94..03f41618b 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_oc.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_oc.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsOc extends UbuntuInitLocalizations { UbuntuInitLocalizationsOc([String locale = 'oc']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Benvengut'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pa.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pa.dart index 0b1c334b2..a86e3850c 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pa.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pa.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsPa extends UbuntuInitLocalizations { UbuntuInitLocalizationsPa([String locale = 'pa']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pl.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pl.dart index 40f7a0770..127bf5177 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pl.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pl.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsPl extends UbuntuInitLocalizations { UbuntuInitLocalizationsPl([String locale = 'pl']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Witamy'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pt.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pt.dart index 0330eecf6..318b0d5d5 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pt.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_pt.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsPt extends UbuntuInitLocalizations { UbuntuInitLocalizationsPt([String locale = 'pt']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Bem-vindo'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ro.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ro.dart index 9bc5d7105..fca52c9e8 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ro.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ro.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsRo extends UbuntuInitLocalizations { UbuntuInitLocalizationsRo([String locale = 'ro']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ru.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ru.dart index 27b0f8f39..9c6a1e1e9 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ru.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ru.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsRu extends UbuntuInitLocalizations { UbuntuInitLocalizationsRu([String locale = 'ru']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Добро пожаловать'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_se.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_se.dart index 7483aa5e7..e5fde7512 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_se.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_se.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsSe extends UbuntuInitLocalizations { UbuntuInitLocalizationsSe([String locale = 'se']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_si.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_si.dart index 5e2e09a0e..5af818aca 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_si.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_si.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsSi extends UbuntuInitLocalizations { UbuntuInitLocalizationsSi([String locale = 'si']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sk.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sk.dart index a9bbdb4a7..0951d1c38 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sk.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sk.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsSk extends UbuntuInitLocalizations { UbuntuInitLocalizationsSk([String locale = 'sk']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Vitajte'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sl.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sl.dart index ecb819fd1..6f764bd3b 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sl.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sl.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsSl extends UbuntuInitLocalizations { UbuntuInitLocalizationsSl([String locale = 'sl']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sq.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sq.dart index ad573ba01..1cf58f160 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sq.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sq.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsSq extends UbuntuInitLocalizations { UbuntuInitLocalizationsSq([String locale = 'sq']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sr.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sr.dart index 0c551323d..8397754d0 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sr.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sr.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsSr extends UbuntuInitLocalizations { UbuntuInitLocalizationsSr([String locale = 'sr']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sv.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sv.dart index 7c1c0ad2d..3cd3d717b 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sv.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_sv.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsSv extends UbuntuInitLocalizations { UbuntuInitLocalizationsSv([String locale = 'sv']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Välkommen'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ta.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ta.dart index 837807253..cd365d0f5 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ta.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ta.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsTa extends UbuntuInitLocalizations { UbuntuInitLocalizationsTa([String locale = 'ta']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_te.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_te.dart index b52569833..088e4573f 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_te.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_te.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsTe extends UbuntuInitLocalizations { UbuntuInitLocalizationsTe([String locale = 'te']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tg.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tg.dart index 59efb56f7..41b8ea7f8 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tg.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tg.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsTg extends UbuntuInitLocalizations { UbuntuInitLocalizationsTg([String locale = 'tg']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_th.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_th.dart index 80af342fa..f646e3f42 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_th.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_th.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsTh extends UbuntuInitLocalizations { UbuntuInitLocalizationsTh([String locale = 'th']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tl.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tl.dart index f77a5cf10..cc13a76d6 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tl.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tl.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsTl extends UbuntuInitLocalizations { UbuntuInitLocalizationsTl([String locale = 'tl']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tr.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tr.dart index 8754d4d7f..34244de76 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tr.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_tr.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsTr extends UbuntuInitLocalizations { UbuntuInitLocalizationsTr([String locale = 'tr']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Hoş Geldiniz'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ug.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ug.dart index 572e0ddb2..9da92585b 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ug.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_ug.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsUg extends UbuntuInitLocalizations { UbuntuInitLocalizationsUg([String locale = 'ug']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_uk.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_uk.dart index 619004d1d..4ad939885 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_uk.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_uk.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsUk extends UbuntuInitLocalizations { UbuntuInitLocalizationsUk([String locale = 'uk']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Ласкаво просимо!'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_vi.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_vi.dart index 42fb5cf69..f81ea48f9 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_vi.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_vi.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsVi extends UbuntuInitLocalizations { UbuntuInitLocalizationsVi([String locale = 'vi']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Chào mừng'; diff --git a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_zh.dart b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_zh.dart index a0d124851..1a060b286 100644 --- a/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_zh.dart +++ b/packages/ubuntu_init/lib/src/l10n/ubuntu_init_localizations_zh.dart @@ -4,16 +4,6 @@ import 'ubuntu_init_localizations.dart'; class UbuntuInitLocalizationsZh extends UbuntuInitLocalizations { UbuntuInitLocalizationsZh([String locale = 'zh']) : super(locale); - @override - String loadingPageTitle(Object DISTRO) { - return 'Welcome to $DISTRO'; - } - - @override - String loadingHeader(Object DISTRO) { - return 'Preparing $DISTRO…'; - } - @override String get welcomePageTitle => 'Welcome'; diff --git a/packages/ubuntu_init/lib/src/loading_page.dart b/packages/ubuntu_init/lib/src/loading_page.dart deleted file mode 100644 index 5424d5a2e..000000000 --- a/packages/ubuntu_init/lib/src/loading_page.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:ubuntu_init/ubuntu_init.dart'; -import 'package:ubuntu_provision/ubuntu_provision.dart'; -import 'package:ubuntu_wizard/ubuntu_wizard.dart'; -import 'package:yaru_widgets/yaru_widgets.dart'; - -class LoadingPage extends ConsumerStatefulWidget { - const LoadingPage({super.key}); - - static Future init(BuildContext context, WidgetRef ref) async { - await Future.wait([ - ref.read(pageImagesProvider).preCache(context), - ref.read(initModelProvider).init(), - ]); - } - - @override - ConsumerState createState() => _LoadingPageState(); -} - -class _LoadingPageState extends ConsumerState { - @override - Widget build(BuildContext context) { - final flavor = ref.watch(flavorProvider); - final lang = UbuntuInitLocalizations.of(context); - final style = Theme.of(context).textTheme.headlineSmall!; - return WizardPage( - title: YaruWindowTitleBar( - title: Text(lang.loadingPageTitle(flavor.name)), - ), - content: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox.square( - dimension: 72, - child: RepaintBoundary(child: YaruCircularProgressIndicator()), - ), - const SizedBox(height: kWizardSpacing * 2), - Text(lang.loadingHeader(flavor.name), style: style), - ], - ), - bottomBar: WizardBar( - leading: WizardButton.previous(context, enabled: false), - trailing: [WizardButton.next(context, enabled: false)], - ), - ); - } -} diff --git a/packages/ubuntu_init/lib/src/routes.dart b/packages/ubuntu_init/lib/src/routes.dart index c5092247e..fe2477738 100644 --- a/packages/ubuntu_init/lib/src/routes.dart +++ b/packages/ubuntu_init/lib/src/routes.dart @@ -1,6 +1,6 @@ sealed class Routes { static Map routeMap = { - 'loading': '/loading', + 'initial': '/', 'welcome': '/welcome', 'locale': '/locale', 'keyboard': '/keyboard', @@ -13,8 +13,7 @@ sealed class Routes { 'theme': '/theme', }; - static String get initial => loading; - static String get loading => routeMap['loading']!; + static String get initial => routeMap['initial']!; static String get welcome => routeMap['welcome']!; static String get locale => routeMap['locale']!; static String get keyboard => routeMap['keyboard']!; diff --git a/packages/ubuntu_init/lib/src/welcome/welcome_page.dart b/packages/ubuntu_init/lib/src/welcome/welcome_page.dart index 2fd287331..b0fb19158 100644 --- a/packages/ubuntu_init/lib/src/welcome/welcome_page.dart +++ b/packages/ubuntu_init/lib/src/welcome/welcome_page.dart @@ -1,9 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:ubuntu_init/src/welcome/welcome_l10n.dart'; import 'package:ubuntu_init/src/welcome/welcome_model.dart'; -import 'package:ubuntu_provision/ubuntu_provision.dart'; +import 'package:ubuntu_provision/interfaces.dart'; import 'package:ubuntu_utils/ubuntu_utils.dart'; import 'package:ubuntu_wizard/ubuntu_wizard.dart'; import 'package:yaru/yaru.dart'; @@ -21,51 +22,65 @@ class WelcomePage extends ConsumerWidget with ProvisioningPage { final l10n = WelcomeLocalizations.of(context); final model = ref.watch(welcomeModelProvider); final theme = Theme.of(context); - - return HorizontalPage( - name: 'welcome', - windowTitle: l10n.welcomePageTitle, - title: l10n.welcomePageHeader(model.productInfo), - content: Column( + return WizardPage( + title: YaruWindowTitleBar( + title: Text(l10n.welcomePageTitle), + ), + content: ListView( children: [ - const Divider(height: 1), + const SizedBox(height: kWizardSpacing * 2), + SvgPicture.asset('assets/ubuntu.svg', package: 'ubuntu_init'), const SizedBox(height: kWizardSpacing), Text( - l10n.welcomeWhatsNew, - style: theme.textTheme.bodyMedium - ?.copyWith(fontWeight: FontWeight.bold), - ), - const SizedBox(height: kWizardSpacing / 2), - YaruTile( - leading: Icon(YaruIcons.log_in, color: theme.primaryColor), - title: Text(l10n.welcomeStartTitle), - subtitle: Text(l10n.welcomeStartSubtitle), - ), - YaruTile( - leading: Icon(YaruIcons.application_bag, color: theme.primaryColor), - title: Text(l10n.welcomeStoreTitle), - subtitle: Text(l10n.welcomeStoreSubtitle), + l10n.welcomePageHeader(model.productInfo), + style: theme.textTheme.titleLarge, + textAlign: TextAlign.center, ), - YaruTile( - leading: Icon(YaruIcons.lock, color: theme.primaryColor), - title: Text(l10n.welcomeSecurityTitle), - subtitle: Text(l10n.welcomeSecuritySubtitle), + const SizedBox(height: kWizardSpacing * 2), + Center( + child: IntrinsicWidth( + child: Column( + children: [ + const Divider(height: 1), + const SizedBox(height: kWizardSpacing), + Text( + l10n.welcomeWhatsNew, + style: theme.textTheme.bodyMedium + ?.copyWith(fontWeight: FontWeight.bold), + ), + const SizedBox(height: kWizardSpacing / 2), + YaruTile( + leading: Icon(YaruIcons.log_in, color: theme.primaryColor), + title: Text(l10n.welcomeStartTitle), + subtitle: Text(l10n.welcomeStartSubtitle), + ), + YaruTile( + leading: Icon(YaruIcons.application_bag, + color: theme.primaryColor), + title: Text(l10n.welcomeStoreTitle), + subtitle: Text(l10n.welcomeStoreSubtitle), + ), + YaruTile( + leading: Icon(YaruIcons.lock, color: theme.primaryColor), + title: Text(l10n.welcomeSecurityTitle), + subtitle: Text(l10n.welcomeSecuritySubtitle), + ), + const SizedBox(height: kWizardSpacing), + ], + ), + ), ), - const SizedBox(height: kWizardSpacing), - Container( - color: Colors.blue, - child: Center( - child: Html( - data: l10n.welcomeChangelogLabel( - model.releaseNotesURL(Localizations.localeOf(context)), - ), - style: { - 'body': Style(margin: Margins.zero), - 'a': Style(color: Theme.of(context).colorScheme.link), - }, - shrinkWrap: true, - onLinkTap: (url, _, __) => launchUrl(url!), + Center( + child: Html( + data: l10n.welcomeChangelogLabel( + model.releaseNotesURL(Localizations.localeOf(context)), ), + style: { + 'body': Style(margin: Margins.zero), + 'a': Style(color: Theme.of(context).colorScheme.link), + }, + shrinkWrap: true, + onLinkTap: (url, _, __) => launchUrl(url!), ), ), ], From 8ca993aab2913217f92ab13d0303bdbcf944b046 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 12 Jan 2024 11:46:42 +0100 Subject: [PATCH 087/106] Remove slide changes --- .../lib/slides/default_slides.dart | 4 +-- .../lib/slides/layouts/test_slide_layout.dart | 33 ------------------- .../lib/slides/slide_layouts.dart | 1 - 3 files changed, 2 insertions(+), 36 deletions(-) delete mode 100644 packages/ubuntu_bootstrap/lib/slides/layouts/test_slide_layout.dart diff --git a/packages/ubuntu_bootstrap/lib/slides/default_slides.dart b/packages/ubuntu_bootstrap/lib/slides/default_slides.dart index 3ca1ad923..addb59319 100644 --- a/packages/ubuntu_bootstrap/lib/slides/default_slides.dart +++ b/packages/ubuntu_bootstrap/lib/slides/default_slides.dart @@ -25,8 +25,8 @@ final defaultSlides = [ Widget _buildWelcomeSlide(BuildContext context) { final lang = UbuntuBootstrapLocalizations.of(context); final product = getService(); - return TestSlideLayout( - //title: Text(lang.installationSlidesWelcomeTitle), + return IntroSlideLayout( + title: Text(lang.installationSlidesWelcomeTitle), body: SlideColumn( children: [ Consumer(builder: (context, ref, child) { diff --git a/packages/ubuntu_bootstrap/lib/slides/layouts/test_slide_layout.dart b/packages/ubuntu_bootstrap/lib/slides/layouts/test_slide_layout.dart deleted file mode 100644 index 87985af6c..000000000 --- a/packages/ubuntu_bootstrap/lib/slides/layouts/test_slide_layout.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/widgets.dart'; - -class TestSlideLayout extends StatelessWidget { - const TestSlideLayout({required this.body, required this.image, super.key}); - - final Widget body; - final Widget image; - - @override - Widget build(BuildContext context) { - return ListView( - padding: const EdgeInsets.symmetric(vertical: 8), - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Spacer(flex: 10), - Expanded( - flex: 30, - child: image, - ), - const Spacer(flex: 10), - Expanded( - flex: 40, - child: body, - ), - const Spacer(flex: 10), - ], - ), - ], - ); - } -} diff --git a/packages/ubuntu_bootstrap/lib/slides/slide_layouts.dart b/packages/ubuntu_bootstrap/lib/slides/slide_layouts.dart index 032f4f6d3..3b8a72339 100644 --- a/packages/ubuntu_bootstrap/lib/slides/slide_layouts.dart +++ b/packages/ubuntu_bootstrap/lib/slides/slide_layouts.dart @@ -3,4 +3,3 @@ export 'layouts/intro_slide_layout.dart'; export 'layouts/landscape_slide_layout.dart'; export 'layouts/outro_slide_layout.dart'; export 'layouts/portrait_slide_layout.dart'; -export 'layouts/test_slide_layout.dart'; From 50062dad82998dd591f7fe82f5a0fc75d44dc023 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 12 Jan 2024 13:47:24 +0100 Subject: [PATCH 088/106] Drop dark mode support through tinting --- .../lib/src/keyboard/keyboard_page.dart | 4 ---- .../lib/src/providers/page_images.dart | 11 +++-------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart b/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart index fc98d8d63..ba961b19b 100644 --- a/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart +++ b/packages/ubuntu_provision/lib/src/keyboard/keyboard_page.dart @@ -36,10 +36,6 @@ class KeyboardPage extends ConsumerWidget with ProvisioningPage { }, ) : null, - padding: const EdgeInsets.symmetric( - horizontal: HorizontalPage.defaultContentPadding, - vertical: 40, // TODO(Lukas): Move to correct place - ), expandContent: true, content: Column( crossAxisAlignment: CrossAxisAlignment.stretch, diff --git a/packages/ubuntu_provision/lib/src/providers/page_images.dart b/packages/ubuntu_provision/lib/src/providers/page_images.dart index 87b69f55a..f19b04285 100644 --- a/packages/ubuntu_provision/lib/src/providers/page_images.dart +++ b/packages/ubuntu_provision/lib/src/providers/page_images.dart @@ -13,6 +13,7 @@ final pageImagesProvider = Provider((ref) => PageImages(getService())); final _log = Logger('page_images'); +final _darkModeSuffix = '_dark'; /// Pre-caches and holds images for all pages. class PageImages { @@ -41,14 +42,8 @@ class PageImages { if (image == null) { return null; } - // TODO(Lukas): How should we handle dark mode? - return ColorFiltered( - colorFilter: ColorFilter.mode( - isDarkMode(context) ? Colors.white : Colors.black, - BlendMode.srcIn, - ), - child: images[pageName], - ); + // TODO(Lukas): Add support for dark mode images + return context.isDarkMode ? images[pageName] : images[pageName]; } Future preCache(BuildContext context) async { From a8a29a6d627f7531912cbef85c73d6a90d3dd2dc Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 12 Jan 2024 13:57:54 +0100 Subject: [PATCH 089/106] Remove now unnecessary surface area sets --- .../test/installer_wizard_test.dart | 1 - .../test/source/source_page_test.dart | 2 -- .../test/storage/storage_dialogs_test.dart | 1 - .../test/storage/storage_page_test.dart | 1 - .../lib/src/providers/page_images.dart | 1 - .../lib/src/services/desktop_service.dart | 34 +++++++++---------- 6 files changed, 17 insertions(+), 23 deletions(-) diff --git a/packages/ubuntu_bootstrap/test/installer_wizard_test.dart b/packages/ubuntu_bootstrap/test/installer_wizard_test.dart index 6f32c69e0..59569b27d 100644 --- a/packages/ubuntu_bootstrap/test/installer_wizard_test.dart +++ b/packages/ubuntu_bootstrap/test/installer_wizard_test.dart @@ -251,7 +251,6 @@ void main() { }); testWidgets('bitlocker', (tester) async { - await tester.binding.setSurfaceSize(const Size(800, 800)); final localeModel = buildLocaleModel(); final storageModel = buildStorageModel(type: StorageType.alongside); final bitlockerModel = buildBitLockerModel(hasBitLocker: true); diff --git a/packages/ubuntu_bootstrap/test/source/source_page_test.dart b/packages/ubuntu_bootstrap/test/source/source_page_test.dart index 5381c6756..7b1325369 100644 --- a/packages/ubuntu_bootstrap/test/source/source_page_test.dart +++ b/packages/ubuntu_bootstrap/test/source/source_page_test.dart @@ -45,7 +45,6 @@ void main() { }); testWidgets('install codecs', (tester) async { - await tester.binding.setSurfaceSize(const Size(800, 800)); final model = buildSourceModel(installCodecs: true); await tester.pumpApp((_) => buildSourcePage(model)); @@ -88,7 +87,6 @@ void main() { }); testWidgets('offline', (tester) async { - await tester.binding.setSurfaceSize(const Size(800, 800)); final model = buildSourceModel(isOnline: false); await tester.pumpApp((_) => buildSourcePage(model)); diff --git a/packages/ubuntu_bootstrap/test/storage/storage_dialogs_test.dart b/packages/ubuntu_bootstrap/test/storage/storage_dialogs_test.dart index a262dce65..4416942f6 100644 --- a/packages/ubuntu_bootstrap/test/storage/storage_dialogs_test.dart +++ b/packages/ubuntu_bootstrap/test/storage/storage_dialogs_test.dart @@ -27,7 +27,6 @@ void main() { testWidgets('select zfs', (tester) async { final model = buildStorageModel(); - await tester.binding.setSurfaceSize(const Size(900, 900)); await tester.pumpWidget(buildPage(tester, model)); final result = showAdvancedFeaturesDialog( diff --git a/packages/ubuntu_bootstrap/test/storage/storage_page_test.dart b/packages/ubuntu_bootstrap/test/storage/storage_page_test.dart index cb7f2231a..7c22bb999 100644 --- a/packages/ubuntu_bootstrap/test/storage/storage_page_test.dart +++ b/packages/ubuntu_bootstrap/test/storage/storage_page_test.dart @@ -26,7 +26,6 @@ void main() { } testWidgets('no existing OS', (tester) async { - await tester.binding.setSurfaceSize(const Size(1200, 1000)); await tester.pumpApp((_) => buildPage(buildStorageModel())); final context = tester.element(find.byType(StoragePage)); diff --git a/packages/ubuntu_provision/lib/src/providers/page_images.dart b/packages/ubuntu_provision/lib/src/providers/page_images.dart index f19b04285..c24c0cd60 100644 --- a/packages/ubuntu_provision/lib/src/providers/page_images.dart +++ b/packages/ubuntu_provision/lib/src/providers/page_images.dart @@ -13,7 +13,6 @@ final pageImagesProvider = Provider((ref) => PageImages(getService())); final _log = Logger('page_images'); -final _darkModeSuffix = '_dark'; /// Pre-caches and holds images for all pages. class PageImages { diff --git a/packages/ubuntu_provision/lib/src/services/desktop_service.dart b/packages/ubuntu_provision/lib/src/services/desktop_service.dart index 01429b368..e7eb96710 100644 --- a/packages/ubuntu_provision/lib/src/services/desktop_service.dart +++ b/packages/ubuntu_provision/lib/src/services/desktop_service.dart @@ -93,24 +93,24 @@ class GnomeService implements DesktopService { @override Future inhibit() async { - await _disableAutoMounting(); - await _disableScreenBlanking(); - await _disableScreensaver(); + //await _disableAutoMounting(); + //await _disableScreenBlanking(); + //await _disableScreensaver(); - await _gnomeSessionManager.connect(); - final cookie = await _gnomeSessionManager.inhibit( - appId: 'com.canonical.ubuntu_desktop_installer', - topLevelXId: 0, - reason: 'Installing Ubuntu', - flags: { - GnomeInhibitionFlag.autoMount, - GnomeInhibitionFlag.idle, - GnomeInhibitionFlag.logout, - GnomeInhibitionFlag.suspend, - GnomeInhibitionFlag.switchUser, - }, - ); - restoreSettings.add(() => _gnomeSessionManager.uninhibit(cookie)); + //await _gnomeSessionManager.connect(); + //final cookie = await _gnomeSessionManager.inhibit( + // appId: 'com.canonical.ubuntu_desktop_installer', + // topLevelXId: 0, + // reason: 'Installing Ubuntu', + // flags: { + // GnomeInhibitionFlag.autoMount, + // GnomeInhibitionFlag.idle, + // GnomeInhibitionFlag.logout, + // GnomeInhibitionFlag.suspend, + // GnomeInhibitionFlag.switchUser, + // }, + //); + //restoreSettings.add(() => _gnomeSessionManager.uninhibit(cookie)); } @override From f7041f037ea84bdfe24f07ff4186e342ed0c0fcc Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 12 Jan 2024 14:03:48 +0100 Subject: [PATCH 090/106] Uncomment accidentally commented lines --- .../lib/src/services/desktop_service.dart | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/ubuntu_provision/lib/src/services/desktop_service.dart b/packages/ubuntu_provision/lib/src/services/desktop_service.dart index e7eb96710..01429b368 100644 --- a/packages/ubuntu_provision/lib/src/services/desktop_service.dart +++ b/packages/ubuntu_provision/lib/src/services/desktop_service.dart @@ -93,24 +93,24 @@ class GnomeService implements DesktopService { @override Future inhibit() async { - //await _disableAutoMounting(); - //await _disableScreenBlanking(); - //await _disableScreensaver(); + await _disableAutoMounting(); + await _disableScreenBlanking(); + await _disableScreensaver(); - //await _gnomeSessionManager.connect(); - //final cookie = await _gnomeSessionManager.inhibit( - // appId: 'com.canonical.ubuntu_desktop_installer', - // topLevelXId: 0, - // reason: 'Installing Ubuntu', - // flags: { - // GnomeInhibitionFlag.autoMount, - // GnomeInhibitionFlag.idle, - // GnomeInhibitionFlag.logout, - // GnomeInhibitionFlag.suspend, - // GnomeInhibitionFlag.switchUser, - // }, - //); - //restoreSettings.add(() => _gnomeSessionManager.uninhibit(cookie)); + await _gnomeSessionManager.connect(); + final cookie = await _gnomeSessionManager.inhibit( + appId: 'com.canonical.ubuntu_desktop_installer', + topLevelXId: 0, + reason: 'Installing Ubuntu', + flags: { + GnomeInhibitionFlag.autoMount, + GnomeInhibitionFlag.idle, + GnomeInhibitionFlag.logout, + GnomeInhibitionFlag.suspend, + GnomeInhibitionFlag.switchUser, + }, + ); + restoreSettings.add(() => _gnomeSessionManager.uninhibit(cookie)); } @override From 9a7ba9131b1cd292bf0d2060c9b1e3175e96461a Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 12 Jan 2024 15:37:00 +0100 Subject: [PATCH 091/106] Fix last widget test --- packages/ubuntu_bootstrap/test/storage/storage_page_test.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ubuntu_bootstrap/test/storage/storage_page_test.dart b/packages/ubuntu_bootstrap/test/storage/storage_page_test.dart index 7c22bb999..5bb725234 100644 --- a/packages/ubuntu_bootstrap/test/storage/storage_page_test.dart +++ b/packages/ubuntu_bootstrap/test/storage/storage_page_test.dart @@ -386,6 +386,7 @@ void main() { }); testWidgets('encrypted lvm selected', (tester) async { + await tester.binding.setSurfaceSize(const Size(1000, 1000)); final model = buildStorageModel(guidedCapability: GuidedCapability.LVM_LUKS); await tester.pumpApp((_) => buildPage(model)); From a887e6ac63106384979f306b6bc7a3d3b66bd91a Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 12 Jan 2024 15:46:42 +0100 Subject: [PATCH 092/106] Revert PageImages test --- .../ubuntu_provision/test/providers/page_images_test.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/ubuntu_provision/test/providers/page_images_test.dart b/packages/ubuntu_provision/test/providers/page_images_test.dart index b09f33284..000cfd7ea 100644 --- a/packages/ubuntu_provision/test/providers/page_images_test.dart +++ b/packages/ubuntu_provision/test/providers/page_images_test.dart @@ -19,13 +19,11 @@ void main() { final image = MockImage(); pageImages.images['testPage'] = image; - pageImages.isDarkMode = (_) => false; final result = pageImages.get('testPage', MockBuildContext()); - expect(result, isInstanceOf()); - expect((result as ColorFiltered).child, isInstanceOf()); - expect(result.child, equals(image)); + expect(result, isInstanceOf()); + expect(result, equals(image)); }); test('preCache correctly loads images from assets and files', () async { From ec1df57b72af3de79f9c14bdeff901dfa342d6f6 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 12 Jan 2024 16:21:31 +0100 Subject: [PATCH 093/106] Larger window for TPM test --- .../ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart b/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart index b43fb304d..481dbf27c 100644 --- a/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart +++ b/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart @@ -179,6 +179,7 @@ void main() { }); testWidgets('tpm', (tester) async { + await tester.binding.setSurfaceSize(const Size(1000, 1000)); await tester.runApp(() => app.main([ '--source-catalog=examples/sources/tpm.yaml', '--dry-run-config=examples/dry-run-configs/tpm.yaml', From cc0666da97ee79683501110bc7f73fc0e9fa77a8 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 12 Jan 2024 16:58:55 +0100 Subject: [PATCH 094/106] Fix isDarkMode mock --- packages/ubuntu_provision/lib/src/providers/page_images.dart | 2 +- packages/ubuntu_provision/test/providers/page_images_test.dart | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/ubuntu_provision/lib/src/providers/page_images.dart b/packages/ubuntu_provision/lib/src/providers/page_images.dart index c24c0cd60..f93e68e3f 100644 --- a/packages/ubuntu_provision/lib/src/providers/page_images.dart +++ b/packages/ubuntu_provision/lib/src/providers/page_images.dart @@ -42,7 +42,7 @@ class PageImages { return null; } // TODO(Lukas): Add support for dark mode images - return context.isDarkMode ? images[pageName] : images[pageName]; + return isDarkMode(context) ? images[pageName] : images[pageName]; } Future preCache(BuildContext context) async { diff --git a/packages/ubuntu_provision/test/providers/page_images_test.dart b/packages/ubuntu_provision/test/providers/page_images_test.dart index 000cfd7ea..b8df05f85 100644 --- a/packages/ubuntu_provision/test/providers/page_images_test.dart +++ b/packages/ubuntu_provision/test/providers/page_images_test.dart @@ -19,6 +19,7 @@ void main() { final image = MockImage(); pageImages.images['testPage'] = image; + pageImages.isDarkMode = (_) => false; final result = pageImages.get('testPage', MockBuildContext()); From 0c2d5f006293cce756f1be5b59678aa47e79d1c2 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 12 Jan 2024 17:16:00 +0100 Subject: [PATCH 095/106] Add TODOs for overflow --- .../ubuntu_bootstrap_test.dart | 41 ++++++++++--------- .../test/installer_wizard_test.dart | 1 + .../test/storage/storage_page_test.dart | 1 + .../test/network/network_page_test.dart | 1 + 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart b/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart index 481dbf27c..04eb0fbf1 100644 --- a/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart +++ b/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart @@ -179,6 +179,7 @@ void main() { }); testWidgets('tpm', (tester) async { + // TODO: Fix StoragePage overflow so that this isn't needed await tester.binding.setSurfaceSize(const Size(1000, 1000)); await tester.runApp(() => app.main([ '--source-catalog=examples/sources/tpm.yaml', @@ -208,31 +209,31 @@ void main() { await tester.tapNext(); await tester.pumpAndSettle(); - await tester.testStoragePage( - type: StorageType.erase, - guidedCapability: GuidedCapability.CORE_BOOT_ENCRYPTED, - ); - await tester.tapNext(); - await tester.pumpAndSettle(); + //await tester.testStoragePage( + // type: StorageType.erase, + // guidedCapability: GuidedCapability.CORE_BOOT_ENCRYPTED, + //); + //await tester.tapNext(); + //await tester.pumpAndSettle(); - await tester.testRecoveryKeyPage(); - await tester.tapNext(); - await tester.pumpAndSettle(); + //await tester.testRecoveryKeyPage(); + //await tester.tapNext(); + //await tester.pumpAndSettle(); - await tester.testConfirmPage(); - await tester.tapConfirm(); - await tester.pumpAndSettle(); + //await tester.testConfirmPage(); + //await tester.tapConfirm(); + //await tester.pumpAndSettle(); - await tester.testInstallPage(); - await tester.pumpAndSettle(); + //await tester.testInstallPage(); + //await tester.pumpAndSettle(); - final windowClosed = YaruTestWindow.waitForClosed(); - await tester.tapContinueTesting(); - await expectLater(windowClosed, completes); + //final windowClosed = YaruTestWindow.waitForClosed(); + //await tester.tapContinueTesting(); + //await expectLater(windowClosed, completes); - await verifySubiquityConfig( - capability: GuidedCapability.CORE_BOOT_ENCRYPTED, - ); + //await verifySubiquityConfig( + // capability: GuidedCapability.CORE_BOOT_ENCRYPTED, + //); }); testWidgets('manual partitioning', (tester) async { diff --git a/packages/ubuntu_bootstrap/test/installer_wizard_test.dart b/packages/ubuntu_bootstrap/test/installer_wizard_test.dart index 59569b27d..32f9d78b0 100644 --- a/packages/ubuntu_bootstrap/test/installer_wizard_test.dart +++ b/packages/ubuntu_bootstrap/test/installer_wizard_test.dart @@ -88,6 +88,7 @@ void main() { }); testWidgets('guided reformat', (tester) async { + // TODO: Fix SourcePage overflow so that this isn't needed await tester.binding.setSurfaceSize(const Size(800, 800)); final loadingModel = buildLoadingModel(); final localeModel = buildLocaleModel(); diff --git a/packages/ubuntu_bootstrap/test/storage/storage_page_test.dart b/packages/ubuntu_bootstrap/test/storage/storage_page_test.dart index 5bb725234..a966cd253 100644 --- a/packages/ubuntu_bootstrap/test/storage/storage_page_test.dart +++ b/packages/ubuntu_bootstrap/test/storage/storage_page_test.dart @@ -386,6 +386,7 @@ void main() { }); testWidgets('encrypted lvm selected', (tester) async { + // TODO: Fix StoragePage overflow so that this isn't needed await tester.binding.setSurfaceSize(const Size(1000, 1000)); final model = buildStorageModel(guidedCapability: GuidedCapability.LVM_LUKS); diff --git a/packages/ubuntu_provision/test/network/network_page_test.dart b/packages/ubuntu_provision/test/network/network_page_test.dart index 6e6a106dc..72287baba 100644 --- a/packages/ubuntu_provision/test/network/network_page_test.dart +++ b/packages/ubuntu_provision/test/network/network_page_test.dart @@ -74,6 +74,7 @@ void main() { }); testWidgets('selects wifi', (tester) async { + // TODO: Fix NetworkPage overflow so that this isn't needed await tester.binding.setSurfaceSize(const Size(800, 800)); final model = NetworkModel(MockNetworkService()); await tester.pumpApp( From 9e1b28f811657d243bc13b2f66d05d5fcf7bdfc8 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 12 Jan 2024 17:29:52 +0100 Subject: [PATCH 096/106] Remove accidentally uncommented lines --- .../ubuntu_bootstrap_test.dart | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart b/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart index 04eb0fbf1..f086f9706 100644 --- a/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart +++ b/packages/ubuntu_bootstrap/integration_test/ubuntu_bootstrap_test.dart @@ -180,7 +180,7 @@ void main() { testWidgets('tpm', (tester) async { // TODO: Fix StoragePage overflow so that this isn't needed - await tester.binding.setSurfaceSize(const Size(1000, 1000)); + await tester.binding.setSurfaceSize(const Size(1200, 1000)); await tester.runApp(() => app.main([ '--source-catalog=examples/sources/tpm.yaml', '--dry-run-config=examples/dry-run-configs/tpm.yaml', @@ -209,31 +209,31 @@ void main() { await tester.tapNext(); await tester.pumpAndSettle(); - //await tester.testStoragePage( - // type: StorageType.erase, - // guidedCapability: GuidedCapability.CORE_BOOT_ENCRYPTED, - //); - //await tester.tapNext(); - //await tester.pumpAndSettle(); + await tester.testStoragePage( + type: StorageType.erase, + guidedCapability: GuidedCapability.CORE_BOOT_ENCRYPTED, + ); + await tester.tapNext(); + await tester.pumpAndSettle(); - //await tester.testRecoveryKeyPage(); - //await tester.tapNext(); - //await tester.pumpAndSettle(); + await tester.testRecoveryKeyPage(); + await tester.tapNext(); + await tester.pumpAndSettle(); - //await tester.testConfirmPage(); - //await tester.tapConfirm(); - //await tester.pumpAndSettle(); + await tester.testConfirmPage(); + await tester.tapConfirm(); + await tester.pumpAndSettle(); - //await tester.testInstallPage(); - //await tester.pumpAndSettle(); + await tester.testInstallPage(); + await tester.pumpAndSettle(); - //final windowClosed = YaruTestWindow.waitForClosed(); - //await tester.tapContinueTesting(); - //await expectLater(windowClosed, completes); + final windowClosed = YaruTestWindow.waitForClosed(); + await tester.tapContinueTesting(); + await expectLater(windowClosed, completes); - //await verifySubiquityConfig( - // capability: GuidedCapability.CORE_BOOT_ENCRYPTED, - //); + await verifySubiquityConfig( + capability: GuidedCapability.CORE_BOOT_ENCRYPTED, + ); }); testWidgets('manual partitioning', (tester) async { From 785dc9aa07e205339375e769c3f3401c3a04963c Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Mon, 15 Jan 2024 15:21:45 +0100 Subject: [PATCH 097/106] docs: update config schema and repo structure in README --- README.md | 80 +++++++++++++++++++++---------------------------------- 1 file changed, 31 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 29c905da7..108304e7b 100644 --- a/README.md +++ b/README.md @@ -2,67 +2,49 @@ [![CI](https://github.com/canonical/ubuntu-desktop-provision/actions/workflows/ci.yml/badge.svg)](https://github.com/canonical/ubuntu-desktop-provision/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/canonical/ubuntu-desktop-provision/branch/main/graph/badge.svg?token=JcedDc47dU)](https://codecov.io/gh/canonical/ubuntu-desktop-provision) -[![weblate](https://hosted.weblate.org/widget/ubuntu-desktop-installer/svg-badge.svg)](https://hosted.weblate.org/engage/ubuntu-desktop-installer/) [![screenshots](https://img.shields.io/badge/screenshots-gray?logo=ubuntu)](https://github.com/canonical/ubuntu-desktop-provision-screenshots) -```mermaid -classDiagram - ubuntu_bootstrap <|-- ubuntu_desktop_installer - ubuntu_bootstrap <|-- ubuntu_flavor_installer - ubuntu_init <|-- ubuntu_core_desktop_init - ubuntu_init <|-- ubuntu_welcome - ubuntu_provision <|-- ubuntu_bootstrap - ubuntu_provision <|-- ubuntu_init - ubuntu_provision: - shared pages - ubuntu_provision: - service interfaces - ubuntu_bootstrap: - subiquity-based services - ubuntu_bootstrap: - configurable subset of pages - ubuntu_init: - xdg/dbus/gsettings-based services - ubuntu_init: - configurable subset of pages - ubuntu_desktop_installer: - (main.dart) - ubuntu_desktop_installer: - snapcraft.yaml - ubuntu_flavor_installer: - (main.dart) - ubuntu_flavor_installer: - snapcraft.yaml - ubuntu_core_desktop_init: - (main.dart) - ubuntu_core_desktop_init: - snapcraft.yaml - ubuntu_welcome: - (main.dart) - ubuntu_welcome: - snapcraft.yaml -``` - -## Used by - -- [Ubuntu Desktop Installer](https://github.com/canonical/ubuntu-desktop-installer) -- [Ubuntu Flavor Installer](https://github.com/canonical/ubuntu-flavor-installer) -- [Ubuntu Core Desktop Init](https://github.com/canonical/ubuntu-core-desktop-init) -- [Ubuntu Welcome](https://github.com/canonical/ubuntu-welcome) - ## Configuration -Supported formats: -- [YAML](https://yaml.org/) (`.yaml`, `.yml`) - - ```yaml - bootstrap: - pages: - - locale - - keyboard - - source - - storage - - init: - pages: - - timezone - - identity - ``` +The Flutter UI can be configured using a [YAML](https://yaml.org/) file. + ```yaml + theme: + accent-color: "#ff0000" + elevated-button-color: "#00ff00" + elevated-button-text-color: "#0000ff" + window-title: "Custom Window Title" + + pages: + locale: + title: "Custom Title for Locale Page" + image: "/path/to/image.png" + visible: true + ... + ``` Lookup order: - `/etc/ubuntu-provision.{yaml,yml}` (admin) - `/usr/local/share/ubuntu-provision.{yaml,yml}` (oem) - `/usr/share/ubuntu-provision.{yaml,yml}` (distro) +## Repository Structure + +### Frontend + +The UI is written in [Flutter](https://flutter.dev/) and consists of multiple Dart/Flutter packages contained in `packages/`. The most important ones are: +* `ubuntu_bootstrap` - Flutter UI that drives `subiquity` in the 'device bootstrap' stage. This is the core of the `ubuntu-desktop-bootstrap` snap built from the `bootstrap-snap` branch and replaces the `ubuntu-desktop-installer`. +* `ubuntu_init` - Flutter UI that drives `provd` in the 'first boot initialization' stage. This is the core of the `ubuntu-desktop-init` snap built from the `init-snap` branch and serves as a replacement for `gnome-initial-setup`. +* `ubuntu_provision` - Flutter package that contains shared code and pages used by both `ubuntu_bootstrap` and `ubuntu_init`. +* `ubuntu_wizard` - Flutter package that provides the common wizard-style UI framework. +* `subiquity_client` and `provd_client` - Dart packages that provide a client library for the different backends. + +### Backend + +TODO + ## Translations -This project is being translated using [Weblate](https://hosted.weblate.org/engage/ubuntu-desktop-installer/), a web tool designed to ease translating for both developers and translators. +TODO ## Contributing From fb467896f17ae824c0d15201805afa281f7e0253 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 16 Jan 2024 08:55:07 +0200 Subject: [PATCH 098/106] refacor: tests use mocked dbus objects --- provd/internal/consts/consts.go | 10 + provd/internal/services/manager.go | 4 +- provd/internal/services/user/export_test.go | 165 ------------ provd/internal/services/user/user.go | 49 ++-- provd/internal/services/user/user_test.go | 271 ++++++++++++++++++-- 5 files changed, 282 insertions(+), 217 deletions(-) delete mode 100644 provd/internal/services/user/export_test.go diff --git a/provd/internal/consts/consts.go b/provd/internal/consts/consts.go index 44570f5b0..67e404f5f 100644 --- a/provd/internal/consts/consts.go +++ b/provd/internal/consts/consts.go @@ -18,3 +18,13 @@ const ( // DefaultSocketPath is the default socket path. DefaultSocketPath = "/run/provd.sock" ) + +// D-Bus constants. +const ( + // dbusAccountsPrefix is the prefix for the Accounts D-Bus interface. + DbusAccountsPrefix = "org.freedesktop.Accounts" + // dbusUserPrefix is the prefix for the User D-Bus interface. + DbusUserPrefix = "org.freedesktop.Accounts.User" + // dbusHostnamePrefix is the prefix for the Hostname D-Bus interface. + DbusHostnamePrefix = "org.freedesktop.hostname1" +) diff --git a/provd/internal/services/manager.go b/provd/internal/services/manager.go index 973dea95b..08bc272ed 100644 --- a/provd/internal/services/manager.go +++ b/provd/internal/services/manager.go @@ -29,7 +29,7 @@ type dbusConnectionAdapter struct { } func (bus dbusConnectionAdapter) Object(iface string, path dbus.ObjectPath) user.DbusObject { - return bus.Object(iface, path) + return bus.Conn.Object(iface, path) } // NewManager returns a new manager after creating all necessary items for our business logic. @@ -46,7 +46,7 @@ func NewManager(ctx context.Context) (m *Manager, err error) { helloService := hello.Service{} - userService := user.New(dbusConnectionAdapter{bus}) + userService := user.New(dbusConnectionAdapter{bus}, "foo", "bar") return &Manager{ helloService: helloService, diff --git a/provd/internal/services/user/export_test.go b/provd/internal/services/user/export_test.go deleted file mode 100644 index 71a64adb4..000000000 --- a/provd/internal/services/user/export_test.go +++ /dev/null @@ -1,165 +0,0 @@ -package user - -import ( - "errors" - - "github.com/godbus/dbus/v5" -) - -// WithAccounts overrides the accounts caller for the service. -func WithAccounts(accounts DbusObject) Option { - return func(o *options) { - o.accounts = accounts - } -} - -// WithHostname overrides the hostname caller for the service. -func WithHostname(hostname DbusObject) Option { - return func(o *options) { - o.hostname = hostname - } -} - -// WithUserFactory overrides the userObjectFactory for the service. -func WithUserFactory(userFactory ObjectFactory) Option { - return func(o *options) { - o.userFactory = userFactory - } -} - -// AccountsObjectMock is a mock AccountsObject. -type AccountsObjectMock struct { - WantError bool - UserObjectPath dbus.ObjectPath - Properties map[string]interface{} -} - -// HostnameObjectMock is a mock HostnameObject. -type HostnameObjectMock struct { - WantError bool - Properties map[string]interface{} -} - -// UserObjectMock is a mock UserObject. -type UserObjectMock struct { - WantError bool - Properties map[string]interface{} -} - -// Call is a mock implementation of the dbus Call method for the AccountsObjectMock. -func (a *AccountsObjectMock) Call(method string, flags dbus.Flags, args ...interface{}) *dbus.Call { - var err error - - if a.WantError { - switch method { - case "org.freedesktop.Accounts.FindUserByName": - dbusError := dbus.Error{ - Name: "org.freedesktop.Accounts.Error.Failed", - Body: []interface{}{"specific error message"}, - } - err = dbusError - default: - err = errors.New("Accounts object error") - } - } - - var callResult interface{} - - switch method { - case "org.freedesktop.Accounts.CreateUser": - callResult = a.UserObjectPath - case "org.freedesktop.Accounts.FindUserByName": - callResult = a.UserObjectPath - default: - err = errors.New("Unsupported accounts method") - } - - return &dbus.Call{ - Err: err, - Body: []interface{}{callResult}, - } -} - -// Call is a mock implementation of the dbus Call method for the HostnameObjectMock. -func (h *HostnameObjectMock) Call(method string, flags dbus.Flags, args ...interface{}) *dbus.Call { - var err error - - if h.WantError { - err = errors.New("Hostname object error") - } - - var callResult interface{} - - switch method { - case "org.freedesktop.hostname1.SetStaticHostname": - callResult = []interface{}{} - default: - err = errors.New("Unsupported hostname method") - } - - return &dbus.Call{ - Err: err, - Body: []interface{}{callResult}, - } -} - -// Call is a mock implementation of the dbus Call method for the UserObjectMock. -func (u *UserObjectMock) Call(method string, flags dbus.Flags, args ...interface{}) *dbus.Call { - var err error - if u.WantError { - err = errors.New("User object error") - } - - callResult := dbus.ObjectPath("/org/freedesktop/Accounts/User1000") - - return &dbus.Call{ - Err: err, - Body: []interface{}{callResult}, - } -} - -// GetProperty is a mock implementation of the dbus GetProperty method for the UserObjectMock. -func (u *UserObjectMock) GetProperty(prop string) (dbus.Variant, error) { - if u.WantError { - return dbus.Variant{}, errors.New("GetProperty error") - } - value, exists := u.Properties[prop] - if !exists { - return dbus.Variant{}, errors.New("User property not found: " + prop) - } - return dbus.MakeVariant(value), nil -} - -// GetProperty is a mock implementation of the dbus GetProperty method for the AccountsObjectMock. -func (a *AccountsObjectMock) GetProperty(prop string) (dbus.Variant, error) { - if a.WantError { - return dbus.Variant{}, errors.New("GetProperty error") - } - value, exists := a.Properties[prop] - if !exists { - return dbus.Variant{}, errors.New("Accounts property not found") - } - return dbus.MakeVariant(value), nil -} - -// GetProperty is a mock implementation of the dbus GetProperty method for the HostnameObjectMock. -func (h *HostnameObjectMock) GetProperty(prop string) (dbus.Variant, error) { - if h.WantError { - return dbus.Variant{}, errors.New("GetProperty error") - } - value, exists := h.Properties[prop] - if !exists { - return dbus.Variant{}, errors.New("Hostname1 property not found") - } - return dbus.MakeVariant(value), nil -} - -// UserObjectFactoryMock is a mock UserObjectFactory. -type UserObjectFactoryMock struct { - UserObject *UserObjectMock -} - -// GetUserObject returns a mock UserObject. -func (f UserObjectFactoryMock) GetUserObject(userObjectPath dbus.ObjectPath) DbusObject { - return f.UserObject -} diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index 465392967..9cbfb09d9 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -9,11 +9,13 @@ import ( "encoding/base64" "errors" "fmt" + "log/slog" "math/big" "os" "regexp" "strings" + "github.com/canonical/ubuntu-desktop-provision/provd/internal/consts" pb "github.com/canonical/ubuntu-desktop-provision/provd/protos" "github.com/godbus/dbus/v5" "google.golang.org/grpc/codes" @@ -22,10 +24,6 @@ import ( ) const ( - // dbusAccountsPrefix is the prefix for the Accounts D-Bus interface. - dbusAccountsPrefix = "org.freedesktop.Accounts" - // dbusHostnamePrefix is the prefix for the Hostname D-Bus interface. - dbusHostnamePrefix = "org.freedesktop.hostname1" // usernameMaxLen is the maximum length of a username. usernameMaxLen = 32 // usernameRegex is the regex for a valid username. @@ -46,17 +44,21 @@ type DbusConnector interface { // Service is the implementation of the User module service. type Service struct { pb.UnimplementedUserServiceServer - conn DbusConnector - accounts DbusObject - hostname DbusObject + Conn DbusConnector + Accounts DbusObject + Hostname DbusObject } // New returns a new instance of the User service. -func New(conn DbusConnector) *Service { +func New(Conn DbusConnector, accountPath string, hostnamePath string) *Service { + acountsObject := Conn.Object(consts.DbusAccountsPrefix, dbus.ObjectPath(accountPath)) + hostnameObject := Conn.Object(consts.DbusHostnamePrefix, dbus.ObjectPath(hostnamePath)) + slog.Info("acountsObject", acountsObject) + slog.Info("hostnameObject", hostnameObject) return &Service{ - conn: conn, - accounts: conn.Object(dbusAccountsPrefix, "/org/freedesktop/Accounts"), - hostname: conn.Object(dbusHostnamePrefix, "/org/freedesktop/hostname1"), + Conn: Conn, + Accounts: acountsObject, + Hostname: hostnameObject, } } @@ -127,13 +129,13 @@ func (s *Service) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*e } password := user.GetPassword() // allow empty password? autologin := user.GetAutoLogin() - hostname := user.GetHostname() - if hostname == "" { - return nil, status.Errorf(codes.InvalidArgument, "received an empty hostname") + Hostname := user.GetHostname() + if Hostname == "" { + return nil, status.Errorf(codes.InvalidArgument, "received an empty Hostname") } // Create the user var userObjectPath dbus.ObjectPath - call := s.accounts.Call(dbusAccountsPrefix+".CreateUser", 0, username, realName, accountType) + call := s.Accounts.Call(consts.DbusAccountsPrefix+".CreateUser", 0, username, realName, accountType) err := call.Store(&userObjectPath) if err != nil { return nil, status.Errorf(codes.Internal, "failed to create user: %s", err) @@ -145,22 +147,23 @@ func (s *Service) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*e } // Set the password for the user - userObject := s.conn.Object(dbusAccountsPrefix, userObjectPath) - err = userObject.Call(dbusAccountsPrefix+".User.SetPassword", 0, hashed, "").Err + userObject := s.Conn.Object(consts.DbusAccountsPrefix, userObjectPath) + slog.Info("userObject", userObject) + err = userObject.Call(consts.DbusUserPrefix+".SetPassword", 0, hashed, "").Err if err != nil { return nil, status.Errorf(codes.Internal, "failed to set password: %s", err) } // Set autologin for the user - err = userObject.Call(dbusAccountsPrefix+".User.SetAutomaticLogin", 0, autologin).Err + err = userObject.Call(consts.DbusUserPrefix+".SetAutomaticLogin", 0, autologin).Err if err != nil { return nil, status.Errorf(codes.Internal, "failed to set autologin: %s", err) } - // Set the hostname - err = s.hostname.Call(dbusHostnamePrefix+".SetStaticHostname", 0, hostname, false).Err + // Set the Hostname + err = s.Hostname.Call(consts.DbusHostnamePrefix+".SetStaticHostname", 0, Hostname, false).Err if err != nil { - return nil, status.Errorf(codes.Internal, "failed to set hostname: %s", err) + return nil, status.Errorf(codes.Internal, "failed to set Hostname: %s", err) } return &emptypb.Empty{}, nil @@ -220,11 +223,11 @@ func (s *Service) ValidateUsername(ctx context.Context, req *pb.ValidateUsername } // Check if username is already in use - err = s.accounts.Call(dbusAccountsPrefix+".FindUserByName", 0, username).Err + err = s.Accounts.Call(consts.DbusAccountsPrefix+".FindUserByName", 0, username).Err if err != nil { var dbusError dbus.Error if errors.As(err, &dbusError) { - if dbusError.Name == dbusAccountsPrefix+".Error.Failed" { + if dbusError.Name == consts.DbusAccountsPrefix+".Error.Failed" { // User not found return &pb.ValidateUsernameResponse{UsernameValidation: pb.UsernameValidation_OK}, nil } diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go index f4885d785..6526c9414 100644 --- a/provd/internal/services/user/user_test.go +++ b/provd/internal/services/user/user_test.go @@ -3,15 +3,19 @@ package user_test import ( "context" "flag" + "fmt" + "log/slog" "net" "os" "path/filepath" "testing" + "github.com/canonical/ubuntu-desktop-provision/provd/internal/consts" "github.com/canonical/ubuntu-desktop-provision/provd/internal/services/user" "github.com/canonical/ubuntu-desktop-provision/provd/internal/testutils" pb "github.com/canonical/ubuntu-desktop-provision/provd/protos" "github.com/godbus/dbus/v5" + "github.com/godbus/dbus/v5/introspect" "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -20,7 +24,7 @@ import ( func TestEmptyCreateUserRequest(t *testing.T) { t.Parallel() - client := newUserClient(t, nil, nil, nil) + client := newUserClient(t, "", "") userResp, err := client.CreateUser(context.Background(), nil) require.Error(t, err, "CreateUser should return an error for nil request") @@ -30,7 +34,7 @@ func TestEmptyCreateUserRequest(t *testing.T) { func TestEmptyValidateUsernameRequest(t *testing.T) { t.Parallel() - client := newUserClient(t, nil, nil, nil) + client := newUserClient(t, "", "") userResp, err := client.ValidateUsername(context.Background(), nil) require.Error(t, err, "ValidateUsername should return an error for nil request") @@ -52,6 +56,9 @@ func TestCreateUser(t *testing.T) { hostnameError bool wantErr bool + + accountsPath string + hostnamePath string }{ "Successfully creates a user": { realName: "Ubuntu", @@ -60,6 +67,9 @@ func TestCreateUser(t *testing.T) { hostname: "ubuntu", isAdmin: true, autoLogin: true, + + accountsPath: "noerror", + hostnamePath: "noerror", }, "Error when realName is empty": { realName: "", @@ -68,6 +78,9 @@ func TestCreateUser(t *testing.T) { hostname: "ubuntu", autoLogin: true, wantErr: true, + + accountsPath: "noerror", + hostnamePath: "noerror", }, "Error when username is empty": { realName: "Ubuntu", @@ -76,6 +89,9 @@ func TestCreateUser(t *testing.T) { hostname: "ubuntu", autoLogin: true, wantErr: true, + + accountsPath: "noerror", + hostnamePath: "noerror", }, "Error when hostname is empty": { realName: "Ubuntu", @@ -84,6 +100,9 @@ func TestCreateUser(t *testing.T) { hostname: "", autoLogin: true, wantErr: true, + + accountsPath: "noerror", + hostnamePath: "noerror", }, "Error from Accounts service": { realName: "Ubuntu", @@ -93,6 +112,9 @@ func TestCreateUser(t *testing.T) { autoLogin: true, accountsError: true, wantErr: true, + + accountsPath: "noerror", + hostnamePath: "error", }, "Error from Hostname service": { realName: "Ubuntu", @@ -102,6 +124,9 @@ func TestCreateUser(t *testing.T) { autoLogin: true, hostnameError: true, wantErr: true, + + accountsPath: "error", + hostnamePath: "noerror", }, } @@ -111,20 +136,7 @@ func TestCreateUser(t *testing.T) { t.Parallel() t.Cleanup(testutils.StartLocalSystemBus()) - accountsMock := &user.AccountsObjectMock{ - UserObjectPath: dbus.ObjectPath("/org/freedesktop/Accounts/User1000"), - WantError: tc.accountsError, - } - - hostnameMock := &user.HostnameObjectMock{ - WantError: tc.hostnameError, - } - - userFactoryMock := user.UserObjectFactoryMock{ - &user.UserObjectMock{}, - } - - client := newUserClient(t, accountsMock, hostnameMock, userFactoryMock) + client := newUserClient(t, tc.accountsPath, tc.hostnamePath) userReq := &pb.CreateUserRequest{ User: &pb.User{ @@ -154,31 +166,39 @@ func TestValidateUsername(t *testing.T) { username string accountsError bool wantErr bool + + endpoint string }{ "Valid username": { username: "newuser", accountsError: true, wantErr: false, + endpoint: "error", }, "Existing username": { username: "existinguser", wantErr: false, + endpoint: "noerror", }, "Empty username": { username: "", wantErr: false, + endpoint: "noerror", }, "Reserved username": { username: "root", wantErr: false, + endpoint: "noerror", }, "Username too long": { username: "thisusernameiswaytoolong1234567890abcdefghijklmnopqrstuvwxyz", wantErr: false, + endpoint: "noerror", }, "Invalid characters in username": { username: "invalid@username", wantErr: false, + endpoint: "noerror", }, } @@ -188,14 +208,7 @@ func TestValidateUsername(t *testing.T) { t.Parallel() t.Cleanup(testutils.StartLocalSystemBus()) - accountsMock := &user.AccountsObjectMock{ - WantError: tc.accountsError, - } - - hostnameMock := &user.HostnameObjectMock{} - userFactoryMock := user.UserObjectFactoryMock{} - - client := newUserClient(t, accountsMock, hostnameMock, userFactoryMock) + client := newUserClient(t, tc.endpoint, "") validateReq := &pb.ValidateUsernameRequest{ Username: tc.username, @@ -215,8 +228,16 @@ func TestValidateUsername(t *testing.T) { } } +type dbusConnectionAdapter struct { + *dbus.Conn +} + +func (bus dbusConnectionAdapter) Object(iface string, path dbus.ObjectPath) user.DbusObject { + return bus.Conn.Object(iface, path) +} + // newUserClient creates a new user client for testing, with a temp unix socket and mock Dbus connection. -func newUserClient(t *testing.T, accountsMock user.DbusObject, hostnameMock user.DbusObject, userFactoryMock user.ObjectFactory) pb.UserServiceClient { +func newUserClient(t *testing.T, accountsPath string, hostnamePath string) pb.UserServiceClient { t.Helper() // socket path is limited in length. tmpDir, err := os.MkdirTemp("", "hello-socket-dir") @@ -229,8 +250,10 @@ func newUserClient(t *testing.T, accountsMock user.DbusObject, hostnameMock user bus := testutils.NewDbusConn(t) + dbusConn := dbusConnectionAdapter{bus} + // Create the service with the necessary mocks - service := user.New(bus, user.WithAccounts(accountsMock), user.WithHostname(hostnameMock), user.WithUserFactory(userFactoryMock)) + service := user.New(dbusConn, "/org/freedesktop/Accounts/"+accountsPath, "/org/freedesktop/hostname1/"+hostnamePath) grpcServer := grpc.NewServer() pb.RegisterUserServiceServer(grpcServer, service) @@ -251,8 +274,202 @@ func newUserClient(t *testing.T, accountsMock user.DbusObject, hostnameMock user return pb.NewUserServiceClient(conn) } +type accountsdbus struct { + endpoint string + wantError bool +} + +func (a accountsdbus) FindUserByName(name string) (string, *dbus.Error) { + if a.wantError { + return "", dbus.NewError("org.freedesktop.Accounts.Error.Failed", []interface{}{"error"}) + } else { + return "/org/freedesktop/Accounts/Usernoerror", nil + } +} + +func (a accountsdbus) CreateUser(username string, realname string, accountType int32) (string, *dbus.Error) { + if a.wantError { + return "", dbus.NewError("org.freedesktop.Accounts.Error.Failed", []interface{}{"error"}) + } else { + return "/org/freedesktop/Accounts/Usernoerror", nil + } +} + +type userdbus struct { + endpoint string + wantError bool +} + +func (u userdbus) SetPassword(password string, hint string) *dbus.Error { + if u.wantError { + return dbus.NewError("org.freedesktop.Accounts.Error.Failed", []interface{}{"error"}) + } else { + return nil + } +} + +func (u userdbus) SetAutomaticLogin(autoLogin bool) *dbus.Error { + if u.wantError { + return dbus.NewError("org.freedesktop.Accounts.Error.Failed", []interface{}{"error"}) + } else { + return nil + } +} + +type hostnamedbus struct { + endpoint string + wantError bool +} + +func (h hostnamedbus) SetStaticHostname(hostname string, someBool bool) *dbus.Error { + if h.wantError { + return dbus.NewError("org.freedesktop.hostname1.Error.Failed", []interface{}{"error"}) + } else { + return nil + } +} + +//dbusError.Name == dbusAccountsPrefix+".Error.Failed" + func TestMain(m *testing.M) { testutils.InstallUpdateFlag() + slog.Info("TestMain") flag.Parse() - os.Exit(m.Run()) + // export domains + defer testutils.StartLocalSystemBus()() + + conn, err := dbus.SystemBusPrivate() + if err != nil { + slog.Error("Setup: can't get a private system bus: %v", err) + } + defer func() { + if err = conn.Close(); err != nil { + slog.Error("Teardown: can't close system dbus connection: %v", err) + } + }() + if err = conn.Auth(nil); err != nil { + slog.Error("Setup: can't auth on private system bus: %v", err) + } + if err = conn.Hello(); err != nil { + slog.Error("Setup: can't send hello message on private system bus: %v", err) + } + + intro := fmt.Sprintf(` + + + + + + + + + + + + + ̀%s`, consts.DbusAccountsPrefix, introspect.IntrospectDataString) + + for _, s := range []accountsdbus{ + { + endpoint: "error", + wantError: true, + }, + { + endpoint: "noerror", + wantError: false, + }, + } { + if err := conn.Export(s, dbus.ObjectPath("/org/freedesktop/Accounts"+"/"+s.endpoint), consts.DbusAccountsPrefix); err != nil { + slog.Error("Setup: could not export %s %v", s.endpoint, err) + } + if err := conn.Export(introspect.Introspectable(intro), dbus.ObjectPath("/org/freedesktop/Accounts"+"/"+s.endpoint), + "org.freedesktop.DBus.Introspectable"); err != nil { + slog.Error("Setup: could not export introspectable for %s: %v", s.endpoint, err) + } + } + reply, err := conn.RequestName(consts.DbusAccountsPrefix, dbus.NameFlagDoNotQueue) + if err != nil { + slog.Error("Setup: Failed to acquire account name on local system bus: %v", err) + } + if reply != dbus.RequestNameReplyPrimaryOwner { + slog.Error("Setup: Failed to acquire account name on local system bus: name is already taken") + } + + // user dbus + + userIntro := fmt.Sprintf(` + + + + + + + + + + ̀%s`, consts.DbusUserPrefix, introspect.IntrospectDataString) + + for _, s := range []userdbus{ + { + endpoint: "error", + wantError: true, + }, + { + endpoint: "noerror", + wantError: false, + }, + } { + if err := conn.Export(s, dbus.ObjectPath("/org/freedesktop/Accounts/User"+s.endpoint), consts.DbusUserPrefix); err != nil { + slog.Error("Setup: could not export %s %v", s.endpoint, err) + } + if err := conn.Export(introspect.Introspectable(userIntro), dbus.ObjectPath("/org/freedesktop/Accounts/User"+s.endpoint), + "org.freedesktop.DBus.Introspectable"); err != nil { + slog.Error("Setup: could not export introspectable for %s: %v", s.endpoint, err) + } + } + reply, err = conn.RequestName(consts.DbusUserPrefix, dbus.NameFlagDoNotQueue) + if err != nil { + slog.Error("Setup: Failed to acquire user name on local system bus: %v", err) + } + if reply != dbus.RequestNameReplyPrimaryOwner { + slog.Error("Setup: Failed to acquire user name on local system bus: name is already taken") + } + + // hostname dbus + hostnameIntro := fmt.Sprintf(` + + + + + + + ̀%s`, consts.DbusHostnamePrefix, introspect.IntrospectDataString) + + for _, s := range []hostnamedbus{ + { + endpoint: "error", + wantError: true, + }, + { + endpoint: "noerror", + wantError: false, + }, + } { + if err := conn.Export(s, dbus.ObjectPath("/org/freedesktop/hostname1/"+s.endpoint), consts.DbusHostnamePrefix); err != nil { + slog.Error("Setup: could not export %s %v", s.endpoint, err) + } + if err := conn.Export(introspect.Introspectable(hostnameIntro), dbus.ObjectPath("/org/freedesktop/hostname1/"+s.endpoint), + "org.freedesktop.DBus.Introspectable"); err != nil { + slog.Error("Setup: could not export introspectable for %s: %v", s.endpoint, err) + } + } + reply, err = conn.RequestName(consts.DbusHostnamePrefix, dbus.NameFlagDoNotQueue) + if err != nil { + slog.Error("Setup: Failed to acquire user name on local system bus: %v", err) + } + if reply != dbus.RequestNameReplyPrimaryOwner { + slog.Error("Setup: Failed to acquire user name on local system bus: name is already taken") + } + + m.Run() } From 99d1e56bf59c5ff48fec4d8eaad767af4bfc54ac Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Tue, 16 Jan 2024 11:17:44 +0100 Subject: [PATCH 099/106] chore: update subiquity submodule --- packages/subiquity_client/subiquity | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/subiquity_client/subiquity b/packages/subiquity_client/subiquity index 01fd8d22a..753ce9f70 160000 --- a/packages/subiquity_client/subiquity +++ b/packages/subiquity_client/subiquity @@ -1 +1 @@ -Subproject commit 01fd8d22a95afff7ede308437856e43832210350 +Subproject commit 753ce9f7012a526df6deda80ac6a91315b332078 From 6994955e6477177006d4564bf78b2dffc5e9a25d Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Tue, 16 Jan 2024 12:37:32 +0100 Subject: [PATCH 100/106] feat: track subiquity's main branch --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index dca60281a..2e5a337f9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "subiquity"] path = packages/subiquity_client/subiquity url = https://github.com/canonical/subiquity.git - branch = ubuntu/mantic + branch = main From 9b2b035f6cbcaf07b1451ae83481070b6f586f69 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 16 Jan 2024 14:34:51 +0200 Subject: [PATCH 101/106] refactor: only mock dbus connection --- provd/internal/services/manager.go | 2 +- provd/internal/services/user/user.go | 9 ++---- provd/internal/services/user/user_test.go | 36 ++++++++++++++++++----- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/provd/internal/services/manager.go b/provd/internal/services/manager.go index 08bc272ed..7dcf78995 100644 --- a/provd/internal/services/manager.go +++ b/provd/internal/services/manager.go @@ -46,7 +46,7 @@ func NewManager(ctx context.Context) (m *Manager, err error) { helloService := hello.Service{} - userService := user.New(dbusConnectionAdapter{bus}, "foo", "bar") + userService := user.New(dbusConnectionAdapter{bus}) return &Manager{ helloService: helloService, diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index 9cbfb09d9..16dc877c6 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -50,11 +50,9 @@ type Service struct { } // New returns a new instance of the User service. -func New(Conn DbusConnector, accountPath string, hostnamePath string) *Service { - acountsObject := Conn.Object(consts.DbusAccountsPrefix, dbus.ObjectPath(accountPath)) - hostnameObject := Conn.Object(consts.DbusHostnamePrefix, dbus.ObjectPath(hostnamePath)) - slog.Info("acountsObject", acountsObject) - slog.Info("hostnameObject", hostnameObject) +func New(Conn DbusConnector) *Service { + acountsObject := Conn.Object(consts.DbusAccountsPrefix, "/org/freedesktop/Accounts") + hostnameObject := Conn.Object(consts.DbusHostnamePrefix, "/org/freedesktop/Accounts") return &Service{ Conn: Conn, Accounts: acountsObject, @@ -140,7 +138,6 @@ func (s *Service) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*e if err != nil { return nil, status.Errorf(codes.Internal, "failed to create user: %s", err) } - hashed, err := hashPassword(password, nil) if err != nil { return nil, status.Errorf(codes.Internal, "failed to generate hashed password: %s", err) diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go index 6526c9414..300ef2c31 100644 --- a/provd/internal/services/user/user_test.go +++ b/provd/internal/services/user/user_test.go @@ -228,12 +228,29 @@ func TestValidateUsername(t *testing.T) { } } -type dbusConnectionAdapter struct { +type dbusConnectionMock struct { *dbus.Conn + accountsPath dbus.ObjectPath + hostnamePath dbus.ObjectPath } -func (bus dbusConnectionAdapter) Object(iface string, path dbus.ObjectPath) user.DbusObject { - return bus.Conn.Object(iface, path) +func (d dbusConnectionMock) Object(iface string, path dbus.ObjectPath) user.DbusObject { + switch iface { + case consts.DbusAccountsPrefix: + // If the path is the default path for Accounts, return the mocked Accounts object for the test + if path == "/org/freedesktop/Accounts" { + return d.Conn.Object(iface, d.accountsPath) + } else { + // Otherwise this is being called on a mocked object, so don't override the path + return d.Conn.Object(iface, path) + } + case consts.DbusHostnamePrefix: + // If the path is the default path for Hostname, return the mocked Hostname object for the test + return d.Conn.Object(iface, d.hostnamePath) + default: + // Unknown interface, return nil + return nil + } } // newUserClient creates a new user client for testing, with a temp unix socket and mock Dbus connection. @@ -250,10 +267,17 @@ func newUserClient(t *testing.T, accountsPath string, hostnamePath string) pb.Us bus := testutils.NewDbusConn(t) - dbusConn := dbusConnectionAdapter{bus} + // Concatenate provided paths with base paths + fullAccountsPath := dbus.ObjectPath("/org/freedesktop/Accounts/" + accountsPath) + fullHostnamePath := dbus.ObjectPath("/org/freedesktop/hostname1/" + hostnamePath) + dbusConn := dbusConnectionMock{ + Conn: bus, + accountsPath: fullAccountsPath, + hostnamePath: fullHostnamePath, + } // Create the service with the necessary mocks - service := user.New(dbusConn, "/org/freedesktop/Accounts/"+accountsPath, "/org/freedesktop/hostname1/"+hostnamePath) + service := user.New(dbusConn) grpcServer := grpc.NewServer() pb.RegisterUserServiceServer(grpcServer, service) @@ -329,8 +353,6 @@ func (h hostnamedbus) SetStaticHostname(hostname string, someBool bool) *dbus.Er } } -//dbusError.Name == dbusAccountsPrefix+".Error.Failed" - func TestMain(m *testing.M) { testutils.InstallUpdateFlag() slog.Info("TestMain") From 50e4b06c70f92a34497b0c2df74b824ff121f59c Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 16 Jan 2024 14:37:18 +0200 Subject: [PATCH 102/106] style: run the linter --- provd/internal/services/user/user_test.go | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go index 300ef2c31..895c2a043 100644 --- a/provd/internal/services/user/user_test.go +++ b/provd/internal/services/user/user_test.go @@ -306,17 +306,15 @@ type accountsdbus struct { func (a accountsdbus) FindUserByName(name string) (string, *dbus.Error) { if a.wantError { return "", dbus.NewError("org.freedesktop.Accounts.Error.Failed", []interface{}{"error"}) - } else { - return "/org/freedesktop/Accounts/Usernoerror", nil } + return "/org/freedesktop/Accounts/Usernoerror", nil } func (a accountsdbus) CreateUser(username string, realname string, accountType int32) (string, *dbus.Error) { if a.wantError { return "", dbus.NewError("org.freedesktop.Accounts.Error.Failed", []interface{}{"error"}) - } else { - return "/org/freedesktop/Accounts/Usernoerror", nil } + return "/org/freedesktop/Accounts/Usernoerror", nil } type userdbus struct { @@ -327,17 +325,15 @@ type userdbus struct { func (u userdbus) SetPassword(password string, hint string) *dbus.Error { if u.wantError { return dbus.NewError("org.freedesktop.Accounts.Error.Failed", []interface{}{"error"}) - } else { - return nil } + return nil } func (u userdbus) SetAutomaticLogin(autoLogin bool) *dbus.Error { if u.wantError { return dbus.NewError("org.freedesktop.Accounts.Error.Failed", []interface{}{"error"}) - } else { - return nil } + return nil } type hostnamedbus struct { @@ -348,9 +344,8 @@ type hostnamedbus struct { func (h hostnamedbus) SetStaticHostname(hostname string, someBool bool) *dbus.Error { if h.wantError { return dbus.NewError("org.freedesktop.hostname1.Error.Failed", []interface{}{"error"}) - } else { - return nil } + return nil } func TestMain(m *testing.M) { From a87174f916d2a1ae4faa8e597c07ec8bdc1ee6a8 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 16 Jan 2024 15:01:42 +0200 Subject: [PATCH 103/106] feat: embed reserved-names file --- provd/internal/services/user/embed.go | 8 ++++++++ provd/internal/services/user/user.go | 8 +++----- 2 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 provd/internal/services/user/embed.go diff --git a/provd/internal/services/user/embed.go b/provd/internal/services/user/embed.go new file mode 100644 index 000000000..31521c93c --- /dev/null +++ b/provd/internal/services/user/embed.go @@ -0,0 +1,8 @@ +package user + +import ( + "embed" +) + +//go:embed reserved-usernames +var reservedUsernamesFS embed.FS diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index 16dc877c6..c11b9c9c1 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -11,7 +11,6 @@ import ( "fmt" "log/slog" "math/big" - "os" "regexp" "strings" @@ -191,7 +190,7 @@ func (s *Service) ValidateUsername(ctx context.Context, req *pb.ValidateUsername // Check if username is in reserved list // Read line by line to avoid loading the whole file into memory - file, err := os.Open("reserved-usernames") + file, err := reservedUsernamesFS.Open("reserved-usernames") if err != nil { return nil, status.Errorf(codes.Internal, "error opening reserved usernames file: %v", err) } @@ -200,9 +199,8 @@ func (s *Service) ValidateUsername(ctx context.Context, req *pb.ValidateUsername isReserved := false scanner := bufio.NewScanner(file) for scanner.Scan() { - line := scanner.Text() - // Ignore comment lines - if strings.HasPrefix(line, "#") { // trim the lines / spaces (stinrgs) + line := strings.TrimSpace(scanner.Text()) // Trim the line and ignore comment lines + if strings.HasPrefix(line, "#") { continue } if line == username { From 2856e96775644e93d451dff5179c9f47e8e9bc71 Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 16 Jan 2024 15:19:04 +0200 Subject: [PATCH 104/106] chore: cleanup code --- .github/workflows/ci.yml | 2 +- provd/internal/services/user/user.go | 2 - provd/internal/services/user/user_test.go | 79 +++++++++++------------ 3 files changed, 40 insertions(+), 43 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2bdc6def3..5e97aba24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -155,7 +155,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Go code sanity check - uses: matthew-hagemann/desktop-engineering/gh-actions/go/code-sanity@main + uses: canonical/desktop-engineering/gh-actions/go/code-sanity@main with: golangci-lint-configfile: ".golangci.yaml" tools-directory: "./provd/tools" diff --git a/provd/internal/services/user/user.go b/provd/internal/services/user/user.go index c11b9c9c1..8cad04672 100644 --- a/provd/internal/services/user/user.go +++ b/provd/internal/services/user/user.go @@ -9,7 +9,6 @@ import ( "encoding/base64" "errors" "fmt" - "log/slog" "math/big" "regexp" "strings" @@ -144,7 +143,6 @@ func (s *Service) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*e // Set the password for the user userObject := s.Conn.Object(consts.DbusAccountsPrefix, userObjectPath) - slog.Info("userObject", userObject) err = userObject.Call(consts.DbusUserPrefix+".SetPassword", 0, hashed, "").Err if err != nil { return nil, status.Errorf(codes.Internal, "failed to set password: %s", err) diff --git a/provd/internal/services/user/user_test.go b/provd/internal/services/user/user_test.go index 895c2a043..ffdfc35e6 100644 --- a/provd/internal/services/user/user_test.go +++ b/provd/internal/services/user/user_test.go @@ -57,8 +57,8 @@ func TestCreateUser(t *testing.T) { wantErr bool - accountsPath string - hostnamePath string + accountsEndpoint string + hostnameEndpoint string }{ "Successfully creates a user": { realName: "Ubuntu", @@ -68,8 +68,8 @@ func TestCreateUser(t *testing.T) { isAdmin: true, autoLogin: true, - accountsPath: "noerror", - hostnamePath: "noerror", + accountsEndpoint: "noerror", + hostnameEndpoint: "noerror", }, "Error when realName is empty": { realName: "", @@ -79,8 +79,8 @@ func TestCreateUser(t *testing.T) { autoLogin: true, wantErr: true, - accountsPath: "noerror", - hostnamePath: "noerror", + accountsEndpoint: "noerror", + hostnameEndpoint: "noerror", }, "Error when username is empty": { realName: "Ubuntu", @@ -90,8 +90,8 @@ func TestCreateUser(t *testing.T) { autoLogin: true, wantErr: true, - accountsPath: "noerror", - hostnamePath: "noerror", + accountsEndpoint: "noerror", + hostnameEndpoint: "noerror", }, "Error when hostname is empty": { realName: "Ubuntu", @@ -101,8 +101,8 @@ func TestCreateUser(t *testing.T) { autoLogin: true, wantErr: true, - accountsPath: "noerror", - hostnamePath: "noerror", + accountsEndpoint: "noerror", + hostnameEndpoint: "noerror", }, "Error from Accounts service": { realName: "Ubuntu", @@ -113,8 +113,8 @@ func TestCreateUser(t *testing.T) { accountsError: true, wantErr: true, - accountsPath: "noerror", - hostnamePath: "error", + accountsEndpoint: "noerror", + hostnameEndpoint: "error", }, "Error from Hostname service": { realName: "Ubuntu", @@ -125,8 +125,8 @@ func TestCreateUser(t *testing.T) { hostnameError: true, wantErr: true, - accountsPath: "error", - hostnamePath: "noerror", + accountsEndpoint: "error", + hostnameEndpoint: "noerror", }, } @@ -136,7 +136,7 @@ func TestCreateUser(t *testing.T) { t.Parallel() t.Cleanup(testutils.StartLocalSystemBus()) - client := newUserClient(t, tc.accountsPath, tc.hostnamePath) + client := newUserClient(t, tc.accountsEndpoint, tc.hostnameEndpoint) userReq := &pb.CreateUserRequest{ User: &pb.User{ @@ -167,38 +167,38 @@ func TestValidateUsername(t *testing.T) { accountsError bool wantErr bool - endpoint string + accountsEndpoint string }{ "Valid username": { - username: "newuser", - accountsError: true, - wantErr: false, - endpoint: "error", + username: "newuser", + accountsError: true, + wantErr: false, + accountsEndpoint: "error", }, "Existing username": { - username: "existinguser", - wantErr: false, - endpoint: "noerror", + username: "existinguser", + wantErr: false, + accountsEndpoint: "noerror", }, "Empty username": { - username: "", - wantErr: false, - endpoint: "noerror", + username: "", + wantErr: false, + accountsEndpoint: "noerror", }, "Reserved username": { - username: "root", - wantErr: false, - endpoint: "noerror", + username: "root", + wantErr: false, + accountsEndpoint: "noerror", }, "Username too long": { - username: "thisusernameiswaytoolong1234567890abcdefghijklmnopqrstuvwxyz", - wantErr: false, - endpoint: "noerror", + username: "thisusernameiswaytoolong1234567890abcdefghijklmnopqrstuvwxyz", + wantErr: false, + accountsEndpoint: "noerror", }, "Invalid characters in username": { - username: "invalid@username", - wantErr: false, - endpoint: "noerror", + username: "invalid@username", + wantErr: false, + accountsEndpoint: "noerror", }, } @@ -208,7 +208,7 @@ func TestValidateUsername(t *testing.T) { t.Parallel() t.Cleanup(testutils.StartLocalSystemBus()) - client := newUserClient(t, tc.endpoint, "") + client := newUserClient(t, tc.accountsEndpoint, "") validateReq := &pb.ValidateUsernameRequest{ Username: tc.username, @@ -254,7 +254,7 @@ func (d dbusConnectionMock) Object(iface string, path dbus.ObjectPath) user.Dbus } // newUserClient creates a new user client for testing, with a temp unix socket and mock Dbus connection. -func newUserClient(t *testing.T, accountsPath string, hostnamePath string) pb.UserServiceClient { +func newUserClient(t *testing.T, accountsEndpoint string, hostnameEndpoint string) pb.UserServiceClient { t.Helper() // socket path is limited in length. tmpDir, err := os.MkdirTemp("", "hello-socket-dir") @@ -268,8 +268,8 @@ func newUserClient(t *testing.T, accountsPath string, hostnamePath string) pb.Us bus := testutils.NewDbusConn(t) // Concatenate provided paths with base paths - fullAccountsPath := dbus.ObjectPath("/org/freedesktop/Accounts/" + accountsPath) - fullHostnamePath := dbus.ObjectPath("/org/freedesktop/hostname1/" + hostnamePath) + fullAccountsPath := dbus.ObjectPath("/org/freedesktop/Accounts/" + accountsEndpoint) + fullHostnamePath := dbus.ObjectPath("/org/freedesktop/hostname1/" + hostnameEndpoint) dbusConn := dbusConnectionMock{ Conn: bus, @@ -350,7 +350,6 @@ func (h hostnamedbus) SetStaticHostname(hostname string, someBool bool) *dbus.Er func TestMain(m *testing.M) { testutils.InstallUpdateFlag() - slog.Info("TestMain") flag.Parse() // export domains defer testutils.StartLocalSystemBus()() From 6718f645fadc32d7ea0828e72f240023052581ba Mon Sep 17 00:00:00 2001 From: matt-hagemann Date: Tue, 16 Jan 2024 16:47:41 +0200 Subject: [PATCH 105/106] style: protofile indentation --- provd/protos/user.proto | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/provd/protos/user.proto b/provd/protos/user.proto index cce43da53..d0fd965ad 100644 --- a/provd/protos/user.proto +++ b/provd/protos/user.proto @@ -33,10 +33,10 @@ message ValidateUsernameResponse { } enum UsernameValidation { - OK = 0; - ALREADY_IN_USE = 1; - SYSTEM_RESERVED = 2; - INVALID_CHARS = 3; - TOO_LONG = 4; - EMPTY = 5; -} \ No newline at end of file + OK = 0; + ALREADY_IN_USE = 1; + SYSTEM_RESERVED = 2; + INVALID_CHARS = 3; + TOO_LONG = 4; + EMPTY = 5; +} From 207cc03258fc71bc66d5a36e01308e115824eea6 Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Thu, 18 Jan 2024 12:20:24 +0100 Subject: [PATCH 106/106] docs: include launchpad bugtracker in README --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 108304e7b..ea1cb09a5 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,11 @@ [![codecov](https://codecov.io/gh/canonical/ubuntu-desktop-provision/branch/main/graph/badge.svg?token=JcedDc47dU)](https://codecov.io/gh/canonical/ubuntu-desktop-provision) [![screenshots](https://img.shields.io/badge/screenshots-gray?logo=ubuntu)](https://github.com/canonical/ubuntu-desktop-provision-screenshots) +## Bugs + +Please report any bugs related to Ubuntu Desktop Provision on [Launchpad](https://bugs.launchpad.net/ubuntu-desktop-provision). +We use the GitHub issue tracker only for issues related to the development of Ubuntu Desktop Provision itself. + ## Configuration The Flutter UI can be configured using a [YAML](https://yaml.org/) file.