diff --git a/ui/app/components/allocation-stat.js b/ui/app/components/allocation-stat.js index 814d43e52370..b53531070ca6 100644 --- a/ui/app/components/allocation-stat.js +++ b/ui/app/components/allocation-stat.js @@ -1,7 +1,7 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; import { alias } from '@ember/object/computed'; -import { formatBytes } from 'nomad-ui/helpers/format-bytes'; +import { formatBytes, formatHertz } from 'nomad-ui/utils/units'; import { tagName } from '@ember-decorators/component'; import classic from 'ember-classic-decorator'; @@ -35,14 +35,15 @@ export default class AllocationStat extends Component { @computed('metric', 'stat.used') get formattedStat() { if (!this.stat) return undefined; - if (this.metric === 'memory') return formatBytes([this.stat.used]); - return this.stat.used; + if (this.metric === 'memory') return formatBytes(this.stat.used); + if (this.metric === 'cpu') return formatHertz(this.stat.used, 'MHz'); + return undefined; } @computed('metric', 'statsTracker.{reservedMemory,reservedCPU}') get formattedReserved() { - if (this.metric === 'memory') return `${this.statsTracker.reservedMemory} MiB`; - if (this.metric === 'cpu') return `${this.statsTracker.reservedCPU} MHz`; + if (this.metric === 'memory') return formatBytes(this.statsTracker.reservedMemory, 'MiB'); + if (this.metric === 'cpu') return formatHertz(this.statsTracker.reservedCPU, 'MHz'); return undefined; } } diff --git a/ui/app/components/primary-metric/allocation.hbs b/ui/app/components/primary-metric/allocation.hbs index 9e884dc331c4..ed69fc077fe4 100644 --- a/ui/app/components/primary-metric/allocation.hbs +++ b/ui/app/components/primary-metric/allocation.hbs @@ -18,9 +18,9 @@
  • {{series.name}} {{#if (eq this.metric "cpu")}} - {{datum.datum.used}} MHz + {{format-scheduled-hertz datum.datum.used}} {{else if (eq this.metric "memory")}} - {{format-bytes datum.datum.used}} + {{format-scheduled-bytes datum.datum.used}} {{else}} {{datum.formatttedY}} {{/if}} @@ -32,9 +32,9 @@
    {{#if (eq this.metric "cpu")}} - {{this.data.lastObject.used}} MHz / {{this.reservedAmount}} MHz Total + {{format-scheduled-hertz this.data.lastObject.used}} / {{format-scheduled-hertz this.reservedAmount}} Total {{else if (eq this.metric "memory")}} - {{format-bytes this.data.lastObject.used}} / {{this.reservedAmount}} MiB Total + {{format-scheduled-bytes this.data.lastObject.used}} / {{format-scheduled-bytes this.reservedAmount start="MiB"}} Total {{else}} {{this.data.lastObject.used}} / {{this.reservedAmount}} Total {{/if}} diff --git a/ui/app/components/primary-metric/node.hbs b/ui/app/components/primary-metric/node.hbs index d0718f02a9e1..c4164ae41729 100644 --- a/ui/app/components/primary-metric/node.hbs +++ b/ui/app/components/primary-metric/node.hbs @@ -18,9 +18,9 @@
    {{#if (eq this.metric "cpu")}} - {{this.data.lastObject.used}} MHz / {{this.reservedAmount}} MHz Total + {{format-scheduled-hertz this.data.lastObject.used}} / {{format-scheduled-hertz this.reservedAmount}} Total {{else if (eq this.metric "memory")}} - {{format-bytes this.data.lastObject.used}} / {{this.reservedAmount}} MiB Total + {{format-scheduled-bytes this.data.lastObject.used}} / {{format-scheduled-bytes this.reservedAmount start="MiB"}} Total {{else}} {{this.data.lastObject.used}} / {{this.reservedAmount}} Total {{/if}} diff --git a/ui/app/components/primary-metric/node.js b/ui/app/components/primary-metric/node.js index 440a3c58ba36..781d3e38eb23 100644 --- a/ui/app/components/primary-metric/node.js +++ b/ui/app/components/primary-metric/node.js @@ -4,6 +4,7 @@ import { task, timeout } from 'ember-concurrency'; import { assert } from '@ember/debug'; import { inject as service } from '@ember/service'; import { action, get } from '@ember/object'; +import { formatScheduledBytes, formatScheduledHertz } from 'nomad-ui/utils/units'; export default class NodePrimaryMetric extends Component { @service('stats-trackers-registry') statsTrackersRegistry; @@ -42,12 +43,22 @@ export default class NodePrimaryMetric extends Component { get reservedAnnotations() { if (this.metric === 'cpu' && get(this.args.node, 'reserved.cpu')) { const cpu = this.args.node.reserved.cpu; - return [{ label: `${cpu} MHz reserved`, percent: cpu / this.reservedAmount }]; + return [ + { + label: `${formatScheduledHertz(cpu, 'MHz')} reserved`, + percent: cpu / this.reservedAmount, + }, + ]; } if (this.metric === 'memory' && get(this.args.node, 'reserved.memory')) { const memory = this.args.node.reserved.memory; - return [{ label: `${memory} MiB reserved`, percent: memory / this.reservedAmount }]; + return [ + { + label: `${formatScheduledBytes(memory, 'MiB')} reserved`, + percent: memory / this.reservedAmount, + }, + ]; } return []; diff --git a/ui/app/components/primary-metric/task.hbs b/ui/app/components/primary-metric/task.hbs index a2ae83487187..718315b5d988 100644 --- a/ui/app/components/primary-metric/task.hbs +++ b/ui/app/components/primary-metric/task.hbs @@ -12,9 +12,9 @@
    {{#if (eq this.metric "cpu")}} - {{this.data.lastObject.used}} MHz / {{this.reservedAmount}} MHz Total + {{format-scheduled-hertz this.data.lastObject.used}} / {{format-scheduled-hertz this.reservedAmount}} Total {{else if (eq this.metric "memory")}} - {{format-bytes this.data.lastObject.used}} / {{this.reservedAmount}} MiB Total + {{format-scheduled-bytes this.data.lastObject.used}} / {{format-scheduled-bytes this.reservedAmount start="MiB"}} Total {{else}} {{this.data.lastObject.used}} / {{this.reservedAmount}} Total {{/if}} diff --git a/ui/app/controllers/topology.js b/ui/app/controllers/topology.js index f77312d163c2..4b9a38b060e9 100644 --- a/ui/app/controllers/topology.js +++ b/ui/app/controllers/topology.js @@ -4,9 +4,12 @@ import { alias } from '@ember/object/computed'; import { inject as service } from '@ember/service'; import { tracked } from '@glimmer/tracking'; import classic from 'ember-classic-decorator'; -import { reduceToLargestUnit } from 'nomad-ui/helpers/format-bytes'; +import { reduceBytes, reduceHertz } from 'nomad-ui/utils/units'; const sumAggregator = (sum, value) => sum + (value || 0); +const formatter = new Intl.NumberFormat(window.navigator.locale || 'en', { + maximumFractionDigits: 2, +}); @classic export default class TopologyControllers extends Controller { @@ -39,12 +42,22 @@ export default class TopologyControllers extends Controller { @computed('totalMemory') get totalMemoryFormatted() { - return reduceToLargestUnit(this.totalMemory)[0].toFixed(2); + return formatter.format(reduceBytes(this.totalMemory)[0]); } @computed('totalMemory') get totalMemoryUnits() { - return reduceToLargestUnit(this.totalMemory)[1]; + return reduceBytes(this.totalMemory)[1]; + } + + @computed('totalCPU') + get totalCPUFormatted() { + return formatter.format(reduceHertz(this.totalCPU, null, 'MHz')[0]); + } + + @computed('totalCPU') + get totalCPUUnits() { + return reduceHertz(this.totalCPU, null, 'MHz')[1]; } @computed('scheduledAllocations.@each.allocatedResources') @@ -86,7 +99,7 @@ export default class TopologyControllers extends Controller { @computed('activeNode') get nodeUtilization() { const node = this.activeNode; - const [formattedMemory, memoryUnits] = reduceToLargestUnit(node.memory * 1024 * 1024); + const [formattedMemory, memoryUnits] = reduceBytes(node.memory * 1024 * 1024); const totalReservedMemory = node.allocations.mapBy('memory').reduce(sumAggregator, 0); const totalReservedCPU = node.allocations.mapBy('cpu').reduce(sumAggregator, 0); diff --git a/ui/app/helpers/format-bytes.js b/ui/app/helpers/format-bytes.js index ba204fbfcbea..eca9cd01703d 100644 --- a/ui/app/helpers/format-bytes.js +++ b/ui/app/helpers/format-bytes.js @@ -1,29 +1,16 @@ import Helper from '@ember/component/helper'; - -const UNITS = ['Bytes', 'KiB', 'MiB', 'GiB']; +import { formatBytes } from 'nomad-ui/utils/units'; /** * Bytes Formatter * - * Usage: {{format-bytes bytes}} + * Usage: {{format-bytes bytes start="KiB"}} * * Outputs the bytes reduced to the largest supported unit size for which * bytes is larger than one. */ -export function reduceToLargestUnit(bytes) { - bytes || (bytes = 0); - let unitIndex = 0; - while (bytes >= 1024 && unitIndex < UNITS.length - 1) { - bytes /= 1024; - unitIndex++; - } - - return [bytes, UNITS[unitIndex]]; -} - -export function formatBytes([bytes]) { - const [number, unit] = reduceToLargestUnit(bytes); - return `${Math.floor(number)} ${unit}`; +function formatBytesHelper([bytes], { start }) { + return formatBytes(bytes, start); } -export default Helper.helper(formatBytes); +export default Helper.helper(formatBytesHelper); diff --git a/ui/app/helpers/format-hertz.js b/ui/app/helpers/format-hertz.js new file mode 100644 index 000000000000..a9560d54045d --- /dev/null +++ b/ui/app/helpers/format-hertz.js @@ -0,0 +1,16 @@ +import Helper from '@ember/component/helper'; +import { formatHertz } from 'nomad-ui/utils/units'; + +/** + * Hertz Formatter + * + * Usage: {{format-hertz hertz}} + * + * Outputs the frequency reduced to the largest supported unit size for which + * the resulting number is larger than one. + */ +function formatHertzHelper([hertz], { start }) { + return formatHertz(hertz, start || 'MHz'); +} + +export default Helper.helper(formatHertzHelper); diff --git a/ui/app/helpers/format-scheduled-bytes.js b/ui/app/helpers/format-scheduled-bytes.js new file mode 100644 index 000000000000..7a6e3c74e440 --- /dev/null +++ b/ui/app/helpers/format-scheduled-bytes.js @@ -0,0 +1,16 @@ +import Helper from '@ember/component/helper'; +import { formatScheduledBytes } from 'nomad-ui/utils/units'; + +/** + * Scheduled Bytes Formatter + * + * Usage: {{format-scheduled-bytes bytes start="KiB"}} + * + * Outputs the bytes reduced to the resolution the scheduler + * and job spec operate at. + */ +function formatScheduledBytesHelper([bytes], { start }) { + return formatScheduledBytes(bytes, start); +} + +export default Helper.helper(formatScheduledBytesHelper); diff --git a/ui/app/helpers/format-scheduled-hertz.js b/ui/app/helpers/format-scheduled-hertz.js new file mode 100644 index 000000000000..9feacc68ed2d --- /dev/null +++ b/ui/app/helpers/format-scheduled-hertz.js @@ -0,0 +1,16 @@ +import Helper from '@ember/component/helper'; +import { formatScheduledHertz } from 'nomad-ui/utils/units'; + +/** + * Scheduled Hertz Formatter + * + * Usage: {{format-scheduled-hertz hertz}} + * + * Outputs the frequency reduced to the resolution the scheduler + * and job spec operate at. + */ +function formatScheduledHertzHelper([hertz], { start }) { + return formatScheduledHertz(hertz, start || 'MHz'); +} + +export default Helper.helper(formatScheduledHertzHelper); diff --git a/ui/app/templates/allocations/allocation/index.hbs b/ui/app/templates/allocations/allocation/index.hbs index 39cf4e7ce484..19593ef11e16 100644 --- a/ui/app/templates/allocations/allocation/index.hbs +++ b/ui/app/templates/allocations/allocation/index.hbs @@ -225,10 +225,10 @@ {{this.preempter.node.shortId}} Reserved CPU - {{this.preempter.resources.cpu}} MHz + {{format-scheduled-hertz this.preempter.resources.cpu}} Reserved Memory - {{this.preempter.resources.memory}} MiB + {{format-scheduled-bytes this.preempter.resources.memory start="MiB"}}
    diff --git a/ui/app/templates/components/task-group-row.hbs b/ui/app/templates/components/task-group-row.hbs index 795e63b93656..7a1d20244299 100644 --- a/ui/app/templates/components/task-group-row.hbs +++ b/ui/app/templates/components/task-group-row.hbs @@ -37,6 +37,6 @@
    {{if this.taskGroup.volumes.length "Yes"}} -{{this.taskGroup.reservedCPU}} MHz -{{this.taskGroup.reservedMemory}} MiB -{{this.taskGroup.reservedEphemeralDisk}} MiB +{{format-scheduled-hertz this.taskGroup.reservedCPU}} +{{format-scheduled-bytes this.taskGroup.reservedMemory start="MiB"}} +{{format-scheduled-bytes this.taskGroup.reservedEphemeralDisk start="MiB"}} diff --git a/ui/app/templates/components/task-row.hbs b/ui/app/templates/components/task-row.hbs index 7dcebffe78d5..5974051d9fc3 100644 --- a/ui/app/templates/components/task-row.hbs +++ b/ui/app/templates/components/task-row.hbs @@ -47,7 +47,7 @@ {{x-icon "alert-triangle" class="is-warning"}} {{else}} -
  • CPU - {{allocation.cpu}} MHz + {{format-scheduled-hertz allocation.cpu}}
  • {{/let}} diff --git a/ui/app/templates/components/topo-viz/datacenter.hbs b/ui/app/templates/components/topo-viz/datacenter.hbs index cf589c216cb4..fcf16da20319 100644 --- a/ui/app/templates/components/topo-viz/datacenter.hbs +++ b/ui/app/templates/components/topo-viz/datacenter.hbs @@ -3,8 +3,8 @@ {{@datacenter.name}} {{this.scheduledAllocations.length}} Allocs {{@datacenter.nodes.length}} Nodes - {{this.aggregatedAllocationResources.memory}}/{{this.aggregatedNodeResources.memory}} MiB, - {{this.aggregatedAllocationResources.cpu}}/{{this.aggregatedNodeResources.cpu}} MHz + {{format-bytes this.aggregatedAllocationResources.memory start="MiB"}}/{{format-bytes this.aggregatedNodeResources.memory start="MiB"}}, + {{format-hertz this.aggregatedAllocationResources.cpu}}/{{format-hertz this.aggregatedNodeResources.cpu}}
    diff --git a/ui/app/templates/components/topo-viz/node.hbs b/ui/app/templates/components/topo-viz/node.hbs index 409466caa94a..91a047043a2b 100644 --- a/ui/app/templates/components/topo-viz/node.hbs +++ b/ui/app/templates/components/topo-viz/node.hbs @@ -8,7 +8,7 @@ {{/if}} {{@node.node.name}} {{this.count}} Allocs - {{@node.memory}} MiB, {{@node.cpu}} MHz + {{format-scheduled-bytes @node.memory start="MiB"}}, {{format-scheduled-hertz @node.cpu}}

    {{/unless}} Task Group Details # Tasks {{this.model.tasks.length}} - Reserved CPU {{this.model.reservedCPU}} MHz - Reserved Memory {{this.model.reservedMemory}} MiB - Reserved Disk {{this.model.reservedEphemeralDisk}} MiB + Reserved CPU {{format-scheduled-hertz this.model.reservedCPU}} + Reserved Memory {{format-scheduled-bytes this.model.reservedMemory start="MiB"}} + Reserved Disk {{format-scheduled-bytes this.model.reservedEphemeralDisk start="MiB"}} {{#if this.model.scaling}} Count Range {{this.model.scaling.min}} to {{this.model.scaling.max}} diff --git a/ui/app/templates/topology.hbs b/ui/app/templates/topology.hbs index ef460795e306..59f509a4c075 100644 --- a/ui/app/templates/topology.hbs +++ b/ui/app/templates/topology.hbs @@ -108,7 +108,7 @@
    - {{format-bytes this.nodeUtilization.totalReservedMemory}} / {{format-bytes this.nodeUtilization.totalMemory}} reserved + {{format-scheduled-bytes this.nodeUtilization.totalReservedMemory}} / {{format-scheduled-bytes this.nodeUtilization.totalMemory}} reserved
    @@ -130,7 +130,7 @@
    - {{this.nodeUtilization.totalReservedCPU}} MHz / {{this.nodeUtilization.totalCPU}} MHz reserved + {{format-scheduled-hertz this.nodeUtilization.totalReservedCPU}} / {{format-scheduled-hertz this.nodeUtilization.totalCPU}} reserved
    {{/let}} @@ -205,7 +205,7 @@
    -

    {{this.totalCPU}} MHz of CPU

    +

    {{this.totalCPUFormatted}} {{this.totalCPUUnits}} of CPU

    @@ -223,7 +223,7 @@
    - {{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' }, + ]); +});