Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ML] Memory usage: Functional tests #197415

Merged
merged 2 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .buildkite/ftr_platform_stateful_configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ enabled:
- x-pack/test/functional/apps/ml/permissions/config.ts
- x-pack/test/functional/apps/ml/short_tests/config.ts
- x-pack/test/functional/apps/ml/stack_management_jobs/config.ts
- x-pack/test/functional/apps/ml/memory_usage/config.ts
- x-pack/test/functional/apps/monitoring/config.ts
- x-pack/test/functional/apps/painless_lab/config.ts
- x-pack/test/functional/apps/remote_clusters/config.ts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export const JobMemoryTreeMap: FC<Props> = ({ node, type, height }) => {
options={typeOptions}
selectedOptions={selectedOptions ?? []}
onChange={setSelectedOptions}
isClearable={false}
data-test-subj="mlJobTreeMapComboBox"
/>

<EuiSpacer size="s" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,18 @@ export const MemoryUsagePage: FC = () => {

{showNodeInfo ? (
<>
<EuiTabs>
<EuiTabs data-test-subj="mlMemoryUsageTabs">
<EuiTab
isSelected={selectedTab === TAB.NODES}
onClick={() => setSelectedTab(TAB.NODES)}
data-test-subj="mlMemoryUsageTab-nodes"
>
<FormattedMessage id="xpack.ml.memoryUsage.nodesTab" defaultMessage="Nodes" />
</EuiTab>
<EuiTab
isSelected={selectedTab === TAB.MEMORY_USAGE}
onClick={() => setSelectedTab(TAB.MEMORY_USAGE)}
data-test-subj="mlMemoryUsageTab-memory-usage"
>
<FormattedMessage id="xpack.ml.memoryUsage.memoryTab" defaultMessage="Memory usage" />
</EuiTab>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
<EuiTab
isSelected={selectedTab === TAB.DETAILS}
onClick={() => setSelectedTab(TAB.DETAILS)}
data-test-subj="mlNodesOverviewPanelDetailsTab"
>
<FormattedMessage
id="xpack.ml.trainedModels.nodesList.expandedRow.detailsTabTitle"
Expand All @@ -73,6 +74,7 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
<EuiTab
isSelected={selectedTab === TAB.MEMORY_USAGE}
onClick={() => setSelectedTab(TAB.MEMORY_USAGE)}
data-test-subj="mlNodesOverviewPanelMemoryTab"
>
<FormattedMessage
id="xpack.ml.trainedModels.nodesList.expandedRow.memoryTabTitle"
Expand All @@ -85,7 +87,7 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
<>
<EuiSpacer size="s" />
<EuiFlexGrid columns={2} gutterSize={'s'}>
<EuiFlexItem>
<EuiFlexItem data-test-subj="mlNodesTableRowDetailsPanel">
<EuiPanel hasShadow={false}>
<EuiTitle size={'xs'}>
<h5>
Expand All @@ -104,7 +106,7 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
</EuiPanel>
</EuiFlexItem>

<EuiFlexItem>
<EuiFlexItem data-test-subj="mlNodesTableRowDetailsAttributesPanel">
<EuiPanel hasShadow={false}>
<EuiTitle size={'xs'}>
<h5>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ export const NodesList: FC<NodesListProps> = ({ compactView = false }) => {
},
box: {
incremental: true,
'data-test-subj': 'mlNodesTableSearchInput',
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const nodesListRouteFactory = (
},
],
enableDatePicker: true,
'data-test-subj': 'mlPageMemoryUsage',
});

const PageWrapper: FC = () => {
Expand Down
20 changes: 20 additions & 0 deletions x-pack/test/functional/apps/ml/memory_usage/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { FtrConfigProviderContext } from '@kbn/test';

export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js'));

return {
...functionalConfig.getAll(),
testFiles: [require.resolve('.')],
junit: {
reportName: 'Chrome X-Pack UI Functional Tests - ML memory_usage',
},
};
}
33 changes: 33 additions & 0 deletions x-pack/test/functional/apps/ml/memory_usage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { FtrProviderContext } from '../../../ftr_provider_context';

export default function ({ getService, loadTestFile }: FtrProviderContext) {
const ml = getService('ml');

describe('machine learning - overview page', function () {
this.tags(['skipFirefox']);

before(async () => {
await ml.securityCommon.createMlRoles();
await ml.securityCommon.createMlUsers();
await ml.securityUI.loginAsMlPowerUser();
});

after(async () => {
await ml.securityUI.logout();

await ml.securityCommon.cleanMlUsers();
await ml.securityCommon.cleanMlRoles();

await ml.testResources.resetKibanaTimeZone();
});

loadTestFile(require.resolve('./memory_usage_page'));
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { FtrProviderContext } from '../../../ftr_provider_context';

export default function ({ getService }: FtrProviderContext) {
const ml = getService('ml');
const esArchiver = getService('esArchiver');

const jobId = 'sample_job';

describe('ML memory usage page', function () {
this.tags(['ml']);

before(async () => {
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote');

const jobConfig = ml.commonConfig.getADFqSingleMetricJobConfig(jobId);

// Create and open AD job
await ml.api.createAnomalyDetectionJob(jobConfig);
await ml.api.openAnomalyDetectionJob(jobId);

await ml.navigation.navigateToMl();
await ml.navigation.navigateToMemoryUsage();
});

after(async () => {
await ml.api.closeAnomalyDetectionJob(jobId);
await ml.api.cleanMlIndices();
});

it('opens page with nodes tab selected', async () => {
await ml.memoryUsage.assertMemoryUsageTabIsSelected('nodes');
});

it('allows sorting', async () => {
await ml.memoryUsage.sortColumn('tableHeaderCell_name_1');
await ml.memoryUsage.assertColumnIsSorted('tableHeaderCell_name_1', 'descending');
});

it('allows searching for a node', async () => {
await ml.memoryUsage.searchForNode('ftr');
await ml.memoryUsage.assertRowCount(1);
});

it('expands node details and displays memory usage details', async () => {
await ml.memoryUsage.expandRow();
await ml.memoryUsage.assertNodeExpandedDetailsPanelsExist();
await ml.memoryUsage.selectNodeExpandedRowTab('mlNodesOverviewPanelMemoryTab');
await ml.memoryUsage.assertChartItemsSelectedByDefault();
await ml.memoryUsage.assertTreeChartExists();
});

it('clears selected chart items', async () => {
await ml.memoryUsage.clearSelectedChartItems();
await ml.memoryUsage.assertEmptyTreeChartExists();
});

it('selects memory usage tab and displays chart', async () => {
await ml.memoryUsage.selectTab('memory-usage');
await ml.memoryUsage.assertTreeChartExists();

await ml.memoryUsage.clearSelectedChartItems();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth adding a step where you leave just the Anomaly Detection Job item and assert the tree chart exists, before clearing all?

Copy link
Contributor Author

@rbrtj rbrtj Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I'm not sure what you mean exactly.
This test is responsible for ensuring that the chart is displayed properly in the memory usage tab.
image
Then, it clears the combo box and checks if the chart displays an empty state.
The same thing was done earlier, but for a chart inside an expanded row of a particular node.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't worry. I was thinking we could do a click on the DFA and Trained model items above the chart to get it into this state, but probably not worth the effort as there is only 1 AD job in this case.

Screenshot 2024-10-25 at 10 07 55

await ml.memoryUsage.assertEmptyTreeChartExists();
});
});
}
5 changes: 4 additions & 1 deletion x-pack/test/functional/services/ml/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ import { MlTableServiceProvider } from './common_table_service';
import { MachineLearningFieldStatsFlyoutProvider } from './field_stats_flyout';
import { MachineLearningDataDriftProvider } from './data_drift';
import { TrainedModelsFlyoutProvider } from './add_trained_models_flyout';
import { MachineLearningMemoryUsageProvider } from './memory_usage';

export function MachineLearningProvider(context: FtrProviderContext) {
const commonAPI = MachineLearningCommonAPIProvider(context);
const commonUI = MachineLearningCommonUIProvider(context);
Expand Down Expand Up @@ -178,7 +180,7 @@ export function MachineLearningProvider(context: FtrProviderContext) {
const deployDFAModelFlyout = DeployDFAModelFlyoutProvider(context, commonUI);
const mlNodesPanel = MlNodesPanelProvider(context);
const notifications = NotificationsProvider(context, commonUI, tableService);

const memoryUsage = MachineLearningMemoryUsageProvider(context);
const cases = MachineLearningCasesProvider(context, swimLane, anomalyCharts);

return {
Expand Down Expand Up @@ -244,5 +246,6 @@ export function MachineLearningProvider(context: FtrProviderContext) {
trainedModelsFlyout,
deployDFAModelFlyout,
trainedModelsTable,
memoryUsage,
};
}
107 changes: 107 additions & 0 deletions x-pack/test/functional/services/ml/memory_usage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';

export function MachineLearningMemoryUsageProvider({ getService }: FtrProviderContext) {
const testSubjects = getService('testSubjects');
const comboBox = getService('comboBox');

return {
async assertNodeExpandedDetailsPanelsExist() {
await testSubjects.existOrFail('mlNodesTableRowDetailsPanel');
await testSubjects.existOrFail('mlNodesTableRowDetailsAttributesPanel');
},

async assertTabIsSelected(tabName: string) {
await testSubjects.existOrFail(`mlNodesOverviewPanel ${tabName}Tab`);
},

async selectTab(tabName: string) {
await testSubjects.click(`mlMemoryUsageTab-${tabName}`);
},

async assertMemoryUsageTabsExist() {
await testSubjects.existOrFail('mlMemoryUsageTabs');
},

async assertMemoryUsageTabIsSelected(tabName: string) {
const isSelected = await testSubjects.getAttribute(
`mlMemoryUsageTab-${tabName}`,
'aria-selected'
);
expect(isSelected).to.eql('true');
},

async assertRowCount(expectedCount: number) {
const rowCount = await this.getRowCount();
expect(rowCount).to.eql(expectedCount);
},

async getAllRows() {
return await testSubjects.findAll('~mlNodesTableRow');
},

async expandRow() {
await testSubjects.click('mlNodesTableRowDetailsToggle');
},

async getRowCount() {
const rows = await this.getAllRows();
return rows.length;
},

async assertColumnHeaderExists(columnName: string) {
await testSubjects.existOrFail(columnName);
},

async assertColumnIsSorted(columnName: string, sortDirection: 'ascending' | 'descending') {
const sorted = await testSubjects.getAttribute(columnName, 'aria-sort');
expect(sorted).to.eql(sortDirection);
},

async sortColumn(columnName: string) {
await this.assertColumnHeaderExists(columnName);
await testSubjects.click(columnName);
},

async assertSearchBarExists() {
await testSubjects.existOrFail('mlNodesTableSearchInput');
},

async searchForNode(nodeId: string) {
await this.assertSearchBarExists();
await testSubjects.setValue('mlNodesTableSearchInput', nodeId);
},

async selectNodeExpandedRowTab(tabName: string) {
await testSubjects.click(tabName);
},

async clearSelectedChartItems() {
await comboBox.clear('~mlJobTreeMap > mlJobTreeMapComboBox');
},

async getSelectedChartItems() {
return await comboBox.getComboBoxSelectedOptions('~mlJobTreeMap > comboBoxInput');
},

async assertChartItemsSelectedByDefault() {
const selectedOptions = await this.getSelectedChartItems();
expect(selectedOptions.length).to.be.greaterThan(0);
},

async assertTreeChartExists() {
await testSubjects.existOrFail('mlJobTreeMap withData');
},

async assertEmptyTreeChartExists() {
await testSubjects.existOrFail('mlJobTreeMap empty');
},
};
}
4 changes: 4 additions & 0 deletions x-pack/test/functional/services/ml/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ export function MachineLearningNavigationProvider({
await this.navigateToArea('~mlMainTab & ~notifications', 'mlPageNotifications');
},

async navigateToMemoryUsage() {
await this.navigateToArea('~mlMainTab & ~nodesOverview', 'mlPageMemoryUsage');
},

async navigateToAnomalyDetection() {
await this.navigateToArea('~mlMainTab & ~anomalyDetection', 'mlPageJobManagement');
},
Expand Down