From dc1d4e65a8db786a84bc5e681261ce527bb6b1a6 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 10 Dec 2021 13:47:33 -0500 Subject: [PATCH 1/2] [path_provider] Fix handling of null application ID The lookup of application ID was handling `null` (Dart null), but not `nullptr` (Dart representation of a C null pointer), so was throwing an exception in applications without an application ID. This includes the `shared_preferences_linux` example application, so this fixes tree breakage. --- .../path_provider_linux/CHANGELOG.md | 5 ++ .../lib/src/get_application_id_real.dart | 68 ++++++++++++++----- .../path_provider_linux/pubspec.yaml | 3 +- .../test/get_application_id_test.dart | 61 +++++++++++++++++ 4 files changed, 120 insertions(+), 17 deletions(-) create mode 100644 packages/path_provider/path_provider_linux/test/get_application_id_test.dart diff --git a/packages/path_provider/path_provider_linux/CHANGELOG.md b/packages/path_provider/path_provider_linux/CHANGELOG.md index f227570bdd1d..cfea5702dbe1 100644 --- a/packages/path_provider/path_provider_linux/CHANGELOG.md +++ b/packages/path_provider/path_provider_linux/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.4 + +* Fixes `getApplicationSupportPath` handling of applications where the + application ID is not set. + ## 2.1.3 * Change getApplicationSupportPath from using executable name to application ID (if provided). diff --git a/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart b/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart index f6d25bbe783e..e287a873e2f2 100644 --- a/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart +++ b/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart @@ -4,6 +4,7 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; +import 'package:meta/meta.dart'; // GApplication* g_application_get_default(); typedef _GApplicationGetDefaultC = IntPtr Function(); @@ -13,30 +14,65 @@ typedef _GApplicationGetDefaultDart = int Function(); typedef _GApplicationGetApplicationIdC = Pointer Function(IntPtr); typedef _GApplicationGetApplicationIdDart = Pointer Function(int); +/// Interface for interacting with libgio. +@visibleForTesting +class GioUtils { + /// Creates a default instance that uses the real libgio. + GioUtils() { + try { + _gio = DynamicLibrary.open('libgio-2.0.so'); + } on ArgumentError { + _gio = null; + } + } + + DynamicLibrary? _gio; + + /// True if libgio was opened successfully. + bool get libraryIsPresent => _gio != null; + + /// Wraps `g_application_get_default`. + int gApplicationGetDefault() { + if (_gio == null) { + return 0; + } + final _GApplicationGetDefaultDart getDefault = _gio! + .lookupFunction<_GApplicationGetDefaultC, _GApplicationGetDefaultDart>( + 'g_application_get_default'); + return getDefault(); + } + + /// Wraps g_application_get_application_id. + Pointer gApplicationGetApplicationId(int app) { + if (_gio == null) { + return nullptr; + } + final _GApplicationGetApplicationIdDart gApplicationGetApplicationId = _gio! + .lookupFunction<_GApplicationGetApplicationIdC, + _GApplicationGetApplicationIdDart>( + 'g_application_get_application_id'); + return gApplicationGetApplicationId(app); + } +} + +/// Allows overriding the defaul GioUtils instance with a fake for testing. +@visibleForTesting +GioUtils? gioUtilsOverride; + /// Gets the application ID for this app. String? getApplicationId() { - DynamicLibrary gio; - try { - gio = DynamicLibrary.open('libgio-2.0.so'); - } on ArgumentError { + final GioUtils gio = gioUtilsOverride ?? GioUtils(); + if (!gio.libraryIsPresent) { return null; } - final _GApplicationGetDefaultDart gApplicationGetDefault = - gio.lookupFunction<_GApplicationGetDefaultC, _GApplicationGetDefaultDart>( - 'g_application_get_default'); - final int app = gApplicationGetDefault(); + + final int app = gio.gApplicationGetDefault(); if (app == 0) { return null; } - - final _GApplicationGetApplicationIdDart gApplicationGetApplicationId = - gio.lookupFunction<_GApplicationGetApplicationIdC, - _GApplicationGetApplicationIdDart>( - 'g_application_get_application_id'); - final Pointer appId = gApplicationGetApplicationId(app); - if (appId == null) { + final Pointer appId = gio.gApplicationGetApplicationId(app); + if (appId == null || appId == nullptr) { return null; } - return appId.toDartString(); } diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index 25d1af9936c8..ec1d8d8b3148 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_linux description: Linux implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.1.3 +version: 2.1.4 environment: sdk: ">=2.12.0 <3.0.0" @@ -19,6 +19,7 @@ dependencies: ffi: ^1.1.2 flutter: sdk: flutter + meta: ^1.3.0 path: ^1.8.0 path_provider_platform_interface: ^2.0.0 xdg_directories: ^0.2.0 diff --git a/packages/path_provider/path_provider_linux/test/get_application_id_test.dart b/packages/path_provider/path_provider_linux/test/get_application_id_test.dart new file mode 100644 index 000000000000..3e272ee59e14 --- /dev/null +++ b/packages/path_provider/path_provider_linux/test/get_application_id_test.dart @@ -0,0 +1,61 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path_provider_linux/src/get_application_id_real.dart'; + +class FakeGioUtils implements GioUtils { + int? application = 0; + Pointer? applicationId = nullptr; + + @override + bool libraryIsPresent = false; + + @override + int gApplicationGetDefault() => application!; + + @override + Pointer gApplicationGetApplicationId(int app) => applicationId!; +} + +void main() { + final FakeGioUtils fakeGio = FakeGioUtils(); + + setUp(() { + gioUtilsOverride = fakeGio; + }); + + tearDown(() { + gioUtilsOverride = null; + }); + + test('returns null if libgio is not available', () { + expect(getApplicationId(), null); + }); + + test('returns null if g_paplication_get_default returns 0', () { + fakeGio.libraryIsPresent = true; + fakeGio.application = 0; + expect(getApplicationId(), null); + }); + + test('returns null if g_application_get_application_id returns nullptr', () { + fakeGio.libraryIsPresent = true; + fakeGio.application = 1; + fakeGio.applicationId = nullptr; + expect(getApplicationId(), null); + }); + + test('returns value if g_application_get_application_id returns a value', () { + fakeGio.libraryIsPresent = true; + fakeGio.application = 1; + const String id = 'foo'; + final Pointer idPtr = id.toNativeUtf8(); + fakeGio.applicationId = idPtr; + expect(getApplicationId(), id); + calloc.free(idPtr); + }); +} From a632135325d8118f6232f465534e46519b6445c6 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 10 Dec 2021 14:19:58 -0500 Subject: [PATCH 2/2] Review feedback --- .../lib/src/get_application_id_real.dart | 2 +- .../test/get_application_id_test.dart | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart b/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart index e287a873e2f2..d2e60fb6ff82 100644 --- a/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart +++ b/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart @@ -55,7 +55,7 @@ class GioUtils { } } -/// Allows overriding the defaul GioUtils instance with a fake for testing. +/// Allows overriding the default GioUtils instance with a fake for testing. @visibleForTesting GioUtils? gioUtilsOverride; diff --git a/packages/path_provider/path_provider_linux/test/get_application_id_test.dart b/packages/path_provider/path_provider_linux/test/get_application_id_test.dart index 3e272ee59e14..d9eb5163b5fe 100644 --- a/packages/path_provider/path_provider_linux/test/get_application_id_test.dart +++ b/packages/path_provider/path_provider_linux/test/get_application_id_test.dart @@ -7,9 +7,9 @@ import 'package:ffi/ffi.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:path_provider_linux/src/get_application_id_real.dart'; -class FakeGioUtils implements GioUtils { - int? application = 0; - Pointer? applicationId = nullptr; +class _FakeGioUtils implements GioUtils { + int? application; + Pointer? applicationId; @override bool libraryIsPresent = false; @@ -22,9 +22,10 @@ class FakeGioUtils implements GioUtils { } void main() { - final FakeGioUtils fakeGio = FakeGioUtils(); + late _FakeGioUtils fakeGio; setUp(() { + fakeGio = _FakeGioUtils(); gioUtilsOverride = fakeGio; });