From 9f021ef1099a1e520eaa4ede8b529e98fbf6a898 Mon Sep 17 00:00:00 2001 From: Seungsoo Lee Date: Tue, 23 Jun 2026 18:49:13 +0900 Subject: [PATCH 1/6] [tizen_package_manager] Fix empty iconPath returned as empty string instead of null --- .../tizen_package_manager/tizen/src/tizen_package_manager.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/tizen_package_manager/tizen/src/tizen_package_manager.cc b/packages/tizen_package_manager/tizen/src/tizen_package_manager.cc index b976141dc..2af7d9d5c 100644 --- a/packages/tizen_package_manager/tizen/src/tizen_package_manager.cc +++ b/packages/tizen_package_manager/tizen/src/tizen_package_manager.cc @@ -112,9 +112,11 @@ std::optional TizenPackageManager::GetPackageData( char *icon_path = nullptr; ret = package_info_get_icon(handle, &icon_path); - if (icon_path) { + if (icon_path && icon_path[0] != '\0') { result.icon_path = icon_path; free(icon_path); + } else if (icon_path) { + free(icon_path); } char *version = nullptr; From cbbfd9a485eb59f0e66ad8c70a9798e9635e0ea3 Mon Sep 17 00:00:00 2001 From: Seungsoo Lee Date: Tue, 23 Jun 2026 18:51:09 +0900 Subject: [PATCH 2/6] [tizen_package_manager] Add regression integration tests - PackageInfo fields: installedStorageType is a valid StorageType value - PackageInfo fields: iconPath is null or a non-empty string - PackageSizeInfo fields: all size fields are non-negative - getPackagesInfo list items: each PackageInfo item has valid field types - getPackagesInfo list items: getPackagesInfo contains current package - getPackageInfo error paths: throws ArgumentError for empty packageId - getPackageInfo error paths: throws PlatformException for invalid packageId - getPackageSizeInfo error paths: throws ArgumentError for empty packageId - install error paths: throws ArgumentError for empty packagePath - uninstall error paths: throws ArgumentError for empty packageId - event streams: onInstallProgressChanged can be listened to without error - event streams: onUninstallProgressChanged can be listened to without error - event streams: onUpdateProgressChanged can be listened to without error --- packages/tizen_package_manager/CHANGELOG.md | 4 + .../tizen_package_manager_test.dart | 139 ++++++++++++++++++ 2 files changed, 143 insertions(+) diff --git a/packages/tizen_package_manager/CHANGELOG.md b/packages/tizen_package_manager/CHANGELOG.md index 54bcbbbc9..df5a3ab26 100644 --- a/packages/tizen_package_manager/CHANGELOG.md +++ b/packages/tizen_package_manager/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Add 13 integration test cases. + ## 0.4.2 * Remove Ecore API. diff --git a/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart b/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart index 5b70f76ae..ae5c49030 100644 --- a/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart +++ b/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; + +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:tizen_package_manager/tizen_package_manager.dart'; @@ -37,4 +40,140 @@ void main() { final List infos = await PackageManager.getPackagesInfo(); expect(infos.isNotEmpty, true); }); + + group('PackageInfo fields', () { + testWidgets('installedStorageType is a valid StorageType value', ( + WidgetTester tester, + ) async { + final PackageInfo info = + await PackageManager.getPackageInfo(currentPackageId); + expect(StorageType.values, contains(info.installedStorageType)); + }); + + testWidgets('iconPath is null or a non-empty string', ( + WidgetTester tester, + ) async { + final PackageInfo info = + await PackageManager.getPackageInfo(currentPackageId); + if (info.iconPath != null) { + expect(info.iconPath, isNotEmpty); + } + }); + }); + + group('PackageSizeInfo fields', () { + testWidgets('all size fields are non-negative', ( + WidgetTester tester, + ) async { + final PackageSizeInfo info = + await PackageManager.getPackageSizeInfo(currentPackageId); + expect(info.dataSize, greaterThanOrEqualTo(0)); + expect(info.cacheSize, greaterThanOrEqualTo(0)); + expect(info.appSize, greaterThanOrEqualTo(0)); + expect(info.externalDataSize, greaterThanOrEqualTo(0)); + expect(info.externalCacheSize, greaterThanOrEqualTo(0)); + expect(info.externalAppSize, greaterThanOrEqualTo(0)); + }); + }); + + group('getPackagesInfo list items', () { + testWidgets('each PackageInfo item has valid field types', ( + WidgetTester tester, + ) async { + final List infos = await PackageManager.getPackagesInfo(); + expect(infos, isNotEmpty); + for (final PackageInfo info in infos) { + expect(info.packageId, isA()); + expect(info.packageId, isNotEmpty); + expect(info.label, isA()); + expect(info.version, isA()); + expect(info.version, isNotEmpty); + expect(PackageType.values, contains(info.packageType)); + expect(StorageType.values, contains(info.installedStorageType)); + expect(info.isSystem, isA()); + expect(info.isPreloaded, isA()); + expect(info.isRemovable, isA()); + if (info.iconPath != null) { + expect(info.iconPath, isNotEmpty); + } + } + }); + + testWidgets('getPackagesInfo contains current package', ( + WidgetTester tester, + ) async { + final List infos = await PackageManager.getPackagesInfo(); + final Iterable ids = infos.map((PackageInfo i) => i.packageId); + expect(ids, contains(currentPackageId)); + }); + }); + + group('getPackageInfo error paths', () { + test('throws ArgumentError for empty packageId', () async { + expect( + () => PackageManager.getPackageInfo(''), + throwsA(isA()), + ); + }); + + test('throws PlatformException for invalid packageId', () async { + expect( + () => PackageManager.getPackageInfo('invalid.package.id.that.does.not.exist'), + throwsA(isA()), + ); + }); + }); + + group('getPackageSizeInfo error paths', () { + test('throws ArgumentError for empty packageId', () async { + expect( + () => PackageManager.getPackageSizeInfo(''), + throwsA(isA()), + ); + }); + + // NOTE: The Tizen package_manager_get_package_size_info API does not + // return an error for a non-existent package ID; it silently returns + // zero-valued size info. This is a platform limitation, so no + // PlatformException test is included here. + }); + + group('install error paths', () { + test('throws ArgumentError for empty packagePath', () async { + expect( + () => PackageManager.install(''), + throwsA(isA()), + ); + }); + }); + + group('uninstall error paths', () { + test('throws ArgumentError for empty packageId', () async { + expect( + () => PackageManager.uninstall(''), + throwsA(isA()), + ); + }); + }); + + group('event streams', () { + test('onInstallProgressChanged can be listened to without error', () async { + final StreamSubscription subscription = + PackageManager.onInstallProgressChanged.listen(null); + await subscription.cancel(); + }); + + test('onUninstallProgressChanged can be listened to without error', + () async { + final StreamSubscription subscription = + PackageManager.onUninstallProgressChanged.listen(null); + await subscription.cancel(); + }); + + test('onUpdateProgressChanged can be listened to without error', () async { + final StreamSubscription subscription = + PackageManager.onUpdateProgressChanged.listen(null); + await subscription.cancel(); + }); + }); } From b0f3c3f89c54c5af657a6480129ae2fc8f719eb2 Mon Sep 17 00:00:00 2001 From: Seungsoo Lee Date: Wed, 1 Jul 2026 13:59:13 +0900 Subject: [PATCH 3/6] [tizen_package_manager] Fix formatting in integration test --- .../example/integration_test/tizen_package_manager_test.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart b/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart index ae5c49030..d9b5fc3f6 100644 --- a/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart +++ b/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart @@ -118,7 +118,8 @@ void main() { test('throws PlatformException for invalid packageId', () async { expect( - () => PackageManager.getPackageInfo('invalid.package.id.that.does.not.exist'), + () => PackageManager.getPackageInfo( + 'invalid.package.id.that.does.not.exist'), throwsA(isA()), ); }); From 5673b981edcb5cc6c85ec78ec29ba94efdc9944f Mon Sep 17 00:00:00 2001 From: Seungsoo Lee Date: Wed, 1 Jul 2026 20:18:01 +0900 Subject: [PATCH 4/6] [tizen_package_manager] Harden package icon path handling Only use the icon path from package_info_get_icon when the call succeeds (PACKAGE_MANAGER_ERROR_NONE) and the path is non-empty, and always free the returned buffer. The icon is optional, so a failure is not treated as fatal; this also simplifies the previous branchy free logic. Bump version to 0.4.3. --- packages/tizen_package_manager/CHANGELOG.md | 4 +++- packages/tizen_package_manager/pubspec.yaml | 2 +- .../tizen/src/tizen_package_manager.cc | 9 ++++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/tizen_package_manager/CHANGELOG.md b/packages/tizen_package_manager/CHANGELOG.md index df5a3ab26..6efd11295 100644 --- a/packages/tizen_package_manager/CHANGELOG.md +++ b/packages/tizen_package_manager/CHANGELOG.md @@ -1,5 +1,7 @@ -## NEXT +## 0.4.3 +* Return a null icon path (instead of an empty string) when a package has no + icon, and ignore `package_info_get_icon` failures since the icon is optional. * Add 13 integration test cases. ## 0.4.2 diff --git a/packages/tizen_package_manager/pubspec.yaml b/packages/tizen_package_manager/pubspec.yaml index 260d1a2a6..0d2596292 100644 --- a/packages/tizen_package_manager/pubspec.yaml +++ b/packages/tizen_package_manager/pubspec.yaml @@ -2,7 +2,7 @@ name: tizen_package_manager description: Tizen package manager APIs. Used to get information about packages installed on a Tizen device. homepage: https://github.com/flutter-tizen/plugins repository: https://github.com/flutter-tizen/plugins/tree/master/packages/tizen_package_manager -version: 0.4.2 +version: 0.4.3 environment: sdk: ">=3.1.0 <4.0.0" diff --git a/packages/tizen_package_manager/tizen/src/tizen_package_manager.cc b/packages/tizen_package_manager/tizen/src/tizen_package_manager.cc index 2af7d9d5c..f2be95b49 100644 --- a/packages/tizen_package_manager/tizen/src/tizen_package_manager.cc +++ b/packages/tizen_package_manager/tizen/src/tizen_package_manager.cc @@ -110,12 +110,15 @@ std::optional TizenPackageManager::GetPackageData( result.type = type; free(type); + // The icon path is optional: a package may have no icon, so a failure here is + // not treated as fatal. Only use the value when the call succeeds and the + // path is non-empty. char *icon_path = nullptr; ret = package_info_get_icon(handle, &icon_path); - if (icon_path && icon_path[0] != '\0') { + if (ret == PACKAGE_MANAGER_ERROR_NONE && icon_path && icon_path[0] != '\0') { result.icon_path = icon_path; - free(icon_path); - } else if (icon_path) { + } + if (icon_path) { free(icon_path); } From 2f217b039d7e32fd645b02574c789b8abdcb9173 Mon Sep 17 00:00:00 2001 From: Seungsoo Lee Date: Wed, 1 Jul 2026 20:18:01 +0900 Subject: [PATCH 5/6] [tizen_package_manager] Use idiomatic async matchers in integration tests Await the error-path expectations with expectLater and use the built-in throwsArgumentError matcher, so the asynchronous ArgumentError/PlatformException throws are actually asserted (the previous unawaited expect() form could pass without verifying the error). Convert the remaining plain test() cases to testWidgets for consistency with the rest of the suite. --- .../tizen_package_manager_test.dart | 60 +++++++++++-------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart b/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart index d9b5fc3f6..da4616ef3 100644 --- a/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart +++ b/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart @@ -109,27 +109,32 @@ void main() { }); group('getPackageInfo error paths', () { - test('throws ArgumentError for empty packageId', () async { - expect( - () => PackageManager.getPackageInfo(''), - throwsA(isA()), + testWidgets('throws ArgumentError for empty packageId', ( + WidgetTester tester, + ) async { + await expectLater( + PackageManager.getPackageInfo(''), + throwsArgumentError, ); }); - test('throws PlatformException for invalid packageId', () async { - expect( - () => PackageManager.getPackageInfo( - 'invalid.package.id.that.does.not.exist'), + testWidgets('throws PlatformException for invalid packageId', ( + WidgetTester tester, + ) async { + await expectLater( + PackageManager.getPackageInfo('invalid.package.id.that.does.not.exist'), throwsA(isA()), ); }); }); group('getPackageSizeInfo error paths', () { - test('throws ArgumentError for empty packageId', () async { - expect( - () => PackageManager.getPackageSizeInfo(''), - throwsA(isA()), + testWidgets('throws ArgumentError for empty packageId', ( + WidgetTester tester, + ) async { + await expectLater( + PackageManager.getPackageSizeInfo(''), + throwsArgumentError, ); }); @@ -140,38 +145,41 @@ void main() { }); group('install error paths', () { - test('throws ArgumentError for empty packagePath', () async { - expect( - () => PackageManager.install(''), - throwsA(isA()), - ); + testWidgets('throws ArgumentError for empty packagePath', ( + WidgetTester tester, + ) async { + await expectLater(PackageManager.install(''), throwsArgumentError); }); }); group('uninstall error paths', () { - test('throws ArgumentError for empty packageId', () async { - expect( - () => PackageManager.uninstall(''), - throwsA(isA()), - ); + testWidgets('throws ArgumentError for empty packageId', ( + WidgetTester tester, + ) async { + await expectLater(PackageManager.uninstall(''), throwsArgumentError); }); }); group('event streams', () { - test('onInstallProgressChanged can be listened to without error', () async { + testWidgets('onInstallProgressChanged can be listened to without error', ( + WidgetTester tester, + ) async { final StreamSubscription subscription = PackageManager.onInstallProgressChanged.listen(null); await subscription.cancel(); }); - test('onUninstallProgressChanged can be listened to without error', - () async { + testWidgets('onUninstallProgressChanged can be listened to without error', ( + WidgetTester tester, + ) async { final StreamSubscription subscription = PackageManager.onUninstallProgressChanged.listen(null); await subscription.cancel(); }); - test('onUpdateProgressChanged can be listened to without error', () async { + testWidgets('onUpdateProgressChanged can be listened to without error', ( + WidgetTester tester, + ) async { final StreamSubscription subscription = PackageManager.onUpdateProgressChanged.listen(null); await subscription.cancel(); From b640597cf444c17ff4445545929b9e1bcdf4b7ff Mon Sep 17 00:00:00 2001 From: Seungsoo Lee Date: Thu, 2 Jul 2026 09:54:25 +0900 Subject: [PATCH 6/6] [tizen_package_manager] Simplify redundant assertions in integration tests Use anyOf(isNull, isNotEmpty) to assert the optional icon path declaratively, and drop the isA/isA and enum-in-values assertions in the getPackagesInfo item test: PackageInfo.fromMap already casts every field (and maps enums), so those types are statically guaranteed and the assertions verified nothing. Only the value-level checks (non-empty id/version, icon path) remain. --- .../tizen_package_manager_test.dart | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart b/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart index da4616ef3..6fc94050f 100644 --- a/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart +++ b/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart @@ -55,9 +55,7 @@ void main() { ) async { final PackageInfo info = await PackageManager.getPackageInfo(currentPackageId); - if (info.iconPath != null) { - expect(info.iconPath, isNotEmpty); - } + expect(info.iconPath, anyOf(isNull, isNotEmpty)); }); }); @@ -77,25 +75,17 @@ void main() { }); group('getPackagesInfo list items', () { - testWidgets('each PackageInfo item has valid field types', ( + testWidgets('each PackageInfo item has valid field values', ( WidgetTester tester, ) async { final List infos = await PackageManager.getPackagesInfo(); expect(infos, isNotEmpty); for (final PackageInfo info in infos) { - expect(info.packageId, isA()); + // Field types are already guaranteed by PackageInfo.fromMap (it casts + // each field), so only the value-level expectations are asserted here. expect(info.packageId, isNotEmpty); - expect(info.label, isA()); - expect(info.version, isA()); expect(info.version, isNotEmpty); - expect(PackageType.values, contains(info.packageType)); - expect(StorageType.values, contains(info.installedStorageType)); - expect(info.isSystem, isA()); - expect(info.isPreloaded, isA()); - expect(info.isRemovable, isA()); - if (info.iconPath != null) { - expect(info.iconPath, isNotEmpty); - } + expect(info.iconPath, anyOf(isNull, isNotEmpty)); } });