From 3b595e2df1f5b2907a0d90b1f2e3f29463f0b2e1 Mon Sep 17 00:00:00 2001 From: John Calderon Date: Tue, 14 Feb 2023 16:57:06 -0500 Subject: [PATCH 1/5] initial testing samples --- .../react-ui/src/sections/Habitat.test.js | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 skyline-vscode/react-ui/src/sections/Habitat.test.js diff --git a/skyline-vscode/react-ui/src/sections/Habitat.test.js b/skyline-vscode/react-ui/src/sections/Habitat.test.js new file mode 100644 index 0000000..19545e7 --- /dev/null +++ b/skyline-vscode/react-ui/src/sections/Habitat.test.js @@ -0,0 +1,37 @@ +import {render, screen } from '@testing-library/react'; + +import Habitat from "./Habitat"; + +const { ResizeObserver } = window; + +beforeEach(() => { + delete window.ResizeObserver; + window.ResizeObserver = jest.fn().mockImplementation(() => ({ + observe: jest.fn(), + unobserve: jest.fn(), + disconnect: jest.fn(), + })); +}); + +afterEach(() => { + window.ResizeObserver = ResizeObserver; + jest.restoreAllMocks(); +}); + +// describe('Habitat', () => { +// it ('should render successfully', () => { +// act(() => { +// render(,container); +// }); +// console.log(container); +// expect(container.textContent).toContain('Loading Habitat predictions.'); +// }); +// }); + +test("loads and displays greeting", async () => { + // ARRANGE + render(); + + // ASSERT + expect(await screen.findByText(/spinner/i)).toHaveTextContent("Loading Habitat predictions."); +}); From b2eb9ef98b169d2ab49bfa9a92da5193f8d0eaf6 Mon Sep 17 00:00:00 2001 From: John Calderon Date: Wed, 15 Feb 2023 16:57:41 -0500 Subject: [PATCH 2/5] added testing for Deployment | Provider | Energy | Memory&Throughput | PerfBarContainer --- skyline-vscode/react-ui/src/App.js | 3 +- skyline-vscode/react-ui/src/data/test_data.js | 536 ++++++++++++++++++ .../src/sections/DeploymentTab.test.js | 75 +++ .../src/sections/EnergyConsumption.test.js | 184 ++++++ .../react-ui/src/sections/Habitat.test.js | 52 +- .../sections/MemThroughputContainer.test.js | 15 + .../src/sections/PerfBarContainer.test.js | 29 + .../src/sections/ProviderPanel.test.js | 64 +++ 8 files changed, 943 insertions(+), 15 deletions(-) create mode 100644 skyline-vscode/react-ui/src/data/test_data.js create mode 100644 skyline-vscode/react-ui/src/sections/DeploymentTab.test.js create mode 100644 skyline-vscode/react-ui/src/sections/EnergyConsumption.test.js create mode 100644 skyline-vscode/react-ui/src/sections/MemThroughputContainer.test.js create mode 100644 skyline-vscode/react-ui/src/sections/PerfBarContainer.test.js create mode 100644 skyline-vscode/react-ui/src/sections/ProviderPanel.test.js diff --git a/skyline-vscode/react-ui/src/App.js b/skyline-vscode/react-ui/src/App.js index c2bc580..162f16c 100644 --- a/skyline-vscode/react-ui/src/App.js +++ b/skyline-vscode/react-ui/src/App.js @@ -29,7 +29,6 @@ import MemThroughputContainer from './sections/MemThroughputContainer'; function acquireApi() { // if (typeof this.acquireApi.api == 'undefined') { if (typeof acquireApi.api === 'undefined') { - console.log("Calling acquire function"); if (typeof acquireVsCodeApi === "function") { let f = window['acquireVsCodeApi']; let a = f(); @@ -50,7 +49,7 @@ function restartProfiling() { }); } -const sendMock = false; +const sendMock = true; function App() { const [analysisState, setAnalysisState] = useState(); diff --git a/skyline-vscode/react-ui/src/data/test_data.js b/skyline-vscode/react-ui/src/data/test_data.js new file mode 100644 index 0000000..2db63da --- /dev/null +++ b/skyline-vscode/react-ui/src/data/test_data.js @@ -0,0 +1,536 @@ +export const labels = [ + { label: "", percentage: 0.1711083135902288, clickable: false }, + { label: "", percentage: 0.01741821166351814, clickable: false }, + { label: "", percentage: 0.07889425299332373, clickable: false }, + { label: "layer4.0", percentage: 15.335198314446881, clickable: false }, + { label: "layer3.0", percentage: 28.00233621209079, clickable: false }, + { label: "layer2.0", percentage: 24.773819017726606, clickable: false }, + { label: "layer1.0", percentage: 25.638582363832683, clickable: false }, + { label: "", percentage: 1.3760387158267198, clickable: false }, + { label: "", percentage: 0.7602536847400398, clickable: false }, + { label: "", percentage: 1.6301396557185361, clickable: false }, + { label: "", percentage: 2.216211257370674, clickable: false }, +]; + +export const renderPerfBars = [ + { + name: "linear", + num_children: 0, + forward_ms: 0.02184533327817917, + backward_ms: 0.03515733405947685, + size_bytes: 131072, + file_refs: [], + depth: 1, + parent: { + name: "root", + num_children: 11, + forward_ms: 12.462421417236328, + backward_ms: 20.851369857788086, + size_bytes: 1386957824, + file_refs: [ + { + path: "resnet.py", + line_no: 182, + run_time_ms: 0.7383040189743042, + size_bytes: 9633792, + }, + { + path: "resnet.py", + line_no: 183, + run_time_ms: 0.5430613160133362, + size_bytes: 52429824, + }, + { + path: "resnet.py", + line_no: 184, + run_time_ms: 0.25326934456825256, + size_bytes: 52428800, + }, + { + path: "resnet.py", + line_no: 185, + run_time_ms: 0.4584106504917145, + size_bytes: 25690112, + }, + { + path: "resnet.py", + line_no: 187, + run_time_ms: 8.541183471679688, + size_bytes: 534001664, + }, + { + path: "resnet.py", + line_no: 85, + run_time_ms: 4.606293201446533, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 86, + run_time_ms: 1.3073066473007202, + size_bytes: 122387456, + }, + { + path: "resnet.py", + line_no: 87, + run_time_ms: 0.6290773153305054, + size_bytes: 122355712, + }, + { + path: "resnet.py", + line_no: 89, + run_time_ms: 8.89958381652832, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 90, + run_time_ms: 0.9847466945648193, + size_bytes: 88833024, + }, + { + path: "resnet.py", + line_no: 91, + run_time_ms: 0.45499733090400696, + size_bytes: 88866816, + }, + { + path: "resnet.py", + line_no: 93, + run_time_ms: 4.2308268547058105, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 94, + run_time_ms: 3.455317258834839, + size_bytes: 357554176, + }, + { + path: "resnet.py", + line_no: 97, + run_time_ms: 3.5921919345855713, + size_bytes: 110262272, + }, + { + path: "resnet.py", + line_no: 99, + run_time_ms: 1.3127679824829102, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 100, + run_time_ms: 1.7585493326187134, + size_bytes: 356384768, + }, + { + path: "resnet.py", + line_no: 188, + run_time_ms: 8.253098487854004, + size_bytes: 372535296, + }, + { + path: "resnet.py", + line_no: 189, + run_time_ms: 9.32863998413086, + size_bytes: 264454144, + }, + { + path: "resnet.py", + line_no: 190, + run_time_ms: 5.108736038208008, + size_bytes: 75653120, + }, + { + path: "resnet.py", + line_no: 192, + run_time_ms: 0.026282666251063347, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 193, + run_time_ms: 0.0058026667684316635, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 194, + run_time_ms: 0.05700266733765602, + size_bytes: 131072, + }, + ], + depth: 0, + }, + percentage: 0.1711083135902288, + }, + { + name: "flatten", + num_children: 0, + forward_ms: 0.003413333324715495, + backward_ms: 0.0023893334437161684, + size_bytes: 0, + file_refs: [], + depth: 1, + percentage: 0.01741821166351814, + }, + { + name: "adaptive_avg_pool2d", + num_children: 0, + forward_ms: 0.015360000543296337, + backward_ms: 0.010922666639089584, + size_bytes: 0, + file_refs: [], + depth: 1, + percentage: 0.07889425299332373, + }, + { + name: "layer4.0", + num_children: 11, + forward_ms: 1.8503680229187012, + backward_ms: 3.2583680152893066, + size_bytes: 75653120, + file_refs: [ + { + path: "resnet.py", + line_no: 85, + run_time_ms: 1.0100053548812866, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 86, + run_time_ms: 0.1133226677775383, + size_bytes: 9973760, + }, + { + path: "resnet.py", + line_no: 87, + run_time_ms: 0.05700266733765602, + size_bytes: 9961472, + }, + { + path: "resnet.py", + line_no: 89, + run_time_ms: 1.686527967453003, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 90, + run_time_ms: 0.08191999793052673, + size_bytes: 5320704, + }, + { + path: "resnet.py", + line_no: 91, + run_time_ms: 0.03379200026392937, + size_bytes: 5373952, + }, + { + path: "resnet.py", + line_no: 93, + run_time_ms: 0.8311466574668884, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 94, + run_time_ms: 0.22937600314617157, + size_bytes: 19316736, + }, + { + path: "resnet.py", + line_no: 97, + run_time_ms: 0.8942933678627014, + size_bytes: 6438912, + }, + { + path: "resnet.py", + line_no: 99, + run_time_ms: 0.077482670545578, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 100, + run_time_ms: 0.09386666864156723, + size_bytes: 19267584, + }, + ], + depth: 1, + percentage: 15.335198314446881, + }, + { + name: "layer3.0", + num_children: 11, + forward_ms: 3.3573546409606934, + backward_ms: 5.971285343170166, + size_bytes: 264454144, + file_refs: [ + { + path: "resnet.py", + line_no: 85, + run_time_ms: 1.815551996231079, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 86, + run_time_ms: 0.30583465099334717, + size_bytes: 28913664, + }, + { + path: "resnet.py", + line_no: 87, + run_time_ms: 0.14267733693122864, + size_bytes: 28901376, + }, + { + path: "resnet.py", + line_no: 89, + run_time_ms: 2.986325263977051, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 90, + run_time_ms: 0.2051413357257843, + size_bytes: 19279872, + }, + { + path: "resnet.py", + line_no: 91, + run_time_ms: 0.08806400001049042, + size_bytes: 19267584, + }, + { + path: "resnet.py", + line_no: 93, + run_time_ms: 1.6817493438720703, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 94, + run_time_ms: 0.5908480286598206, + size_bytes: 78168064, + }, + { + path: "resnet.py", + line_no: 97, + run_time_ms: 0.8191999793052673, + size_bytes: 12853248, + }, + { + path: "resnet.py", + line_no: 99, + run_time_ms: 0.29286399483680725, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 100, + run_time_ms: 0.40038397908210754, + size_bytes: 77070336, + }, + ], + depth: 1, + percentage: 28.00233621209079, + }, + { + name: "layer2.0", + num_children: 11, + forward_ms: 3.1767892837524414, + backward_ms: 5.0763092041015625, + size_bytes: 372535296, + file_refs: [ + { + path: "resnet.py", + line_no: 85, + run_time_ms: 1.0803200006484985, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 86, + run_time_ms: 0.4505600035190582, + size_bytes: 44961792, + }, + { + path: "resnet.py", + line_no: 87, + run_time_ms: 0.22493866086006165, + size_bytes: 44957696, + }, + { + path: "resnet.py", + line_no: 89, + run_time_ms: 2.2603094577789307, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 90, + run_time_ms: 0.26077866554260254, + size_bytes: 25694208, + }, + { + path: "resnet.py", + line_no: 91, + run_time_ms: 0.12458667159080505, + size_bytes: 25690112, + }, + { + path: "resnet.py", + line_no: 93, + run_time_ms: 0.8956586718559265, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 94, + run_time_ms: 1.0052266120910645, + size_bytes: 102776832, + }, + { + path: "resnet.py", + line_no: 97, + run_time_ms: 1.0629119873046875, + size_bytes: 25694208, + }, + { + path: "resnet.py", + line_no: 99, + run_time_ms: 0.37614935636520386, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 100, + run_time_ms: 0.5116586685180664, + size_bytes: 102760448, + }, + ], + depth: 1, + percentage: 24.773819017726606, + }, + { + name: "layer1.0", + num_children: 11, + forward_ms: 3.3675947189331055, + backward_ms: 5.17358922958374, + size_bytes: 534001664, + file_refs: [ + { + path: "resnet.py", + line_no: 85, + run_time_ms: 0.7004159688949585, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 86, + run_time_ms: 0.4375893473625183, + size_bytes: 38538240, + }, + { + path: "resnet.py", + line_no: 87, + run_time_ms: 0.20445865392684937, + size_bytes: 38535168, + }, + { + path: "resnet.py", + line_no: 89, + run_time_ms: 1.9664212465286255, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 90, + run_time_ms: 0.4369066655635834, + size_bytes: 38538240, + }, + { + path: "resnet.py", + line_no: 91, + run_time_ms: 0.20855465531349182, + size_bytes: 38535168, + }, + { + path: "resnet.py", + line_no: 93, + run_time_ms: 0.822272002696991, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 94, + run_time_ms: 1.6298667192459106, + size_bytes: 157292544, + }, + { + path: "resnet.py", + line_no: 97, + run_time_ms: 0.8157866597175598, + size_bytes: 65275904, + }, + { + path: "resnet.py", + line_no: 99, + run_time_ms: 0.566271960735321, + size_bytes: 0, + }, + { + path: "resnet.py", + line_no: 100, + run_time_ms: 0.7526400089263916, + size_bytes: 157286400, + }, + ], + depth: 1, + percentage: 25.638582363832683, + }, + { + name: "max_pool2d", + num_children: 0, + forward_ms: 0.10922666639089584, + backward_ms: 0.3491840064525604, + size_bytes: 25690112, + file_refs: [], + depth: 1, + percentage: 1.3760387158267198, + }, + { + name: "relu", + num_children: 0, + forward_ms: 0.12697599828243256, + backward_ms: 0.1262933313846588, + size_bytes: 52428800, + file_refs: [], + depth: 1, + percentage: 0.7602536847400398, + }, + { + name: "add_, batch_norm", + num_children: 0, + forward_ms: 0.19865600764751434, + backward_ms: 0.34440532326698303, + size_bytes: 52429824, + file_refs: [], + depth: 1, + percentage: 1.6301396557185361, + }, + { + name: "conv2d", + num_children: 0, + forward_ms: 0.2348373383283615, + backward_ms: 0.5034666657447815, + size_bytes: 9633792, + file_refs: [], + depth: 1, + percentage: 2.216211257370674, + }, +]; diff --git a/skyline-vscode/react-ui/src/sections/DeploymentTab.test.js b/skyline-vscode/react-ui/src/sections/DeploymentTab.test.js new file mode 100644 index 0000000..b43fc85 --- /dev/null +++ b/skyline-vscode/react-ui/src/sections/DeploymentTab.test.js @@ -0,0 +1,75 @@ +import { render, screen } from "@testing-library/react"; +import DeploymentTab from "./DeploymentTab"; + +const { ResizeObserver } = window; +const numIterations = 10000; + +const data = [ + ["source", 22.029312], + ["P100", 14.069682], + ["P4000", 127.268085], // 27.268085 + ["RTX2070", 16.088268], + ["RTX2080Ti", 11.826558], + ["T4", 22.029312], + ["V100", 10.182922], + ["A100", 10.068596], + ["RTX3090", 9.841998], + ["A40", 11.558072], + ["A4000", 14.67059], + ["RTX4000", 20.2342], +]; + +beforeEach(() => { + delete window.ResizeObserver; + window.ResizeObserver = jest.fn().mockImplementation(() => ({ + observe: jest.fn(), + unobserve: jest.fn(), + disconnect: jest.fn(), + })); +}); + +afterEach(() => { + window.ResizeObserver = ResizeObserver; + jest.restoreAllMocks(); +}); + +jest.mock("recharts", () => { + const OriginalRechartsModule = jest.requireActual("recharts"); + + return { + ...OriginalRechartsModule, + ResponsiveContainer: ({ height, children }) => ( +
+ {children} +
+ ), + }; +}); + +test("Shows loading spinner when there is no habitat data", () => { + // ARRANGE + render(); + + // ASSERT + expect(screen.getByText(/loading information/i)).toBeTruthy(); + const deploymentSection = screen.queryByText(/deployment target/i); + expect(deploymentSection).toBeNull(); +}); + +test("Shows deployment target when there is habitat data", () => { + // ARRANGE + const { container } = render( + + ); + + // ASSERT + expect(screen.getByText(/deployment target/i)).toBeTruthy(); + const deploymentSection = screen.queryByText(/loading information/i); + expect(deploymentSection).toBeNull(); + expect( + container.querySelector(".recharts-responsive-container") + ).toBeTruthy(); +}); diff --git a/skyline-vscode/react-ui/src/sections/EnergyConsumption.test.js b/skyline-vscode/react-ui/src/sections/EnergyConsumption.test.js new file mode 100644 index 0000000..2b056ca --- /dev/null +++ b/skyline-vscode/react-ui/src/sections/EnergyConsumption.test.js @@ -0,0 +1,184 @@ +import { render, screen } from "@testing-library/react"; +import EnergyConsumption from "./EnergyConsumption"; + +const { ResizeObserver } = window; + +const numIterations = 10000; + +const energy_complete = { + current: { + total_consumption: 2.3, + components: [ + { type: "ENERGY_UNSPECIFIED", consumption: 0 }, + { type: "ENERGY_CPU_DRAM", consumption: 1.15 }, + { type: "ENERGY_GPU", consumption: 1.15 }, + ], + }, + past_measurements: [ + { + total_consumption: 8.1, + components: [ + { type: "ENERGY_UNSPECIFIED", consumption: 0 }, + { type: "ENERGY_CPU_DRAM", consumption: 7.05 }, + { type: "ENERGY_GPU", consumption: 1.05 }, + ], + }, + { + total_consumption: 9.3, + components: [ + { type: "ENERGY_UNSPECIFIED", consumption: 0 }, + { type: "ENERGY_CPU_DRAM", consumption: 1.15 }, + { type: "ENERGY_GPU", consumption: 8.15 }, + ], + }, + ], +}; + +const energy_only_current = { + current: { + total_consumption: 2.3, + components: [ + { type: "ENERGY_UNSPECIFIED", consumption: 0 }, + { type: "ENERGY_CPU_DRAM", consumption: 1.15 }, + { type: "ENERGY_GPU", consumption: 1.15 }, + ], + }, +}; + +const energy_only_past = { + past_measurements: [ + { + total_consumption: 8.1, + components: [ + { type: "ENERGY_UNSPECIFIED", consumption: 0 }, + { type: "ENERGY_CPU_DRAM", consumption: 7.05 }, + { type: "ENERGY_GPU", consumption: 1.05 }, + ], + }, + { + total_consumption: 9.3, + components: [ + { type: "ENERGY_UNSPECIFIED", consumption: 0 }, + { type: "ENERGY_CPU_DRAM", consumption: 1.15 }, + { type: "ENERGY_GPU", consumption: 8.15 }, + ], + }, + ], +}; + +const energy_empty_data = { + current: {}, + past_measurements: {}, +}; + +beforeEach(() => { + delete window.ResizeObserver; + window.ResizeObserver = jest.fn().mockImplementation(() => ({ + observe: jest.fn(), + unobserve: jest.fn(), + disconnect: jest.fn(), + })); +}); + +afterEach(() => { + window.ResizeObserver = ResizeObserver; + jest.restoreAllMocks(); +}); + +jest.mock("recharts", () => { + const OriginalRechartsModule = jest.requireActual("recharts"); + + return { + ...OriginalRechartsModule, + ResponsiveContainer: ({ height, children }) => ( +
+ {children} +
+ ), + }; +}); + +test("No energy data: shows only loading spinner", () => { + // ARRANGE + render(); + + // ASSERT + expect( + screen.getByText(/loading energy and environmental data/i) + ).toBeTruthy(); +}); + +test("Only current data: shows both pie and graph data", () => { + // ARRANGE + const { container } = render( + + ); + + // ASSERT + expect(screen.getByText(/total consumption/i)).toBeTruthy(); + expect(screen.getByText(/relative to your other experiments/i)).toBeTruthy(); + + expect( + container.querySelector(".recharts-responsive-container") + ).toBeTruthy(); +}); + +test("Only past experiments: shows only graph data", () => { + // ARRANGE + const { container } = render( + + ); + + // ASSERT + expect(screen.getByText(/could not load the data/i)).toBeTruthy(); + expect(screen.getByText(/relative to your other experiments/i)).toBeTruthy(); + + expect( + container.querySelector(".recharts-responsive-container") + ).toBeTruthy(); +}); + +test("Empty data: shows could not load data", () => { + // ARRANGE + const { container } = render( + + ); + + // ASSERT + expect(screen.getAllByText(/could not load the data/i)).toBeTruthy(); + + expect(container.querySelector(".recharts-responsive-container")).toBeFalsy(); +}); + +test("All information Complete: show both charts", () => { + const { container } = render( + + ); + + // ASSERT + expect(screen.getAllByText(/breakdown/i)).toBeTruthy(); + expect(screen.getByText(/relative to your other experiments/i)).toBeTruthy(); + + expect( + container.querySelector(".recharts-responsive-container") + ).toBeTruthy(); + + expect( + container.querySelector(".bargraph-container") + ).toBeTruthy(); +}); diff --git a/skyline-vscode/react-ui/src/sections/Habitat.test.js b/skyline-vscode/react-ui/src/sections/Habitat.test.js index 19545e7..80d4b20 100644 --- a/skyline-vscode/react-ui/src/sections/Habitat.test.js +++ b/skyline-vscode/react-ui/src/sections/Habitat.test.js @@ -1,9 +1,23 @@ import {render, screen } from '@testing-library/react'; - import Habitat from "./Habitat"; const { ResizeObserver } = window; +const data = [ + ["source", 22.029312], + ["P100",14.069682], + ["P4000", 127.268085], // 27.268085 + ["RTX2070", 16.088268], + ["RTX2080Ti", 11.826558], + ["T4", 22.029312], + ["V100", 10.182922], + ["A100", 10.068596], + ["RTX3090", 9.841998], + ["A40", 11.558072], + ["A4000", 14.67059], + ["RTX4000", 20.2342], +]; + beforeEach(() => { delete window.ResizeObserver; window.ResizeObserver = jest.fn().mockImplementation(() => ({ @@ -18,20 +32,32 @@ afterEach(() => { jest.restoreAllMocks(); }); -// describe('Habitat', () => { -// it ('should render successfully', () => { -// act(() => { -// render(,container); -// }); -// console.log(container); -// expect(container.textContent).toContain('Loading Habitat predictions.'); -// }); -// }); - -test("loads and displays greeting", async () => { +jest.mock('recharts', () => { + const OriginalRechartsModule = jest.requireActual('recharts'); + + return { + ...OriginalRechartsModule, + ResponsiveContainer: ({ height, children }) => ( +
+ {children} +
+ ), + }; +}); + +test("Shows loading spinner when there is no habitat data", () => { // ARRANGE render(); // ASSERT - expect(await screen.findByText(/spinner/i)).toHaveTextContent("Loading Habitat predictions."); + expect(screen.getByText(/loading habitat predictions/i)).toBeTruthy(); +}); + +test("Shows graph when habitat data is present", () => { + // ARRANGE + const { container } = render(); + + // ASSERT + expect(container.querySelector('.recharts-responsive-container')).toBeTruthy(); + }); diff --git a/skyline-vscode/react-ui/src/sections/MemThroughputContainer.test.js b/skyline-vscode/react-ui/src/sections/MemThroughputContainer.test.js new file mode 100644 index 0000000..8c98c07 --- /dev/null +++ b/skyline-vscode/react-ui/src/sections/MemThroughputContainer.test.js @@ -0,0 +1,15 @@ +import {render, screen } from '@testing-library/react'; +import MemThroughputContainer from './MemThroughputContainer'; + +import { profiling_data } from "../data/mock_data"; + +test("Display sliders", () => { + // ARRANGE + render(); + + // ASSERT + const batchSize = screen.queryByText(/using predicted batch size/i); + expect(batchSize).toBeNull(); + expect(screen.getByText(/peak memory usage/i)).toBeTruthy(); + expect(screen.getAllByText(/throughput/i)).toBeTruthy(); + }); \ No newline at end of file diff --git a/skyline-vscode/react-ui/src/sections/PerfBarContainer.test.js b/skyline-vscode/react-ui/src/sections/PerfBarContainer.test.js new file mode 100644 index 0000000..7274856 --- /dev/null +++ b/skyline-vscode/react-ui/src/sections/PerfBarContainer.test.js @@ -0,0 +1,29 @@ +import { render, screen } from "@testing-library/react"; +import PerfBarContainer from "./PerfBarContainer"; +import { labels, renderPerfBars } from "../data/test_data"; + +test("No display when there is no information", () => { + // ARRANGE + render(); + + // ASSERT + const layer1 = screen.queryByText(/layer1.0/i); + expect(layer1).toBeNull(); + const layer2 = screen.queryByText(/layer2.0/i); + expect(layer2).toBeNull(); + const layer3 = screen.queryByText(/layer3.0/i); + expect(layer3).toBeNull(); + const layer4 = screen.queryByText(/layer4.0/i); + expect(layer4).toBeNull(); +}); + +test("Display set of layers", () => { + // ARRANGE + render(); + + // ASSERT + expect(screen.getByText(/layer1.0/i)).toBeTruthy(); + expect(screen.getByText(/layer2.0/i)).toBeTruthy(); + expect(screen.getByText(/layer3.0/i)).toBeTruthy(); + expect(screen.getByText(/layer4.0/i)).toBeTruthy(); + }); \ No newline at end of file diff --git a/skyline-vscode/react-ui/src/sections/ProviderPanel.test.js b/skyline-vscode/react-ui/src/sections/ProviderPanel.test.js new file mode 100644 index 0000000..b9db390 --- /dev/null +++ b/skyline-vscode/react-ui/src/sections/ProviderPanel.test.js @@ -0,0 +1,64 @@ +import { render, screen } from "@testing-library/react"; +import ProviderPanel from "./ProviderPanel"; + +const { ResizeObserver } = window; +const numIterations = 10000; + +const data = [ + ["source", 22.029312], + ["P100", 14.069682], + ["P4000", 127.268085], // 27.268085 + ["RTX2070", 16.088268], + ["RTX2080Ti", 11.826558], + ["T4", 22.029312], + ["V100", 10.182922], + ["A100", 10.068596], + ["RTX3090", 9.841998], + ["A40", 11.558072], + ["A4000", 14.67059], + ["RTX4000", 20.2342], +]; + +beforeEach(() => { + delete window.ResizeObserver; + window.ResizeObserver = jest.fn().mockImplementation(() => ({ + observe: jest.fn(), + unobserve: jest.fn(), + disconnect: jest.fn(), + })); +}); + +afterEach(() => { + window.ResizeObserver = ResizeObserver; + jest.restoreAllMocks(); +}); + +jest.mock("recharts", () => { + const OriginalRechartsModule = jest.requireActual("recharts"); + + return { + ...OriginalRechartsModule, + ResponsiveContainer: ({ height, children }) => ( +
+ {children} +
+ ), + }; +}); + +test("Shows a scatter chart", () => { + // ARRANGE + const { container } = render( + + ); + + // ASSERT + expect(screen.getByText(/Providers/i)).toBeTruthy(); + expect( + container.querySelector(".recharts-responsive-container") + ).toBeTruthy(); + expect(screen.getByText(/select a configuration/i)).toBeTruthy(); +}); From 0b598cfc2d643d9cec33b236fd3ec84c465c6730 Mon Sep 17 00:00:00 2001 From: John Calderon Date: Thu, 16 Feb 2023 16:49:46 -0500 Subject: [PATCH 3/5] completed testing for principal sections and utils --- .../src/sections/DeploymentTab.test.js | 2 +- .../src/sections/EnergyConsumption.test.js | 10 +-- .../react-ui/src/sections/Habitat.test.js | 2 +- .../react-ui/src/sections/Iterations.test.js | 36 +++++++++ .../react-ui/src/sections/ProviderPanel.js | 1 - .../src/sections/ProviderPanel.test.js | 2 +- .../src/sections/WelcomeScreen.test.js | 15 ++++ skyline-vscode/react-ui/src/utils/utils.js | 6 +- .../react-ui/src/utils/utils.test.js | 77 +++++++++++++++++++ 9 files changed, 139 insertions(+), 12 deletions(-) create mode 100644 skyline-vscode/react-ui/src/sections/Iterations.test.js create mode 100644 skyline-vscode/react-ui/src/sections/WelcomeScreen.test.js create mode 100644 skyline-vscode/react-ui/src/utils/utils.test.js diff --git a/skyline-vscode/react-ui/src/sections/DeploymentTab.test.js b/skyline-vscode/react-ui/src/sections/DeploymentTab.test.js index b43fc85..f4e1c4b 100644 --- a/skyline-vscode/react-ui/src/sections/DeploymentTab.test.js +++ b/skyline-vscode/react-ui/src/sections/DeploymentTab.test.js @@ -70,6 +70,6 @@ test("Shows deployment target when there is habitat data", () => { const deploymentSection = screen.queryByText(/loading information/i); expect(deploymentSection).toBeNull(); expect( - container.querySelector(".recharts-responsive-container") + container.querySelector(".recharts-responsive-container") // eslint-disable-line ).toBeTruthy(); }); diff --git a/skyline-vscode/react-ui/src/sections/EnergyConsumption.test.js b/skyline-vscode/react-ui/src/sections/EnergyConsumption.test.js index 2b056ca..6c88e46 100644 --- a/skyline-vscode/react-ui/src/sections/EnergyConsumption.test.js +++ b/skyline-vscode/react-ui/src/sections/EnergyConsumption.test.js @@ -125,7 +125,7 @@ test("Only current data: shows both pie and graph data", () => { expect(screen.getByText(/relative to your other experiments/i)).toBeTruthy(); expect( - container.querySelector(".recharts-responsive-container") + container.querySelector(".recharts-responsive-container") // eslint-disable-line ).toBeTruthy(); }); @@ -143,7 +143,7 @@ test("Only past experiments: shows only graph data", () => { expect(screen.getByText(/relative to your other experiments/i)).toBeTruthy(); expect( - container.querySelector(".recharts-responsive-container") + container.querySelector(".recharts-responsive-container") // eslint-disable-line ).toBeTruthy(); }); @@ -159,7 +159,7 @@ test("Empty data: shows could not load data", () => { // ASSERT expect(screen.getAllByText(/could not load the data/i)).toBeTruthy(); - expect(container.querySelector(".recharts-responsive-container")).toBeFalsy(); + expect(container.querySelector(".recharts-responsive-container")).toBeFalsy(); // eslint-disable-line }); test("All information Complete: show both charts", () => { @@ -175,10 +175,10 @@ test("All information Complete: show both charts", () => { expect(screen.getByText(/relative to your other experiments/i)).toBeTruthy(); expect( - container.querySelector(".recharts-responsive-container") + container.querySelector(".recharts-responsive-container") // eslint-disable-line ).toBeTruthy(); expect( - container.querySelector(".bargraph-container") + container.querySelector(".bargraph-container") // eslint-disable-line ).toBeTruthy(); }); diff --git a/skyline-vscode/react-ui/src/sections/Habitat.test.js b/skyline-vscode/react-ui/src/sections/Habitat.test.js index 80d4b20..710d8ba 100644 --- a/skyline-vscode/react-ui/src/sections/Habitat.test.js +++ b/skyline-vscode/react-ui/src/sections/Habitat.test.js @@ -58,6 +58,6 @@ test("Shows graph when habitat data is present", () => { const { container } = render(); // ASSERT - expect(container.querySelector('.recharts-responsive-container')).toBeTruthy(); + expect(container.querySelector('.recharts-responsive-container')).toBeTruthy(); // eslint-disable-line }); diff --git a/skyline-vscode/react-ui/src/sections/Iterations.test.js b/skyline-vscode/react-ui/src/sections/Iterations.test.js new file mode 100644 index 0000000..1c3cac8 --- /dev/null +++ b/skyline-vscode/react-ui/src/sections/Iterations.test.js @@ -0,0 +1,36 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import Iterations from "./Iterations"; + +const setNumIterations = () => {}; + +test("Allows user interact with input to set epoch and iteration per epochs", async () => { + // ARRANGE + render(); + const epochs = screen.getByDisplayValue("50"); + const iterPerEpoch = screen.getByDisplayValue("2000"); + // ACT + await userEvent.clear(epochs); + await userEvent.type(epochs, "874"); + await userEvent.clear(iterPerEpoch); + await userEvent.type(iterPerEpoch, "4567"); + + // ASSERT + expect(screen.getByText(/4 million/i)).toBeTruthy(); +}); + +test("Set upper bound to 1e21", async () => { + // ARRANGE + render(); + const epochs = screen.getByDisplayValue("50"); + const iterPerEpoch = screen.getByDisplayValue("2000"); + // ACT + await userEvent.clear(epochs); + await userEvent.type(epochs, '1000000000'); + await userEvent.clear(iterPerEpoch); + await userEvent.type(iterPerEpoch, '1000000000000'); + await userEvent.click(screen.getByText('submit')) + + // ASSERT + expect(screen.getByText(/the total number of iterations should be less than 1e21/i)).toBeTruthy(); +}); diff --git a/skyline-vscode/react-ui/src/sections/ProviderPanel.js b/skyline-vscode/react-ui/src/sections/ProviderPanel.js index 0c99919..c868e42 100644 --- a/skyline-vscode/react-ui/src/sections/ProviderPanel.js +++ b/skyline-vscode/react-ui/src/sections/ProviderPanel.js @@ -305,7 +305,6 @@ const ProviderPanel = ({ numIterations, habitatData }) => { {providerPanelSettings.clicked.info.ngpus} - {/* {gpus[providerPanelSettings.clicked.info.gpu].vmem} GB */} {providerPanelSettings.clicked.vmem} GB diff --git a/skyline-vscode/react-ui/src/sections/ProviderPanel.test.js b/skyline-vscode/react-ui/src/sections/ProviderPanel.test.js index b9db390..042ae50 100644 --- a/skyline-vscode/react-ui/src/sections/ProviderPanel.test.js +++ b/skyline-vscode/react-ui/src/sections/ProviderPanel.test.js @@ -58,7 +58,7 @@ test("Shows a scatter chart", () => { // ASSERT expect(screen.getByText(/Providers/i)).toBeTruthy(); expect( - container.querySelector(".recharts-responsive-container") + container.querySelector(".recharts-responsive-container") // eslint-disable-line ).toBeTruthy(); expect(screen.getByText(/select a configuration/i)).toBeTruthy(); }); diff --git a/skyline-vscode/react-ui/src/sections/WelcomeScreen.test.js b/skyline-vscode/react-ui/src/sections/WelcomeScreen.test.js new file mode 100644 index 0000000..4a12012 --- /dev/null +++ b/skyline-vscode/react-ui/src/sections/WelcomeScreen.test.js @@ -0,0 +1,15 @@ +import {render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event' +import WelcomeScreen from './WelcomeScreen'; + +test("User should only be allow to click on begin analysis only once", async() => { + // ARRANGE + render(); + + // ACT + await userEvent.click(screen.getByText(/begin analysis/i)); + + // ASSERT + expect(screen.getByText(/analyzing project/i)).toBeTruthy(); + expect(screen.getByText(/analyzing project/i).getAttribute("disabled")).toBe(""); + }); \ No newline at end of file diff --git a/skyline-vscode/react-ui/src/utils/utils.js b/skyline-vscode/react-ui/src/utils/utils.js index a707b68..85de6ea 100644 --- a/skyline-vscode/react-ui/src/utils/utils.js +++ b/skyline-vscode/react-ui/src/utils/utils.js @@ -59,9 +59,9 @@ export function unitScale(quantity, unit){ } -export function timeFormatter(quantity){ +export function homeEnergyUsedFormat(quantity){ if (quantity >= 1){ - return `${quantity} homes' energy use for one year` + return [parseFloat(Number(quantity).toFixed(2)),'year'] } let idx = 0; let converter = []; @@ -181,7 +181,7 @@ export function energy_data(currentTotal){ `${parseFloat(Number(carbon_emission_tons*1000).toFixed(2))} kg`: `${parseFloat(Number(carbon_emission_tons).toFixed(2))} Metric Tons`; const miles = unitScale(carbon_emission_tons * ENERGY_CONVERSION_UNITS['miles'],'generic'); - const household = timeFormatter(carbon_emission_tons * ENERGY_CONVERSION_UNITS['household']); + const household = homeEnergyUsedFormat(carbon_emission_tons * ENERGY_CONVERSION_UNITS['household']); const phone = unitScale(carbon_emission_tons * ENERGY_CONVERSION_UNITS['phone'],'generic'); return { diff --git a/skyline-vscode/react-ui/src/utils/utils.test.js b/skyline-vscode/react-ui/src/utils/utils.test.js new file mode 100644 index 0000000..26deb5f --- /dev/null +++ b/skyline-vscode/react-ui/src/utils/utils.test.js @@ -0,0 +1,77 @@ +import { + currencyFormat, + numberFormat, + calculate_training_time, + energy_data, + homeEnergyUsedFormat, + unitScale +} from "./utils"; + +test('Unit Scale', () => { + // Energy + expect(unitScale(40,'energy')).toStrictEqual({"scale": "J", "scale_index": 0, "val": 40}); + expect(unitScale(4.1e3,'energy')).toStrictEqual({"scale": "KJ", "scale_index": 1, "val": 4.1}); + expect(unitScale(4.1e5,'energy')).toStrictEqual({"scale": "KJ", "scale_index": 1, "val": 410}); + expect(unitScale(4.1e6,'energy')).toStrictEqual({"scale": "MJ", "scale_index": 2, "val": 4.1}); + expect(unitScale(4.1e8,'energy')).toStrictEqual({"scale": "MJ", "scale_index": 2, "val": 410}); + expect(unitScale(4.1e9,'energy')).toStrictEqual({"scale": "GJ", "scale_index": 3, "val": 4.1}); + expect(unitScale(4.1e11,'energy')).toStrictEqual({"scale": "GJ", "scale_index": 3, "val": 410}); + expect(unitScale(4.1e12,'energy')).toStrictEqual({"scale": "TJ", "scale_index": 4, "val": 4.1}); + expect(unitScale(4.1e14,'energy')).toStrictEqual({"scale": "TJ", "scale_index": 4, "val": 410}); + expect(unitScale(4.1e15,'energy')).toStrictEqual({"scale": "PJ", "scale_index": 5, "val": 4.1}); + expect(unitScale(4.1e17,'energy')).toStrictEqual({"scale": "PJ", "scale_index": 5, "val": 410}); + expect(unitScale(4.1e18,'energy')).toStrictEqual({"scale": "EJ", "scale_index": 6, "val": 4.1}); + // Generic + expect(unitScale(40,'generic')).toStrictEqual({"scale": "", "scale_index": 0, "val": 40}); + expect(unitScale(400,'generic')).toStrictEqual({"scale": "", "scale_index": 0, "val": 400}); + expect(unitScale(4e3,'generic')).toStrictEqual({"scale": "Thousands", "scale_index": 1, "val": 4.0}); + expect(unitScale(4.2e5,'generic')).toStrictEqual({"scale": "Thousands", "scale_index": 1, "val": 420}); + expect(unitScale(4.2e6,'generic')).toStrictEqual({"scale": "Millions", "scale_index": 2, "val": 4.2}); + expect(unitScale(4.2e8,'generic')).toStrictEqual({"scale": "Millions", "scale_index": 2, "val": 420}); + expect(unitScale(4.2e9,'generic')).toStrictEqual({"scale": "Billions", "scale_index": 3, "val": 4.2}); + expect(unitScale(4.2e11,'generic')).toStrictEqual({"scale": "Billions", "scale_index": 3, "val": 420}); + expect(unitScale(4.2e12,'generic')).toStrictEqual({"scale": "Trillion", "scale_index": 4, "val": 4.2}); + expect(unitScale(4.2e14,'generic')).toStrictEqual({"scale": "Trillion", "scale_index": 4, "val": 420}); + expect(unitScale(4.2e15,'generic')).toStrictEqual({"scale": "Quadrillion", "scale_index": 5, "val": 4.2}); +}) + +test('Home energy used format', () => { + expect(homeEnergyUsedFormat(1)).toStrictEqual([1,'year']); + expect(homeEnergyUsedFormat(1e-1)).toStrictEqual([36.5,'day']); + expect(homeEnergyUsedFormat(1e-3)).toStrictEqual([8.76,'hour']); + expect(homeEnergyUsedFormat(1e-4)).toStrictEqual([52.56,'minute']); + expect(homeEnergyUsedFormat(1e-6)).toStrictEqual([31.54,'second']); + expect(homeEnergyUsedFormat(1e-8)).toStrictEqual([315.36,'msec']); +}) + +test("energy conversion", () => { + const totalEnergy = 1.84e6; + expect(energy_data(totalEnergy)).toStrictEqual({ + kwh: 0.51, + carbon: "0.22 kg", + miles: "0.55 ", + household: [14.65,"minute"], + phone: "26.92 ", + }); +}); + +test("Calculate training time", () => { + const numIterations = 200000; + const instance = { + x: 14.069682, + info: { + ngpus: 4, + }, + }; + expect(calculate_training_time(numIterations, instance)).toBe( + 0.19541224999999998 + ); +}); + +test("format number: expect number 3991558 to be 4 million", () => { + expect(numberFormat(3991558)).toBe("4 million"); +}); + +test("format currency: expect number 3991558 to be $4M", () => { + expect(currencyFormat(3991558)).toBe("$4M"); +}); From b74cf250a21df849d25a05e86cddc3f98a7f1a7f Mon Sep 17 00:00:00 2001 From: John Calderon Date: Fri, 17 Feb 2023 14:30:26 -0500 Subject: [PATCH 4/5] changed unit test for app.js | changed UI design of local gpu --- skyline-vscode/react-ui/src/App.js | 2 +- skyline-vscode/react-ui/src/App.test.js | 17 +++++------ .../src/sections/MemThroughputContainer.js | 29 ++++++++++++------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/skyline-vscode/react-ui/src/App.js b/skyline-vscode/react-ui/src/App.js index 162f16c..b723985 100644 --- a/skyline-vscode/react-ui/src/App.js +++ b/skyline-vscode/react-ui/src/App.js @@ -49,7 +49,7 @@ function restartProfiling() { }); } -const sendMock = true; +const sendMock = false; function App() { const [analysisState, setAnalysisState] = useState(); diff --git a/skyline-vscode/react-ui/src/App.test.js b/skyline-vscode/react-ui/src/App.test.js index 4338cfa..f91c1cb 100644 --- a/skyline-vscode/react-ui/src/App.test.js +++ b/skyline-vscode/react-ui/src/App.test.js @@ -1,12 +1,9 @@ -import {act, create} from 'react-test-renderer'; +import { render, screen } from "@testing-library/react"; import App from './App'; -describe('App', () => { - it ('should render successfully', () => { - let root; - act(() => { - root = create(); - }); - expect(root).toBeTruthy(); - }); -}); +test("Render App correctly, it must show begin analysis button", () => { + render(); + // For testing there is no default socket connection. It will show the warning screen + + expect(screen.getByText(/connection has been lost to the profiler. please reconnect the profiler and double check your ports then click connect/i)).toBeTruthy(); +}) diff --git a/skyline-vscode/react-ui/src/sections/MemThroughputContainer.js b/skyline-vscode/react-ui/src/sections/MemThroughputContainer.js index 0fb4855..bba41e9 100644 --- a/skyline-vscode/react-ui/src/sections/MemThroughputContainer.js +++ b/skyline-vscode/react-ui/src/sections/MemThroughputContainer.js @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import { Alert, Badge, Container, Col, Row } from "react-bootstrap"; +import { Alert, Badge, Container, Col, Row, Button } from "react-bootstrap"; import styled from "styled-components"; import BarSlider from "../components/BarSlider"; import Subheader from "../components/Subheader"; @@ -144,20 +144,16 @@ const MemThroughputContainer = ({ analysisState, SENDMOCK }) => { - +
+ Peak Memory Usage
- Peak Memory Usage -
-
- + Local + {analysisState["hardware_info"]["gpus"][0]} - - Local -
@@ -186,7 +182,7 @@ const MemThroughputContainer = ({ analysisState, SENDMOCK }) => {
- +
Throughput
@@ -221,11 +217,22 @@ const MemThroughputContainer = ({ analysisState, SENDMOCK }) => { }; const Wrapper = styled.main` - .gpu-details{ + .gpu-details { display: flex; flex-direction: row; justify-content: space-between; align-items: center; + @media (max-width: 1000px) { + line-height:0.5; + flex-direction: column; + align-items: flex-start; + } + } + .memory-title{ + @media (max-width: 1000px) { + margin-top:0.25rem; + margin-bottom:0.4rem; + } } `; From db1d7555bc942ab89367143f3ae098446330dadc Mon Sep 17 00:00:00 2001 From: John Calderon Date: Fri, 17 Feb 2023 15:31:50 -0500 Subject: [PATCH 5/5] changed app.test.js --- skyline-vscode/react-ui/src/App.js | 8 +++++--- skyline-vscode/react-ui/src/App.test.js | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/skyline-vscode/react-ui/src/App.js b/skyline-vscode/react-ui/src/App.js index b723985..e4cfbd0 100644 --- a/skyline-vscode/react-ui/src/App.js +++ b/skyline-vscode/react-ui/src/App.js @@ -71,9 +71,11 @@ function App() { const connect = function() { resetApp(); let vscode = App.vscodeApi; - vscode.postMessage({ - command: "connect" - }); + if(vscode){ + vscode.postMessage({ + command: "connect" + }); + } } const processAnalysisState = function (state) { diff --git a/skyline-vscode/react-ui/src/App.test.js b/skyline-vscode/react-ui/src/App.test.js index f91c1cb..d8e8b93 100644 --- a/skyline-vscode/react-ui/src/App.test.js +++ b/skyline-vscode/react-ui/src/App.test.js @@ -1,9 +1,11 @@ import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; import App from './App'; -test("Render App correctly, it must show begin analysis button", () => { +// For testing there is no default socket connection. It will show the error screen +test("Render App correctly, must show a connection error and button to reset it", async() => { render(); - // For testing there is no default socket connection. It will show the warning screen - expect(screen.getByText(/connection has been lost to the profiler. please reconnect the profiler and double check your ports then click connect/i)).toBeTruthy(); + expect(screen.getByText(/connection error/i)).toBeTruthy(); + await userEvent.click(screen.getByText('Reconnect')); })