-
{{this.totalCPU}} MHz of CPU
+
{{this.totalCPUFormatted}} {{this.totalCPUUnits}} of CPU
- {{this.totalReservedCPU}} MHz / {{this.totalCPU}} MHz reserved
+ {{format-hertz this.totalReservedCPU}} / {{format-hertz this.totalCPU}} reserved
{{/if}}
diff --git a/ui/app/utils/resources-diffs.js b/ui/app/utils/resources-diffs.js
index ef13ca9022d4..068469f6ad06 100644
--- a/ui/app/utils/resources-diffs.js
+++ b/ui/app/utils/resources-diffs.js
@@ -1,6 +1,6 @@
import d3Format from 'd3-format';
-import { reduceToLargestUnit } from 'nomad-ui/helpers/format-bytes';
+import { formatBytes, formatHertz } from 'nomad-ui/utils/units';
const formatPercent = d3Format.format('+.0%');
const sumAggregate = (total, val) => total + val;
@@ -86,14 +86,9 @@ class ResourceDiffs {
const delta = Math.abs(this.aggregateDiff);
if (this.units === 'MiB') {
- if (delta === 0) {
- return '0 MiB';
- }
-
- const [memory, units] = reduceToLargestUnit(delta * 1024 * 1024);
- const formattedMemory = Number.isInteger(memory) ? memory : memory.toFixed(2);
-
- return `${formattedMemory} ${units}`;
+ return formatBytes(delta, 'MiB');
+ } else if (this.units === 'MHz') {
+ return formatHertz(delta, 'MHz');
} else {
return `${delta} ${this.units}`;
}
diff --git a/ui/app/utils/units.js b/ui/app/utils/units.js
new file mode 100644
index 000000000000..d5c6284fc19e
--- /dev/null
+++ b/ui/app/utils/units.js
@@ -0,0 +1,68 @@
+export const BYTES_UNITS = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'];
+export const HERTZ_UNITS = ['Hz', 'KHz', 'MHz', 'GHz', 'THz', 'PHz'];
+
+const locale = window.navigator.locale || 'en';
+const decimalFormatter = new Intl.NumberFormat(locale, {
+ maximumFractionDigits: 2,
+});
+const roundFormatter = new Intl.NumberFormat(locale, {
+ maximumFractionDigits: 0,
+});
+
+const unitReducer = (number = 0, interval, units, maxUnit, startingUnit) => {
+ if (startingUnit && units.indexOf(startingUnit) !== -1) {
+ units = units.slice(units.indexOf(startingUnit));
+ }
+ if (maxUnit && units.indexOf(maxUnit) !== -1) {
+ units = units.slice(0, units.indexOf(maxUnit) + 1);
+ }
+
+ // Reduce negative numbers by temporarily flipping them positive.
+ const negative = number < 0;
+ if (negative) {
+ number *= -1;
+ }
+
+ let unitIndex = 0;
+ while (number >= interval && unitIndex < units.length - 1) {
+ number /= interval;
+ unitIndex++;
+ }
+
+ if (negative) {
+ number *= -1;
+ }
+ return [number, units[unitIndex]];
+};
+
+export function reduceBytes(bytes = 0, maxUnitSize, startingUnitSize) {
+ return unitReducer(bytes, 1024, BYTES_UNITS, maxUnitSize, startingUnitSize);
+}
+
+export function reduceHertz(hertz = 0, maxUnitSize, startingUnitSize) {
+ return unitReducer(hertz, 1000, HERTZ_UNITS, maxUnitSize, startingUnitSize);
+}
+
+// General purpose formatters meant to reduce units as much
+// as possible.
+export function formatBytes(bytes, startingUnitSize) {
+ const [number, unit] = reduceBytes(bytes, null, startingUnitSize);
+ return `${decimalFormatter.format(number)} ${unit}`;
+}
+
+export function formatHertz(hertz, startingUnitSize) {
+ const [number, unit] = reduceHertz(hertz, null, startingUnitSize);
+ return `${decimalFormatter.format(number)} ${unit}`;
+}
+
+// Specialized formatters meant to reduce units to the resolution
+// the scheduler and job specs operate at.
+export function formatScheduledBytes(bytes, startingUnitSize) {
+ const [number, unit] = reduceBytes(bytes, 'MiB', startingUnitSize);
+ return `${roundFormatter.format(number)} ${unit}`;
+}
+
+export function formatScheduledHertz(hertz, startingUnitSize) {
+ const [number, unit] = reduceHertz(hertz, 'MHz', startingUnitSize);
+ return `${roundFormatter.format(number)} ${unit}`;
+}
diff --git a/ui/tests/acceptance/behaviors/fs.js b/ui/tests/acceptance/behaviors/fs.js
index 923d46c7bdd2..114078261f65 100644
--- a/ui/tests/acceptance/behaviors/fs.js
+++ b/ui/tests/acceptance/behaviors/fs.js
@@ -2,7 +2,7 @@ import { test } from 'qunit';
import { currentURL, visit } from '@ember/test-helpers';
import { filesForPath } from 'nomad-ui/mirage/config';
-import { formatBytes } from 'nomad-ui/helpers/format-bytes';
+import { formatBytes } from 'nomad-ui/utils/units';
import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
import Response from 'ember-cli-mirage/response';
@@ -130,7 +130,7 @@ export default function browseFilesystem({
const fileRecord = sortedFiles[2];
assert.equal(file.name, fileRecord.name);
assert.ok(file.isFile);
- assert.equal(file.size, formatBytes([fileRecord.size]));
+ assert.equal(file.size, formatBytes(fileRecord.size));
assert.equal(file.lastModified, moment(fileRecord.modTime).fromNow());
});
diff --git a/ui/tests/acceptance/client-detail-test.js b/ui/tests/acceptance/client-detail-test.js
index 1da68e9cfb4f..890a03982370 100644
--- a/ui/tests/acceptance/client-detail-test.js
+++ b/ui/tests/acceptance/client-detail-test.js
@@ -4,7 +4,7 @@ import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
-import { formatBytes } from 'nomad-ui/helpers/format-bytes';
+import { formatBytes, formatHertz } from 'nomad-ui/utils/units';
import moment from 'moment';
import ClientDetail from 'nomad-ui/tests/pages/clients/detail';
import Clients from 'nomad-ui/tests/pages/clients/list';
@@ -163,9 +163,10 @@ module('Acceptance | client detail', function(hooks) {
Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks) / cpuUsed,
'CPU %'
);
+ const roundedTicks = Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks);
assert.equal(
allocationRow.cpuTooltip,
- `${Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks)} / ${cpuUsed} MHz`,
+ `${formatHertz(roundedTicks, 'MHz')} / ${formatHertz(cpuUsed, 'MHz')}`,
'Detailed CPU information is in a tooltip'
);
assert.equal(
@@ -175,7 +176,10 @@ module('Acceptance | client detail', function(hooks) {
);
assert.equal(
allocationRow.memTooltip,
- `${formatBytes([allocStats.resourceUsage.MemoryStats.RSS])} / ${memoryUsed} MiB`,
+ `${formatBytes(allocStats.resourceUsage.MemoryStats.RSS)} / ${formatBytes(
+ memoryUsed,
+ 'MiB'
+ )}`,
'Detailed memory information is in a tooltip'
);
});
diff --git a/ui/tests/acceptance/optimize-test.js b/ui/tests/acceptance/optimize-test.js
index db8856af3fc6..d645d6963a00 100644
--- a/ui/tests/acceptance/optimize-test.js
+++ b/ui/tests/acceptance/optimize-test.js
@@ -5,6 +5,7 @@ import { setupMirage } from 'ember-cli-mirage/test-support';
import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
import Response from 'ember-cli-mirage/response';
import moment from 'moment';
+import { formatBytes, formatHertz } from 'nomad-ui/utils/units';
import Optimize from 'nomad-ui/tests/pages/optimize';
import Layout from 'nomad-ui/tests/pages/layout';
@@ -153,7 +154,7 @@ module('Acceptance | optimize', function(hooks) {
assert.equal(
summary.cpu,
- cpuDiff ? `${cpuSign}${cpuDiff} MHz ${cpuSign}${cpuDiffPercent}%` : ''
+ cpuDiff ? `${cpuSign}${formatHertz(cpuDiff, 'MHz')} ${cpuSign}${cpuDiffPercent}%` : ''
);
assert.equal(
summary.memory,
@@ -162,7 +163,9 @@ module('Acceptance | optimize', function(hooks) {
assert.equal(
summary.aggregateCpu,
- cpuDiff ? `${cpuSign}${cpuDiff * currentTaskGroupAllocations.length} MHz` : ''
+ cpuDiff
+ ? `${cpuSign}${formatHertz(cpuDiff * currentTaskGroupAllocations.length, 'MHz')}`
+ : ''
);
assert.equal(
@@ -773,15 +776,5 @@ function formattedMemDiff(memDiff) {
const absMemDiff = Math.abs(memDiff);
const negativeSign = memDiff < 0 ? '-' : '';
- if (absMemDiff >= 1024) {
- const gibDiff = absMemDiff / 1024;
-
- if (Number.isInteger(gibDiff)) {
- return `${negativeSign}${gibDiff} GiB`;
- } else {
- return `${negativeSign}${gibDiff.toFixed(2)} GiB`;
- }
- } else {
- return `${negativeSign}${absMemDiff} MiB`;
- }
+ return negativeSign + formatBytes(absMemDiff, 'MiB');
}
diff --git a/ui/tests/acceptance/plugin-detail-test.js b/ui/tests/acceptance/plugin-detail-test.js
index 0630e2c0300c..75824bf8a53e 100644
--- a/ui/tests/acceptance/plugin-detail-test.js
+++ b/ui/tests/acceptance/plugin-detail-test.js
@@ -4,7 +4,7 @@ import { setupApplicationTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
import moment from 'moment';
-import { formatBytes } from 'nomad-ui/helpers/format-bytes';
+import { formatBytes, formatHertz } from 'nomad-ui/utils/units';
import PluginDetail from 'nomad-ui/tests/pages/storage/plugins/detail';
import Layout from 'nomad-ui/tests/pages/layout';
@@ -125,9 +125,10 @@ module('Acceptance | plugin detail', function(hooks) {
Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks) / cpuUsed,
'CPU %'
);
+ const roundedTicks = Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks);
assert.equal(
allocationRow.cpuTooltip,
- `${Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks)} / ${cpuUsed} MHz`,
+ `${formatHertz(roundedTicks, 'MHz')} / ${formatHertz(cpuUsed, 'MHz')}`,
'Detailed CPU information is in a tooltip'
);
assert.equal(
@@ -137,7 +138,10 @@ module('Acceptance | plugin detail', function(hooks) {
);
assert.equal(
allocationRow.memTooltip,
- `${formatBytes([allocStats.resourceUsage.MemoryStats.RSS])} / ${memoryUsed} MiB`,
+ `${formatBytes(allocStats.resourceUsage.MemoryStats.RSS)} / ${formatBytes(
+ memoryUsed,
+ 'MiB'
+ )}`,
'Detailed memory information is in a tooltip'
);
});
diff --git a/ui/tests/acceptance/task-group-detail-test.js b/ui/tests/acceptance/task-group-detail-test.js
index 17577ef4f428..e097b27a9d3e 100644
--- a/ui/tests/acceptance/task-group-detail-test.js
+++ b/ui/tests/acceptance/task-group-detail-test.js
@@ -3,7 +3,12 @@ import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
-import { formatBytes } from 'nomad-ui/helpers/format-bytes';
+import {
+ formatBytes,
+ formatHertz,
+ formatScheduledBytes,
+ formatScheduledHertz,
+} from 'nomad-ui/utils/units';
import TaskGroup from 'nomad-ui/tests/pages/jobs/job/task-group';
import Layout from 'nomad-ui/tests/pages/layout';
import pageSizeSelect from './behaviors/page-size-select';
@@ -84,17 +89,17 @@ module('Acceptance | task group detail', function(hooks) {
assert.equal(TaskGroup.tasksCount, `# Tasks ${tasks.length}`, '# Tasks');
assert.equal(
TaskGroup.cpu,
- `Reserved CPU ${totalCPU} MHz`,
+ `Reserved CPU ${formatScheduledHertz(totalCPU, 'MHz')}`,
'Aggregated CPU reservation for all tasks'
);
assert.equal(
TaskGroup.mem,
- `Reserved Memory ${totalMemory} MiB`,
+ `Reserved Memory ${formatScheduledBytes(totalMemory, 'MiB')}`,
'Aggregated Memory reservation for all tasks'
);
assert.equal(
TaskGroup.disk,
- `Reserved Disk ${totalDisk} MiB`,
+ `Reserved Disk ${formatScheduledBytes(totalDisk, 'MiB')}`,
'Aggregated Disk reservation for all tasks'
);
@@ -209,9 +214,10 @@ module('Acceptance | task group detail', function(hooks) {
'CPU %'
);
+ const roundedTicks = Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks);
assert.equal(
allocationRow.cpuTooltip,
- `${Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks)} / ${cpuUsed} MHz`,
+ `${formatHertz(roundedTicks, 'MHz')} / ${formatHertz(cpuUsed, 'MHz')}`,
'Detailed CPU information is in a tooltip'
);
@@ -223,7 +229,10 @@ module('Acceptance | task group detail', function(hooks) {
assert.equal(
allocationRow.memTooltip,
- `${formatBytes([allocStats.resourceUsage.MemoryStats.RSS])} / ${memoryUsed} MiB`,
+ `${formatBytes(allocStats.resourceUsage.MemoryStats.RSS)} / ${formatBytes(
+ memoryUsed,
+ 'MiB'
+ )}`,
'Detailed memory information is in a tooltip'
);
});
diff --git a/ui/tests/acceptance/topology-test.js b/ui/tests/acceptance/topology-test.js
index ef2d8bc143b5..45dd8a99a645 100644
--- a/ui/tests/acceptance/topology-test.js
+++ b/ui/tests/acceptance/topology-test.js
@@ -5,7 +5,12 @@ import { setupApplicationTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
import Topology from 'nomad-ui/tests/pages/topology';
-import { reduceToLargestUnit } from 'nomad-ui/helpers/format-bytes';
+import {
+ formatBytes,
+ formatScheduledBytes,
+ formatHertz,
+ formatScheduledHertz,
+} from 'nomad-ui/utils/units';
import queryString from 'query-string';
const sumResources = (list, dimension) =>
@@ -60,17 +65,14 @@ module('Acceptance | topology', function(hooks) {
assert.equal(Topology.clusterInfoPanel.memoryProgressValue, reservedMem / totalMem);
assert.equal(Topology.clusterInfoPanel.cpuProgressValue, reservedCPU / totalCPU);
- const [rNum, rUnit] = reduceToLargestUnit(reservedMem * 1024 * 1024);
- const [tNum, tUnit] = reduceToLargestUnit(totalMem * 1024 * 1024);
-
assert.equal(
Topology.clusterInfoPanel.memoryAbsoluteValue,
- `${Math.floor(rNum)} ${rUnit} / ${Math.floor(tNum)} ${tUnit} reserved`
+ `${formatBytes(reservedMem * 1024 * 1024)} / ${formatBytes(totalMem * 1024 * 1024)} reserved`
);
assert.equal(
Topology.clusterInfoPanel.cpuAbsoluteValue,
- `${reservedCPU} MHz / ${totalCPU} MHz reserved`
+ `${formatHertz(reservedCPU, 'MHz')} / ${formatHertz(totalCPU, 'MHz')} reserved`
);
});
@@ -199,17 +201,20 @@ module('Acceptance | topology', function(hooks) {
assert.equal(Topology.nodeInfoPanel.memoryProgressValue, reservedMem / totalMem);
assert.equal(Topology.nodeInfoPanel.cpuProgressValue, reservedCPU / totalCPU);
- const [rNum, rUnit] = reduceToLargestUnit(reservedMem * 1024 * 1024);
- const [tNum, tUnit] = reduceToLargestUnit(totalMem * 1024 * 1024);
-
assert.equal(
Topology.nodeInfoPanel.memoryAbsoluteValue,
- `${Math.floor(rNum)} ${rUnit} / ${Math.floor(tNum)} ${tUnit} reserved`
+ `${formatScheduledBytes(reservedMem * 1024 * 1024)} / ${formatScheduledBytes(
+ totalMem,
+ 'MiB'
+ )} reserved`
);
assert.equal(
Topology.nodeInfoPanel.cpuAbsoluteValue,
- `${reservedCPU} MHz / ${totalCPU} MHz reserved`
+ `${formatScheduledHertz(reservedCPU, 'MHz')} / ${formatScheduledHertz(
+ totalCPU,
+ 'MHz'
+ )} reserved`
);
await Topology.nodeInfoPanel.visitNode();
diff --git a/ui/tests/acceptance/volume-detail-test.js b/ui/tests/acceptance/volume-detail-test.js
index 06406e01db01..6b36b000ee9f 100644
--- a/ui/tests/acceptance/volume-detail-test.js
+++ b/ui/tests/acceptance/volume-detail-test.js
@@ -4,7 +4,7 @@ import { setupApplicationTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
import moment from 'moment';
-import { formatBytes } from 'nomad-ui/helpers/format-bytes';
+import { formatBytes, formatHertz } from 'nomad-ui/utils/units';
import VolumeDetail from 'nomad-ui/tests/pages/storage/volumes/detail';
import Layout from 'nomad-ui/tests/pages/layout';
@@ -140,9 +140,10 @@ module('Acceptance | volume detail', function(hooks) {
Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks) / cpuUsed,
'CPU %'
);
+ const roundedTicks = Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks);
assert.equal(
allocationRow.cpuTooltip,
- `${Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks)} / ${cpuUsed} MHz`,
+ `${formatHertz(roundedTicks, 'MHz')} / ${formatHertz(cpuUsed, 'MHz')}`,
'Detailed CPU information is in a tooltip'
);
assert.equal(
@@ -152,7 +153,10 @@ module('Acceptance | volume detail', function(hooks) {
);
assert.equal(
allocationRow.memTooltip,
- `${formatBytes([allocStats.resourceUsage.MemoryStats.RSS])} / ${memoryUsed} MiB`,
+ `${formatBytes(allocStats.resourceUsage.MemoryStats.RSS)} / ${formatBytes(
+ memoryUsed,
+ 'MiB'
+ )}`,
'Detailed memory information is in a tooltip'
);
});
diff --git a/ui/tests/integration/components/das/recommendation-card-test.js b/ui/tests/integration/components/das/recommendation-card-test.js
index 2be3d71e5485..4145e616ace2 100644
--- a/ui/tests/integration/components/das/recommendation-card-test.js
+++ b/ui/tests/integration/components/das/recommendation-card-test.js
@@ -22,13 +22,7 @@ module('Integration | Component | das/recommendation-card', function(hooks) {
this._super(...arguments);
},
- urlFor(
- route,
- slug,
- {
- queryParams: { namespace },
- }
- ) {
+ urlFor(route, slug, { queryParams: { namespace } }) {
return `${route}:${slug}?namespace=${namespace}`;
},
});
@@ -99,7 +93,7 @@ module('Integration | Component | das/recommendation-card', function(hooks) {
})
);
- await render(hbs`
`);
+ await render(hbs`
`);
assert.equal(RecommendationCard.slug.jobName, 'job-name');
assert.equal(RecommendationCard.slug.groupName, 'group-name');
@@ -241,7 +235,7 @@ module('Integration | Component | das/recommendation-card', function(hooks) {
})
);
- await render(hbs`
`);
+ await render(hbs`
`);
assert.notOk(RecommendationCard.togglesTable.toggleAllIsPresent);
assert.notOk(RecommendationCard.togglesTable.toggleAllCPU.isPresent);
@@ -281,7 +275,7 @@ module('Integration | Component | das/recommendation-card', function(hooks) {
})
);
- await render(hbs`
`);
+ await render(hbs`
`);
await RecommendationCard.togglesTable.tasks[0].cpu.toggle();
await RecommendationCard.togglesTable.tasks[0].memory.toggle();
@@ -320,7 +314,7 @@ module('Integration | Component | das/recommendation-card', function(hooks) {
})
);
- await render(hbs`
`);
+ await render(hbs`
`);
assert.equal(RecommendationCard.totalsTable.recommended.memory.text, '128 MiB');
assert.equal(RecommendationCard.totalsTable.unitDiff.memory, '0 MiB');
@@ -378,7 +372,7 @@ module('Integration | Component | das/recommendation-card', function(hooks) {
})
);
- await render(hbs`
`);
+ await render(hbs`
`);
assert.ok(RecommendationCard.togglesTable.toggleAllMemory.isDisabled);
assert.notOk(RecommendationCard.togglesTable.toggleAllMemory.isActive);
@@ -447,7 +441,7 @@ module('Integration | Component | das/recommendation-card', function(hooks) {
})
);
- await render(hbs`
`);
+ await render(hbs`
`);
const [cpuRec1, memRec1, cpuRec2, memRec2] = this.summary.recommendations;
@@ -486,7 +480,7 @@ module('Integration | Component | das/recommendation-card', function(hooks) {
assert.equal(
RecommendationCard.narrative.trim(),
- 'Applying the selected recommendations will save an aggregate 1000 MHz of CPU across 10 allocations.'
+ 'Applying the selected recommendations will save an aggregate 1 GHz of CPU across 10 allocations.'
);
this.summary.toggleRecommendation(cpuRec1);
@@ -499,7 +493,7 @@ module('Integration | Component | das/recommendation-card', function(hooks) {
assert.equal(
RecommendationCard.narrative.trim(),
- 'Applying the selected recommendations will save an aggregate 1000 MHz of CPU across 10 allocations.'
+ 'Applying the selected recommendations will save an aggregate 1 GHz of CPU across 10 allocations.'
);
this.summary.toggleRecommendation(memRec2);
@@ -508,7 +502,7 @@ module('Integration | Component | das/recommendation-card', function(hooks) {
assert.equal(
RecommendationCard.narrative.trim(),
- 'Applying the selected recommendations will save an aggregate 1000 MHz of CPU and 1.25 GiB of memory across 10 allocations.'
+ 'Applying the selected recommendations will save an aggregate 1 GHz of CPU and 1.25 GiB of memory across 10 allocations.'
);
});
@@ -574,7 +568,7 @@ module('Integration | Component | das/recommendation-card', function(hooks) {
})
);
- await render(hbs`
`);
+ await render(hbs`
`);
assert.equal(
RecommendationCard.narrative.trim(),
diff --git a/ui/tests/integration/components/image-file-test.js b/ui/tests/integration/components/image-file-test.js
index 424cb9e3ab79..80299e815aef 100644
--- a/ui/tests/integration/components/image-file-test.js
+++ b/ui/tests/integration/components/image-file-test.js
@@ -5,7 +5,7 @@ import hbs from 'htmlbars-inline-precompile';
import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit';
import sinon from 'sinon';
import RSVP from 'rsvp';
-import { formatBytes } from 'nomad-ui/helpers/format-bytes';
+import { formatBytes } from 'nomad-ui/utils/units';
module('Integration | Component | image file', function(hooks) {
setupRenderingTest(hooks);
@@ -81,7 +81,7 @@ module('Integration | Component | image file', function(hooks) {
'Width and height are formatted correctly'
);
assert.ok(
- statsEl.textContent.trim().endsWith(formatBytes([commonProperties.size]) + ')'),
+ statsEl.textContent.trim().endsWith(formatBytes(commonProperties.size) + ')'),
'Human-formatted size is included'
);
});
diff --git a/ui/tests/integration/components/job-page/parts/task-groups-test.js b/ui/tests/integration/components/job-page/parts/task-groups-test.js
index c662aca392ca..86722b02622e 100644
--- a/ui/tests/integration/components/job-page/parts/task-groups-test.js
+++ b/ui/tests/integration/components/job-page/parts/task-groups-test.js
@@ -6,6 +6,7 @@ import sinon from 'sinon';
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
import { setupRenderingTest } from 'ember-qunit';
import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit';
+import { formatScheduledHertz, formatScheduledBytes } from 'nomad-ui/utils/units';
module('Integration | Component | job-page/parts/task-groups', function(hooks) {
setupRenderingTest(hooks);
@@ -46,10 +47,10 @@ module('Integration | Component | job-page/parts/task-groups', function(hooks) {
await this.render(hbs`
+ @job={{this.job}}
+ @sortProperty={{this.sortProperty}}
+ @sortDescending={{this.sortDescending}}
+ @gotoTaskGroup={{this.gotoTaskGroup}} />
`);
assert.equal(
@@ -80,10 +81,10 @@ module('Integration | Component | job-page/parts/task-groups', function(hooks) {
await this.render(hbs`
+ @job={{this.job}}
+ @sortProperty={{this.sortProperty}}
+ @sortDescending={{this.sortDescending}}
+ @gotoTaskGroup={{this.gotoTaskGroup}} />
`);
const taskGroupRow = find('[data-test-task-group]');
@@ -105,17 +106,17 @@ module('Integration | Component | job-page/parts/task-groups', function(hooks) {
);
assert.equal(
taskGroupRow.querySelector('[data-test-task-group-cpu]').textContent.trim(),
- `${taskGroup.get('reservedCPU')} MHz`,
+ `${formatScheduledHertz(taskGroup.get('reservedCPU'), 'MHz')}`,
'Reserved CPU'
);
assert.equal(
taskGroupRow.querySelector('[data-test-task-group-mem]').textContent.trim(),
- `${taskGroup.get('reservedMemory')} MiB`,
+ `${formatScheduledBytes(taskGroup.get('reservedMemory'), 'MiB')}`,
'Reserved Memory'
);
assert.equal(
taskGroupRow.querySelector('[data-test-task-group-disk]').textContent.trim(),
- `${taskGroup.get('reservedEphemeralDisk')} MiB`,
+ `${formatScheduledBytes(taskGroup.get('reservedEphemeralDisk'), 'MiB')}`,
'Reserved Disk'
);
});
@@ -145,10 +146,10 @@ module('Integration | Component | job-page/parts/task-groups', function(hooks) {
await this.render(hbs`
+ @job={{this.job}}
+ @sortProperty={{this.sortProperty}}
+ @sortDescending={{this.sortDescending}}
+ @gotoTaskGroup={{this.gotoTaskGroup}} />
`);
await click('[data-test-task-group]');
diff --git a/ui/tests/integration/components/primary-metric/node-test.js b/ui/tests/integration/components/primary-metric/node-test.js
index 17fbba87dc3c..e444c79ac881 100644
--- a/ui/tests/integration/components/primary-metric/node-test.js
+++ b/ui/tests/integration/components/primary-metric/node-test.js
@@ -6,6 +6,7 @@ import hbs from 'htmlbars-inline-precompile';
import { setupPrimaryMetricMocks, primaryMetric } from './primary-metric';
import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit';
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
+import { formatScheduledHertz } from 'nomad-ui/utils/units';
module('Integration | Component | PrimaryMetric::Node', function(hooks) {
setupRenderingTest(hooks);
@@ -57,7 +58,7 @@ module('Integration | Component | PrimaryMetric::Node', function(hooks) {
assert.ok(find('[data-test-annotation]'));
assert.equal(
find('[data-test-annotation]').textContent.trim(),
- `${resource.reserved.cpu} MHz reserved`
+ `${formatScheduledHertz(resource.reserved.cpu, 'MHz')} reserved`
);
});
diff --git a/ui/tests/integration/components/topo-viz/datacenter-test.js b/ui/tests/integration/components/topo-viz/datacenter-test.js
index 52da79c89bf0..25e7c3b8fb86 100644
--- a/ui/tests/integration/components/topo-viz/datacenter-test.js
+++ b/ui/tests/integration/components/topo-viz/datacenter-test.js
@@ -8,6 +8,7 @@ import { create } from 'ember-cli-page-object';
import sinon from 'sinon';
import faker from 'nomad-ui/mirage/faker';
import topoVizDatacenterPageObject from 'nomad-ui/tests/pages/components/topo-viz/datacenter';
+import { formatBytes, formatHertz } from 'nomad-ui/utils/units';
const TopoVizDatacenter = create(topoVizDatacenterPageObject());
@@ -107,8 +108,16 @@ module('Integration | Component | TopoViz::Datacenter', function(hooks) {
assert.ok(TopoVizDatacenter.label.includes(this.datacenter.name));
assert.ok(TopoVizDatacenter.label.includes(`${this.datacenter.nodes.length} Nodes`));
assert.ok(TopoVizDatacenter.label.includes(`${allocs.length} Allocs`));
- assert.ok(TopoVizDatacenter.label.includes(`${memoryReserved}/${memoryTotal} MiB`));
- assert.ok(TopoVizDatacenter.label.includes(`${cpuReserved}/${cpuTotal} MHz`));
+ assert.ok(
+ TopoVizDatacenter.label.includes(
+ `${formatBytes(memoryReserved, 'MiB')}/${formatBytes(memoryTotal, 'MiB')}`
+ )
+ );
+ assert.ok(
+ TopoVizDatacenter.label.includes(
+ `${formatHertz(cpuReserved, 'MHz')}/${formatHertz(cpuTotal, 'MHz')}`
+ )
+ );
});
test('when @isSingleColumn is true, the FlexMasonry layout gets one column, otherwise it gets two', async function(assert) {
diff --git a/ui/tests/integration/components/topo-viz/node-test.js b/ui/tests/integration/components/topo-viz/node-test.js
index e56d4064500b..a85df060d6fa 100644
--- a/ui/tests/integration/components/topo-viz/node-test.js
+++ b/ui/tests/integration/components/topo-viz/node-test.js
@@ -8,6 +8,7 @@ import faker from 'nomad-ui/mirage/faker';
import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import topoVisNodePageObject from 'nomad-ui/tests/pages/components/topo-viz/node';
+import { formatScheduledBytes, formatScheduledHertz } from 'nomad-ui/utils/units';
const TopoVizNode = create(topoVisNodePageObject());
@@ -110,8 +111,8 @@ module('Integration | Component | TopoViz::Node', function(hooks) {
`${this.node.allocations.filterBy('allocation.isScheduled').length} Allocs`
)
);
- assert.ok(TopoVizNode.label.includes(`${this.node.memory} MiB`));
- assert.ok(TopoVizNode.label.includes(`${this.node.cpu} MHz`));
+ assert.ok(TopoVizNode.label.includes(`${formatScheduledBytes(this.node.memory, 'MiB')}`));
+ assert.ok(TopoVizNode.label.includes(`${formatScheduledHertz(this.node.cpu, 'MHz')}`));
});
test('the status icon indicates when the node is draining', async function(assert) {
diff --git a/ui/tests/unit/helpers/format-bytes-test.js b/ui/tests/unit/helpers/format-bytes-test.js
deleted file mode 100644
index b837d31be1ab..000000000000
--- a/ui/tests/unit/helpers/format-bytes-test.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import { module, test } from 'qunit';
-import { formatBytes } from 'nomad-ui/helpers/format-bytes';
-
-module('Unit | Helper | format-bytes', function() {
- test('formats null/undefined as 0 bytes', function(assert) {
- assert.equal(formatBytes([undefined]), '0 Bytes');
- assert.equal(formatBytes([null]), '0 Bytes');
- });
-
- test('formats x < 1024 as bytes', function(assert) {
- assert.equal(formatBytes([0]), '0 Bytes');
- assert.equal(formatBytes([100]), '100 Bytes');
- assert.equal(formatBytes([1023]), '1023 Bytes');
- });
-
- test('formats 1024 <= x < 1024 * 1024 as KiB', function(assert) {
- assert.equal(formatBytes([1024]), '1 KiB');
- assert.equal(formatBytes([125952]), '123 KiB');
- assert.equal(formatBytes([1024 * 1024 - 1]), '1023 KiB');
- });
-
- test('formats 1024 * 1024 <= x < 1024 * 1024 * 1024 as MiB', function(assert) {
- assert.equal(formatBytes([1024 * 1024]), '1 MiB');
- assert.equal(formatBytes([128974848]), '123 MiB');
- });
-
- test('formats 1024 * 1024 * 1024 <= x < 1024 * 1024 * 1024 * 1024 as GiB', function(assert) {
- assert.equal(formatBytes([1024 * 1024 * 1024]), '1 GiB');
- assert.equal(formatBytes([1024 * 1024 * 1024 * 4]), '4 GiB');
- });
-
- test('formats x > 1024 * 1024 * 1024 * 1024 as GiB, since it is the highest allowed unit', function(assert) {
- assert.equal(formatBytes([1024 * 1024 * 1024 * 1024]), '1024 GiB');
- assert.equal(formatBytes([1024 * 1024 * 1024 * 1024 * 4]), '4096 GiB');
- });
-});
diff --git a/ui/tests/unit/utils/units-test.js b/ui/tests/unit/utils/units-test.js
new file mode 100644
index 000000000000..bb2f06e02bac
--- /dev/null
+++ b/ui/tests/unit/utils/units-test.js
@@ -0,0 +1,155 @@
+import { module, test } from 'qunit';
+import * as units from 'nomad-ui/utils/units';
+
+function table(fn, cases) {
+ cases.forEach(testCase => {
+ test(testCase.name || testCase.out, function(assert) {
+ assert.deepEqual(fn.apply(null, testCase.in), testCase.out);
+ });
+ });
+}
+
+module('Unit | Util | units#formatBytes', function() {
+ table.call(this, units.formatBytes, [
+ { in: [null], out: '0 Bytes', name: 'formats null as 0 bytes' },
+ { in: [undefined], out: '0 Bytes', name: 'formats undefined as 0 bytes' },
+ { in: [0], out: '0 Bytes', name: 'formats x < 1024 as bytes' },
+ { in: [100], out: '100 Bytes' },
+ { in: [1023], out: '1,023 Bytes' },
+ { in: [1024], out: '1 KiB', name: 'formats 1024 <= x < 1024^2 as KiB' },
+ { in: [1024 ** 2 - 1024 * 0.01], out: '1,023.99 KiB' },
+ { in: [1024 ** 2], out: '1 MiB', name: 'formats 1024^2 <= x < 1024^3 as MiB' },
+ { in: [1024 ** 2 * 1.016], out: '1.02 MiB' },
+ { in: [1024 ** 3], out: '1 GiB', name: 'formats 1024^3 <= x < 1024^4 as GiB' },
+ { in: [1024 ** 3 * 512.5], out: '512.5 GiB' },
+ { in: [1024 ** 4], out: '1 TiB', name: 'formats 1024^4 <= x < 1024^5 as TiB' },
+ { in: [1024 ** 4 * 2.1234], out: '2.12 TiB' },
+ { in: [1024 ** 5], out: '1 PiB', name: 'formats x > 1024^5 as PiB' },
+ { in: [1024 ** 5 * 4000], out: '4,000 PiB' },
+ {
+ in: [1024 ** 2, 'MiB'],
+ out: '1 TiB',
+ name: 'accepts a starting unit size as an optional argument',
+ },
+ { in: [1024 ** 2 * -1], out: '-1 MiB', name: 'negative values are still reduced' },
+ ]);
+});
+
+module('Unit | Util | units#formatScheduledBytes', function() {
+ table.call(this, units.formatScheduledBytes, [
+ { in: [null], out: '0 Bytes', name: 'formats null as 0 bytes' },
+ { in: [undefined], out: '0 Bytes', name: 'formats undefined as 0 bytes' },
+ { in: [0], out: '0 Bytes', name: 'formats x < 1024 as bytes' },
+ {
+ in: [1024 ** 3],
+ out: '1,024 MiB',
+ name: 'max unit is MiB, just like Nomad expects in job specs',
+ },
+ {
+ in: [1024 ** 3 + 1024 ** 2 * 0.6],
+ out: '1,025 MiB',
+ name: 'MiBs are rounded to whole numbers',
+ },
+ {
+ in: [2000, 'MiB'],
+ out: '2,000 MiB',
+ name: 'accepts a starting unit size as an optional argument',
+ },
+ { in: [1024 ** 3 * -1], out: '-1,024 MiB', name: 'negative values are still reduced' },
+ ]);
+});
+
+module('Unit | Util | units#formatHertz', function() {
+ table.call(this, units.formatHertz, [
+ { in: [null], out: '0 Hz', name: 'formats null as 0 Hz' },
+ { in: [undefined], out: '0 Hz', name: 'formats undefined as 0 Hz' },
+ { in: [0], out: '0 Hz', name: 'formats x < 1000 as Hz' },
+ { in: [999], out: '999 Hz' },
+ { in: [1000], out: '1 KHz', name: 'formats 1000 <= x < 1000^2 as KHz' },
+ { in: [1000 ** 2 - 10], out: '999.99 KHz' },
+ { in: [1000 ** 2], out: '1 MHz', name: 'formats 1000^2 <= x < 1000^3 as MHz' },
+ { in: [1000 ** 2 * 5.234], out: '5.23 MHz' },
+ { in: [1000 ** 3], out: '1 GHz', name: 'formats 1000^3 <= x < 1000^4 as GHz' },
+ { in: [1000 ** 3 * 500.238], out: '500.24 GHz' },
+ { in: [1000 ** 4], out: '1 THz', name: 'formats 1000^4 <= x < 1000^5 as THz' },
+ { in: [1000 ** 4 * 12], out: '12 THz' },
+ { in: [1000 ** 5], out: '1 PHz', name: 'formats x > 1000^5 as PHz' },
+ { in: [1000 ** 5 * 34567.89], out: '34,567.89 PHz' },
+ {
+ in: [2000, 'KHz'],
+ out: '2 MHz',
+ name: 'accepts a starting unit size as an optional argument',
+ },
+ { in: [1000 ** 3 * -1], out: '-1 GHz', name: 'negative values are still reduced' },
+ ]);
+});
+
+module('Unit | Util | units#formatScheduledHertz', function() {
+ table.call(this, units.formatScheduledHertz, [
+ { in: [null], out: '0 Hz', name: 'formats null as 0 Hz' },
+ { in: [undefined], out: '0 Hz', name: 'formats undefined as 0 Hz' },
+ { in: [0], out: '0 Hz', name: 'formats x < 1024 as Hz' },
+ {
+ in: [1000 ** 3],
+ out: '1,000 MHz',
+ name: 'max unit is MHz, just like Nomad expects in job specs',
+ },
+ {
+ in: [1000 ** 3 + 1000 ** 2 * 0.6],
+ out: '1,001 MHz',
+ name: 'MHz are rounded to whole numbers',
+ },
+ {
+ in: [2000, 'MHz'],
+ out: '2,000 MHz',
+ name: 'accepts a starting unit size as an optional argument',
+ },
+ { in: [1000 ** 3 * -1], out: '-1,000 MHz', name: 'negative values are still reduced' },
+ ]);
+});
+
+module('Unit | Util | units#reduceBytes', function() {
+ table.call(this, units.reduceBytes, [
+ { in: [], out: [0, 'Bytes'], name: 'No args behavior results in valid output' },
+ { in: [1024 ** 6], out: [1024, 'PiB'], name: 'Max default unit is PiB' },
+ {
+ in: [1024 ** 6 * 1.12345],
+ out: [1024 * 1.12345, 'PiB'],
+ name: 'Returned numbers are not formatted',
+ },
+ {
+ in: [1024 ** 2 * 2, 'KiB'],
+ out: [2048, 'KiB'],
+ name: 'Reduction ends when the specified max unit is reached',
+ },
+ {
+ in: [1024 ** 2, 'MiB', 'KiB'],
+ out: [1024, 'MiB'],
+ name: 'accepts a starting unit size as an optional argument',
+ },
+ { in: [1024 ** 3 * -1], out: [-1, 'GiB'], name: 'negative values are still reduced' },
+ ]);
+});
+
+module('Unit | Util | units#reduceHertz', function() {
+ table.call(this, units.reduceHertz, [
+ { in: [], out: [0, 'Hz'], name: 'No args behavior results in valid output' },
+ { in: [1000 ** 6], out: [1000, 'PHz'], name: 'Max default unit is PHz' },
+ {
+ in: [1000 ** 6 * 1.12345],
+ out: [1000 * 1.12345, 'PHz'],
+ name: 'Returned numbers are not formatted',
+ },
+ {
+ in: [1000 ** 4 * 2, 'GHz'],
+ out: [2000, 'GHz'],
+ name: 'Reduction ends when the specified max unit is reached',
+ },
+ {
+ in: [1000 * 2, 'THz', 'MHz'],
+ out: [2, 'GHz'],
+ name: 'accepts a starting unit size as an optional argument',
+ },
+ { in: [1000 ** 3 * -1], out: [-1, 'GHz'], name: 'negative values are still reduced' },
+ ]);
+});