mirror of
https://github.com/easydiffusion/easydiffusion.git
synced 2025-01-04 05:19:12 +01:00
432 lines
16 KiB
JavaScript
432 lines
16 KiB
JavaScript
"use strict"
|
|
|
|
const JASMINE_SESSION_ID = `jasmine-${String(Date.now()).slice(8)}`
|
|
|
|
beforeEach(function() {
|
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = 15 * 60 * 1000 // Test timeout after 15 minutes
|
|
jasmine.addMatchers({
|
|
toBeOneOf: function() {
|
|
return {
|
|
compare: function(actual, expected) {
|
|
return {
|
|
pass: expected.includes(actual),
|
|
}
|
|
},
|
|
}
|
|
},
|
|
})
|
|
})
|
|
describe("stable-diffusion-ui", function() {
|
|
beforeEach(function() {
|
|
expect(typeof SD).toBe("object")
|
|
expect(typeof SD.serverState).toBe("object")
|
|
expect(typeof SD.serverState.status).toBe("string")
|
|
})
|
|
it("should be able to reach the backend", async function() {
|
|
expect(SD.serverState.status).toBe(SD.ServerStates.unavailable)
|
|
SD.sessionId = JASMINE_SESSION_ID
|
|
await SD.init()
|
|
expect(SD.isServerAvailable()).toBeTrue()
|
|
})
|
|
|
|
it("enfore the current task state", function() {
|
|
const task = new SD.Task()
|
|
expect(task.status).toBe(SD.TaskStatus.init)
|
|
expect(task.isPending).toBeTrue()
|
|
|
|
task._setStatus(SD.TaskStatus.pending)
|
|
expect(task.status).toBe(SD.TaskStatus.pending)
|
|
expect(task.isPending).toBeTrue()
|
|
expect(function() {
|
|
task._setStatus(SD.TaskStatus.init)
|
|
}).toThrowError()
|
|
|
|
task._setStatus(SD.TaskStatus.waiting)
|
|
expect(task.status).toBe(SD.TaskStatus.waiting)
|
|
expect(task.isPending).toBeTrue()
|
|
expect(function() {
|
|
task._setStatus(SD.TaskStatus.pending)
|
|
}).toThrowError()
|
|
|
|
task._setStatus(SD.TaskStatus.processing)
|
|
expect(task.status).toBe(SD.TaskStatus.processing)
|
|
expect(task.isPending).toBeTrue()
|
|
expect(function() {
|
|
task._setStatus(SD.TaskStatus.pending)
|
|
}).toThrowError()
|
|
|
|
task._setStatus(SD.TaskStatus.failed)
|
|
expect(task.status).toBe(SD.TaskStatus.failed)
|
|
expect(task.isPending).toBeFalse()
|
|
expect(function() {
|
|
task._setStatus(SD.TaskStatus.processing)
|
|
}).toThrowError()
|
|
expect(function() {
|
|
task._setStatus(SD.TaskStatus.completed)
|
|
}).toThrowError()
|
|
})
|
|
it("should be able to run tasks", async function() {
|
|
expect(typeof SD.Task.run).toBe("function")
|
|
const promiseGenerator = (function*(val) {
|
|
expect(val).toBe("start")
|
|
expect(yield 1 + 1).toBe(4)
|
|
expect(yield 2 + 2).toBe(8)
|
|
yield asyncDelay(500)
|
|
expect(yield 3 + 3).toBe(12)
|
|
expect(yield 4 + 4).toBe(16)
|
|
return 8 + 8
|
|
})("start")
|
|
const callback = function({ value, done }) {
|
|
return { value: 2 * value, done }
|
|
}
|
|
expect(await SD.Task.run(promiseGenerator, { callback })).toBe(32)
|
|
})
|
|
it("should be able to queue tasks", async function() {
|
|
expect(typeof SD.Task.enqueue).toBe("function")
|
|
const promiseGenerator = (function*(val) {
|
|
expect(val).toBe("start")
|
|
expect(yield 1 + 1).toBe(4)
|
|
expect(yield 2 + 2).toBe(8)
|
|
yield asyncDelay(500)
|
|
expect(yield 3 + 3).toBe(12)
|
|
expect(yield 4 + 4).toBe(16)
|
|
return 8 + 8
|
|
})("start")
|
|
const callback = function({ value, done }) {
|
|
return { value: 2 * value, done }
|
|
}
|
|
const gen = SD.Task.asGenerator({ generator: promiseGenerator, callback })
|
|
expect(await SD.Task.enqueue(gen)).toBe(32)
|
|
})
|
|
it("should be able to chain handlers", async function() {
|
|
expect(typeof SD.Task.enqueue).toBe("function")
|
|
const promiseGenerator = (function*(val) {
|
|
expect(val).toBe("start")
|
|
expect(yield { test: "1" }).toEqual({ test: "1", foo: "bar" })
|
|
expect(yield 2 + 2).toEqual(8)
|
|
yield asyncDelay(500)
|
|
expect(yield 3 + 3).toEqual(12)
|
|
expect(yield { test: 4 }).toEqual({ test: 8, foo: "bar" })
|
|
return { test: 8 }
|
|
})("start")
|
|
const gen1 = SD.Task.asGenerator({
|
|
generator: promiseGenerator,
|
|
callback: function({ value, done }) {
|
|
if (typeof value === "object") {
|
|
value["foo"] = "bar"
|
|
}
|
|
return { value, done }
|
|
},
|
|
})
|
|
const gen2 = SD.Task.asGenerator({
|
|
generator: gen1,
|
|
callback: function({ value, done }) {
|
|
if (typeof value === "number") {
|
|
value = 2 * value
|
|
}
|
|
if (typeof value === "object" && typeof value.test === "number") {
|
|
value.test = 2 * value.test
|
|
}
|
|
return { value, done }
|
|
},
|
|
})
|
|
expect(await SD.Task.enqueue(gen2)).toEqual({ test: 32, foo: "bar" })
|
|
})
|
|
describe("ServiceContainer", function() {
|
|
it("should be able to register providers", function() {
|
|
const cont = new ServiceContainer(
|
|
function foo() {
|
|
this.bar = ""
|
|
},
|
|
function bar() {
|
|
return () => 0
|
|
},
|
|
{ name: "zero", definition: 0 },
|
|
{ name: "ctx", definition: () => Object.create(null), singleton: true },
|
|
{
|
|
name: "test",
|
|
definition: (ctx, missing, one, foo) => {
|
|
expect(ctx).toEqual({ ran: true })
|
|
expect(one).toBe(1)
|
|
expect(typeof foo).toBe("object")
|
|
expect(foo.bar).toBeDefined()
|
|
expect(typeof missing).toBe("undefined")
|
|
return { foo: "bar" }
|
|
},
|
|
dependencies: ["ctx", "missing", "one", "foo"],
|
|
}
|
|
)
|
|
const fooObj = cont.get("foo")
|
|
expect(typeof fooObj).toBe("object")
|
|
fooObj.ran = true
|
|
|
|
const ctx = cont.get("ctx")
|
|
expect(ctx).toEqual({})
|
|
ctx.ran = true
|
|
|
|
const bar = cont.get("bar")
|
|
expect(typeof bar).toBe("function")
|
|
expect(bar()).toBe(0)
|
|
|
|
cont.register({ name: "one", definition: 1 })
|
|
const test = cont.get("test")
|
|
expect(typeof test).toBe("object")
|
|
expect(test.foo).toBe("bar")
|
|
})
|
|
})
|
|
it("should be able to stream data in chunks", async function() {
|
|
expect(SD.isServerAvailable()).toBeTrue()
|
|
const nbr_steps = 15
|
|
let res = await fetch("/render", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
prompt: "a photograph of an astronaut riding a horse",
|
|
negative_prompt: "",
|
|
width: 128,
|
|
height: 128,
|
|
seed: Math.floor(Math.random() * 10000000),
|
|
|
|
sampler: "plms",
|
|
use_stable_diffusion_model: "sd-v1-4",
|
|
num_inference_steps: nbr_steps,
|
|
guidance_scale: 7.5,
|
|
|
|
numOutputsParallel: 1,
|
|
stream_image_progress: true,
|
|
show_only_filtered_image: true,
|
|
output_format: "jpeg",
|
|
|
|
session_id: JASMINE_SESSION_ID,
|
|
}),
|
|
})
|
|
expect(res.ok).toBeTruthy()
|
|
const renderRequest = await res.json()
|
|
expect(typeof renderRequest.stream).toBe("string")
|
|
expect(renderRequest.task).toBeDefined()
|
|
|
|
// Wait for server status to update.
|
|
await SD.waitUntil(
|
|
() => {
|
|
console.log("Waiting for %s to be received...", renderRequest.task)
|
|
return !SD.serverState.tasks || SD.serverState.tasks[String(renderRequest.task)]
|
|
},
|
|
250,
|
|
10 * 60 * 1000
|
|
)
|
|
// Wait for task to start on server.
|
|
await SD.waitUntil(() => {
|
|
console.log("Waiting for %s to start...", renderRequest.task)
|
|
return !SD.serverState.tasks || SD.serverState.tasks[String(renderRequest.task)] !== "pending"
|
|
}, 250)
|
|
|
|
const reader = new SD.ChunkedStreamReader(renderRequest.stream)
|
|
const parseToString = reader.parse
|
|
reader.parse = function(value) {
|
|
value = parseToString.call(this, value)
|
|
if (!value || value.length <= 0) {
|
|
return
|
|
}
|
|
return reader.readStreamAsJSON(value.join(""))
|
|
}
|
|
reader.onNext = function({ done, value }) {
|
|
console.log(value)
|
|
if (typeof value === "object" && "status" in value) {
|
|
done = true
|
|
}
|
|
return { done, value }
|
|
}
|
|
let lastUpdate = undefined
|
|
let stepCount = 0
|
|
let complete = false
|
|
//for await (const stepUpdate of reader) {
|
|
for await (const stepUpdate of reader.open()) {
|
|
console.log("ChunkedStreamReader received ", stepUpdate)
|
|
lastUpdate = stepUpdate
|
|
if (complete) {
|
|
expect(stepUpdate.status).toBe("succeeded")
|
|
expect(stepUpdate.output).toHaveSize(1)
|
|
} else {
|
|
expect(stepUpdate.total_steps).toBe(nbr_steps)
|
|
expect(stepUpdate.step).toBe(stepCount)
|
|
if (stepUpdate.step === stepUpdate.total_steps) {
|
|
complete = true
|
|
} else {
|
|
stepCount++
|
|
}
|
|
}
|
|
}
|
|
for (let i = 1; i <= 5; ++i) {
|
|
res = await fetch(renderRequest.stream)
|
|
expect(res.ok).toBeTruthy()
|
|
const cachedResponse = await res.json()
|
|
console.log("Cache test %s received %o", i, cachedResponse)
|
|
expect(lastUpdate).toEqual(cachedResponse)
|
|
}
|
|
})
|
|
|
|
describe("should be able to make renders", function() {
|
|
beforeEach(function() {
|
|
expect(SD.isServerAvailable()).toBeTrue()
|
|
})
|
|
it("basic inline request", async function() {
|
|
let stepCount = 0
|
|
let complete = false
|
|
const result = await SD.render(
|
|
{
|
|
prompt: "a photograph of an astronaut riding a horse",
|
|
width: 128,
|
|
height: 128,
|
|
num_inference_steps: 10,
|
|
show_only_filtered_image: false,
|
|
//"use_face_correction": 'GFPGANv1.3',
|
|
use_upscale: "RealESRGAN_x4plus",
|
|
session_id: JASMINE_SESSION_ID,
|
|
},
|
|
function(event) {
|
|
console.log(this, event)
|
|
if ("update" in event) {
|
|
const stepUpdate = event.update
|
|
if (complete || (stepUpdate.status && stepUpdate.step === stepUpdate.total_steps)) {
|
|
expect(stepUpdate.status).toBe("succeeded")
|
|
expect(stepUpdate.output).toHaveSize(2)
|
|
} else {
|
|
expect(stepUpdate.step).toBe(stepCount)
|
|
if (stepUpdate.step === stepUpdate.total_steps) {
|
|
complete = true
|
|
} else {
|
|
stepCount++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
)
|
|
console.log(result)
|
|
expect(result.status).toBe("succeeded")
|
|
expect(result.output).toHaveSize(2)
|
|
})
|
|
it("post and reader request", async function() {
|
|
const renderTask = new SD.RenderTask({
|
|
prompt: "a photograph of an astronaut riding a horse",
|
|
width: 128,
|
|
height: 128,
|
|
seed: SD.MAX_SEED_VALUE,
|
|
num_inference_steps: 10,
|
|
session_id: JASMINE_SESSION_ID,
|
|
})
|
|
expect(renderTask.status).toBe(SD.TaskStatus.init)
|
|
|
|
const timeout = -1
|
|
const renderRequest = await renderTask.post(timeout)
|
|
expect(typeof renderRequest.stream).toBe("string")
|
|
expect(renderTask.status).toBe(SD.TaskStatus.waiting)
|
|
expect(renderTask.streamUrl).toBe(renderRequest.stream)
|
|
|
|
await renderTask.waitUntil({
|
|
state: SD.TaskStatus.processing,
|
|
callback: () => console.log("Waiting for render task to start..."),
|
|
})
|
|
expect(renderTask.status).toBe(SD.TaskStatus.processing)
|
|
|
|
let stepCount = 0
|
|
let complete = false
|
|
//for await (const stepUpdate of renderTask.reader) {
|
|
for await (const stepUpdate of renderTask.reader.open()) {
|
|
console.log(stepUpdate)
|
|
if (complete || (stepUpdate.status && stepUpdate.step === stepUpdate.total_steps)) {
|
|
expect(stepUpdate.status).toBe("succeeded")
|
|
expect(stepUpdate.output).toHaveSize(1)
|
|
} else {
|
|
expect(stepUpdate.step).toBe(stepCount)
|
|
if (stepUpdate.step === stepUpdate.total_steps) {
|
|
complete = true
|
|
} else {
|
|
stepCount++
|
|
}
|
|
}
|
|
}
|
|
expect(renderTask.status).toBe(SD.TaskStatus.completed)
|
|
expect(renderTask.result.status).toBe("succeeded")
|
|
expect(renderTask.result.output).toHaveSize(1)
|
|
})
|
|
it("queued request", async function() {
|
|
let stepCount = 0
|
|
let complete = false
|
|
const renderTask = new SD.RenderTask({
|
|
prompt: "a photograph of an astronaut riding a horse",
|
|
width: 128,
|
|
height: 128,
|
|
num_inference_steps: 10,
|
|
show_only_filtered_image: false,
|
|
//"use_face_correction": 'GFPGANv1.3',
|
|
use_upscale: "RealESRGAN_x4plus",
|
|
session_id: JASMINE_SESSION_ID,
|
|
})
|
|
await renderTask.enqueue(function(event) {
|
|
console.log(this, event)
|
|
if ("update" in event) {
|
|
const stepUpdate = event.update
|
|
if (complete || (stepUpdate.status && stepUpdate.step === stepUpdate.total_steps)) {
|
|
expect(stepUpdate.status).toBe("succeeded")
|
|
expect(stepUpdate.output).toHaveSize(2)
|
|
} else {
|
|
expect(stepUpdate.step).toBe(stepCount)
|
|
if (stepUpdate.step === stepUpdate.total_steps) {
|
|
complete = true
|
|
} else {
|
|
stepCount++
|
|
}
|
|
}
|
|
}
|
|
})
|
|
console.log(renderTask.result)
|
|
expect(renderTask.result.status).toBe("succeeded")
|
|
expect(renderTask.result.output).toHaveSize(2)
|
|
})
|
|
})
|
|
describe("# Special cases", function() {
|
|
it("should throw an exception on set for invalid sessionId", function() {
|
|
expect(function() {
|
|
SD.sessionId = undefined
|
|
}).toThrowError("Can't set sessionId to undefined.")
|
|
})
|
|
})
|
|
})
|
|
|
|
const loadCompleted = window.onload
|
|
let loadEvent = undefined
|
|
window.onload = function(evt) {
|
|
loadEvent = evt
|
|
}
|
|
if (!PLUGINS.SELFTEST) {
|
|
PLUGINS.SELFTEST = {}
|
|
}
|
|
loadUIPlugins().then(function() {
|
|
console.log("loadCompleted", loadEvent)
|
|
describe("@Plugins", function() {
|
|
it("exposes hooks to overide", function() {
|
|
expect(typeof PLUGINS.IMAGE_INFO_BUTTONS).toBe("object")
|
|
expect(typeof PLUGINS.TASK_CREATE).toBe("object")
|
|
})
|
|
describe("supports selftests", function() {
|
|
// Hook to allow plugins to define tests.
|
|
const pluginsTests = Object.keys(PLUGINS.SELFTEST).filter((key) => PLUGINS.SELFTEST.hasOwnProperty(key))
|
|
if (!pluginsTests || pluginsTests.length <= 0) {
|
|
it("but nothing loaded...", function() {
|
|
expect(true).toBeTruthy()
|
|
})
|
|
return
|
|
}
|
|
for (const pTest of pluginsTests) {
|
|
describe(pTest, function() {
|
|
const testFn = PLUGINS.SELFTEST[pTest]
|
|
return Promise.resolve(testFn.call(jasmine, pTest))
|
|
})
|
|
}
|
|
})
|
|
})
|
|
loadCompleted.call(window, loadEvent)
|
|
})
|