[path_provider] Fix handling of null application ID (#4606)
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.
diff --git a/packages/path_provider/path_provider_linux/CHANGELOG.md b/packages/path_provider/path_provider_linux/CHANGELOG.md
index f227570..cfea570 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 f6d25bb..d2e60fb 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 _GApplicationGetApplicationIdC = Pointer<Utf8> Function(IntPtr);
typedef _GApplicationGetApplicationIdDart = Pointer<Utf8> 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<Utf8> 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 default 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<Utf8> appId = gApplicationGetApplicationId(app);
- if (appId == null) {
+ final Pointer<Utf8> 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 25d1af9..ec1d8d8 100644
--- a/packages/path_provider/path_provider_linux/pubspec.yaml
+++ b/packages/path_provider/path_provider_linux/pubspec.yaml
@@ -2,7 +2,7 @@
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 @@
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 0000000..d9eb516
--- /dev/null
+++ b/packages/path_provider/path_provider_linux/test/get_application_id_test.dart
@@ -0,0 +1,62 @@
+// 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;
+ Pointer<Utf8>? applicationId;
+
+ @override
+ bool libraryIsPresent = false;
+
+ @override
+ int gApplicationGetDefault() => application!;
+
+ @override
+ Pointer<Utf8> gApplicationGetApplicationId(int app) => applicationId!;
+}
+
+void main() {
+ late _FakeGioUtils fakeGio;
+
+ setUp(() {
+ fakeGio = _FakeGioUtils();
+ 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<Utf8> idPtr = id.toNativeUtf8();
+ fakeGio.applicationId = idPtr;
+ expect(getApplicationId(), id);
+ calloc.free(idPtr);
+ });
+}