Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ linter:
- no_duplicate_case_values
- non_constant_identifier_names
- overridden_fields
- package_api_docs
- package_names
- package_prefixed_library_names
- prefer_adjacent_string_concatenation
Expand Down
12 changes: 9 additions & 3 deletions lib/config/windows_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,27 @@ part 'windows_config.g.dart';
checked: true,
)
class WindowsConfig {
/// Specifies weather to generate icons for web
/// Specifies whether to generate icons for Windows
final bool generate;

/// Image path for web
/// Image path for Windows
@JsonKey(name: 'image_path')
final String? imagePath;

/// Size of the icon to generate
/// DEPRECATED: kept only for backward-compat. Ignored by the generator.
@Deprecated(
'Ignored. Windows .ico is generated with multiple sizes by default.',
)
@JsonKey(name: 'icon_size')
final int? iconSize;

/// Creates a instance of [WindowsConfig]
const WindowsConfig({
this.generate = false,
this.imagePath,
@Deprecated(
'Ignored. Windows .ico is generated with multiple sizes by default.',
)
this.iconSize,
});

Expand Down
32 changes: 20 additions & 12 deletions lib/windows/windows_icon_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ class WindowsIconGenerator extends IconGenerator {
WindowsIconGenerator(IconGeneratorContext context)
: super(context, 'Windows');

// Minimal, sensible defaults for Windows ICOs.
static const List<int> _icoSizes = [16, 24, 32, 48, 256];

@override
Future<void> createIcons() async {
final imgFilePath = path.join(
Expand Down Expand Up @@ -51,14 +54,13 @@ class WindowsIconGenerator extends IconGenerator {
return false;
}

// if icon_size is given it should be between 48<=icon_size<=256
// because .ico only supports this size
if (windowsConfig.iconSize != null &&
(windowsConfig.iconSize! < 48 || windowsConfig.iconSize! > 256)) {
context.logger.error(
'Invalid windows.icon_size=${windowsConfig.iconSize}. Icon size should be between 48<=icon_size<=256',
// DEPRECATED: read only to warn, then ignored.
// ignore: deprecated_member_use_from_same_package
if (windowsConfig.iconSize != null) {
context.logger.verbose(
'DEPRECATED: `windows.icon_size` is ignored. The generator now '
'produces a multi-size .ico (16, 24, 32, 48, 256).',
);
return false;
}
final entitesToCheck = [
path.join(context.prefixPath, constants.windowsDirPath),
Expand All @@ -80,13 +82,19 @@ class WindowsIconGenerator extends IconGenerator {
}

Future<void> _generateIcon(Image image) async {
final favIcon = utils.createResizedImage(
context.windowsConfig!.iconSize ?? constants.windowsDefaultIconSize,
image,
);
// Build a multi-frame ICO: one frame per target size.
Image? multi;
for (final sz in _icoSizes) {
final resized = utils.createResizedImage(sz, image);
if (multi == null) {
multi = resized;
} else {
multi.addFrame(resized);
}
}
final favIconFile = await utils.createFileIfNotExist(
path.join(context.prefixPath, constants.windowsIconFilePath),
);
await favIconFile.writeAsBytes(encodeIco(favIcon));
await favIconFile.writeAsBytes(encodeIco(multi!));
}
}
3 changes: 2 additions & 1 deletion test/abs/icon_generator_test.mocks.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Mocks generated by Mockito 5.4.4 from annotations
// Mocks generated by Mockito 5.4.6 from annotations
// in flutter_launcher_icons/test/abs/icon_generator_test.dart.
// Do not manually edit this file.

Expand All @@ -18,6 +18,7 @@ import 'package:mockito/src/dummies.dart' as _i4;
// 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: must_be_immutable
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_parenthesis
// ignore_for_file: camel_case_types
Expand Down
1 change: 1 addition & 0 deletions test/config_test.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// ignore_for_file: deprecated_member_use_from_same_package
import 'package:flutter_launcher_icons/config/config.dart';
import 'package:flutter_launcher_icons/custom_exceptions.dart';
import 'package:path/path.dart' as path;
Expand Down
3 changes: 2 additions & 1 deletion test/macos/macos_icon_generator_test.mocks.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Mocks generated by Mockito 5.4.4 from annotations
// Mocks generated by Mockito 5.4.6 from annotations
// in flutter_launcher_icons/test/macos/macos_icon_generator_test.dart.
// Do not manually edit this file.

Expand All @@ -18,6 +18,7 @@ import 'package:mockito/src/dummies.dart' as _i4;
// 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: must_be_immutable
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_parenthesis
// ignore_for_file: camel_case_types
Expand Down
15 changes: 12 additions & 3 deletions test/main_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,28 @@ void main() {
test(
'iOS image list used to generate Contents.json for icon directory is correct size (with dark icon)',
() {
expect(ios.createImageList('blah', 'dark-blah', null).length, 16 * 2 + 1); // 16 normal, 16 dark icons + 1 marketing icon
expect(
ios.createImageList('blah', 'dark-blah', null).length,
16 * 2 + 1,
); // 16 normal, 16 dark icons + 1 marketing icon
});

test(
'iOS image list used to generate Contents.json for icon directory is correct size (with tinted icon)',
() {
expect(ios.createImageList('blah', null, 'tinted-blah').length, 16 * 2 + 1); // 16 normal, 16 tinted icons + 1 marketing icon
expect(
ios.createImageList('blah', null, 'tinted-blah').length,
16 * 2 + 1,
); // 16 normal, 16 tinted icons + 1 marketing icon
});

test(
'iOS image list used to generate Contents.json for icon directory is correct size (with dark and tinted icon)',
() {
expect(ios.createImageList('blah', 'dark-blah', 'tinted-blah').length, 16 * 3 + 1); // 16 normal, 16 dark, 16 tinted icons + 1 marketing icon
expect(
ios.createImageList('blah', 'dark-blah', 'tinted-blah').length,
16 * 3 + 1,
); // 16 normal, 16 dark, 16 tinted icons + 1 marketing icon
});

group('config file from args', () {
Expand Down
48 changes: 45 additions & 3 deletions test/package_version_test.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import 'dart:io';

import 'package:flutter_launcher_icons/pubspec_parser.dart';
import 'package:flutter_launcher_icons/src/version.dart';
import 'package:path/path.dart' as p;
import 'package:test/test.dart';

void main() {
/// this helps avoid an issue where the pubspec version has been increased but
/// This helps avoid an issue where the pubspec version has been increased but
/// build runner has not been run to up the version which is displayed
/// when flutter_launcher_icons is run
/// when flutter_launcher_icons is run.
///
/// Note: We locate `pubspec.yaml` robustly (CWD can vary in CI/Windows).
test('package version is correct', () {
final yamlMap = PubspecParser.fromPathToMap('pubspec.yaml');
final pubspecPath = _locatePubspec();
final yamlMap = PubspecParser.fromPathToMap(pubspecPath);
final yamlVersion = yamlMap['version'] as String;
expect(
yamlVersion,
Expand All @@ -17,3 +23,39 @@ void main() {
);
});
}

/// Locate the repo's pubspec regardless of current working dir.
/// Tries from CWD, then from this test file's directory, walking up.
String _locatePubspec() {
String? searchFrom(Directory start) {
var dir = start;
for (var i = 0; i < 25; i++) {
final candidate = p.join(dir.path, 'pubspec.yaml');
if (File(candidate).existsSync()) {
return candidate;
}
final parent = dir.parent;
if (parent.path == dir.path) {
break;
}
dir = parent;
}
return null;
}

final fromCwd = searchFrom(Directory.current);
if (fromCwd != null) {
return fromCwd;
}

final scriptDir = File.fromUri(Platform.script).parent;
final fromScript = searchFrom(scriptDir);
if (fromScript != null) {
return fromScript;
}

throw StateError(
'Could not find pubspec.yaml. Tried starting from: '
'${Directory.current.path} and ${scriptDir.path}',
);
}
28 changes: 21 additions & 7 deletions test/windows/windows_icon_generator_test.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// ignore_for_file: deprecated_member_use_from_same_package
import 'dart:io';

import 'package:flutter_launcher_icons/abs/icon_generator.dart';
Expand Down Expand Up @@ -85,15 +86,28 @@ void main() {
});

test(
'should return false when windows.icon_size is not between 48 and 256',
() {
'should warn (deprecated) but not fail when windows.icon_size is set',
() async {
// Ensure required filesystem entities exist so any failure is NOT due to missing files.
await d.dir('fli_test', [
d.dir('windows'),
d.file('app_icon.png', testImageFile.readAsBytesSync()),
]).create();
await expectLater(
d.dir('fli_test', [
d.dir('windows'),
d.file('app_icon.png', anything),
]).validate(),
completes,
);

// Set any value; it’s ignored but should trigger a deprecation log.
when(mockWindowsConfig.iconSize).thenReturn(40);
expect(generator.validateRequirements(), isFalse);
verify(mockWindowsConfig.iconSize).called(equals(3));

when(mockWindowsConfig.iconSize).thenReturn(257);
expect(generator.validateRequirements(), isFalse);
verify(mockWindowsConfig.iconSize).called(equals(4));
expect(generator.validateRequirements(), isTrue);

// We logged a deprecation notice (non-fatal).
verify(mockLogger.verbose(argThat(contains('DEPRECATED')))).called(1);
});

test('should return false when windows dir does not exist', () async {
Expand Down
3 changes: 2 additions & 1 deletion test/windows/windows_icon_generator_test.mocks.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Mocks generated by Mockito 5.4.4 from annotations
// Mocks generated by Mockito 5.4.6 from annotations
// in flutter_launcher_icons/test/windows/windows_icon_generator_test.dart.
// Do not manually edit this file.

Expand All @@ -18,6 +18,7 @@ import 'package:mockito/src/dummies.dart' as _i4;
// 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: must_be_immutable
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_parenthesis
// ignore_for_file: camel_case_types
Expand Down