From 49f1e180ffd165d651c0eb73a4815e13c69aab55 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Fri, 24 Jun 2022 20:22:06 +0300 Subject: [PATCH 01/16] feat: add caching to run failed and longer tests first --- docs/config/index.md | 13 ++++++ packages/vitest/src/node/cache.ts | 68 +++++++++++++++++++++++++++++ packages/vitest/src/node/config.ts | 8 +++- packages/vitest/src/node/core.ts | 17 ++++++-- packages/vitest/src/node/pool.ts | 17 ++++++++ packages/vitest/src/node/state.ts | 2 + packages/vitest/src/types/config.ts | 14 +++++- 7 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 packages/vitest/src/node/cache.ts diff --git a/docs/config/index.md b/docs/config/index.md index e32dde79acf1..1c39cd645334 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -564,3 +564,16 @@ RegExp pattern for files that will return en empty CSS file. A number of tests that are allowed to run at the same time marked with `test.concurrent`. Test above this limit will be queued to run when available slot appears. + +### cache + +- **Type**: `false | { path? }` + +Options to configure Vitest cache policy. At the moment Vitest stores cache for test results to run the longer and failed tests first. + +#### cache.path + +- **Type**: `string` +- **Default**: `node_modules/.vitest` + +Path to cache directory. diff --git a/packages/vitest/src/node/cache.ts b/packages/vitest/src/node/cache.ts new file mode 100644 index 000000000000..a843150a3435 --- /dev/null +++ b/packages/vitest/src/node/cache.ts @@ -0,0 +1,68 @@ +import { existsSync, promises as fsp } from 'fs' +import { dirname, resolve } from 'pathe' +import type { File, ResolvedConfig } from '../types' +import { version } from '../../package.json' + +interface SuiteResultCache { + failed: boolean + duration: number +} + +export class ResultsCache { + private cache = new Map() + public config!: ResolvedConfig['cache'] + + setConfig(config: ResolvedConfig['cache']) { + this.config = config + } + + getResults(id: string) { + return this.cache.get(id) + } + + async readFromCache() { + if (this.config === false) + return + + const resultsCachePath = resolve(this.config.path, 'results.json') + if (existsSync(resultsCachePath)) { + const resultsCache = await fsp.readFile(resultsCachePath, 'utf8') + this.cache = new Map(JSON.parse(resultsCache).results) + } + } + + updateResults(files: File[]) { + files.forEach((file) => { + const result = file.result + if (!result) + return + const duration = result.duration || 0 + this.cache.set(file.filepath, { + duration: duration >= 0 ? duration : 0, + failed: result.state === 'fail', + }) + }) + } + + removeFromCache(filepath: string) { + this.cache.delete(filepath) + } + + async writeToCache() { + if (this.config === false) + return + + const resultsCachePath = resolve(this.config.path, 'results.json') + const resultsCache = Array.from(this.cache.entries()) + + const cacheDirname = dirname(resultsCachePath) + + if (!existsSync(cacheDirname)) + await fsp.mkdir(cacheDirname, { recursive: true }) + + await fsp.writeFile(resultsCachePath, JSON.stringify({ + version, + results: resultsCache, + })) + } +} diff --git a/packages/vitest/src/node/config.ts b/packages/vitest/src/node/config.ts index db54616fb4f4..ec8ff773b77a 100644 --- a/packages/vitest/src/node/config.ts +++ b/packages/vitest/src/node/config.ts @@ -7,7 +7,7 @@ import type { ApiConfig, ResolvedConfig, UserConfig } from '../types' import { defaultPort } from '../constants' import { configDefaults } from '../defaults' import { resolveC8Options } from '../integrations/coverage' -import { toArray } from '../utils' +import { slash, toArray } from '../utils' const extraInlineDeps = [ /^(?!.*(?:node_modules)).*\.mjs$/, @@ -181,5 +181,11 @@ export function resolveConfig( if (typeof resolved.css === 'object') resolved.css.include ??= [/\.module\./] + resolved.cache ??= { path: '' } + if (resolved.cache) { + const path = slash(resolved.cache.path || 'node_modules/.vitest') + resolved.cache.path = resolve(resolved.root, path) + } + return resolved } diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index f9cd5d1df83b..da64992b04e4 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -91,6 +91,9 @@ export class Vitest { if (resolved.coverage.enabled) await cleanCoverage(resolved.coverage, resolved.coverage.clean) + + this.state.results.setConfig(resolved.cache) + await this.state.results.readFromCache() } getSerializableConfig() { @@ -205,7 +208,7 @@ export class Vitest { return runningTests } - async runFiles(files: string[]) { + async runFiles(paths: string[]) { await this.runningPromise this.runningPromise = (async () => { @@ -217,16 +220,21 @@ export class Vitest { this.snapshot.clear() this.state.clearErrors() try { - await this.pool.runTests(files, invalidates) + await this.pool.runTests(paths, invalidates) } catch (err) { this.state.catchError(err, 'Unhandled Error') } - if (hasFailed(this.state.getFiles())) + const files = this.state.getFiles() + + if (hasFailed(files)) process.exitCode = 1 - await this.report('onFinished', this.state.getFiles(), this.state.getUnhandledErrors()) + await this.report('onFinished', files, this.state.getUnhandledErrors()) + + this.state.results.updateResults(files) + await this.state.results.writeToCache() })() .finally(() => { this.runningPromise = undefined @@ -352,6 +360,7 @@ export class Vitest { if (this.state.filesMap.has(id)) { this.state.filesMap.delete(id) + this.state.results.removeFromCache(id) this.changedTests.delete(id) this.report('onTestRemoved', id) } diff --git a/packages/vitest/src/node/pool.ts b/packages/vitest/src/node/pool.ts index 227d745b8336..03437659140b 100644 --- a/packages/vitest/src/node/pool.ts +++ b/packages/vitest/src/node/pool.ts @@ -110,6 +110,23 @@ export function createPool(ctx: Vitest): WorkerPool { .map(({ file }) => file) } + files = files.sort((a, b) => { + const aState = ctx.state.results.getResults(a) + const bState = ctx.state.results.getResults(b) + + if (!aState || !bState) + return !aState && bState ? -1 : !bState && aState ? 1 : 0 + + // run failed first + if (aState.failed && !bState.failed) + return -1 + if (!aState.failed && bState.failed) + return 1 + + // run longer first + return bState.duration - aState.duration + }) + if (!ctx.config.threads) { await runFiles(config, files) } diff --git a/packages/vitest/src/node/state.ts b/packages/vitest/src/node/state.ts index c39976d47615..d6eaf1db74d9 100644 --- a/packages/vitest/src/node/state.ts +++ b/packages/vitest/src/node/state.ts @@ -1,10 +1,12 @@ import type { ErrorWithDiff, File, Task, TaskResultPack, UserConsoleLog } from '../types' +import { ResultsCache } from './cache' export class StateManager { filesMap = new Map() idMap = new Map() taskFileMap = new WeakMap() errorsSet = new Set() + results = new ResultsCache() catchError(err: unknown, type: string) { (err as ErrorWithDiff).type = type diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index f5abed1209bf..6728668e24b5 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -361,6 +361,14 @@ export interface InlineConfig { * @default 5 */ maxConcurrency?: number + + /** + * Options for configuring cache policy. + * @default { path: 'node_modules/.vitest' } + */ + cache?: false | { + path?: string + } } export interface UserConfig extends InlineConfig { @@ -407,7 +415,7 @@ export interface UserConfig extends InlineConfig { shard?: string } -export interface ResolvedConfig extends Omit, 'config' | 'filters' | 'coverage' | 'testNamePattern' | 'related' | 'api' | 'reporters' | 'resolveSnapshotPath' | 'shard'> { +export interface ResolvedConfig extends Omit, 'config' | 'filters' | 'coverage' | 'testNamePattern' | 'related' | 'api' | 'reporters' | 'resolveSnapshotPath' | 'shard' | 'cache'> { base?: string config?: string @@ -427,4 +435,8 @@ export interface ResolvedConfig extends Omit, 'config' | 'f index: number count: number } + + cache: { + path: string + } | false } From 855144bb961d5b5846becdf58e19b313925a11c0 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Fri, 24 Jun 2022 20:25:32 +0300 Subject: [PATCH 02/16] chore: cleanup --- packages/vitest/src/node/cache.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/vitest/src/node/cache.ts b/packages/vitest/src/node/cache.ts index a843150a3435..e5c0530a56bf 100644 --- a/packages/vitest/src/node/cache.ts +++ b/packages/vitest/src/node/cache.ts @@ -1,4 +1,4 @@ -import { existsSync, promises as fsp } from 'fs' +import fs from 'fs' import { dirname, resolve } from 'pathe' import type { File, ResolvedConfig } from '../types' import { version } from '../../package.json' @@ -25,8 +25,8 @@ export class ResultsCache { return const resultsCachePath = resolve(this.config.path, 'results.json') - if (existsSync(resultsCachePath)) { - const resultsCache = await fsp.readFile(resultsCachePath, 'utf8') + if (fs.existsSync(resultsCachePath)) { + const resultsCache = await fs.promises.readFile(resultsCachePath, 'utf8') this.cache = new Map(JSON.parse(resultsCache).results) } } @@ -57,10 +57,10 @@ export class ResultsCache { const cacheDirname = dirname(resultsCachePath) - if (!existsSync(cacheDirname)) - await fsp.mkdir(cacheDirname, { recursive: true }) + if (!fs.existsSync(cacheDirname)) + await fs.promises.mkdir(cacheDirname, { recursive: true }) - await fsp.writeFile(resultsCachePath, JSON.stringify({ + await fs.promises.writeFile(resultsCachePath, JSON.stringify({ version, results: resultsCache, })) From 174ab62835d765f8fa6fa2185bca89196c4ff1b0 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Sat, 2 Jul 2022 09:39:36 +0300 Subject: [PATCH 03/16] chore: add clearCache --- docs/config/index.md | 4 +-- docs/guide/cli.md | 4 +++ packages/vitest/src/node/cache/index.ts | 36 +++++++++++++++++++ .../src/node/{cache.ts => cache/results.ts} | 23 ++++++------ packages/vitest/src/node/cli.ts | 4 +++ packages/vitest/src/node/config.ts | 11 +++--- packages/vitest/src/node/state.ts | 2 +- packages/vitest/src/types/config.ts | 6 ++-- test/cache/.gitignore | 1 + test/cache/package.json | 11 ++++++ test/cache/test/clear-cache.test.ts | 26 ++++++++++++++ test/cache/vitest-custom.config.ts | 9 +++++ test/cache/vitest.config.ts | 10 ++++++ 13 files changed, 123 insertions(+), 24 deletions(-) create mode 100644 packages/vitest/src/node/cache/index.ts rename packages/vitest/src/node/{cache.ts => cache/results.ts} (64%) create mode 100644 test/cache/.gitignore create mode 100644 test/cache/package.json create mode 100644 test/cache/test/clear-cache.test.ts create mode 100644 test/cache/vitest-custom.config.ts create mode 100644 test/cache/vitest.config.ts diff --git a/docs/config/index.md b/docs/config/index.md index 1c39cd645334..e6f0ef760c36 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -567,11 +567,11 @@ Test above this limit will be queued to run when available slot appears. ### cache -- **Type**: `false | { path? }` +- **Type**: `false | { dir? }` Options to configure Vitest cache policy. At the moment Vitest stores cache for test results to run the longer and failed tests first. -#### cache.path +#### cache.dir - **Type**: `string` - **Default**: `node_modules/.vitest` diff --git a/docs/guide/cli.md b/docs/guide/cli.md index 0699d1535fea..c5ee86dde314 100644 --- a/docs/guide/cli.md +++ b/docs/guide/cli.md @@ -36,6 +36,10 @@ Useful to run with [`lint-staged`](https://github.com/okonet/lint-staged) or wit vitest related /src/index.ts /src/hello-world.js ``` +### `vitest clearCache` + +Clears cache folder. + ## Options | Options | | diff --git a/packages/vitest/src/node/cache/index.ts b/packages/vitest/src/node/cache/index.ts new file mode 100644 index 000000000000..c18d5612d90c --- /dev/null +++ b/packages/vitest/src/node/cache/index.ts @@ -0,0 +1,36 @@ +import fs from 'fs' +import { findUp } from 'find-up' +import { resolve } from 'pathe' +import { loadConfigFromFile } from 'vite' +import { configFiles } from '../../constants' +import type { CliOptions } from '../cli-api' +import { slash } from '../../utils' + +export class VitestCache { + static resolveCacheDir(root: string, dir: string | undefined) { + return resolve(root, slash(dir || 'node_modules/.vitest')) + } + + static async clearCache(options: CliOptions) { + const root = resolve(options.root || process.cwd()) + + const configPath = options.config + ? resolve(root, options.config) + : await findUp(configFiles, { cwd: root } as any) + + const config = await loadConfigFromFile({ command: 'serve', mode: 'test' }, configPath) + + if (!config) + throw new Error(`[vitest] Not able to load config from ${configPath}`) + + const cache = config.config.test?.cache + + if (!cache) + throw new Error('[vitest] Cache is disabled') + + const cachePath = VitestCache.resolveCacheDir(root, cache.dir) + + if (fs.existsSync(cachePath)) + fs.rmSync(cachePath, { recursive: true, force: true }) + } +} diff --git a/packages/vitest/src/node/cache.ts b/packages/vitest/src/node/cache/results.ts similarity index 64% rename from packages/vitest/src/node/cache.ts rename to packages/vitest/src/node/cache/results.ts index e5c0530a56bf..1ef0f97e521f 100644 --- a/packages/vitest/src/node/cache.ts +++ b/packages/vitest/src/node/cache/results.ts @@ -1,7 +1,7 @@ import fs from 'fs' import { dirname, resolve } from 'pathe' -import type { File, ResolvedConfig } from '../types' -import { version } from '../../package.json' +import type { File, ResolvedConfig } from '../../types' +import { version } from '../../../package.json' interface SuiteResultCache { failed: boolean @@ -10,10 +10,11 @@ interface SuiteResultCache { export class ResultsCache { private cache = new Map() - public config!: ResolvedConfig['cache'] + private cachePath: string | null = null setConfig(config: ResolvedConfig['cache']) { - this.config = config + if (config) + this.cachePath = resolve(config.dir, 'results.json') } getResults(id: string) { @@ -21,12 +22,11 @@ export class ResultsCache { } async readFromCache() { - if (this.config === false) + if (!this.cachePath) return - const resultsCachePath = resolve(this.config.path, 'results.json') - if (fs.existsSync(resultsCachePath)) { - const resultsCache = await fs.promises.readFile(resultsCachePath, 'utf8') + if (fs.existsSync(this.cachePath)) { + const resultsCache = await fs.promises.readFile(this.cachePath, 'utf8') this.cache = new Map(JSON.parse(resultsCache).results) } } @@ -49,18 +49,17 @@ export class ResultsCache { } async writeToCache() { - if (this.config === false) + if (!this.cachePath) return - const resultsCachePath = resolve(this.config.path, 'results.json') const resultsCache = Array.from(this.cache.entries()) - const cacheDirname = dirname(resultsCachePath) + const cacheDirname = dirname(this.cachePath) if (!fs.existsSync(cacheDirname)) await fs.promises.mkdir(cacheDirname, { recursive: true }) - await fs.promises.writeFile(resultsCachePath, JSON.stringify({ + await fs.promises.writeFile(this.cachePath, JSON.stringify({ version, results: resultsCache, })) diff --git a/packages/vitest/src/node/cli.ts b/packages/vitest/src/node/cli.ts index 496f4ce88f28..02cdab26363c 100644 --- a/packages/vitest/src/node/cli.ts +++ b/packages/vitest/src/node/cli.ts @@ -1,6 +1,7 @@ import cac from 'cac' import c from 'picocolors' import { version } from '../../package.json' +import { VitestCache } from './cache' import type { CliOptions } from './cli-api' import { startVitest } from './cli-api' import { divider } from './reporters/renderers/utils' @@ -57,6 +58,9 @@ cli .command('[...filters]') .action(start) +cli.command('clearCache') + .action(VitestCache.clearCache) + cli.parse() async function runRelated(relatedFiles: string[] | string, argv: CliOptions) { diff --git a/packages/vitest/src/node/config.ts b/packages/vitest/src/node/config.ts index ec8ff773b77a..7ec1791bcfb7 100644 --- a/packages/vitest/src/node/config.ts +++ b/packages/vitest/src/node/config.ts @@ -7,7 +7,8 @@ import type { ApiConfig, ResolvedConfig, UserConfig } from '../types' import { defaultPort } from '../constants' import { configDefaults } from '../defaults' import { resolveC8Options } from '../integrations/coverage' -import { slash, toArray } from '../utils' +import { toArray } from '../utils' +import { VitestCache } from './cache' const extraInlineDeps = [ /^(?!.*(?:node_modules)).*\.mjs$/, @@ -181,11 +182,9 @@ export function resolveConfig( if (typeof resolved.css === 'object') resolved.css.include ??= [/\.module\./] - resolved.cache ??= { path: '' } - if (resolved.cache) { - const path = slash(resolved.cache.path || 'node_modules/.vitest') - resolved.cache.path = resolve(resolved.root, path) - } + resolved.cache ??= { dir: '' } + if (resolved.cache) + resolved.cache.dir = VitestCache.resolveCacheDir(resolved.root, resolved.cache.dir) return resolved } diff --git a/packages/vitest/src/node/state.ts b/packages/vitest/src/node/state.ts index d6eaf1db74d9..34b2dc1ac7fd 100644 --- a/packages/vitest/src/node/state.ts +++ b/packages/vitest/src/node/state.ts @@ -1,5 +1,5 @@ import type { ErrorWithDiff, File, Task, TaskResultPack, UserConsoleLog } from '../types' -import { ResultsCache } from './cache' +import { ResultsCache } from './cache/results' export class StateManager { filesMap = new Map() diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index 6728668e24b5..656919ddfdf0 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -364,10 +364,10 @@ export interface InlineConfig { /** * Options for configuring cache policy. - * @default { path: 'node_modules/.vitest' } + * @default { dir: 'node_modules/.vitest' } */ cache?: false | { - path?: string + dir?: string } } @@ -437,6 +437,6 @@ export interface ResolvedConfig extends Omit, 'config' | 'f } cache: { - path: string + dir: string } | false } diff --git a/test/cache/.gitignore b/test/cache/.gitignore new file mode 100644 index 000000000000..f8a91fd2ba6b --- /dev/null +++ b/test/cache/.gitignore @@ -0,0 +1 @@ +cache/* \ No newline at end of file diff --git a/test/cache/package.json b/test/cache/package.json new file mode 100644 index 000000000000..f7632e3d4479 --- /dev/null +++ b/test/cache/package.json @@ -0,0 +1,11 @@ +{ + "name": "@vitest/test-cache", + "private": true, + "scripts": { + "test": "vitest", + "coverage": "vitest run --coverage" + }, + "devDependencies": { + "vitest": "workspace:*" + } +} diff --git a/test/cache/test/clear-cache.test.ts b/test/cache/test/clear-cache.test.ts new file mode 100644 index 000000000000..c96c1af83c5e --- /dev/null +++ b/test/cache/test/clear-cache.test.ts @@ -0,0 +1,26 @@ +import fs, { promises as fsp } from 'fs' +import { resolve } from 'pathe' +import { describe, expect, test } from 'vitest' +import { VitestCache } from '../../../packages/vitest/src/node/cache/index' + +const root = resolve(__dirname, '..') + +const pathBase = resolve(root, 'cache/.vitest-base') +const pathCustom = resolve(root, 'cache/.vitest-custom') + +describe('vitest cache', async () => { + await fsp.mkdir(pathBase, { recursive: true }) + await fsp.mkdir(pathCustom, { recursive: true }) + + test('clears cache without specifying config path', async () => { + await VitestCache.clearCache({}) + + expect(fs.existsSync(pathBase)).toBe(false) + }) + + test('clears cache with specified config path', async () => { + await VitestCache.clearCache({ config: 'vitest-custom.config.ts' }) + + expect(fs.existsSync(pathCustom)).toBe(false) + }) +}) diff --git a/test/cache/vitest-custom.config.ts b/test/cache/vitest-custom.config.ts new file mode 100644 index 000000000000..6087616010f8 --- /dev/null +++ b/test/cache/vitest-custom.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vite' + +export default defineConfig({ + test: { + cache: { + dir: 'cache/.vitest-custom', + }, + }, +}) diff --git a/test/cache/vitest.config.ts b/test/cache/vitest.config.ts new file mode 100644 index 000000000000..2822c141f94f --- /dev/null +++ b/test/cache/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vite' + +export default defineConfig({ + test: { + threads: false, + cache: { + dir: 'cache/.vitest-base', + }, + }, +}) From 37199e7b778d848889bf2b3434ef2b6887e73d4a Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Sat, 2 Jul 2022 09:41:03 +0300 Subject: [PATCH 04/16] chore: update lockfile --- pnpm-lock.yaml | 129 +++++++++++++++++++++++++------------------------ 1 file changed, 66 insertions(+), 63 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9ce95af5be8b..815eea7092c7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -108,11 +108,10 @@ importers: esno: 0.14.1 fast-glob: 3.2.11 https-localhost: 4.7.1 - unocss: 0.39.3_vite@2.9.12 - unplugin-vue-components: 0.19.6_7felabkkrfdt7dxozyactxvh24 - vite: 2.9.12 - vite-plugin-pwa: 0.12.0_vite@2.9.12 - vitepress: 1.0.0-alpha.4_ohobp6rpsmerwlq5ipwfh5yigy + unocss: 0.39.3_vite@2.9.10 + unplugin-vue-components: 0.19.6_hd26raqwpnm7hl4c5jnifs3the + vite-plugin-pwa: 0.12.0_vite@2.9.10 + vitepress: 1.0.0-alpha.2_ohobp6rpsmerwlq5ipwfh5yigy workbox-window: 6.5.3 examples/basic: @@ -163,8 +162,8 @@ importers: lit: 2.2.5 devDependencies: '@vitest/ui': link:../../packages/ui - happy-dom: 5.3.2 - vite: 2.9.12 + happy-dom: 6.0.0 + vite: 2.9.10 vitest: link:../../packages/vitest examples/mocks: @@ -268,7 +267,7 @@ importers: '@vitest/ui': link:../../packages/ui enzyme: 3.11.0 enzyme-adapter-react-16: 1.15.6_7ltvq4e2railvf5uya4ffxpe2a - vite: 2.9.12 + vite: 2.9.10 vitest: link:../../packages/vitest examples/react-mui: @@ -295,7 +294,7 @@ importers: dependencies: '@emotion/react': 11.9.0_3dj5wppwohj5ocihzt4m54mr2a '@emotion/styled': 11.8.1_3zgpe2oef7sbs566rsy6a7qm7i - '@mui/lab': 5.0.0-alpha.87_xjii3a7ufdwggkrf6imrcribfm + '@mui/lab': 5.0.0-alpha.88_xjii3a7ufdwggkrf6imrcribfm '@mui/material': 5.8.3_rikzftoujo3cmwul4lespwcm6i history: 5.3.0 notistack: 2.0.5_ozyq7dscrvuimqma7xstouyo4e @@ -766,6 +765,12 @@ importers: devDependencies: rollup: 2.75.7 + test/cache: + specifiers: + vitest: workspace:* + devDependencies: + vitest: link:../../packages/vitest + test/cjs: specifiers: '@types/fs-extra': ^9.0.13 @@ -3736,8 +3741,8 @@ packages: react-is: 17.0.2 dev: false - /@mui/base/5.0.0-alpha.86_sfoxds7t5ydpegc3knd667wn6m: - resolution: {integrity: sha512-0vi/Nni1mizrgrzKeyksEjw5JVSrgT8Vr2NhxzFtYxqpMgtdSrBvcmcuzBf9kE/ECMPbgpSIcqv0nLbLZUYkOQ==} + /@mui/base/5.0.0-alpha.87_sfoxds7t5ydpegc3knd667wn6m: + resolution: {integrity: sha512-PuxRYrvG63Yj/UTwf4hSwZ5ClMv88iXHK+5hUV1CrG3kNPo6FFQiIFNRaNpRt/3nsXj6+xygJByNFA8m4Leetg==} engines: {node: '>=12.0.0'} peerDependencies: '@types/react': ^17.0.0 || ^18.0.0 @@ -3750,7 +3755,7 @@ packages: '@babel/runtime': 7.17.9 '@emotion/is-prop-valid': 1.1.2 '@mui/types': 7.1.4 - '@mui/utils': 5.8.4_react@17.0.2 + '@mui/utils': 5.8.6_react@17.0.2 '@popperjs/core': 2.11.5 clsx: 1.1.1 prop-types: 15.8.1 @@ -3759,8 +3764,8 @@ packages: react-is: 17.0.2 dev: false - /@mui/lab/5.0.0-alpha.87_xjii3a7ufdwggkrf6imrcribfm: - resolution: {integrity: sha512-Zuth3wdX5wSpn1aMJsnJqX1lRiNTUcUs7HaCQI1AufbuE+84Drl5c5kANLtb6VOEGoThqvbINpne+oGGoJuqwA==} + /@mui/lab/5.0.0-alpha.88_xjii3a7ufdwggkrf6imrcribfm: + resolution: {integrity: sha512-YS2NPw0D0CHG9z9Y6Wjocl3g2LNzdXdkvORPoyc05ea9Xm+m8buddvMeTuL/r/e3S7yLK8HOMN2uHE0rwD/oVQ==} engines: {node: '>=12.0.0'} peerDependencies: '@emotion/react': ^11.5.0 @@ -3792,11 +3797,11 @@ packages: '@babel/runtime': 7.17.9 '@emotion/react': 11.9.0_3dj5wppwohj5ocihzt4m54mr2a '@emotion/styled': 11.8.1_3zgpe2oef7sbs566rsy6a7qm7i - '@mui/base': 5.0.0-alpha.86_sfoxds7t5ydpegc3knd667wn6m + '@mui/base': 5.0.0-alpha.87_sfoxds7t5ydpegc3knd667wn6m '@mui/material': 5.8.3_rikzftoujo3cmwul4lespwcm6i - '@mui/system': 5.8.5_bgqmsvm4hz6izcmpcwescmz73y - '@mui/utils': 5.8.4_react@17.0.2 - '@mui/x-date-pickers': 5.0.0-alpha.1_4app2jldh5bx4balclxn7wcw7i + '@mui/system': 5.8.6_bgqmsvm4hz6izcmpcwescmz73y + '@mui/utils': 5.8.6_react@17.0.2 + '@mui/x-date-pickers': 5.0.0-alpha.1_a27cws6df4ygyi4adk5k5wbyme clsx: 1.1.1 date-fns: 2.28.0 prop-types: 15.8.1 @@ -3858,6 +3863,22 @@ packages: react: 17.0.2 dev: false + /@mui/private-theming/5.8.6_react@17.0.2: + resolution: {integrity: sha512-yHsJk1qU9r/q0DlnxGRJPHyM0Y/nUv8FTNgDTiI9I58GWuVuZqeTUr7JRvPh6ybeP/FLtW5eXEavRK9wxVk4uQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.17.9 + '@mui/utils': 5.8.6_react@17.0.2 + prop-types: 15.8.1 + react: 17.0.2 + dev: false + /@mui/styled-engine/5.8.0_bgqmsvm4hz6izcmpcwescmz73y: resolution: {integrity: sha512-Q3spibB8/EgeMYHc+/o3RRTnAYkSl7ROCLhXJ830W8HZ2/iDiyYp16UcxKPurkXvLhUaILyofPVrP3Su2uKsAw==} engines: {node: '>=12.0.0'} @@ -3908,8 +3929,8 @@ packages: react: 17.0.2 dev: false - /@mui/system/5.8.5_bgqmsvm4hz6izcmpcwescmz73y: - resolution: {integrity: sha512-1bhITHp5sX/CVEf1QwtBWvW+kNnH+GU7lKz0CeAL1RyH9dWvoL9Yt/+i/L8hJ6jVZB/7Au2F6MsyDPt8V1jfdA==} + /@mui/system/5.8.6_bgqmsvm4hz6izcmpcwescmz73y: + resolution: {integrity: sha512-+a+rD58XltKQHDrrjcuCta2cUBqdnLDUDwnphSLCMFigRl8/uk+R+fdQRlMNRXAOgnMb8ioWIgfjxri5pmTH4A==} engines: {node: '>=12.0.0'} peerDependencies: '@emotion/react': ^11.5.0 @@ -3927,10 +3948,10 @@ packages: '@babel/runtime': 7.17.9 '@emotion/react': 11.9.0_3dj5wppwohj5ocihzt4m54mr2a '@emotion/styled': 11.8.1_3zgpe2oef7sbs566rsy6a7qm7i - '@mui/private-theming': 5.8.4_react@17.0.2 + '@mui/private-theming': 5.8.6_react@17.0.2 '@mui/styled-engine': 5.8.0_bgqmsvm4hz6izcmpcwescmz73y '@mui/types': 7.1.4 - '@mui/utils': 5.8.4_react@17.0.2 + '@mui/utils': 5.8.6_react@17.0.2 clsx: 1.1.1 csstype: 3.1.0 prop-types: 15.8.1 @@ -3983,7 +4004,21 @@ packages: react-is: 17.0.2 dev: false - /@mui/x-date-pickers/5.0.0-alpha.1_4app2jldh5bx4balclxn7wcw7i: + /@mui/utils/5.8.6_react@17.0.2: + resolution: {integrity: sha512-QM2Sd1xZo2jOt2Vz5Rmro+pi2FLJyiv4+OjxkUwXR3oUM65KSMAMLl/KNYU55s3W3DLRFP5MVwE4FhAbHseHAg==} + engines: {node: '>=12.0.0'} + peerDependencies: + react: ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.17.9 + '@types/prop-types': 15.7.5 + '@types/react-is': 17.0.3 + prop-types: 15.8.1 + react: 17.0.2 + react-is: 17.0.2 + dev: false + + /@mui/x-date-pickers/5.0.0-alpha.1_a27cws6df4ygyi4adk5k5wbyme: resolution: {integrity: sha512-dLPkRiIn2Gr0momblxiOnIwrxn4SijVix+8e08mwAGWhiWcmWep1O9XTRDpZsjB0kjHYCf+kZjlRX4dxnj2acg==} engines: {node: '>=12.0.0'} peerDependencies: @@ -4010,8 +4045,8 @@ packages: '@date-io/luxon': 2.13.1 '@date-io/moment': 2.13.1 '@mui/material': 5.8.3_rikzftoujo3cmwul4lespwcm6i - '@mui/system': 5.8.5_bgqmsvm4hz6izcmpcwescmz73y - '@mui/utils': 5.8.4_react@17.0.2 + '@mui/system': 5.8.6_bgqmsvm4hz6izcmpcwescmz73y + '@mui/utils': 5.8.6_react@17.0.2 clsx: 1.1.1 date-fns: 2.28.0 prop-types: 15.8.1 @@ -12828,8 +12863,8 @@ packages: - encoding dev: true - /happy-dom/5.3.2: - resolution: {integrity: sha512-TVTS1wGeSi3oCi0gqBA97DvsvS5nOuB2GTSdQlvxQ/JgntDjXHbf/xj1VMXenLomGjm9Keu0IW5ExBXHhoE8zQ==} + /happy-dom/6.0.0: + resolution: {integrity: sha512-Nk+hyCemZGIyQ0gDLyb6K1PFOHepfec101dG93z5g8+LIYSUZWT3FDzWUIUwfYkcAnaRCM9hMDgmR2pFLUYE9w==} dependencies: css.escape: 1.5.1 he: 1.2.0 @@ -20045,39 +20080,7 @@ packages: - webpack dev: true - /unplugin-vue-components/0.19.6_vite@2.9.13+vue@3.2.36: - resolution: {integrity: sha512-APvrJ9Hpid1MLT0G4PWerMJgARhNw6dzz0pcCwCxaO2DR7VyvDacMqjOQNC6ukq7FSw3wzD8VH+9i3EFXwkGmw==} - engines: {node: '>=14'} - peerDependencies: - '@babel/parser': ^7.15.8 - '@babel/traverse': ^7.15.4 - vue: 2 || 3 - peerDependenciesMeta: - '@babel/parser': - optional: true - '@babel/traverse': - optional: true - dependencies: - '@antfu/utils': 0.5.2 - '@rollup/pluginutils': 4.2.1 - chokidar: 3.5.3 - debug: 4.3.4 - fast-glob: 3.2.11 - local-pkg: 0.4.1 - magic-string: 0.26.2 - minimatch: 5.0.1 - resolve: 1.22.0 - unplugin: 0.6.3_vite@2.9.13 - vue: 3.2.36 - transitivePeerDependencies: - - esbuild - - rollup - - supports-color - - vite - - webpack - dev: true - - /unplugin-vue2-script-setup/0.10.2_o74ad4fw2dfsqw4anmljjinhmu: + /unplugin-vue2-script-setup/0.10.2_2bloa65zsoqadlob4lncrwg3um: resolution: {integrity: sha512-m886AZIZCXMiAbTbOPQEk+MqmtHBX3dENheFXjcHSRetwBsD0KVDBteQVD5XrSBzDdQKeKw6IU4dV+2eGcLJ0A==} peerDependencies: '@vue/composition-api': ^1.4.3 @@ -20659,14 +20662,14 @@ packages: optionalDependencies: fsevents: 2.3.2 - /vitepress/1.0.0-alpha.4_ohobp6rpsmerwlq5ipwfh5yigy: - resolution: {integrity: sha512-bOAA4KW6vYGlkbcrPLZLTKWTgXVroObU+o9xj9EENyEl6yg26WWvfN7DGA4BftjdM5O8nR93Z5khPQ3W/tFE7Q==} + /vitepress/1.0.0-alpha.2_ohobp6rpsmerwlq5ipwfh5yigy: + resolution: {integrity: sha512-twSsmx2DI/3XgZZ8KLyRAH8RgK2Zj4kJx/kwhZhimwndYqQDrtcIaAcuV+P3FKukZ+cYtm9yt9qohz631jKx4A==} engines: {node: '>=14.6.0'} hasBin: true dependencies: '@docsearch/css': 3.1.0 '@docsearch/js': 3.1.0_ohobp6rpsmerwlq5ipwfh5yigy - '@vitejs/plugin-vue': 2.3.3_vite@2.9.13+vue@3.2.37 + '@vitejs/plugin-vue': 2.3.3_vite@2.9.10+vue@3.2.37 '@vue/devtools-api': 6.1.4 '@vueuse/core': 8.7.4_vue@3.2.37 body-scroll-lock: 4.0.0-beta.0 From cbee24ccdf0fc4416cc23717bd06f6d0096fed9c Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Sat, 2 Jul 2022 09:50:59 +0300 Subject: [PATCH 05/16] chore: add filesCache --- packages/vitest/src/node/cache/files.ts | 23 +++++++++++++++++++++++ packages/vitest/src/node/core.ts | 5 +++++ packages/vitest/src/node/pool.ts | 12 ++++++++++-- packages/vitest/src/node/state.ts | 2 ++ 4 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 packages/vitest/src/node/cache/files.ts diff --git a/packages/vitest/src/node/cache/files.ts b/packages/vitest/src/node/cache/files.ts new file mode 100644 index 000000000000..19a3a1c8e816 --- /dev/null +++ b/packages/vitest/src/node/cache/files.ts @@ -0,0 +1,23 @@ +import fs, { type Stats } from 'fs' + +type FileCacheStats = Pick + +export class FilesCache { + public cache = new Map() + + public getStats(fsPath: string): FileCacheStats | undefined { + return this.cache.get(fsPath) + } + + public async updateStats(fsPath: string) { + if (!fs.existsSync(fsPath)) + return + + const stats = await fs.promises.stat(fsPath) + this.cache.set(fsPath, { size: stats.size }) + } + + public removeStats(fsPath: string) { + this.cache.delete(fsPath) + } +} diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index da64992b04e4..c888742b45e3 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -136,6 +136,9 @@ export class Vitest { process.exit(exitCode) } + // populate once, update cache on watch + await Promise.all(files.map(file => this.state.stats.updateStats(file))) + await this.runFiles(files) if (this.config.coverage.enabled) @@ -361,6 +364,7 @@ export class Vitest { if (this.state.filesMap.has(id)) { this.state.filesMap.delete(id) this.state.results.removeFromCache(id) + this.state.stats.removeStats(id) this.changedTests.delete(id) this.report('onTestRemoved', id) } @@ -369,6 +373,7 @@ export class Vitest { id = slash(id) if (await this.isTargetFile(id)) { this.changedTests.add(id) + await this.state.stats.updateStats(id) this.scheduleRerun(id) } } diff --git a/packages/vitest/src/node/pool.ts b/packages/vitest/src/node/pool.ts index 03437659140b..9e48dae56200 100644 --- a/packages/vitest/src/node/pool.ts +++ b/packages/vitest/src/node/pool.ts @@ -114,8 +114,16 @@ export function createPool(ctx: Vitest): WorkerPool { const aState = ctx.state.results.getResults(a) const bState = ctx.state.results.getResults(b) - if (!aState || !bState) - return !aState && bState ? -1 : !bState && aState ? 1 : 0 + if (!aState || !bState) { + const statsA = ctx.state.stats.getStats(a) + const statsB = ctx.state.stats.getStats(b) + + if (!statsA || !statsB) + return !aState && bState ? -1 : !bState && aState ? 1 : 0 + + // run larger files first + return statsB.size - statsA.size + } // run failed first if (aState.failed && !bState.failed) diff --git a/packages/vitest/src/node/state.ts b/packages/vitest/src/node/state.ts index 34b2dc1ac7fd..b899a8fb9d3d 100644 --- a/packages/vitest/src/node/state.ts +++ b/packages/vitest/src/node/state.ts @@ -1,4 +1,5 @@ import type { ErrorWithDiff, File, Task, TaskResultPack, UserConsoleLog } from '../types' +import { FilesCache } from './cache/files' import { ResultsCache } from './cache/results' export class StateManager { @@ -7,6 +8,7 @@ export class StateManager { taskFileMap = new WeakMap() errorsSet = new Set() results = new ResultsCache() + stats = new FilesCache() catchError(err: unknown, type: string) { (err as ErrorWithDiff).type = type From 92dd0234e437cd9b1873f9dbf719e814cdd14430 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Sat, 2 Jul 2022 10:27:12 +0300 Subject: [PATCH 06/16] refactor: add sequelizer --- packages/vitest/src/node/cache/results.ts | 2 +- packages/vitest/src/node/pool.ts | 53 ++-------- .../src/node/sequelizers/BaseSequelizer.ts | 66 +++++++++++++ packages/vitest/src/node/sequelizers/types.ts | 10 ++ packages/vitest/src/node/state.ts | 8 ++ test/core/test/sequelizers.test.ts | 97 +++++++++++++++++++ 6 files changed, 189 insertions(+), 47 deletions(-) create mode 100644 packages/vitest/src/node/sequelizers/BaseSequelizer.ts create mode 100644 packages/vitest/src/node/sequelizers/types.ts create mode 100644 test/core/test/sequelizers.test.ts diff --git a/packages/vitest/src/node/cache/results.ts b/packages/vitest/src/node/cache/results.ts index 1ef0f97e521f..151bb940fb98 100644 --- a/packages/vitest/src/node/cache/results.ts +++ b/packages/vitest/src/node/cache/results.ts @@ -3,7 +3,7 @@ import { dirname, resolve } from 'pathe' import type { File, ResolvedConfig } from '../../types' import { version } from '../../../package.json' -interface SuiteResultCache { +export interface SuiteResultCache { failed: boolean duration: number } diff --git a/packages/vitest/src/node/pool.ts b/packages/vitest/src/node/pool.ts index 9e48dae56200..bc1cb0b9492e 100644 --- a/packages/vitest/src/node/pool.ts +++ b/packages/vitest/src/node/pool.ts @@ -1,7 +1,6 @@ import { MessageChannel } from 'worker_threads' import { pathToFileURL } from 'url' import { cpus } from 'os' -import { createHash } from 'crypto' import { resolve } from 'pathe' import type { Options as TinypoolOptions } from 'tinypool' import { Tinypool } from 'tinypool' @@ -9,8 +8,9 @@ import { createBirpc } from 'birpc' import type { RawSourceMap } from 'vite-node' import type { ResolvedConfig, WorkerContext, WorkerRPC } from '../types' import { distDir } from '../constants' -import { AggregateError, slash } from '../utils' +import { AggregateError } from '../utils' import type { Vitest } from './core' +import { BaseSequelizer } from './sequelizers/BaseSequelizer' export type RunWithFiles = (files: string[], invalidates?: string[]) => Promise @@ -86,54 +86,15 @@ export function createPool(ctx: Vitest): WorkerPool { } } + const sequelizer = new BaseSequelizer(ctx) + return async (files, invalidates) => { const config = ctx.getSerializableConfig() - if (config.shard) { - const { index, count } = config.shard - const shardSize = Math.ceil(files.length / count) - const shardStart = shardSize * (index - 1) - const shardEnd = shardSize * index - files = files - .map((file) => { - const fullPath = resolve(slash(config.root), slash(file)) - const specPath = fullPath.slice(config.root.length) - return { - file, - hash: createHash('sha1') - .update(specPath) - .digest('hex'), - } - }) - .sort((a, b) => (a.hash < b.hash ? -1 : a.hash > b.hash ? 1 : 0)) - .slice(shardStart, shardEnd) - .map(({ file }) => file) - } - - files = files.sort((a, b) => { - const aState = ctx.state.results.getResults(a) - const bState = ctx.state.results.getResults(b) - - if (!aState || !bState) { - const statsA = ctx.state.stats.getStats(a) - const statsB = ctx.state.stats.getStats(b) - - if (!statsA || !statsB) - return !aState && bState ? -1 : !bState && aState ? 1 : 0 - - // run larger files first - return statsB.size - statsA.size - } - - // run failed first - if (aState.failed && !bState.failed) - return -1 - if (!aState.failed && bState.failed) - return 1 + if (config.shard) + files = await sequelizer.shard(files) - // run longer first - return bState.duration - aState.duration - }) + files = await sequelizer.sort(files) if (!ctx.config.threads) { await runFiles(config, files) diff --git a/packages/vitest/src/node/sequelizers/BaseSequelizer.ts b/packages/vitest/src/node/sequelizers/BaseSequelizer.ts new file mode 100644 index 000000000000..06dd4b8c5d8e --- /dev/null +++ b/packages/vitest/src/node/sequelizers/BaseSequelizer.ts @@ -0,0 +1,66 @@ +import { createHash } from 'crypto' +import { resolve } from 'pathe' +import { slash } from 'vite-node/utils' +import type { Vitest } from '../core' +import type { TestSequelizer } from './types' + +export class BaseSequelizer implements TestSequelizer { + protected ctx: Vitest + + constructor(ctx: Vitest) { + this.ctx = ctx + } + + // async so it can be extended by other sequelizers + public async shard(files: string[]): Promise { + const config = this.ctx.getSerializableConfig() + const { index, count } = config.shard! + const shardSize = Math.ceil(files.length / count) + const shardStart = shardSize * (index - 1) + const shardEnd = shardSize * index + return [...files] + .map((file) => { + const fullPath = resolve(slash(config.root), slash(file)) + const specPath = fullPath.slice(config.root.length) + return { + file, + hash: createHash('sha1') + .update(specPath) + .digest('hex'), + } + }) + .sort((a, b) => (a.hash < b.hash ? -1 : a.hash > b.hash ? 1 : 0)) + .slice(shardStart, shardEnd) + .map(({ file }) => file) + } + + // async so it can be extended by other sequelizers + public async sort(files: string[]): Promise { + const ctx = this.ctx + return [...files].sort((a, b) => { + const aState = ctx.state.getFileTestResults(a) + const bState = ctx.state.getFileTestResults(b) + + if (!aState || !bState) { + const statsA = ctx.state.getFileStats(a) + const statsB = ctx.state.getFileStats(b) + + // run unknown forst + if (!statsA || !statsB) + return !statsA && statsB ? -1 : !statsB && statsA ? 1 : 0 + + // run larger files first + return statsB.size - statsA.size + } + + // run failed first + if (aState.failed && !bState.failed) + return -1 + if (!aState.failed && bState.failed) + return 1 + + // run longer first + return bState.duration - aState.duration + }) + } +} diff --git a/packages/vitest/src/node/sequelizers/types.ts b/packages/vitest/src/node/sequelizers/types.ts new file mode 100644 index 000000000000..456eed048aaa --- /dev/null +++ b/packages/vitest/src/node/sequelizers/types.ts @@ -0,0 +1,10 @@ +import type { Awaitable } from '../../types' + +export interface TestSequelizer { + /** + * Slicing tests into shards. Will be run before `sort`. + * Only run, if `shard` is defined. + */ + shard(files: string[]): Awaitable + sort(files: string[]): Awaitable +} diff --git a/packages/vitest/src/node/state.ts b/packages/vitest/src/node/state.ts index b899a8fb9d3d..7cb9cf66563a 100644 --- a/packages/vitest/src/node/state.ts +++ b/packages/vitest/src/node/state.ts @@ -10,6 +10,14 @@ export class StateManager { results = new ResultsCache() stats = new FilesCache() + getFileTestResults(id: string) { + return this.results.getResults(id) + } + + getFileStats(id: string) { + return this.stats.getStats(id) + } + catchError(err: unknown, type: string) { (err as ErrorWithDiff).type = type this.errorsSet.add(err) diff --git a/test/core/test/sequelizers.test.ts b/test/core/test/sequelizers.test.ts new file mode 100644 index 000000000000..1980f266a709 --- /dev/null +++ b/test/core/test/sequelizers.test.ts @@ -0,0 +1,97 @@ +import type { Vitest } from 'vitest' +import { describe, expect, test, vi } from 'vitest' +import { BaseSequelizer } from '../../../packages/vitest/src/node/sequelizers/BaseSequelizer' + +const buildCtx = () => { + return { + state: { + getFileTestResults: vi.fn(), + getFileStats: vi.fn(), + }, + } as unknown as Vitest +} + +describe('test sequelizers', () => { + test('sorting when no info is available', async () => { + const sequelizer = new BaseSequelizer(buildCtx()) + const files = ['a', 'b', 'c'] + const sorted = await sequelizer.sort(files) + expect(sorted).toStrictEqual(files) + }) + + test('prioritaze unknown files', async () => { + const ctx = buildCtx() + vi.spyOn(ctx.state, 'getFileStats').mockImplementation((file) => { + if (file === 'b') + return { size: 2 } + }) + const sequelizer = new BaseSequelizer(ctx) + const files = ['b', 'a', 'c'] + const sorted = await sequelizer.sort(files) + expect(sorted).toStrictEqual(['a', 'c', 'b']) + }) + + test('sort by size, larger first', async () => { + const ctx = buildCtx() + vi.spyOn(ctx.state, 'getFileStats').mockImplementation((file) => { + if (file === 'a') + return { size: 1 } + if (file === 'b') + return { size: 2 } + if (file === 'c') + return { size: 3 } + }) + const sequelizer = new BaseSequelizer(ctx) + const files = ['b', 'a', 'c'] + const sorted = await sequelizer.sort(files) + expect(sorted).toStrictEqual(['c', 'b', 'a']) + }) + + test('sort by results, failed first', async () => { + const ctx = buildCtx() + vi.spyOn(ctx.state, 'getFileTestResults').mockImplementation((file) => { + if (file === 'a') + return { failed: false, duration: 1 } + if (file === 'b') + return { failed: true, duration: 1 } + if (file === 'c') + return { failed: true, duration: 1 } + }) + const sequelizer = new BaseSequelizer(ctx) + const files = ['b', 'a', 'c'] + const sorted = await sequelizer.sort(files) + expect(sorted).toStrictEqual(['b', 'c', 'a']) + }) + + test('sort by results, long first', async () => { + const ctx = buildCtx() + vi.spyOn(ctx.state, 'getFileTestResults').mockImplementation((file) => { + if (file === 'a') + return { failed: true, duration: 1 } + if (file === 'b') + return { failed: true, duration: 2 } + if (file === 'c') + return { failed: true, duration: 3 } + }) + const sequelizer = new BaseSequelizer(ctx) + const files = ['b', 'a', 'c'] + const sorted = await sequelizer.sort(files) + expect(sorted).toStrictEqual(['c', 'b', 'a']) + }) + + test('sort by results, long and failed first', async () => { + const ctx = buildCtx() + vi.spyOn(ctx.state, 'getFileTestResults').mockImplementation((file) => { + if (file === 'a') + return { failed: false, duration: 1 } + if (file === 'b') + return { failed: false, duration: 6 } + if (file === 'c') + return { failed: true, duration: 3 } + }) + const sequelizer = new BaseSequelizer(ctx) + const files = ['b', 'a', 'c'] + const sorted = await sequelizer.sort(files) + expect(sorted).toStrictEqual(['c', 'b', 'a']) + }) +}) From 553d2855859083ce2db994226fa2d95617bb774b Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Sat, 2 Jul 2022 10:30:01 +0300 Subject: [PATCH 07/16] chore: lockfile --- pnpm-lock.yaml | 117 +++++++++++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 48 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 815eea7092c7..7307fc07519c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -104,14 +104,15 @@ importers: '@iconify-json/carbon': 1.1.5 '@types/node': 17.0.35 '@unocss/reset': 0.39.3 - '@vitejs/plugin-vue': 2.3.3_vite@2.9.12+vue@3.2.37 + '@vitejs/plugin-vue': 2.3.3_vite@2.9.13+vue@3.2.37 esno: 0.14.1 fast-glob: 3.2.11 https-localhost: 4.7.1 - unocss: 0.39.3_vite@2.9.10 - unplugin-vue-components: 0.19.6_hd26raqwpnm7hl4c5jnifs3the - vite-plugin-pwa: 0.12.0_vite@2.9.10 - vitepress: 1.0.0-alpha.2_ohobp6rpsmerwlq5ipwfh5yigy + unocss: 0.39.3_vite@2.9.13 + unplugin-vue-components: 0.19.6_kreax3dlfell3h46tvpocbcg3m + vite: 2.9.13 + vite-plugin-pwa: 0.12.0_vite@2.9.13 + vitepress: 1.0.0-alpha.4_ohobp6rpsmerwlq5ipwfh5yigy workbox-window: 6.5.3 examples/basic: @@ -163,7 +164,7 @@ importers: devDependencies: '@vitest/ui': link:../../packages/ui happy-dom: 6.0.0 - vite: 2.9.10 + vite: 2.9.13 vitest: link:../../packages/vitest examples/mocks: @@ -198,7 +199,7 @@ importers: typescript: 4.6.3 vitest: workspace:* dependencies: - next: 12.1.5_ezdxe4hg7n3pawg24sxf3xmgta + next: 12.1.5_zpnidt7m3osuk7shl3s4oenomq react: 18.0.0 react-dom: 18.0.0_react@18.0.0 devDependencies: @@ -267,7 +268,7 @@ importers: '@vitest/ui': link:../../packages/ui enzyme: 3.11.0 enzyme-adapter-react-16: 1.15.6_7ltvq4e2railvf5uya4ffxpe2a - vite: 2.9.10 + vite: 2.9.13 vitest: link:../../packages/vitest examples/react-mui: @@ -6958,7 +6959,7 @@ packages: '@unocss/core': 0.39.3 dev: true - /@unocss/vite/0.39.3_vite@2.9.12: + /@unocss/vite/0.39.3_vite@2.9.13: resolution: {integrity: sha512-JT21v6ZwLCHPGVfjoWsOdSkMhFNiW2robhQke33WLlRGyT5U4K1SWLxNk+XPDbFdP+WZdcVJi5W5yG8Mm27WBw==} peerDependencies: vite: ^2.9.0 @@ -6970,7 +6971,7 @@ packages: '@unocss/scope': 0.39.3 '@unocss/transformer-directives': 0.39.3 magic-string: 0.26.2 - vite: 2.9.12 + vite: 2.9.13 dev: true /@unocss/vite/0.39.3_vite@3.0.0-beta.4: @@ -7056,17 +7057,6 @@ packages: vue: 3.2.36 dev: true - /@vitejs/plugin-vue/2.3.3_vite@2.9.12+vue@3.2.37: - resolution: {integrity: sha512-SmQLDyhz+6lGJhPELsBdzXGc+AcaT8stgkbiTFGpXPe8Tl1tJaBw1A6pxDqDuRsVkD8uscrkx3hA7QDOoKYtyw==} - engines: {node: '>=12.0.0'} - peerDependencies: - vite: ^2.5.10 - vue: ^3.2.25 - dependencies: - vite: 2.9.12 - vue: 3.2.37 - dev: true - /@vitejs/plugin-vue/2.3.3_vite@2.9.13+vue@3.2.36: resolution: {integrity: sha512-SmQLDyhz+6lGJhPELsBdzXGc+AcaT8stgkbiTFGpXPe8Tl1tJaBw1A6pxDqDuRsVkD8uscrkx3hA7QDOoKYtyw==} engines: {node: '>=12.0.0'} @@ -15852,7 +15842,7 @@ packages: resolution: {integrity: sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==} dev: true - /next/12.1.5_ezdxe4hg7n3pawg24sxf3xmgta: + /next/12.1.5_zpnidt7m3osuk7shl3s4oenomq: resolution: {integrity: sha512-YGHDpyfgCfnT5GZObsKepmRnne7Kzp7nGrac07dikhutWQug7hHg85/+sPJ4ZW5Q2pDkb+n0FnmLkmd44htIJQ==} engines: {node: '>=12.22.0'} hasBin: true @@ -15875,7 +15865,7 @@ packages: postcss: 8.4.5 react: 18.0.0 react-dom: 18.0.0_react@18.0.0 - styled-jsx: 5.0.1_uyynoipo3v3vrfv6si7tyrw7ku + styled-jsx: 5.0.1_react@18.0.0 optionalDependencies: '@next/swc-android-arm-eabi': 12.1.5 '@next/swc-android-arm64': 12.1.5 @@ -19049,7 +19039,7 @@ packages: inline-style-parser: 0.1.1 dev: true - /styled-jsx/5.0.1_uyynoipo3v3vrfv6si7tyrw7ku: + /styled-jsx/5.0.1_react@18.0.0: resolution: {integrity: sha512-+PIZ/6Uk40mphiQJJI1202b+/dYeTVd9ZnMPR80pgiWbjIwvN2zIp4r9et0BgqBuShh48I0gttPlAXA7WVvBxw==} engines: {node: '>= 12.0.0'} peerDependencies: @@ -19062,7 +19052,6 @@ packages: babel-plugin-macros: optional: true dependencies: - '@babel/core': 7.18.2 react: 18.0.0 dev: false @@ -19908,7 +19897,7 @@ packages: detect-node: 2.1.0 dev: false - /unocss/0.39.3_vite@2.9.12: + /unocss/0.39.3_vite@2.9.13: resolution: {integrity: sha512-+BZazovI1A+jlW0+GuSSABHQjBLpu2sQkLXriBTdZiPYZAqJJdiWHuQ6VPzF4Al5WM4VPpOgX5mUYWusJ813qw==} engines: {node: '>=14'} peerDependencies: @@ -19931,7 +19920,7 @@ packages: '@unocss/transformer-compile-class': 0.39.3 '@unocss/transformer-directives': 0.39.3 '@unocss/transformer-variant-group': 0.39.3 - '@unocss/vite': 0.39.3_vite@2.9.12 + '@unocss/vite': 0.39.3_vite@2.9.13 transitivePeerDependencies: - supports-color - vite @@ -20016,7 +20005,7 @@ packages: - webpack dev: true - /unplugin-vue-components/0.19.6_7felabkkrfdt7dxozyactxvh24: + /unplugin-vue-components/0.19.6_ho3dgvxj7kkgrvtaxg2bcfeivi: resolution: {integrity: sha512-APvrJ9Hpid1MLT0G4PWerMJgARhNw6dzz0pcCwCxaO2DR7VyvDacMqjOQNC6ukq7FSw3wzD8VH+9i3EFXwkGmw==} engines: {node: '>=14'} peerDependencies: @@ -20038,7 +20027,7 @@ packages: magic-string: 0.26.2 minimatch: 5.0.1 resolve: 1.22.0 - unplugin: 0.6.3_4mifhig4dhgnrt67tg257xfjai + unplugin: 0.6.3_jckpywlixytlu7mc7cyl6sgysq vue: 3.2.37 transitivePeerDependencies: - esbuild @@ -20048,7 +20037,7 @@ packages: - webpack dev: true - /unplugin-vue-components/0.19.6_ho3dgvxj7kkgrvtaxg2bcfeivi: + /unplugin-vue-components/0.19.6_kreax3dlfell3h46tvpocbcg3m: resolution: {integrity: sha512-APvrJ9Hpid1MLT0G4PWerMJgARhNw6dzz0pcCwCxaO2DR7VyvDacMqjOQNC6ukq7FSw3wzD8VH+9i3EFXwkGmw==} engines: {node: '>=14'} peerDependencies: @@ -20070,7 +20059,7 @@ packages: magic-string: 0.26.2 minimatch: 5.0.1 resolve: 1.22.0 - unplugin: 0.6.3_jckpywlixytlu7mc7cyl6sgysq + unplugin: 0.6.3_m534jlwloqhcwra3khtpc6k6dm vue: 3.2.37 transitivePeerDependencies: - esbuild @@ -20080,7 +20069,39 @@ packages: - webpack dev: true - /unplugin-vue2-script-setup/0.10.2_2bloa65zsoqadlob4lncrwg3um: + /unplugin-vue-components/0.19.6_vite@2.9.13+vue@3.2.36: + resolution: {integrity: sha512-APvrJ9Hpid1MLT0G4PWerMJgARhNw6dzz0pcCwCxaO2DR7VyvDacMqjOQNC6ukq7FSw3wzD8VH+9i3EFXwkGmw==} + engines: {node: '>=14'} + peerDependencies: + '@babel/parser': ^7.15.8 + '@babel/traverse': ^7.15.4 + vue: 2 || 3 + peerDependenciesMeta: + '@babel/parser': + optional: true + '@babel/traverse': + optional: true + dependencies: + '@antfu/utils': 0.5.2 + '@rollup/pluginutils': 4.2.1 + chokidar: 3.5.3 + debug: 4.3.4 + fast-glob: 3.2.11 + local-pkg: 0.4.1 + magic-string: 0.26.2 + minimatch: 5.0.1 + resolve: 1.22.0 + unplugin: 0.6.3_vite@2.9.13 + vue: 3.2.36 + transitivePeerDependencies: + - esbuild + - rollup + - supports-color + - vite + - webpack + dev: true + + /unplugin-vue2-script-setup/0.10.2_o74ad4fw2dfsqw4anmljjinhmu: resolution: {integrity: sha512-m886AZIZCXMiAbTbOPQEk+MqmtHBX3dENheFXjcHSRetwBsD0KVDBteQVD5XrSBzDdQKeKw6IU4dV+2eGcLJ0A==} peerDependencies: '@vue/composition-api': ^1.4.3 @@ -20093,16 +20114,16 @@ packages: '@antfu/utils': 0.5.2 '@babel/core': 7.18.2 '@babel/generator': 7.18.2 - '@babel/parser': 7.18.3 + '@babel/parser': 7.18.4 '@babel/traverse': 7.18.2 - '@babel/types': 7.18.2 + '@babel/types': 7.18.4 '@rollup/pluginutils': 4.2.1 - '@vue/compiler-core': 3.2.36 - '@vue/compiler-dom': 3.2.36 + '@vue/compiler-core': 3.2.37 + '@vue/compiler-dom': 3.2.37 '@vue/composition-api': 1.6.2_vue@2.6.14 - '@vue/reactivity-transform': 3.2.36 + '@vue/reactivity-transform': 3.2.37 '@vue/runtime-dom': 3.2.37 - '@vue/shared': 3.2.36 + '@vue/shared': 3.2.37 defu: 5.0.1 magic-string: 0.25.9 unplugin: 0.3.3_vite@2.9.13 @@ -20135,7 +20156,7 @@ packages: webpack-virtual-modules: 0.4.3 dev: true - /unplugin/0.6.3_4mifhig4dhgnrt67tg257xfjai: + /unplugin/0.6.3_jckpywlixytlu7mc7cyl6sgysq: resolution: {integrity: sha512-CoW88FQfCW/yabVc4bLrjikN9HC8dEvMU4O7B6K2jsYMPK0l6iAnd9dpJwqGcmXJKRCU9vwSsy653qg+RK0G6A==} peerDependencies: esbuild: '>=0.13' @@ -20153,14 +20174,13 @@ packages: optional: true dependencies: chokidar: 3.5.3 - esbuild: 0.14.47 rollup: 2.75.7 - vite: 2.9.12 + vite: 3.0.0-beta.4 webpack-sources: 3.2.3 webpack-virtual-modules: 0.4.3 dev: true - /unplugin/0.6.3_jckpywlixytlu7mc7cyl6sgysq: + /unplugin/0.6.3_m534jlwloqhcwra3khtpc6k6dm: resolution: {integrity: sha512-CoW88FQfCW/yabVc4bLrjikN9HC8dEvMU4O7B6K2jsYMPK0l6iAnd9dpJwqGcmXJKRCU9vwSsy653qg+RK0G6A==} peerDependencies: esbuild: '>=0.13' @@ -20178,8 +20198,9 @@ packages: optional: true dependencies: chokidar: 3.5.3 + esbuild: 0.14.47 rollup: 2.75.7 - vite: 3.0.0-beta.4 + vite: 2.9.13 webpack-sources: 3.2.3 webpack-virtual-modules: 0.4.3 dev: true @@ -20457,7 +20478,7 @@ packages: - supports-color dev: true - /vite-plugin-pwa/0.12.0_vite@2.9.12: + /vite-plugin-pwa/0.12.0_vite@2.9.13: resolution: {integrity: sha512-KYD+cnS5ExLF3T28NkfzBLZ53ehHlp+qMhHGFNh0zlVGpFHrJkL2v9wd4AMi7ZkBTffgeNatIFiv8rhCsMSxBQ==} peerDependencies: vite: ^2.0.0 @@ -20466,7 +20487,7 @@ packages: fast-glob: 3.2.11 pretty-bytes: 6.0.0 rollup: 2.75.7 - vite: 2.9.12 + vite: 2.9.13 workbox-build: 6.5.3 workbox-window: 6.5.3 transitivePeerDependencies: @@ -20662,14 +20683,14 @@ packages: optionalDependencies: fsevents: 2.3.2 - /vitepress/1.0.0-alpha.2_ohobp6rpsmerwlq5ipwfh5yigy: - resolution: {integrity: sha512-twSsmx2DI/3XgZZ8KLyRAH8RgK2Zj4kJx/kwhZhimwndYqQDrtcIaAcuV+P3FKukZ+cYtm9yt9qohz631jKx4A==} + /vitepress/1.0.0-alpha.4_ohobp6rpsmerwlq5ipwfh5yigy: + resolution: {integrity: sha512-bOAA4KW6vYGlkbcrPLZLTKWTgXVroObU+o9xj9EENyEl6yg26WWvfN7DGA4BftjdM5O8nR93Z5khPQ3W/tFE7Q==} engines: {node: '>=14.6.0'} hasBin: true dependencies: '@docsearch/css': 3.1.0 '@docsearch/js': 3.1.0_ohobp6rpsmerwlq5ipwfh5yigy - '@vitejs/plugin-vue': 2.3.3_vite@2.9.10+vue@3.2.37 + '@vitejs/plugin-vue': 2.3.3_vite@2.9.13+vue@3.2.37 '@vue/devtools-api': 6.1.4 '@vueuse/core': 8.7.4_vue@3.2.37 body-scroll-lock: 4.0.0-beta.0 From 63d27d4179cf1c8c9ca18bf7d9aef5865ded7623 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Sat, 2 Jul 2022 10:33:39 +0300 Subject: [PATCH 08/16] refactor: renaming --- packages/vitest/src/node/cache/files.ts | 8 ++++---- packages/vitest/src/node/state.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/vitest/src/node/cache/files.ts b/packages/vitest/src/node/cache/files.ts index 19a3a1c8e816..093236ef65f4 100644 --- a/packages/vitest/src/node/cache/files.ts +++ b/packages/vitest/src/node/cache/files.ts @@ -1,11 +1,11 @@ import fs, { type Stats } from 'fs' -type FileCacheStats = Pick +type FileStatsCache = Pick -export class FilesCache { - public cache = new Map() +export class FilesStatsCache { + public cache = new Map() - public getStats(fsPath: string): FileCacheStats | undefined { + public getStats(fsPath: string): FileStatsCache | undefined { return this.cache.get(fsPath) } diff --git a/packages/vitest/src/node/state.ts b/packages/vitest/src/node/state.ts index 7cb9cf66563a..029f5eb20e6c 100644 --- a/packages/vitest/src/node/state.ts +++ b/packages/vitest/src/node/state.ts @@ -1,5 +1,5 @@ import type { ErrorWithDiff, File, Task, TaskResultPack, UserConsoleLog } from '../types' -import { FilesCache } from './cache/files' +import { FilesStatsCache } from './cache/files' import { ResultsCache } from './cache/results' export class StateManager { @@ -8,7 +8,7 @@ export class StateManager { taskFileMap = new WeakMap() errorsSet = new Set() results = new ResultsCache() - stats = new FilesCache() + stats = new FilesStatsCache() getFileTestResults(id: string) { return this.results.getResults(id) From 78bf586ef5a170fdd21193027a2f7363adf2e94d Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Sat, 2 Jul 2022 10:36:00 +0300 Subject: [PATCH 09/16] chore: dont override version --- packages/vitest/src/node/cache/results.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/vitest/src/node/cache/results.ts b/packages/vitest/src/node/cache/results.ts index 151bb940fb98..16c7fbde7ab1 100644 --- a/packages/vitest/src/node/cache/results.ts +++ b/packages/vitest/src/node/cache/results.ts @@ -11,6 +11,7 @@ export interface SuiteResultCache { export class ResultsCache { private cache = new Map() private cachePath: string | null = null + private version: string = version setConfig(config: ResolvedConfig['cache']) { if (config) @@ -27,7 +28,9 @@ export class ResultsCache { if (fs.existsSync(this.cachePath)) { const resultsCache = await fs.promises.readFile(this.cachePath, 'utf8') - this.cache = new Map(JSON.parse(resultsCache).results) + const { results, version } = JSON.parse(resultsCache) + this.cache = new Map(results) + this.version = version } } @@ -60,7 +63,7 @@ export class ResultsCache { await fs.promises.mkdir(cacheDirname, { recursive: true }) await fs.promises.writeFile(this.cachePath, JSON.stringify({ - version, + version: this.version, results: resultsCache, })) } From dae2bc00aca6bb9772e0cd8048abf52b064e8cc9 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Sat, 2 Jul 2022 11:30:33 +0300 Subject: [PATCH 10/16] chore: fix clearCache --- packages/vitest/src/node/cache/index.ts | 11 ++++++++--- packages/vitest/src/node/cache/results.ts | 12 ++++++++---- packages/vitest/src/node/cli.ts | 11 ++++++++++- packages/vitest/src/node/core.ts | 2 +- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/packages/vitest/src/node/cache/index.ts b/packages/vitest/src/node/cache/index.ts index c18d5612d90c..da9942472864 100644 --- a/packages/vitest/src/node/cache/index.ts +++ b/packages/vitest/src/node/cache/index.ts @@ -25,12 +25,17 @@ export class VitestCache { const cache = config.config.test?.cache - if (!cache) + if (cache === false) throw new Error('[vitest] Cache is disabled') - const cachePath = VitestCache.resolveCacheDir(root, cache.dir) + const cachePath = VitestCache.resolveCacheDir(root, cache?.dir) - if (fs.existsSync(cachePath)) + let cleared = false + + if (fs.existsSync(cachePath)) { fs.rmSync(cachePath, { recursive: true, force: true }) + cleared = true + } + return { dir: cachePath, cleared } } } diff --git a/packages/vitest/src/node/cache/results.ts b/packages/vitest/src/node/cache/results.ts index 16c7fbde7ab1..bd78d27b8c30 100644 --- a/packages/vitest/src/node/cache/results.ts +++ b/packages/vitest/src/node/cache/results.ts @@ -12,14 +12,16 @@ export class ResultsCache { private cache = new Map() private cachePath: string | null = null private version: string = version + private root = '/' - setConfig(config: ResolvedConfig['cache']) { + setConfig(root: string, config: ResolvedConfig['cache']) { + this.root = root if (config) this.cachePath = resolve(config.dir, 'results.json') } - getResults(id: string) { - return this.cache.get(id) + getResults(fsPath: string) { + return this.cache.get(fsPath.slice(this.root.length)) } async readFromCache() { @@ -40,7 +42,9 @@ export class ResultsCache { if (!result) return const duration = result.duration || 0 - this.cache.set(file.filepath, { + // store as relative, so cache would be the same in CI and locally + const relativePath = file.filepath.slice(this.root.length) + this.cache.set(relativePath, { duration: duration >= 0 ? duration : 0, failed: result.state === 'fail', }) diff --git a/packages/vitest/src/node/cli.ts b/packages/vitest/src/node/cli.ts index 02cdab26363c..788f87d551f8 100644 --- a/packages/vitest/src/node/cli.ts +++ b/packages/vitest/src/node/cli.ts @@ -59,10 +59,19 @@ cli .action(start) cli.command('clearCache') - .action(VitestCache.clearCache) + .action(clearCache) cli.parse() +async function clearCache(args: CliOptions) { + const { dir, cleared } = await VitestCache.clearCache(args) + + if (cleared) + console.log(c.bgGreen(' VITEST '), `Cache cleared at ${dir}`) + else + console.log(c.bgRed(' VITEST '), `No cache found at ${dir}`) +} + async function runRelated(relatedFiles: string[] | string, argv: CliOptions) { argv.related = relatedFiles argv.passWithNoTests ??= true diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index c888742b45e3..d31b0c1e1505 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -92,7 +92,7 @@ export class Vitest { if (resolved.coverage.enabled) await cleanCoverage(resolved.coverage, resolved.coverage.clean) - this.state.results.setConfig(resolved.cache) + this.state.results.setConfig(resolved.root, resolved.cache) await this.state.results.readFromCache() } From e396589b58aeb80fdc8e731f04acfee26f768ce1 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Sat, 2 Jul 2022 11:39:44 +0300 Subject: [PATCH 11/16] chore: guard slice --- packages/vitest/src/node/cache/results.ts | 4 ++-- packages/vitest/src/node/sequelizers/BaseSequelizer.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vitest/src/node/cache/results.ts b/packages/vitest/src/node/cache/results.ts index bd78d27b8c30..3a0ea347824e 100644 --- a/packages/vitest/src/node/cache/results.ts +++ b/packages/vitest/src/node/cache/results.ts @@ -21,7 +21,7 @@ export class ResultsCache { } getResults(fsPath: string) { - return this.cache.get(fsPath.slice(this.root.length)) + return this.cache.get(fsPath?.slice(this.root.length)) } async readFromCache() { @@ -43,7 +43,7 @@ export class ResultsCache { return const duration = result.duration || 0 // store as relative, so cache would be the same in CI and locally - const relativePath = file.filepath.slice(this.root.length) + const relativePath = file.filepath?.slice(this.root.length) this.cache.set(relativePath, { duration: duration >= 0 ? duration : 0, failed: result.state === 'fail', diff --git a/packages/vitest/src/node/sequelizers/BaseSequelizer.ts b/packages/vitest/src/node/sequelizers/BaseSequelizer.ts index 06dd4b8c5d8e..623fd4bef6e3 100644 --- a/packages/vitest/src/node/sequelizers/BaseSequelizer.ts +++ b/packages/vitest/src/node/sequelizers/BaseSequelizer.ts @@ -21,7 +21,7 @@ export class BaseSequelizer implements TestSequelizer { return [...files] .map((file) => { const fullPath = resolve(slash(config.root), slash(file)) - const specPath = fullPath.slice(config.root.length) + const specPath = fullPath?.slice(config.root.length) return { file, hash: createHash('sha1') From b9e251d1c7b72717283a80847e99ca46f68664e9 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Sat, 2 Jul 2022 13:16:22 +0300 Subject: [PATCH 12/16] chore: cleanup --- packages/vitest/src/node/cache/index.ts | 7 ++---- packages/vitest/src/node/cli.ts | 24 ++++++++++++------- .../src/node/sequelizers/BaseSequelizer.ts | 2 +- packages/vitest/src/node/sequelizers/types.ts | 6 ++--- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/packages/vitest/src/node/cache/index.ts b/packages/vitest/src/node/cache/index.ts index da9942472864..15d2fad3938c 100644 --- a/packages/vitest/src/node/cache/index.ts +++ b/packages/vitest/src/node/cache/index.ts @@ -20,13 +20,10 @@ export class VitestCache { const config = await loadConfigFromFile({ command: 'serve', mode: 'test' }, configPath) - if (!config) - throw new Error(`[vitest] Not able to load config from ${configPath}`) - - const cache = config.config.test?.cache + const cache = config?.config.test?.cache if (cache === false) - throw new Error('[vitest] Cache is disabled') + throw new Error('Cache is disabled') const cachePath = VitestCache.resolveCacheDir(root, cache?.dir) diff --git a/packages/vitest/src/node/cli.ts b/packages/vitest/src/node/cli.ts index 788f87d551f8..2de03b8f41e1 100644 --- a/packages/vitest/src/node/cli.ts +++ b/packages/vitest/src/node/cli.ts @@ -58,18 +58,26 @@ cli .command('[...filters]') .action(start) -cli.command('clearCache') - .action(clearCache) +cli.command('clean ') + .action(clean) cli.parse() -async function clearCache(args: CliOptions) { - const { dir, cleared } = await VitestCache.clearCache(args) +async function clean(subject: string, args: CliOptions) { + if (subject !== 'cache') + return - if (cleared) - console.log(c.bgGreen(' VITEST '), `Cache cleared at ${dir}`) - else - console.log(c.bgRed(' VITEST '), `No cache found at ${dir}`) + try { + const { dir, cleared } = await VitestCache.clearCache(args) + + if (cleared) + console.log(c.bgGreen(' VITEST '), `Cache cleared at ${dir}`) + else + console.log(c.bgRed(' ERROR '), `No cache found at ${dir}`) + } + catch (err: any) { + console.log(c.bgRed(' ERROR '), err.message) + } } async function runRelated(relatedFiles: string[] | string, argv: CliOptions) { diff --git a/packages/vitest/src/node/sequelizers/BaseSequelizer.ts b/packages/vitest/src/node/sequelizers/BaseSequelizer.ts index 623fd4bef6e3..e5b2c989d40d 100644 --- a/packages/vitest/src/node/sequelizers/BaseSequelizer.ts +++ b/packages/vitest/src/node/sequelizers/BaseSequelizer.ts @@ -45,7 +45,7 @@ export class BaseSequelizer implements TestSequelizer { const statsA = ctx.state.getFileStats(a) const statsB = ctx.state.getFileStats(b) - // run unknown forst + // run unknown first if (!statsA || !statsB) return !statsA && statsB ? -1 : !statsB && statsA ? 1 : 0 diff --git a/packages/vitest/src/node/sequelizers/types.ts b/packages/vitest/src/node/sequelizers/types.ts index 456eed048aaa..1ddd7bd82542 100644 --- a/packages/vitest/src/node/sequelizers/types.ts +++ b/packages/vitest/src/node/sequelizers/types.ts @@ -2,9 +2,9 @@ import type { Awaitable } from '../../types' export interface TestSequelizer { /** - * Slicing tests into shards. Will be run before `sort`. - * Only run, if `shard` is defined. - */ + * Slicing tests into shards. Will be run before `sort`. + * Only run, if `shard` is defined. + */ shard(files: string[]): Awaitable sort(files: string[]): Awaitable } From 4f401f16ec063f642cdaea42a51ec634cd38904d Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Sat, 2 Jul 2022 13:17:28 +0300 Subject: [PATCH 13/16] docs: cleanup --- docs/guide/cli.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/cli.md b/docs/guide/cli.md index c5ee86dde314..e43fccc19775 100644 --- a/docs/guide/cli.md +++ b/docs/guide/cli.md @@ -36,7 +36,7 @@ Useful to run with [`lint-staged`](https://github.com/okonet/lint-staged) or wit vitest related /src/index.ts /src/hello-world.js ``` -### `vitest clearCache` +### `vitest clean cache` Clears cache folder. From 073b4dd77a850afcd2690704eb5eb57de1218da1 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Sat, 2 Jul 2022 13:20:01 +0300 Subject: [PATCH 14/16] chore: cleanup --- packages/vitest/src/node/cli.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/vitest/src/node/cli.ts b/packages/vitest/src/node/cli.ts index 2de03b8f41e1..1e3c3304e90b 100644 --- a/packages/vitest/src/node/cli.ts +++ b/packages/vitest/src/node/cli.ts @@ -64,8 +64,10 @@ cli.command('clean ') cli.parse() async function clean(subject: string, args: CliOptions) { - if (subject !== 'cache') + if (subject !== 'cache') { + console.log(c.bgRed(' ERROR '), `Unknown clean type: "${subject}".\nSupported types:\n - cache`) return + } try { const { dir, cleared } = await VitestCache.clearCache(args) From f87b88aef46aa6638176c4e7845a145b079b567e Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Sun, 3 Jul 2022 14:45:38 +0300 Subject: [PATCH 15/16] chore: remove command for now --- packages/vitest/src/node/cli.ts | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/packages/vitest/src/node/cli.ts b/packages/vitest/src/node/cli.ts index 1e3c3304e90b..496f4ce88f28 100644 --- a/packages/vitest/src/node/cli.ts +++ b/packages/vitest/src/node/cli.ts @@ -1,7 +1,6 @@ import cac from 'cac' import c from 'picocolors' import { version } from '../../package.json' -import { VitestCache } from './cache' import type { CliOptions } from './cli-api' import { startVitest } from './cli-api' import { divider } from './reporters/renderers/utils' @@ -58,30 +57,8 @@ cli .command('[...filters]') .action(start) -cli.command('clean ') - .action(clean) - cli.parse() -async function clean(subject: string, args: CliOptions) { - if (subject !== 'cache') { - console.log(c.bgRed(' ERROR '), `Unknown clean type: "${subject}".\nSupported types:\n - cache`) - return - } - - try { - const { dir, cleared } = await VitestCache.clearCache(args) - - if (cleared) - console.log(c.bgGreen(' VITEST '), `Cache cleared at ${dir}`) - else - console.log(c.bgRed(' ERROR '), `No cache found at ${dir}`) - } - catch (err: any) { - console.log(c.bgRed(' ERROR '), err.message) - } -} - async function runRelated(relatedFiles: string[] | string, argv: CliOptions) { argv.related = relatedFiles argv.passWithNoTests ??= true From 84440046d2417e87918a6f6d72c21758d21c38b8 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Sun, 3 Jul 2022 14:46:28 +0300 Subject: [PATCH 16/16] refactor: cleanup --- packages/vitest/src/node/cache/results.ts | 10 ++++++---- .../vitest/src/node/sequelizers/BaseSequelizer.ts | 12 ++++++------ packages/vitest/src/node/sequelizers/types.ts | 5 +++++ 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/vitest/src/node/cache/results.ts b/packages/vitest/src/node/cache/results.ts index 3a0ea347824e..9c67bbcd42b6 100644 --- a/packages/vitest/src/node/cache/results.ts +++ b/packages/vitest/src/node/cache/results.ts @@ -59,16 +59,18 @@ export class ResultsCache { if (!this.cachePath) return - const resultsCache = Array.from(this.cache.entries()) + const results = Array.from(this.cache.entries()) const cacheDirname = dirname(this.cachePath) if (!fs.existsSync(cacheDirname)) await fs.promises.mkdir(cacheDirname, { recursive: true }) - await fs.promises.writeFile(this.cachePath, JSON.stringify({ + const cache = JSON.stringify({ version: this.version, - results: resultsCache, - })) + results, + }) + + await fs.promises.writeFile(this.cachePath, cache) } } diff --git a/packages/vitest/src/node/sequelizers/BaseSequelizer.ts b/packages/vitest/src/node/sequelizers/BaseSequelizer.ts index e5b2c989d40d..dc6a8ec9a4a9 100644 --- a/packages/vitest/src/node/sequelizers/BaseSequelizer.ts +++ b/packages/vitest/src/node/sequelizers/BaseSequelizer.ts @@ -13,7 +13,7 @@ export class BaseSequelizer implements TestSequelizer { // async so it can be extended by other sequelizers public async shard(files: string[]): Promise { - const config = this.ctx.getSerializableConfig() + const { config } = this.ctx const { index, count } = config.shard! const shardSize = Math.ceil(files.length / count) const shardStart = shardSize * (index - 1) @@ -36,14 +36,14 @@ export class BaseSequelizer implements TestSequelizer { // async so it can be extended by other sequelizers public async sort(files: string[]): Promise { - const ctx = this.ctx + const { state } = this.ctx return [...files].sort((a, b) => { - const aState = ctx.state.getFileTestResults(a) - const bState = ctx.state.getFileTestResults(b) + const aState = state.getFileTestResults(a) + const bState = state.getFileTestResults(b) if (!aState || !bState) { - const statsA = ctx.state.getFileStats(a) - const statsB = ctx.state.getFileStats(b) + const statsA = state.getFileStats(a) + const statsB = state.getFileStats(b) // run unknown first if (!statsA || !statsB) diff --git a/packages/vitest/src/node/sequelizers/types.ts b/packages/vitest/src/node/sequelizers/types.ts index 1ddd7bd82542..5442d0a1c4b2 100644 --- a/packages/vitest/src/node/sequelizers/types.ts +++ b/packages/vitest/src/node/sequelizers/types.ts @@ -1,4 +1,5 @@ import type { Awaitable } from '../../types' +import type { Vitest } from '../core' export interface TestSequelizer { /** @@ -8,3 +9,7 @@ export interface TestSequelizer { shard(files: string[]): Awaitable sort(files: string[]): Awaitable } + +export interface TestSequelizerContructor { + new (ctx: Vitest): TestSequelizer +}