mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-10 19:47:42 +00:00
feat(torrents): add global open-file limit
This commit is contained in:
@@ -6,6 +6,10 @@ import {
|
||||
} from './useSettingsStore';
|
||||
import * as ipc from '../ipc';
|
||||
import type { PairingTokenHydration } from '../bindings/PairingTokenHydration';
|
||||
import {
|
||||
DEFAULT_TORRENT_MAX_OPEN_FILES,
|
||||
MAX_TORRENT_MAX_OPEN_FILES
|
||||
} from '../utils/downloads';
|
||||
|
||||
vi.mock('../ipc', () => ({
|
||||
invokeCommand: vi.fn()
|
||||
@@ -57,6 +61,66 @@ describe('Torrent peer discovery preferences', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Torrent open-file limit preference', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
useSettingsStore.setState({ torrentMaxOpenFiles: DEFAULT_TORRENT_MAX_OPEN_FILES });
|
||||
});
|
||||
|
||||
it('applies a bounded global limit before persisting it', async () => {
|
||||
vi.mocked(ipc.invokeCommand).mockResolvedValue(undefined);
|
||||
|
||||
await useSettingsStore.getState().setTorrentMaxOpenFiles(256);
|
||||
|
||||
expect(ipc.invokeCommand).toHaveBeenCalledWith('set_torrent_max_open_files', {
|
||||
max_open_files: 256
|
||||
});
|
||||
expect(useSettingsStore.getState().torrentMaxOpenFiles).toBe(256);
|
||||
});
|
||||
|
||||
it('rejects unsafe values without changing the saved limit', async () => {
|
||||
await expect(useSettingsStore.getState().setTorrentMaxOpenFiles(0)).rejects.toThrow();
|
||||
await expect(
|
||||
useSettingsStore.getState().setTorrentMaxOpenFiles(MAX_TORRENT_MAX_OPEN_FILES + 1)
|
||||
).rejects.toThrow();
|
||||
|
||||
expect(ipc.invokeCommand).not.toHaveBeenCalledWith(
|
||||
'set_torrent_max_open_files',
|
||||
expect.anything()
|
||||
);
|
||||
expect(useSettingsStore.getState().torrentMaxOpenFiles)
|
||||
.toBe(DEFAULT_TORRENT_MAX_OPEN_FILES);
|
||||
});
|
||||
|
||||
it('serializes rapid updates so the native global option cannot reorder', async () => {
|
||||
let releaseFirst!: () => void;
|
||||
const firstUpdate = new Promise<void>(resolve => {
|
||||
releaseFirst = resolve;
|
||||
});
|
||||
const events: string[] = [];
|
||||
vi.mocked(ipc.invokeCommand).mockImplementation(async (command: string, args?: unknown) => {
|
||||
if (command !== 'set_torrent_max_open_files') return undefined;
|
||||
const value = (args as { max_open_files: number }).max_open_files;
|
||||
events.push(`start:${value}`);
|
||||
if (value === 256) await firstUpdate;
|
||||
events.push(`finish:${value}`);
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const first = useSettingsStore.getState().setTorrentMaxOpenFiles(256);
|
||||
const second = useSettingsStore.getState().setTorrentMaxOpenFiles(512);
|
||||
await vi.waitFor(() => expect(events).toEqual(['start:256']));
|
||||
expect(useSettingsStore.getState().torrentMaxOpenFiles)
|
||||
.toBe(DEFAULT_TORRENT_MAX_OPEN_FILES);
|
||||
|
||||
releaseFirst();
|
||||
await Promise.all([first, second]);
|
||||
|
||||
expect(events).toEqual(['start:256', 'finish:256', 'start:512', 'finish:512']);
|
||||
expect(useSettingsStore.getState().torrentMaxOpenFiles).toBe(512);
|
||||
});
|
||||
});
|
||||
|
||||
describe('calendar preference', () => {
|
||||
it('keeps Gregorian as the default and persists explicit calendar choices', async () => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
@@ -20,7 +20,13 @@ import {
|
||||
DEFAULT_CATEGORY_SUBFOLDERS,
|
||||
normalizeDownloadLocationSettings
|
||||
} from '../utils/downloadLocations';
|
||||
import { normalizeSpeedLimitForBackend } from '../utils/downloads';
|
||||
import {
|
||||
DEFAULT_TORRENT_MAX_OPEN_FILES,
|
||||
MAX_TORRENT_MAX_OPEN_FILES,
|
||||
MIN_TORRENT_MAX_OPEN_FILES,
|
||||
normalizeSpeedLimitForBackend,
|
||||
normalizeTorrentMaxOpenFiles
|
||||
} from '../utils/downloads';
|
||||
import i18n from '../i18n';
|
||||
import { isAppLocalePreference, type AppLocalePreference } from '../i18n/locales';
|
||||
import {
|
||||
@@ -30,6 +36,7 @@ import {
|
||||
} from '../utils/dateTime';
|
||||
|
||||
let settingsQueue: Promise<void> = Promise.resolve();
|
||||
let torrentMaxOpenFilesQueue: Promise<void> = Promise.resolve();
|
||||
let pairingTokenHydrationRequest: Promise<PairingTokenHydration> | null = null;
|
||||
const settingsPersistenceErrorListeners = new Set<() => void>();
|
||||
let settingsPersistenceFailed = false;
|
||||
@@ -238,6 +245,7 @@ export interface SettingsState {
|
||||
torrentEnableDht6: boolean;
|
||||
torrentEnablePex: boolean;
|
||||
torrentEnableLpd: boolean;
|
||||
torrentMaxOpenFiles: number;
|
||||
customUserAgent: string;
|
||||
askWhereToSaveEachFile: boolean;
|
||||
preventsSleepWhileDownloading: boolean;
|
||||
@@ -292,6 +300,7 @@ export interface SettingsState {
|
||||
setTorrentEnableDht6: (enabled: boolean) => void;
|
||||
setTorrentEnablePex: (enabled: boolean) => void;
|
||||
setTorrentEnableLpd: (enabled: boolean) => void;
|
||||
setTorrentMaxOpenFiles: (value: number) => Promise<void>;
|
||||
setCustomUserAgent: (userAgent: string) => void;
|
||||
setAskWhereToSaveEachFile: (ask: boolean) => void;
|
||||
setPreventsSleepWhileDownloading: (prevent: boolean) => void;
|
||||
@@ -372,6 +381,7 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
torrentEnableDht6: false,
|
||||
torrentEnablePex: true,
|
||||
torrentEnableLpd: false,
|
||||
torrentMaxOpenFiles: DEFAULT_TORRENT_MAX_OPEN_FILES,
|
||||
customUserAgent: '',
|
||||
askWhereToSaveEachFile: false,
|
||||
preventsSleepWhileDownloading: true,
|
||||
@@ -470,6 +480,22 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
setTorrentEnableDht6: (torrentEnableDht6) => set({ torrentEnableDht6 }),
|
||||
setTorrentEnablePex: (torrentEnablePex) => set({ torrentEnablePex }),
|
||||
setTorrentEnableLpd: (torrentEnableLpd) => set({ torrentEnableLpd }),
|
||||
setTorrentMaxOpenFiles: (value) => {
|
||||
const normalized = normalizeTorrentMaxOpenFiles(value);
|
||||
if (normalized === undefined) {
|
||||
return Promise.reject(new Error(
|
||||
`Torrent maximum open files must be between ${MIN_TORRENT_MAX_OPEN_FILES} and ${MAX_TORRENT_MAX_OPEN_FILES}`
|
||||
));
|
||||
}
|
||||
const apply = async () => {
|
||||
await invoke('set_torrent_max_open_files', { max_open_files: normalized });
|
||||
info('Settings updated: torrentMaxOpenFiles');
|
||||
set({ torrentMaxOpenFiles: normalized });
|
||||
};
|
||||
const result = torrentMaxOpenFilesQueue.then(apply, apply);
|
||||
torrentMaxOpenFilesQueue = result.then(() => undefined, () => undefined);
|
||||
return result;
|
||||
},
|
||||
setCustomUserAgent: (customUserAgent) => set({ customUserAgent }),
|
||||
setAskWhereToSaveEachFile: (askWhereToSaveEachFile) => set({ askWhereToSaveEachFile }),
|
||||
setPreventsSleepWhileDownloading: (preventsSleepWhileDownloading) => {
|
||||
@@ -655,6 +681,7 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
torrentEnableDht6: state.torrentEnableDht6,
|
||||
torrentEnablePex: state.torrentEnablePex,
|
||||
torrentEnableLpd: state.torrentEnableLpd,
|
||||
torrentMaxOpenFiles: state.torrentMaxOpenFiles,
|
||||
customUserAgent: state.customUserAgent,
|
||||
askWhereToSaveEachFile: state.askWhereToSaveEachFile,
|
||||
preventsSleepWhileDownloading: state.preventsSleepWhileDownloading,
|
||||
@@ -704,6 +731,8 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
torrentEnableDht6: persistedBoolean(persisted.torrentEnableDht6, currentState.torrentEnableDht6),
|
||||
torrentEnablePex: persistedBoolean(persisted.torrentEnablePex, currentState.torrentEnablePex),
|
||||
torrentEnableLpd: persistedBoolean(persisted.torrentEnableLpd, currentState.torrentEnableLpd),
|
||||
torrentMaxOpenFiles: normalizeTorrentMaxOpenFiles(persisted.torrentMaxOpenFiles)
|
||||
?? currentState.torrentMaxOpenFiles,
|
||||
sidebarPosition: isAllowedSetting(SIDEBAR_POSITION_VALUES, persisted.sidebarPosition)
|
||||
? persisted.sidebarPosition
|
||||
: currentState.sidebarPosition,
|
||||
|
||||
Reference in New Issue
Block a user