|
@@ -1,22 +1,22 @@
|
|
|
/** Disk output, resource bounds, and cancellation around the external kit. */
|
|
/** Disk output, resource bounds, and cancellation around the external kit. */
|
|
|
import { access, mkdir, readFile, stat, writeFile } from 'node:fs/promises'
|
|
import { access, mkdir, readFile, stat, writeFile } from 'node:fs/promises'
|
|
|
import { dirname } from 'node:path'
|
|
import { dirname } from 'node:path'
|
|
|
-import { DocumentSourceKey, type DocumentRenderRequest } from '@deepseek-ai/dsh-document-render'
|
|
|
|
|
|
|
+import { DocumentSourceKey, type DocumentConvertRequest } from '@deepseek-ai/dsh-document-convert'
|
|
|
import { Context } from '@deepseek-ai/cordis'
|
|
import { Context } from '@deepseek-ai/cordis'
|
|
|
import type { Converter, ConverterOptions } from '@deepseek-ai/libreoffice-kit'
|
|
import type { Converter, ConverterOptions } from '@deepseek-ai/libreoffice-kit'
|
|
|
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
|
|
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
|
|
|
-import LibreOfficeRenderer, { Config } from '../src/index.ts'
|
|
|
|
|
|
|
+import LibreOfficeConverter, { Config } from '../src/index.ts'
|
|
|
|
|
|
|
|
const kit = vi.hoisted(() => ({ create: vi.fn<(options?: ConverterOptions) => Promise<Converter>>() }))
|
|
const kit = vi.hoisted(() => ({ create: vi.fn<(options?: ConverterOptions) => Promise<Converter>>() }))
|
|
|
vi.mock('@deepseek-ai/libreoffice-kit', () => ({ createConverter: kit.create }))
|
|
vi.mock('@deepseek-ai/libreoffice-kit', () => ({ createConverter: kit.create }))
|
|
|
|
|
|
|
|
const pdf = Buffer.from('%PDF-1.7\npreview\n%%EOF\n')
|
|
const pdf = Buffer.from('%PDF-1.7\npreview\n%%EOF\n')
|
|
|
const input = new Uint8Array([80, 75, 3, 4])
|
|
const input = new Uint8Array([80, 75, 3, 4])
|
|
|
-const request: DocumentRenderRequest = { extension: 'docx', priority: 'foreground', source: {
|
|
|
|
|
|
|
+const request: DocumentConvertRequest = { extension: 'docx', priority: 'foreground', source: {
|
|
|
key: DocumentSourceKey('source'), version: 'v1', bytes: input.length,
|
|
key: DocumentSourceKey('source'), version: 'v1', bytes: input.length,
|
|
|
read: async () => ({ bytes: input, version: 'v1' }),
|
|
read: async () => ({ bytes: input, version: 'v1' }),
|
|
|
} }
|
|
} }
|
|
|
-function distinct(index: number): DocumentRenderRequest {
|
|
|
|
|
|
|
+function distinct(index: number): DocumentConvertRequest {
|
|
|
return { ...request, source: { ...request.source, key: DocumentSourceKey(`source-${index}`),
|
|
return { ...request, source: { ...request.source, key: DocumentSourceKey(`source-${index}`),
|
|
|
read: async () => ({ bytes: new Uint8Array([80, 75, 3, index]), version: 'v1' }) } }
|
|
read: async () => ({ bytes: new Uint8Array([80, 75, 3, index]), version: 'v1' }) } }
|
|
|
}
|
|
}
|
|
@@ -35,14 +35,14 @@ beforeEach(() => {
|
|
|
})
|
|
})
|
|
|
afterEach(async () => { await ctx.fiber.dispose() })
|
|
afterEach(async () => { await ctx.fiber.dispose() })
|
|
|
|
|
|
|
|
-async function mount(config: Partial<Config> = {}): Promise<LibreOfficeRenderer> {
|
|
|
|
|
- await ctx.plugin(LibreOfficeRenderer, config)
|
|
|
|
|
- return ctx.documentRender as LibreOfficeRenderer
|
|
|
|
|
|
|
+async function mount(config: Partial<Config> = {}): Promise<LibreOfficeConverter> {
|
|
|
|
|
+ await ctx.plugin(LibreOfficeConverter, config)
|
|
|
|
|
+ return ctx.documentConvert as LibreOfficeConverter
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
it('preserves the kit font defaults when font configuration is omitted', async () => {
|
|
it('preserves the kit font defaults when font configuration is omitted', async () => {
|
|
|
- const renderer = await mount()
|
|
|
|
|
- await renderer.render(request)
|
|
|
|
|
|
|
+ const provider = await mount()
|
|
|
|
|
+ await provider.convert(request)
|
|
|
expect(kit.create.mock.calls[0]![0]).not.toHaveProperty('fontDirectories')
|
|
expect(kit.create.mock.calls[0]![0]).not.toHaveProperty('fontDirectories')
|
|
|
expect(kit.create.mock.calls[0]![0]).not.toHaveProperty('fontFallbacks')
|
|
expect(kit.create.mock.calls[0]![0]).not.toHaveProperty('fontFallbacks')
|
|
|
expect(Config({ fontDirectories: [] }).fontDirectories).toEqual([])
|
|
expect(Config({ fontDirectories: [] }).fontDirectories).toEqual([])
|
|
@@ -65,7 +65,7 @@ it('passes configured limits to the kit, reuses the converter, and removes all s
|
|
|
maxImageResolution: 144, maxArchiveEntries: 32, maxUncompressedBytes: 4096,
|
|
maxImageResolution: 144, maxArchiveEntries: 32, maxUncompressedBytes: 4096,
|
|
|
timeoutMs: 321, fontDirectories: [], fontFallbacks: [['sans-serif', 'Arial'], ['serif', 'Times New Roman']],
|
|
timeoutMs: 321, fontDirectories: [], fontFallbacks: [['sans-serif', 'Arial'], ['serif', 'Times New Roman']],
|
|
|
maxFontFiles: 12, maxFontFileBytes: 4096, maxLoadedFontBytes: 8192 })
|
|
maxFontFiles: 12, maxFontFileBytes: 4096, maxLoadedFontBytes: 8192 })
|
|
|
- const renderer = await mount(config)
|
|
|
|
|
|
|
+ const provider = await mount(config)
|
|
|
let scratch = ''
|
|
let scratch = ''
|
|
|
render.mockImplementation(async ({ inputPath, outputPath }) => {
|
|
render.mockImplementation(async ({ inputPath, outputPath }) => {
|
|
|
scratch = dirname(inputPath)
|
|
scratch = dirname(inputPath)
|
|
@@ -77,10 +77,10 @@ it('passes configured limits to the kit, reuses the converter, and removes all s
|
|
|
await writeFile(outputPath, pdf)
|
|
await writeFile(outputPath, pdf)
|
|
|
return { backend: 'native', missingFonts: ['Missing Serif'] }
|
|
return { backend: 'native', missingFonts: ['Missing Serif'] }
|
|
|
})
|
|
})
|
|
|
- const result = await renderer.render(request)
|
|
|
|
|
|
|
+ const result = await provider.convert(request)
|
|
|
expect(result).toMatchObject({ pdf: Uint8Array.from(pdf), missingFonts: ['Missing Serif'] })
|
|
expect(result).toMatchObject({ pdf: Uint8Array.from(pdf), missingFonts: ['Missing Serif'] })
|
|
|
await expect(access(scratch)).rejects.toMatchObject({ code: 'ENOENT' })
|
|
await expect(access(scratch)).rejects.toMatchObject({ code: 'ENOENT' })
|
|
|
- await renderer.render(request)
|
|
|
|
|
|
|
+ await provider.convert(request)
|
|
|
const { maxConcurrentConversions: _count, maxQueuedJobs: _queued, maxReaders: _readers, maxSourceBytes: _source,
|
|
const { maxConcurrentConversions: _count, maxQueuedJobs: _queued, maxReaders: _readers, maxSourceBytes: _source,
|
|
|
maxBackgroundConversions: _background, maxCachedEntries: _entries, maxCachedBytes: _cached,
|
|
maxBackgroundConversions: _background, maxCachedEntries: _entries, maxCachedBytes: _cached,
|
|
|
maxSourceEntries: _aliases, ...options } = config
|
|
maxSourceEntries: _aliases, ...options } = config
|
|
@@ -91,13 +91,13 @@ it('passes configured limits to the kit, reuses the converter, and removes all s
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('refuses an oversized input before allocating a converter', async () => {
|
|
it('refuses an oversized input before allocating a converter', async () => {
|
|
|
- const renderer = await mount({ maxInputBytes: 3 })
|
|
|
|
|
- await expect(renderer.render(request)).rejects.toMatchObject({ code: 'input-too-large' })
|
|
|
|
|
|
|
+ const provider = await mount({ maxInputBytes: 3 })
|
|
|
|
|
+ await expect(provider.convert(request)).rejects.toMatchObject({ code: 'input-too-large' })
|
|
|
expect(kit.create).not.toHaveBeenCalled()
|
|
expect(kit.create).not.toHaveBeenCalled()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it.each(['missing', 'directory', 'not-pdf', 'incomplete', 'too-large'] as const)('rejects %s kit output and removes its directory', async (kind) => {
|
|
it.each(['missing', 'directory', 'not-pdf', 'incomplete', 'too-large'] as const)('rejects %s kit output and removes its directory', async (kind) => {
|
|
|
- const renderer = await mount({ maxOutputBytes: pdf.length })
|
|
|
|
|
|
|
+ const provider = await mount({ maxOutputBytes: pdf.length })
|
|
|
let scratch = ''
|
|
let scratch = ''
|
|
|
render.mockImplementation(async ({ inputPath, outputPath }) => {
|
|
render.mockImplementation(async ({ inputPath, outputPath }) => {
|
|
|
scratch = dirname(inputPath)
|
|
scratch = dirname(inputPath)
|
|
@@ -107,31 +107,31 @@ it.each(['missing', 'directory', 'not-pdf', 'incomplete', 'too-large'] as const)
|
|
|
else if (kind === 'too-large') await writeFile(outputPath, Buffer.concat([pdf, pdf]))
|
|
else if (kind === 'too-large') await writeFile(outputPath, Buffer.concat([pdf, pdf]))
|
|
|
return { backend: 'wasm', missingFonts: [] }
|
|
return { backend: 'wasm', missingFonts: [] }
|
|
|
})
|
|
})
|
|
|
- await expect(renderer.render(request)).rejects.toMatchObject({ code: kind === 'too-large' ? 'output-too-large' : 'invalid-output' })
|
|
|
|
|
|
|
+ await expect(provider.convert(request)).rejects.toMatchObject({ code: kind === 'too-large' ? 'output-too-large' : 'invalid-output' })
|
|
|
await expect(access(scratch)).rejects.toMatchObject({ code: 'ENOENT' })
|
|
await expect(access(scratch)).rejects.toMatchObject({ code: 'ENOENT' })
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('retries initialization after a failed kit factory and reports unclassified errors', async () => {
|
|
it('retries initialization after a failed kit factory and reports unclassified errors', async () => {
|
|
|
- const renderer = await mount()
|
|
|
|
|
|
|
+ const provider = await mount()
|
|
|
kit.create.mockRejectedValueOnce(Object.assign(new Error('absent asset'), { code: 'unavailable' }))
|
|
kit.create.mockRejectedValueOnce(Object.assign(new Error('absent asset'), { code: 'unavailable' }))
|
|
|
- await expect(renderer.render(request)).rejects.toMatchObject({ code: 'unavailable' })
|
|
|
|
|
- expect(await renderer.render(request)).toMatchObject({ pdf: Uint8Array.from(pdf), missingFonts: ['Missing Serif'] })
|
|
|
|
|
|
|
+ await expect(provider.convert(request)).rejects.toMatchObject({ code: 'unavailable' })
|
|
|
|
|
+ expect(await provider.convert(request)).toMatchObject({ pdf: Uint8Array.from(pdf), missingFonts: ['Missing Serif'] })
|
|
|
expect(kit.create).toHaveBeenCalledTimes(2)
|
|
expect(kit.create).toHaveBeenCalledTimes(2)
|
|
|
render.mockRejectedValueOnce(new Error('unexpected engine failure'))
|
|
render.mockRejectedValueOnce(new Error('unexpected engine failure'))
|
|
|
- await expect(renderer.render(distinct(9))).rejects.toMatchObject({ code: 'failed' })
|
|
|
|
|
|
|
+ await expect(provider.convert(distinct(9))).rejects.toMatchObject({ code: 'failed' })
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it.each([
|
|
it.each([
|
|
|
'input-too-large', 'output-too-large', 'invalid-document', 'unsupported-format', 'invalid-output', 'timeout', 'unavailable',
|
|
'input-too-large', 'output-too-large', 'invalid-document', 'unsupported-format', 'invalid-output', 'timeout', 'unavailable',
|
|
|
] as const)('preserves the kit %s failure for the document consumer', async (code) => {
|
|
] as const)('preserves the kit %s failure for the document consumer', async (code) => {
|
|
|
- const renderer = await mount()
|
|
|
|
|
|
|
+ const provider = await mount()
|
|
|
const cause = Object.assign(new Error('conversion failed'), { code })
|
|
const cause = Object.assign(new Error('conversion failed'), { code })
|
|
|
render.mockRejectedValueOnce(cause)
|
|
render.mockRejectedValueOnce(cause)
|
|
|
- await expect(renderer.render(request)).rejects.toMatchObject({ code, cause })
|
|
|
|
|
|
|
+ await expect(provider.convert(request)).rejects.toMatchObject({ code, cause })
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('cancels a queued caller without starting or stopping another conversion', async () => {
|
|
it('cancels a queued caller without starting or stopping another conversion', async () => {
|
|
|
- const renderer = await mount({ maxConcurrentConversions: 1 })
|
|
|
|
|
|
|
+ const provider = await mount({ maxConcurrentConversions: 1 })
|
|
|
const entered = Promise.withResolvers<AbortSignal>()
|
|
const entered = Promise.withResolvers<AbortSignal>()
|
|
|
const release = Promise.withResolvers<undefined>()
|
|
const release = Promise.withResolvers<undefined>()
|
|
|
render.mockImplementationOnce(async ({ outputPath }, signal) => {
|
|
render.mockImplementationOnce(async ({ outputPath }, signal) => {
|
|
@@ -140,11 +140,11 @@ it('cancels a queued caller without starting or stopping another conversion', as
|
|
|
await writeFile(outputPath, pdf)
|
|
await writeFile(outputPath, pdf)
|
|
|
return { backend: 'wasm', missingFonts: [] }
|
|
return { backend: 'wasm', missingFonts: [] }
|
|
|
})
|
|
})
|
|
|
- const first = renderer.render(request)
|
|
|
|
|
|
|
+ const first = provider.convert(request)
|
|
|
try {
|
|
try {
|
|
|
const activeSignal = await entered.promise
|
|
const activeSignal = await entered.promise
|
|
|
const controller = new AbortController()
|
|
const controller = new AbortController()
|
|
|
- const second = renderer.render(request, controller.signal)
|
|
|
|
|
|
|
+ const second = provider.convert(request, controller.signal)
|
|
|
const rejected = expect(second).rejects.toMatchObject({ name: 'AbortError' })
|
|
const rejected = expect(second).rejects.toMatchObject({ name: 'AbortError' })
|
|
|
await Promise.resolve(undefined)
|
|
await Promise.resolve(undefined)
|
|
|
controller.abort()
|
|
controller.abort()
|
|
@@ -152,11 +152,11 @@ it('cancels a queued caller without starting or stopping another conversion', as
|
|
|
expect(render).toHaveBeenCalledOnce()
|
|
expect(render).toHaveBeenCalledOnce()
|
|
|
expect(activeSignal.aborted).toBe(false)
|
|
expect(activeSignal.aborted).toBe(false)
|
|
|
} finally { release.resolve(undefined); await first }
|
|
} finally { release.resolve(undefined); await first }
|
|
|
- expect(await renderer.render(request)).toHaveProperty('pdf')
|
|
|
|
|
|
|
+ expect(await provider.convert(request)).toHaveProperty('pdf')
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('bounds active converters and resumes queued work when a slot becomes free', async () => {
|
|
it('bounds active converters and resumes queued work when a slot becomes free', async () => {
|
|
|
- const renderer = await mount({ maxConcurrentConversions: 2 })
|
|
|
|
|
|
|
+ const provider = await mount({ maxConcurrentConversions: 2 })
|
|
|
const both = Promise.withResolvers<undefined>()
|
|
const both = Promise.withResolvers<undefined>()
|
|
|
const release = Promise.withResolvers<undefined>()
|
|
const release = Promise.withResolvers<undefined>()
|
|
|
let calls = 0
|
|
let calls = 0
|
|
@@ -166,7 +166,7 @@ it('bounds active converters and resumes queued work when a slot becomes free',
|
|
|
await writeFile(outputPath, pdf)
|
|
await writeFile(outputPath, pdf)
|
|
|
return { backend: 'native', missingFonts: [] }
|
|
return { backend: 'native', missingFonts: [] }
|
|
|
})
|
|
})
|
|
|
- const work = [renderer.render(distinct(1)), renderer.render(distinct(2)), renderer.render(distinct(3))]
|
|
|
|
|
|
|
+ const work = [provider.convert(distinct(1)), provider.convert(distinct(2)), provider.convert(distinct(3))]
|
|
|
try {
|
|
try {
|
|
|
await both.promise
|
|
await both.promise
|
|
|
expect(kit.create).toHaveBeenCalledTimes(2)
|
|
expect(kit.create).toHaveBeenCalledTimes(2)
|
|
@@ -177,7 +177,7 @@ it('bounds active converters and resumes queued work when a slot becomes free',
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it.each(['caller', 'provider'] as const)('joins late kit completion and scratch cleanup after %s cancellation', async (owner) => {
|
|
it.each(['caller', 'provider'] as const)('joins late kit completion and scratch cleanup after %s cancellation', async (owner) => {
|
|
|
- const renderer = await mount()
|
|
|
|
|
|
|
+ const provider = await mount()
|
|
|
const entered = Promise.withResolvers<{ signal: AbortSignal; scratch: string }>()
|
|
const entered = Promise.withResolvers<{ signal: AbortSignal; scratch: string }>()
|
|
|
const release = Promise.withResolvers<undefined>()
|
|
const release = Promise.withResolvers<undefined>()
|
|
|
render.mockImplementation(async ({ inputPath, outputPath }, signal) => {
|
|
render.mockImplementation(async ({ inputPath, outputPath }, signal) => {
|
|
@@ -187,7 +187,7 @@ it.each(['caller', 'provider'] as const)('joins late kit completion and scratch
|
|
|
return { backend: 'native', missingFonts: [] }
|
|
return { backend: 'native', missingFonts: [] }
|
|
|
})
|
|
})
|
|
|
const caller = new AbortController()
|
|
const caller = new AbortController()
|
|
|
- const work = renderer.render(request, caller.signal)
|
|
|
|
|
|
|
+ const work = provider.convert(request, caller.signal)
|
|
|
const rejected = expect(work).rejects.toMatchObject(owner === 'caller' ? { name: 'AbortError' } : { code: 'unavailable' })
|
|
const rejected = expect(work).rejects.toMatchObject(owner === 'caller' ? { name: 'AbortError' } : { code: 'unavailable' })
|
|
|
const { signal, scratch } = await entered.promise
|
|
const { signal, scratch } = await entered.promise
|
|
|
let settled = false
|
|
let settled = false
|
|
@@ -207,11 +207,11 @@ it.each(['caller', 'provider'] as const)('joins late kit completion and scratch
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('joins initialization during disposal and never starts a render after cancellation', async () => {
|
|
it('joins initialization during disposal and never starts a render after cancellation', async () => {
|
|
|
- const renderer = await mount()
|
|
|
|
|
|
|
+ const provider = await mount()
|
|
|
const entered = Promise.withResolvers<undefined>()
|
|
const entered = Promise.withResolvers<undefined>()
|
|
|
const release = Promise.withResolvers<Converter>()
|
|
const release = Promise.withResolvers<Converter>()
|
|
|
kit.create.mockImplementationOnce(() => { entered.resolve(undefined); return release.promise })
|
|
kit.create.mockImplementationOnce(() => { entered.resolve(undefined); return release.promise })
|
|
|
- const work = renderer.render(request)
|
|
|
|
|
|
|
+ const work = provider.convert(request)
|
|
|
const rejected = expect(work).rejects.toMatchObject({ code: 'unavailable' })
|
|
const rejected = expect(work).rejects.toMatchObject({ code: 'unavailable' })
|
|
|
await entered.promise
|
|
await entered.promise
|
|
|
const closing = ctx.fiber.dispose()
|
|
const closing = ctx.fiber.dispose()
|
|
@@ -220,29 +220,29 @@ it('joins initialization during disposal and never starts a render after cancell
|
|
|
await closing
|
|
await closing
|
|
|
expect(render).not.toHaveBeenCalled()
|
|
expect(render).not.toHaveBeenCalled()
|
|
|
expect(dispose).toHaveBeenCalledOnce()
|
|
expect(dispose).toHaveBeenCalledOnce()
|
|
|
- await expect(renderer.render(request)).rejects.toMatchObject({ code: 'unavailable' })
|
|
|
|
|
|
|
+ await expect(provider.convert(request)).rejects.toMatchObject({ code: 'unavailable' })
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('rejects caller cancellation before allocating any conversion resources', async () => {
|
|
it('rejects caller cancellation before allocating any conversion resources', async () => {
|
|
|
- const renderer = await mount()
|
|
|
|
|
|
|
+ const provider = await mount()
|
|
|
const reason = new Error('caller cancelled')
|
|
const reason = new Error('caller cancelled')
|
|
|
- await expect(renderer.render(request, AbortSignal.abort(reason))).rejects.toBe(reason)
|
|
|
|
|
|
|
+ await expect(provider.convert(request, AbortSignal.abort(reason))).rejects.toBe(reason)
|
|
|
expect(kit.create).not.toHaveBeenCalled()
|
|
expect(kit.create).not.toHaveBeenCalled()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('rejects relative font directories during provider configuration', async () => {
|
|
it('rejects relative font directories during provider configuration', async () => {
|
|
|
- expect(() => new LibreOfficeRenderer(ctx, Config({ fontDirectories: ['relative/fonts'] })))
|
|
|
|
|
|
|
+ expect(() => new LibreOfficeConverter(ctx, Config({ fontDirectories: ['relative/fonts'] })))
|
|
|
.toThrow('fontDirectories must contain absolute paths')
|
|
.toThrow('fontDirectories must contain absolute paths')
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('rejects source capacity below one permitted input before allocating a converter', () => {
|
|
it('rejects source capacity below one permitted input before allocating a converter', () => {
|
|
|
- expect(() => new LibreOfficeRenderer(ctx, Config({ maxInputBytes: 4, maxSourceBytes: 3 })))
|
|
|
|
|
|
|
+ expect(() => new LibreOfficeConverter(ctx, Config({ maxInputBytes: 4, maxSourceBytes: 3 })))
|
|
|
.toThrow('maxSourceBytes must be at least maxInputBytes')
|
|
.toThrow('maxSourceBytes must be at least maxInputBytes')
|
|
|
expect(kit.create).not.toHaveBeenCalled()
|
|
expect(kit.create).not.toHaveBeenCalled()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('joins every converter disposal before reporting an engine cleanup failure', async () => {
|
|
it('joins every converter disposal before reporting an engine cleanup failure', async () => {
|
|
|
- const renderer = await mount({ maxConcurrentConversions: 2 })
|
|
|
|
|
|
|
+ const provider = await mount({ maxConcurrentConversions: 2 })
|
|
|
const entered = Promise.withResolvers<undefined>()
|
|
const entered = Promise.withResolvers<undefined>()
|
|
|
const released = Promise.withResolvers<undefined>()
|
|
const released = Promise.withResolvers<undefined>()
|
|
|
const cleanupEntered = Promise.withResolvers<undefined>()
|
|
const cleanupEntered = Promise.withResolvers<undefined>()
|
|
@@ -258,7 +258,7 @@ it('joins every converter disposal before reporting an engine cleanup failure',
|
|
|
kit.create.mockReset()
|
|
kit.create.mockReset()
|
|
|
.mockResolvedValueOnce({ backend: 'native', render, dispose: async () => { throw failed } })
|
|
.mockResolvedValueOnce({ backend: 'native', render, dispose: async () => { throw failed } })
|
|
|
.mockResolvedValueOnce({ backend: 'native', render, dispose: async () => { cleanupEntered.resolve(undefined); await cleanupRelease.promise } })
|
|
.mockResolvedValueOnce({ backend: 'native', render, dispose: async () => { cleanupEntered.resolve(undefined); await cleanupRelease.promise } })
|
|
|
- const work = [renderer.render(distinct(1)), renderer.render(distinct(2))]
|
|
|
|
|
|
|
+ const work = [provider.convert(distinct(1)), provider.convert(distinct(2))]
|
|
|
try { await entered.promise } finally { released.resolve(undefined); await Promise.all(work) }
|
|
try { await entered.promise } finally { released.resolve(undefined); await Promise.all(work) }
|
|
|
let disposed = false
|
|
let disposed = false
|
|
|
const closing = ctx.fiber.dispose().then(() => { disposed = true })
|
|
const closing = ctx.fiber.dispose().then(() => { disposed = true })
|