From 622d152a015636ae88c2833e2a20e8ae48a94f98 Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Thu, 10 Mar 2022 16:11:26 +0000 Subject: [PATCH 1/8] only clear the userDataDir explicitly, reuse otherwise --- .../helpers/commands/exists-eventually.ts | 2 +- packages/compass-e2e-tests/helpers/compass.ts | 74 +++++++++++-------- .../tests/time-to-first-query.test.ts | 59 ++++++++++++--- 3 files changed, 92 insertions(+), 43 deletions(-) diff --git a/packages/compass-e2e-tests/helpers/commands/exists-eventually.ts b/packages/compass-e2e-tests/helpers/commands/exists-eventually.ts index 36d7954e862..aee43f190d7 100644 --- a/packages/compass-e2e-tests/helpers/commands/exists-eventually.ts +++ b/packages/compass-e2e-tests/helpers/commands/exists-eventually.ts @@ -3,7 +3,7 @@ import type { CompassBrowser } from '../compass-browser'; export async function existsEventually( browser: CompassBrowser, selector: string, - timeout = 10000 + timeout = 1000 ): Promise { try { // return true if it exists before the timeout expires diff --git a/packages/compass-e2e-tests/helpers/compass.ts b/packages/compass-e2e-tests/helpers/compass.ts index 65a40b12229..b8870146604 100644 --- a/packages/compass-e2e-tests/helpers/compass.ts +++ b/packages/compass-e2e-tests/helpers/compass.ts @@ -42,20 +42,14 @@ let j = 0; // For the html //let k = 0; -type CompassOptions = { - userDataDir: string; -}; - export class Compass { browser: CompassBrowser; renderLogs: any[]; // TODO logs: LogEntry[]; logPath?: string; - userDataDir: string; - constructor(browser: CompassBrowser, options: CompassOptions) { + constructor(browser: CompassBrowser) { this.browser = browser; - this.userDataDir = options.userDataDir; this.logs = []; this.renderLogs = []; @@ -190,16 +184,6 @@ export class Compass { debug(`Writing Compass application log to ${compassLogPath}`); await fs.writeFile(compassLogPath, compassLog.raw); this.logs = compassLog.structured; - - debug('Removing user data'); - try { - await fs.rmdir(this.userDataDir, { recursive: true }); - } catch (e) { - debug( - `Failed to remove temporary user data directory at ${this.userDataDir}:` - ); - debug(e); - } } async capturePage( @@ -215,23 +199,51 @@ export class Compass { } } -async function startCompass( - testPackagedApp = ['1', 'true'].includes(process.env.TEST_PACKAGED_APP ?? ''), - opts = {} -): Promise { - const nowFormatted = formattedDate(); +interface StartCompassOptions { + firstRun?: boolean; +} + +let defaultUserDataDir: string; - const userDataDir = path.join( - os.tmpdir(), - `user-data-dir-${Date.now().toString(32)}-${++i}` +async function startCompass(opts: StartCompassOptions = {}): Promise { + const testPackagedApp = ['1', 'true'].includes( + process.env.TEST_PACKAGED_APP ?? '' ); + + const nowFormatted = formattedDate(); + + // If this is not the first run, but we want it to be, delete the user data + // dir so it will be recreated below. + if (defaultUserDataDir && opts.firstRun) { + debug('Removing user data'); + try { + await fs.rmdir(defaultUserDataDir, { recursive: true }); + } catch (e) { + debug( + `Failed to remove temporary user data directory at ${defaultUserDataDir}:` + ); + debug(e); + } + } + + // Calculate the userDataDir once so it will be the same between runs. That + // way we can test first run vs second run experience. + if (!defaultUserDataDir) { + defaultUserDataDir = path.join( + os.tmpdir(), + `user-data-dir-${Date.now().toString(32)}-${++i}` + ); + } const chromedriverLogPath = path.join( LOG_PATH, `chromedriver.${nowFormatted}.log` ); const webdriverLogPath = path.join(LOG_PATH, 'webdriver'); - await fs.mkdir(userDataDir, { recursive: true }); + // Ensure that the user data dir exists + // TODO: try catch + await fs.mkdir(defaultUserDataDir, { recursive: true }); + // Chromedriver will fail if log path doesn't exist, webdriver doesn't care, // for consistency let's mkdir for both of them just in case await fs.mkdir(path.dirname(chromedriverLogPath), { recursive: true }); @@ -252,7 +264,7 @@ async function startCompass( // https://peter.sh/experiments/chromium-command-line-switches/ // https://www.electronjs.org/docs/latest/api/command-line-switches chromeArgs.push( - `--user-data-dir=${userDataDir}`, + `--user-data-dir=${defaultUserDataDir}`, // Chromecast feature that is enabled by default in some chrome versions // and breaks the app on Ubuntu '--media-router=0', @@ -323,7 +335,7 @@ async function startCompass( // @ts-expect-error const browser = await remote(options); - const compass = new Compass(browser, { userDataDir }); + const compass = new Compass(browser); await compass.recordLogs(); @@ -514,8 +526,10 @@ function augmentError(error: Error, stack: string) { error.stack = `${error.stack ?? ''}\nvia ${strippedLines.join('\n')}`; } -export async function beforeTests(): Promise { - const compass = await startCompass(); +export async function beforeTests( + opts?: StartCompassOptions +): Promise { + const compass = await startCompass(opts); const { browser } = compass; diff --git a/packages/compass-e2e-tests/tests/time-to-first-query.test.ts b/packages/compass-e2e-tests/tests/time-to-first-query.test.ts index 3e6be270551..307b9e8bc37 100644 --- a/packages/compass-e2e-tests/tests/time-to-first-query.test.ts +++ b/packages/compass-e2e-tests/tests/time-to-first-query.test.ts @@ -4,11 +4,22 @@ import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; describe('Time to first query', function () { - let compass: Compass; + let compass: Compass|undefined; - it('can open compass, connect to a database and run a query on a collection', async function () { + afterEach(async function () { + // cleanup outside of the test so that the time it takes to run does not + // get added to the time it took to run the first query + if (compass) { + // even though this is after (not afterEach) currentTest points to the last test + await afterTest(compass, this.currentTest); + await afterTests(compass); + compass = undefined; + } + }); + + it('can open compass, connect to a database and run a query on a collection (first run)', async function () { // start compass inside the test so that the time is measured together - compass = await beforeTests(); + compass = await beforeTests({ firstRun: true }); const { browser } = compass; @@ -41,14 +52,38 @@ describe('Time to first query', function () { expect(text).to.equal('42'); }); - // eslint-disable-next-line mocha/no-hooks-for-single-case - after(async function () { - // cleanup outside of the test so that the time it takes to run does not - // get added to the time it took to run the first query - if (compass) { - // even though this is after (not afterEach) currentTest points to the last test - await afterTest(compass, this.currentTest); - await afterTests(compass); - } + it('can open compass, connect to a database and run a query on a collection (second run onwards)', async function () { + // start compass inside the test so that the time is measured together + compass = await beforeTests(); + + const { browser } = compass; + + await browser.connectWithConnectionString('mongodb://localhost:27018/test'); + + await browser.navigateToCollectionTab('test', 'numbers', 'Documents'); + + // search for the document with id == 42 and wait for just one result to appear + const aceCommentElement = await browser.$( + '#query-bar-option-input-filter .ace_scroller' + ); + await aceCommentElement.click(); + + await browser.keys('{ i: 42 }'); + const filterButtonElement = await browser.$( + Selectors.queryBarApplyFilterButton('Documents') + ); + await filterButtonElement.click(); + await browser.waitUntil(async () => { + // we start off with 20 results (assuming no filter) and we expect to + // have just one once the filter finishes + const result = await browser.$$('.document-list .document'); + return result.length === 1; + }); + + const documentElementValue = await browser.$( + '.document-list .document .element-value-is-int32' + ); + const text = await documentElementValue.getText(); + expect(text).to.equal('42'); }); }); From 7697e68298c1101d84ac4147f88bbcedd792b1de Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Thu, 10 Mar 2022 16:11:38 +0000 Subject: [PATCH 2/8] move time to first query to the front --- packages/compass-e2e-tests/index.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/compass-e2e-tests/index.ts b/packages/compass-e2e-tests/index.ts index 43470d07e5c..e3558c96a85 100644 --- a/packages/compass-e2e-tests/index.ts +++ b/packages/compass-e2e-tests/index.ts @@ -22,6 +22,8 @@ const keychain = createUnlockedKeychain(); // We can't import mongodb here yet because native modules will be recompiled let metricsClient: MongoClient; +const FIRST_TEST = 'tests/time-to-first-query.test.ts'; + async function setup() { await keychain.activate(); @@ -112,10 +114,18 @@ async function main() { } } - const tests = await promisify(glob)('tests/**/*.{test,spec}.ts', { + const rawTests = await promisify(glob)('tests/**/*.{test,spec}.ts', { cwd: __dirname, }); + // The only test file that's interested in the first-run experience (at the + // time of writing) is time-to-first-query.ts and that happens to be + // alphabetically right at the end. Which is fine, but the first test to run + // will also get the slow first run experience for no good reason unless it is + // the time-to-first-query.ts test. + // So yeah.. this is a bit of a micro optimisation. + const tests = [FIRST_TEST, ...rawTests.filter((t) => t !== FIRST_TEST)]; + const bail = process.argv.includes('--bail'); const mocha = new Mocha({ From 009b47b8f7b5512ab8300e9abc6d58b1de32f8ac Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Thu, 10 Mar 2022 16:21:47 +0000 Subject: [PATCH 3/8] cleanup the userDataDir after the tests --- packages/compass-e2e-tests/helpers/compass.ts | 25 +++++++++++-------- packages/compass-e2e-tests/index.ts | 4 +++ .../tests/time-to-first-query.test.ts | 2 +- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/packages/compass-e2e-tests/helpers/compass.ts b/packages/compass-e2e-tests/helpers/compass.ts index b8870146604..d25befab95a 100644 --- a/packages/compass-e2e-tests/helpers/compass.ts +++ b/packages/compass-e2e-tests/helpers/compass.ts @@ -1,6 +1,6 @@ import { inspect } from 'util'; import { ObjectId, EJSON } from 'bson'; -import { promises as fs } from 'fs'; +import { promises as fs, rmdirSync } from 'fs'; import type Mocha from 'mocha'; import path from 'path'; import os from 'os'; @@ -205,6 +205,19 @@ interface StartCompassOptions { let defaultUserDataDir: string; +export function removeUserDataDir(): void { + debug('Removing user data'); + try { + // this is sync so we can use it in cleanup() in index.ts + rmdirSync(defaultUserDataDir, { recursive: true }); + } catch (e) { + debug( + `Failed to remove temporary user data directory at ${defaultUserDataDir}:` + ); + debug(e); + } +} + async function startCompass(opts: StartCompassOptions = {}): Promise { const testPackagedApp = ['1', 'true'].includes( process.env.TEST_PACKAGED_APP ?? '' @@ -215,15 +228,7 @@ async function startCompass(opts: StartCompassOptions = {}): Promise { // If this is not the first run, but we want it to be, delete the user data // dir so it will be recreated below. if (defaultUserDataDir && opts.firstRun) { - debug('Removing user data'); - try { - await fs.rmdir(defaultUserDataDir, { recursive: true }); - } catch (e) { - debug( - `Failed to remove temporary user data directory at ${defaultUserDataDir}:` - ); - debug(e); - } + removeUserDataDir(); } // Calculate the userDataDir once so it will be the same between runs. That diff --git a/packages/compass-e2e-tests/index.ts b/packages/compass-e2e-tests/index.ts index e3558c96a85..fa052c74052 100644 --- a/packages/compass-e2e-tests/index.ts +++ b/packages/compass-e2e-tests/index.ts @@ -12,6 +12,7 @@ import { compileCompassAssets, buildCompass, LOG_PATH, + removeUserDataDir, } from './helpers/compass'; import { createUnlockedKeychain } from './helpers/keychain'; import ResultLogger from './helpers/result-logger'; @@ -50,6 +51,8 @@ async function setup() { } function cleanup() { + removeUserDataDir(); + keychain.reset(); const disableStartStop = process.argv.includes('--disable-start-stop'); @@ -213,6 +216,7 @@ async function run() { if (metricsClient) { await metricsClient.close(); } + cleanup(); } } diff --git a/packages/compass-e2e-tests/tests/time-to-first-query.test.ts b/packages/compass-e2e-tests/tests/time-to-first-query.test.ts index 307b9e8bc37..71ea1a83a0a 100644 --- a/packages/compass-e2e-tests/tests/time-to-first-query.test.ts +++ b/packages/compass-e2e-tests/tests/time-to-first-query.test.ts @@ -4,7 +4,7 @@ import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; describe('Time to first query', function () { - let compass: Compass|undefined; + let compass: Compass | undefined; afterEach(async function () { // cleanup outside of the test so that the time it takes to run does not From 4012242ceec55322afd9b2db22aabae9306e9946 Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Thu, 10 Mar 2022 16:25:06 +0000 Subject: [PATCH 4/8] rm TODO --- packages/compass-e2e-tests/helpers/compass.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/compass-e2e-tests/helpers/compass.ts b/packages/compass-e2e-tests/helpers/compass.ts index d25befab95a..2a9c1a68c85 100644 --- a/packages/compass-e2e-tests/helpers/compass.ts +++ b/packages/compass-e2e-tests/helpers/compass.ts @@ -246,7 +246,6 @@ async function startCompass(opts: StartCompassOptions = {}): Promise { const webdriverLogPath = path.join(LOG_PATH, 'webdriver'); // Ensure that the user data dir exists - // TODO: try catch await fs.mkdir(defaultUserDataDir, { recursive: true }); // Chromedriver will fail if log path doesn't exist, webdriver doesn't care, From f18742cde5dfb8a8eaf8e4cd2fd5592e3268f5e2 Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Fri, 11 Mar 2022 11:38:33 +0000 Subject: [PATCH 5/8] try and anticipate whether the tour/privacy modals will appear or not --- .../helpers/commands/exists-eventually.ts | 2 +- packages/compass-e2e-tests/helpers/compass.ts | 25 +++++++++++++++---- packages/compass/src/app/index.js | 6 ++--- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/compass-e2e-tests/helpers/commands/exists-eventually.ts b/packages/compass-e2e-tests/helpers/commands/exists-eventually.ts index aee43f190d7..36d7954e862 100644 --- a/packages/compass-e2e-tests/helpers/commands/exists-eventually.ts +++ b/packages/compass-e2e-tests/helpers/commands/exists-eventually.ts @@ -3,7 +3,7 @@ import type { CompassBrowser } from '../compass-browser'; export async function existsEventually( browser: CompassBrowser, selector: string, - timeout = 1000 + timeout = 10000 ): Promise { try { // return true if it exists before the timeout expires diff --git a/packages/compass-e2e-tests/helpers/compass.ts b/packages/compass-e2e-tests/helpers/compass.ts index 2a9c1a68c85..a42f128dde3 100644 --- a/packages/compass-e2e-tests/helpers/compass.ts +++ b/packages/compass-e2e-tests/helpers/compass.ts @@ -44,12 +44,14 @@ let j = 0; export class Compass { browser: CompassBrowser; + isFirstRun: boolean; renderLogs: any[]; // TODO logs: LogEntry[]; logPath?: string; - constructor(browser: CompassBrowser) { + constructor(browser: CompassBrowser, { isFirstRun = false } = {}) { this.browser = browser; + this.isFirstRun = isFirstRun; this.logs = []; this.renderLogs = []; @@ -84,7 +86,16 @@ export class Compass { // first arg is usually == text, but not always const args = []; for (const arg of message.args()) { - args.push(await arg.jsonValue()); + let value; + try { + value = await arg.jsonValue(); + } + catch (err) { + // there are still some edge cases we can't easily convert into text + console.error('could not convert', arg); + value = '¯\\_(ツ)_/¯'; + } + args.push(value); } // uncomment to see browser logs @@ -225,6 +236,8 @@ async function startCompass(opts: StartCompassOptions = {}): Promise { const nowFormatted = formattedDate(); + const isFirstRun = opts.firstRun || !defaultUserDataDir; + // If this is not the first run, but we want it to be, delete the user data // dir so it will be recreated below. if (defaultUserDataDir && opts.firstRun) { @@ -339,7 +352,7 @@ async function startCompass(opts: StartCompassOptions = {}): Promise { // @ts-expect-error const browser = await remote(options); - const compass = new Compass(browser); + const compass = new Compass(browser, { isFirstRun }); await compass.recordLogs(); @@ -538,8 +551,10 @@ export async function beforeTests( const { browser } = compass; await browser.waitForConnectionScreen(); - await browser.closeTourModal(); - await browser.closePrivacySettingsModal(); + if (compass.isFirstRun) { + await browser.closeTourModal(); + await browser.closePrivacySettingsModal(); + } return compass; } diff --git a/packages/compass/src/app/index.js b/packages/compass/src/app/index.js index 6b61ebc6735..4683e81406b 100644 --- a/packages/compass/src/app/index.js +++ b/packages/compass/src/app/index.js @@ -72,7 +72,7 @@ ipc.once('app:launched', function() { const { log, mongoLogId, debug, track } = require('@mongodb-js/compass-logging').createLoggerAndTelemetry('COMPASS-APP'); - + /** * The top-level application singleton that brings everything together! */ @@ -308,9 +308,7 @@ var Application = View.extend({ var currentVersion = APP_VERSION; var save = false; if ( - semver.lt(oldVersion, currentVersion) || - // So we can test the tour in any e2e environment, not only on prod - process.env.APP_ENV === 'webdriverio' + semver.lt(oldVersion, currentVersion) ) { prefs.showFeatureTour = oldVersion; save = true; From c1b66f57fb493109d71840494c8abbab8770e3b6 Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Fri, 11 Mar 2022 11:50:21 +0000 Subject: [PATCH 6/8] first run for logging so it makes all the logs --- packages/compass-e2e-tests/tests/logging.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compass-e2e-tests/tests/logging.test.ts b/packages/compass-e2e-tests/tests/logging.test.ts index 71cc74e6e4c..9ce68d5d4e3 100644 --- a/packages/compass-e2e-tests/tests/logging.test.ts +++ b/packages/compass-e2e-tests/tests/logging.test.ts @@ -11,7 +11,7 @@ describe('Logging and Telemetry integration', function () { before(async function () { telemetry = await startTelemetryServer(); - const compass = await beforeTests(); + const compass = await beforeTests({ firstRun: true }); const { browser } = compass; try { await browser.connectWithConnectionString( From eb13a4eea3462bd15b3d8b523fbcb87c367738d8 Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Fri, 11 Mar 2022 12:08:52 +0000 Subject: [PATCH 7/8] new way to force the tour to show --- packages/compass-e2e-tests/helpers/compass.ts | 7 ++-- .../tests/time-to-first-query.test.ts | 39 +++++++++++++++++++ packages/compass/src/app/index.js | 5 ++- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/packages/compass-e2e-tests/helpers/compass.ts b/packages/compass-e2e-tests/helpers/compass.ts index a42f128dde3..2aa1ed63556 100644 --- a/packages/compass-e2e-tests/helpers/compass.ts +++ b/packages/compass-e2e-tests/helpers/compass.ts @@ -89,8 +89,7 @@ export class Compass { let value; try { value = await arg.jsonValue(); - } - catch (err) { + } catch (err) { // there are still some edge cases we can't easily convert into text console.error('could not convert', arg); value = '¯\\_(ツ)_/¯'; @@ -551,8 +550,10 @@ export async function beforeTests( const { browser } = compass; await browser.waitForConnectionScreen(); - if (compass.isFirstRun) { + if (process.env.SHOW_TOUR) { await browser.closeTourModal(); + } + if (compass.isFirstRun) { await browser.closePrivacySettingsModal(); } diff --git a/packages/compass-e2e-tests/tests/time-to-first-query.test.ts b/packages/compass-e2e-tests/tests/time-to-first-query.test.ts index 71ea1a83a0a..546915e54f4 100644 --- a/packages/compass-e2e-tests/tests/time-to-first-query.test.ts +++ b/packages/compass-e2e-tests/tests/time-to-first-query.test.ts @@ -9,6 +9,7 @@ describe('Time to first query', function () { afterEach(async function () { // cleanup outside of the test so that the time it takes to run does not // get added to the time it took to run the first query + delete process.env.SHOW_TOUR; if (compass) { // even though this is after (not afterEach) currentTest points to the last test await afterTest(compass, this.currentTest); @@ -86,4 +87,42 @@ describe('Time to first query', function () { const text = await documentElementValue.getText(); expect(text).to.equal('42'); }); + + it('can open compass, connect to a database and run a query on a collection (new version)', async function () { + // force the tour modal which would normally only appear for new versions + process.env.SHOW_TOUR = 'true'; + + // start compass inside the test so that the time is measured together + compass = await beforeTests(); + + const { browser } = compass; + + await browser.connectWithConnectionString('mongodb://localhost:27018/test'); + + await browser.navigateToCollectionTab('test', 'numbers', 'Documents'); + + // search for the document with id == 42 and wait for just one result to appear + const aceCommentElement = await browser.$( + '#query-bar-option-input-filter .ace_scroller' + ); + await aceCommentElement.click(); + + await browser.keys('{ i: 42 }'); + const filterButtonElement = await browser.$( + Selectors.queryBarApplyFilterButton('Documents') + ); + await filterButtonElement.click(); + await browser.waitUntil(async () => { + // we start off with 20 results (assuming no filter) and we expect to + // have just one once the filter finishes + const result = await browser.$$('.document-list .document'); + return result.length === 1; + }); + + const documentElementValue = await browser.$( + '.document-list .document .element-value-is-int32' + ); + const text = await documentElementValue.getText(); + expect(text).to.equal('42'); + }); }); diff --git a/packages/compass/src/app/index.js b/packages/compass/src/app/index.js index 4683e81406b..06a8cc46f7a 100644 --- a/packages/compass/src/app/index.js +++ b/packages/compass/src/app/index.js @@ -308,7 +308,10 @@ var Application = View.extend({ var currentVersion = APP_VERSION; var save = false; if ( - semver.lt(oldVersion, currentVersion) + semver.lt(oldVersion, currentVersion) || + // this is so we can test the tour modal in E2E tests where the version + // is always the same + process.env.SHOW_TOUR ) { prefs.showFeatureTour = oldVersion; save = true; From 2d278a35a2d62a8de310fcb7a6fec9543ef5d72c Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Fri, 11 Mar 2022 15:11:42 +0000 Subject: [PATCH 8/8] attempt at a fix for windows --- packages/compass-e2e-tests/helpers/compass.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/compass-e2e-tests/helpers/compass.ts b/packages/compass-e2e-tests/helpers/compass.ts index 2aa1ed63556..a03dd5f2302 100644 --- a/packages/compass-e2e-tests/helpers/compass.ts +++ b/packages/compass-e2e-tests/helpers/compass.ts @@ -213,9 +213,12 @@ interface StartCompassOptions { firstRun?: boolean; } -let defaultUserDataDir: string; +let defaultUserDataDir: string | undefined; export function removeUserDataDir(): void { + if (!defaultUserDataDir) { + return; + } debug('Removing user data'); try { // this is sync so we can use it in cleanup() in index.ts @@ -241,6 +244,9 @@ async function startCompass(opts: StartCompassOptions = {}): Promise { // dir so it will be recreated below. if (defaultUserDataDir && opts.firstRun) { removeUserDataDir(); + // windows seems to be weird about us deleting and recreating this dir, so + // just make a new one for next time + defaultUserDataDir = undefined; } // Calculate the userDataDir once so it will be the same between runs. That