未验证 提交 3aaf810e 编写于 作者: C Connor Peet

experiments: allow multiple activation events

上级 1f32001c
......@@ -3,26 +3,27 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { distinct } from 'vs/base/common/arrays';
import { RunOnceWorker } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry';
import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { match } from 'vs/base/common/glob';
import { Disposable } from 'vs/base/common/lifecycle';
import { equals } from 'vs/base/common/objects';
import { language, OperatingSystem, OS } from 'vs/base/common/platform';
import { isDefined } from 'vs/base/common/types';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { language, OperatingSystem, OS } from 'vs/base/common/platform';
import { Disposable } from 'vs/base/common/lifecycle';
import { match } from 'vs/base/common/glob';
import { IRequestService, asJson } from 'vs/platform/request/common/request';
import { ITextFileService, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
import { CancellationToken } from 'vs/base/common/cancellation';
import { distinct } from 'vs/base/common/arrays';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IProductService } from 'vs/platform/product/common/productService';
import { asJson, IRequestService } from 'vs/platform/request/common/request';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceTagsService } from 'vs/workbench/contrib/tags/common/workspaceTags';
import { RunOnceWorker } from 'vs/base/common/async';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { equals } from 'vs/base/common/objects';
import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { ITextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
export const enum ExperimentState {
Evaluating,
......@@ -93,7 +94,7 @@ interface IExperimentStorageState {
* be incremented when adding a condition, otherwise experiments might activate
* on older versions of VS Code where not intended.
*/
export const currentSchemaVersion = 4;
export const currentSchemaVersion = 5;
interface IRawExperiment {
id: string;
......@@ -107,7 +108,7 @@ interface IRawExperiment {
userSetting?: { [key: string]: unknown; };
// Start the experiment if the number of activation events have happened over the last week:
activationEvent?: {
event: string;
event: string | string[];
uniqueDays?: number;
minEvents: number;
};
......@@ -285,7 +286,8 @@ export class ExperimentService extends Disposable implements IExperimentService
this.storageService.remove('allExperiments', StorageScope.GLOBAL);
}
const activationEvents = new Set(rawExperiments.map(exp => exp.condition?.activationEvent?.event).filter(evt => !!evt));
const activationEvents = new Set(rawExperiments.map(exp => exp.condition?.activationEvent?.event)
.filter(isDefined).flatMap(evt => typeof evt === 'string' ? [evt] : []));
if (activationEvents.size) {
this._register(this.extensionService.onWillActivateByEvent(evt => {
if (activationEvents.has(evt.event)) {
......@@ -402,7 +404,14 @@ export class ExperimentService extends Disposable implements IExperimentService
this.storageService.store(key, JSON.stringify(record), StorageScope.GLOBAL, StorageTarget.MACHINE);
this._experiments
.filter(e => e.state === ExperimentState.Evaluating && e.raw?.condition?.activationEvent?.event === event)
.filter(e => {
const lookingFor = e.raw?.condition?.activationEvent?.event;
if (e.state !== ExperimentState.Evaluating || !lookingFor) {
return false;
}
return typeof lookingFor === 'string' ? lookingFor === event : lookingFor?.includes(event);
})
.forEach(e => this.evaluateExperiment(e.raw!));
}
......@@ -412,14 +421,18 @@ export class ExperimentService extends Disposable implements IExperimentService
return true;
}
const { count } = getCurrentActivationRecord(safeParse(this.storageService.get(experimentEventStorageKey(setting.event), StorageScope.GLOBAL), undefined));
let total = 0;
let uniqueDays = 0;
for (const entry of count) {
if (entry > 0) {
uniqueDays++;
total += entry;
const events = typeof setting.event === 'string' ? [setting.event] : setting.event;
for (const event of events) {
const { count } = getCurrentActivationRecord(safeParse(this.storageService.get(experimentEventStorageKey(event), StorageScope.GLOBAL), undefined));
for (const entry of count) {
if (entry > 0) {
uniqueDays++;
total += entry;
}
}
}
......
......@@ -5,34 +5,32 @@
import * as assert from 'assert';
import * as sinon from 'sinon';
import { ExperimentActionType, ExperimentState, IExperiment, ExperimentService, getCurrentActivationRecord, currentSchemaVersion } from 'vs/workbench/contrib/experiments/common/experimentService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices';
import {
IExtensionManagementService, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, ILocalExtension, InstallExtensionResult
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
import { timeout } from 'vs/base/common/async';
import { Emitter } from 'vs/base/common/event';
import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test';
import { NativeURLService } from 'vs/platform/url/common/urlService';
import { IURLService } from 'vs/platform/url/common/url';
import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { OS } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { DidUninstallExtensionEvent, IExtensionIdentifier, IExtensionManagementService, ILocalExtension, InstallExtensionEvent, InstallExtensionResult } from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IProductService } from 'vs/platform/product/common/productService';
import { IWillActivateEvent, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { timeout } from 'vs/base/common/async';
import { TestExtensionService } from 'vs/workbench/test/common/workbenchTestServices';
import { OS } from 'vs/base/common/platform';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { IURLService } from 'vs/platform/url/common/url';
import { NativeURLService } from 'vs/platform/url/common/urlService';
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
import { currentSchemaVersion, ExperimentActionType, ExperimentService, ExperimentState, getCurrentActivationRecord, IExperiment } from 'vs/workbench/contrib/experiments/common/experimentService';
import { IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test';
import { IExtensionService, IWillActivateEvent } from 'vs/workbench/services/extensions/common/extensions';
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { TestWorkspaceTrustManagementService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService';
import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestExtensionService } from 'vs/workbench/test/common/workbenchTestServices';
interface ExperimentSettings {
enabled?: boolean;
......@@ -407,6 +405,35 @@ suite('Experiment Service', () => {
});
});
test('Activation event allows multiple', () => {
experimentData = {
experiments: [
{
id: 'experiment1',
enabled: true,
condition: {
activationEvent: {
event: ['other:event', 'my:event'],
minEvents: 5,
}
}
}
]
};
instantiationService.stub(IStorageService, 'get', (a: string, b: StorageScope, c?: string) => {
return a === 'experimentEventRecord-my-event'
? JSON.stringify({ count: [10], mostRecentBucket: Date.now() })
: undefined;
});
testObject = instantiationService.createInstance(TestExperimentService);
return testObject.getExperimentById('experiment1').then(result => {
assert.strictEqual(result.enabled, true);
assert.strictEqual(result.state, ExperimentState.Run);
});
});
test('Activation event does not work with old data', () => {
experimentData = {
experiments: [
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册