mirror of
https://github.com/usetrmnl/byos_laravel.git
synced 2026-01-13 15:07:49 +00:00
315 lines
11 KiB
PHP
315 lines
11 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Enums\ImageFormat;
|
|
use App\Models\Device;
|
|
use App\Models\DeviceModel;
|
|
use App\Services\ImageGenerationService;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
it('get_image_settings returns device model settings when available', function (): void {
|
|
// Create a DeviceModel
|
|
$deviceModel = DeviceModel::factory()->create([
|
|
'width' => 1024,
|
|
'height' => 768,
|
|
'colors' => 256,
|
|
'bit_depth' => 8,
|
|
'scale_factor' => 1.5,
|
|
'rotation' => 90,
|
|
'mime_type' => 'image/png',
|
|
'offset_x' => 10,
|
|
'offset_y' => 20,
|
|
]);
|
|
|
|
// Create a device with the DeviceModel
|
|
$device = Device::factory()->create([
|
|
'device_model_id' => $deviceModel->id,
|
|
]);
|
|
|
|
// Use reflection to access private method
|
|
$reflection = new ReflectionClass(ImageGenerationService::class);
|
|
$method = $reflection->getMethod('getImageSettings');
|
|
$method->setAccessible(true);
|
|
|
|
$settings = $method->invoke(null, $device);
|
|
|
|
// Assert DeviceModel settings are used
|
|
expect($settings['width'])->toBe(1024);
|
|
expect($settings['height'])->toBe(768);
|
|
expect($settings['colors'])->toBe(256);
|
|
expect($settings['bit_depth'])->toBe(8);
|
|
expect($settings['scale_factor'])->toBe(1.5);
|
|
expect($settings['rotation'])->toBe(90);
|
|
expect($settings['mime_type'])->toBe('image/png');
|
|
expect($settings['offset_x'])->toBe(10);
|
|
expect($settings['offset_y'])->toBe(20);
|
|
expect($settings['use_model_settings'])->toBe(true);
|
|
})->skipOnCi();
|
|
|
|
it('get_image_settings falls back to device settings when no device model', function (): void {
|
|
// Create a device without DeviceModel
|
|
$device = Device::factory()->create([
|
|
'width' => 800,
|
|
'height' => 480,
|
|
'rotate' => 180,
|
|
'image_format' => ImageFormat::PNG_8BIT_GRAYSCALE->value,
|
|
]);
|
|
|
|
// Use reflection to access private method
|
|
$reflection = new ReflectionClass(ImageGenerationService::class);
|
|
$method = $reflection->getMethod('getImageSettings');
|
|
$method->setAccessible(true);
|
|
|
|
$settings = $method->invoke(null, $device);
|
|
|
|
// Assert device settings are used
|
|
expect($settings['width'])->toBe(800);
|
|
expect($settings['height'])->toBe(480);
|
|
expect($settings['rotation'])->toBe(180);
|
|
expect($settings['image_format'])->toBe(ImageFormat::PNG_8BIT_GRAYSCALE->value);
|
|
expect($settings['use_model_settings'])->toBe(false);
|
|
})->skipOnCi();
|
|
|
|
it('get_image_settings uses defaults for missing device properties', function (): void {
|
|
// Create a device without DeviceModel and missing properties
|
|
$device = Device::factory()->create([
|
|
'width' => null,
|
|
'height' => null,
|
|
'rotate' => null,
|
|
// image_format has a default value of 'auto', so we can't set it to null
|
|
]);
|
|
|
|
// Use reflection to access private method
|
|
$reflection = new ReflectionClass(ImageGenerationService::class);
|
|
$method = $reflection->getMethod('getImageSettings');
|
|
$method->setAccessible(true);
|
|
|
|
$settings = $method->invoke(null, $device);
|
|
|
|
// Assert default values are used
|
|
expect($settings['width'])->toBe(800);
|
|
expect($settings['height'])->toBe(480);
|
|
expect($settings['rotation'])->toBe(0);
|
|
expect($settings['colors'])->toBe(2);
|
|
expect($settings['bit_depth'])->toBe(1);
|
|
expect($settings['scale_factor'])->toBe(1.0);
|
|
expect($settings['mime_type'])->toBe('image/png');
|
|
expect($settings['offset_x'])->toBe(0);
|
|
expect($settings['offset_y'])->toBe(0);
|
|
// image_format will be null if the device doesn't have it set, which is the expected behavior
|
|
expect($settings['image_format'])->toBeNull();
|
|
})->skipOnCi();
|
|
|
|
it('determine_image_format_from_model returns correct formats', function (): void {
|
|
// Use reflection to access private method
|
|
$reflection = new ReflectionClass(ImageGenerationService::class);
|
|
$method = $reflection->getMethod('determineImageFormatFromModel');
|
|
$method->setAccessible(true);
|
|
|
|
// Test BMP format
|
|
$bmpModel = DeviceModel::factory()->create([
|
|
'mime_type' => 'image/bmp',
|
|
'bit_depth' => 1,
|
|
'colors' => 2,
|
|
]);
|
|
$format = $method->invoke(null, $bmpModel);
|
|
expect($format)->toBe(ImageFormat::BMP3_1BIT_SRGB->value);
|
|
|
|
// Test PNG 8-bit grayscale format
|
|
$pngGrayscaleModel = DeviceModel::factory()->create([
|
|
'mime_type' => 'image/png',
|
|
'bit_depth' => 8,
|
|
'colors' => 2,
|
|
]);
|
|
$format = $method->invoke(null, $pngGrayscaleModel);
|
|
expect($format)->toBe(ImageFormat::PNG_8BIT_GRAYSCALE->value);
|
|
|
|
// Test PNG 8-bit 256 color format
|
|
$png256Model = DeviceModel::factory()->create([
|
|
'mime_type' => 'image/png',
|
|
'bit_depth' => 8,
|
|
'colors' => 256,
|
|
]);
|
|
$format = $method->invoke(null, $png256Model);
|
|
expect($format)->toBe(ImageFormat::PNG_8BIT_256C->value);
|
|
|
|
// Test PNG 2-bit 4 color format
|
|
$png4ColorModel = DeviceModel::factory()->create([
|
|
'mime_type' => 'image/png',
|
|
'bit_depth' => 2,
|
|
'colors' => 4,
|
|
]);
|
|
$format = $method->invoke(null, $png4ColorModel);
|
|
expect($format)->toBe(ImageFormat::PNG_2BIT_4C->value);
|
|
|
|
// Test unknown format returns AUTO
|
|
$unknownModel = DeviceModel::factory()->create([
|
|
'mime_type' => 'image/jpeg',
|
|
'bit_depth' => 16,
|
|
'colors' => 65536,
|
|
]);
|
|
$format = $method->invoke(null, $unknownModel);
|
|
expect($format)->toBe(ImageFormat::AUTO->value);
|
|
})->skipOnCi();
|
|
|
|
it('cleanup_folder identifies active images correctly', function (): void {
|
|
// Create devices with images
|
|
$device1 = Device::factory()->create(['current_screen_image' => 'active-uuid-1']);
|
|
$device2 = Device::factory()->create(['current_screen_image' => 'active-uuid-2']);
|
|
$device3 = Device::factory()->create(['current_screen_image' => null]);
|
|
|
|
// Create a plugin with image
|
|
$plugin = App\Models\Plugin::factory()->create(['current_image' => 'plugin-uuid']);
|
|
|
|
// For unit testing, we could test the logic that determines active UUIDs
|
|
$activeDeviceImageUuids = Device::pluck('current_screen_image')->filter()->toArray();
|
|
$activePluginImageUuids = App\Models\Plugin::pluck('current_image')->filter()->toArray();
|
|
$activeImageUuids = array_merge($activeDeviceImageUuids, $activePluginImageUuids);
|
|
|
|
expect($activeImageUuids)->toContain('active-uuid-1');
|
|
expect($activeImageUuids)->toContain('active-uuid-2');
|
|
expect($activeImageUuids)->toContain('plugin-uuid');
|
|
expect($activeImageUuids)->not->toContain(null);
|
|
});
|
|
|
|
it('reset_if_not_cacheable detects device models', function (): void {
|
|
// Create a plugin
|
|
$plugin = App\Models\Plugin::factory()->create(['current_image' => 'test-uuid']);
|
|
|
|
// Create a device with DeviceModel
|
|
Device::factory()->create([
|
|
'device_model_id' => DeviceModel::factory()->create()->id,
|
|
]);
|
|
|
|
// Test that the method detects DeviceModels and resets cache
|
|
ImageGenerationService::resetIfNotCacheable($plugin);
|
|
|
|
$plugin->refresh();
|
|
expect($plugin->current_image)->toBeNull();
|
|
})->skipOnCi();
|
|
|
|
it('reset_if_not_cacheable detects custom dimensions', function (): void {
|
|
// Create a plugin
|
|
$plugin = App\Models\Plugin::factory()->create(['current_image' => 'test-uuid']);
|
|
|
|
// Create a device with custom dimensions
|
|
Device::factory()->create([
|
|
'width' => 1024, // Different from default 800
|
|
'height' => 768, // Different from default 480
|
|
]);
|
|
|
|
// Test that the method detects custom dimensions and resets cache
|
|
ImageGenerationService::resetIfNotCacheable($plugin);
|
|
|
|
$plugin->refresh();
|
|
expect($plugin->current_image)->toBeNull();
|
|
})->skipOnCi();
|
|
|
|
it('reset_if_not_cacheable preserves cache for standard devices', function (): void {
|
|
// Create a plugin
|
|
$plugin = App\Models\Plugin::factory()->create(['current_image' => 'test-uuid']);
|
|
|
|
// Create devices with standard dimensions
|
|
Device::factory()->count(3)->create([
|
|
'width' => 800,
|
|
'height' => 480,
|
|
'rotate' => 0,
|
|
]);
|
|
|
|
// Test that the method preserves cache for standard devices
|
|
ImageGenerationService::resetIfNotCacheable($plugin);
|
|
|
|
$plugin->refresh();
|
|
expect($plugin->current_image)->toBe('test-uuid');
|
|
})->skipOnCi();
|
|
|
|
it('reset_if_not_cacheable preserves cache for og_png and og_plus device models', function (): void {
|
|
// Create a plugin
|
|
$plugin = App\Models\Plugin::factory()->create(['current_image' => 'test-uuid']);
|
|
|
|
// Create og_png device model
|
|
$ogPngModel = DeviceModel::factory()->create([
|
|
'name' => 'test_og_png',
|
|
'width' => 800,
|
|
'height' => 480,
|
|
'rotation' => 0,
|
|
]);
|
|
|
|
// Create og_plus device model
|
|
$ogPlusModel = DeviceModel::factory()->create([
|
|
'name' => 'test_og_plus',
|
|
'width' => 800,
|
|
'height' => 480,
|
|
'rotation' => 0,
|
|
]);
|
|
|
|
// Create devices with og_png and og_plus device models
|
|
Device::factory()->create(['device_model_id' => $ogPngModel->id]);
|
|
Device::factory()->create(['device_model_id' => $ogPlusModel->id]);
|
|
|
|
// Test that the method preserves cache for standard device models
|
|
ImageGenerationService::resetIfNotCacheable($plugin);
|
|
|
|
$plugin->refresh();
|
|
expect($plugin->current_image)->toBe('test-uuid');
|
|
})->skipOnCi();
|
|
|
|
it('reset_if_not_cacheable resets cache for non-standard device models', function (): void {
|
|
// Create a plugin
|
|
$plugin = App\Models\Plugin::factory()->create(['current_image' => 'test-uuid']);
|
|
|
|
// Create a non-standard device model (e.g., kindle)
|
|
$kindleModel = DeviceModel::factory()->create([
|
|
'name' => 'test_amazon_kindle_2024',
|
|
'width' => 1400,
|
|
'height' => 840,
|
|
'rotation' => 90,
|
|
]);
|
|
|
|
// Create a device with the non-standard device model
|
|
Device::factory()->create(['device_model_id' => $kindleModel->id]);
|
|
|
|
// Test that the method resets cache for non-standard device models
|
|
ImageGenerationService::resetIfNotCacheable($plugin);
|
|
|
|
$plugin->refresh();
|
|
expect($plugin->current_image)->toBeNull();
|
|
})->skipOnCi();
|
|
|
|
it('reset_if_not_cacheable handles null plugin', function (): void {
|
|
// Test that the method handles null plugin gracefully
|
|
expect(fn () => ImageGenerationService::resetIfNotCacheable(null))->not->toThrow(Exception::class);
|
|
})->skipOnCi();
|
|
|
|
it('image_format enum includes new 2bit 4c format', function (): void {
|
|
// Test that the new format is properly defined in the enum
|
|
expect(ImageFormat::PNG_2BIT_4C->value)->toBe('png_2bit_4c');
|
|
expect(ImageFormat::PNG_2BIT_4C->label())->toBe('PNG 2-bit Grayscale 4c');
|
|
});
|
|
|
|
it('device model relationship works correctly', function (): void {
|
|
// Create a DeviceModel
|
|
$deviceModel = DeviceModel::factory()->create();
|
|
|
|
// Create a device with the DeviceModel
|
|
$device = Device::factory()->create([
|
|
'device_model_id' => $deviceModel->id,
|
|
]);
|
|
|
|
// Test the relationship
|
|
expect($device->deviceModel)->toBeInstanceOf(DeviceModel::class);
|
|
expect($device->deviceModel->id)->toBe($deviceModel->id);
|
|
});
|
|
|
|
it('device without device model returns null relationship', function (): void {
|
|
// Create a device without DeviceModel
|
|
$device = Device::factory()->create([
|
|
'device_model_id' => null,
|
|
]);
|
|
|
|
// Test the relationship returns null
|
|
expect($device->deviceModel)->toBeNull();
|
|
});
|