]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Refactored formatter service 21677/head
authorStephan Müller <smueller@suse.com>
Thu, 26 Apr 2018 13:25:04 +0000 (15:25 +0200)
committerStephan Müller <smueller@suse.com>
Wed, 2 May 2018 09:21:49 +0000 (11:21 +0200)
Added tests which caused a refactorization of the service.

Signed-off-by: Stephan Müller <smueller@suse.com>
src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/dimless-binary.pipe.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/formatter.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/formatter.service.ts

index 13686a1a1971cfdf7a8372afc2e1858ac7ac3486..b1784fcf16b3503cf5392741765188bb7b3d9aa6 100644 (file)
@@ -15,9 +15,9 @@ export class DimlessBinaryPipe implements PipeTransform {
       'GiB',
       'TiB',
       'PiB',
-      'Eib',
-      'Zib',
-      'Yib'
+      'EiB',
+      'ZiB',
+      'YiB'
     ]);
   }
 }
index f32f92889ce68c3f0033e3eef94e20f35586d81d..42f64e9b20d10cd5d339bb8f2edcaa6e6756d950 100644 (file)
@@ -1,51 +1,91 @@
 import { TestBed } from '@angular/core/testing';
 
+import { DimlessBinaryPipe } from '../pipes/dimless-binary.pipe';
 import { FormatterService } from './formatter.service';
 
 describe('FormatterService', () => {
   let service: FormatterService;
+  let dimlessBinaryPipe: DimlessBinaryPipe;
+
+  const convertToBytesAndBack = (value: string, newValue?: string) => {
+    expect(dimlessBinaryPipe.transform(service.toBytes(value))).toBe(newValue || value);
+  };
+
   beforeEach(() => {
     TestBed.configureTestingModule({
-      providers: [FormatterService]
+      providers: [FormatterService, DimlessBinaryPipe]
     });
     service = new FormatterService();
+    dimlessBinaryPipe = new DimlessBinaryPipe(service);
   });
 
   it('should be created', () => {
     expect(service).toBeTruthy();
   });
 
-  it('should not convert 10xyz to bytes (failure)', () => {
-    const bytes = service.toBytes('10xyz');
-    expect(bytes).toBeNull();
-  });
+  describe('truncate', () => {
+    it('should do test integer values', () => {
+      expect(service.truncate('1234', 8)).toBe('1234');
+      expect(service.truncate(1234, 8)).toBe('1234');
+    });
 
-  it('should not convert 1.1.1KiB to bytes (failure)', () => {
-    const bytes = service.toBytes('1.1.1KiB');
-    expect(bytes).toBeNull();
+    it('should do test floating values', () => {
+      const value = '1234.567899000';
+      expect(service.truncate(value, 0)).toBe('1235');
+      expect(service.truncate(value, 1)).toBe('1234.6');
+      expect(service.truncate(value, 3)).toBe('1234.568');
+      expect(service.truncate(value, 4)).toBe('1234.5679');
+      expect(service.truncate(value, 5)).toBe('1234.5679');
+      expect(service.truncate(value, 6)).toBe('1234.567899');
+      expect(service.truncate(value, 7)).toBe('1234.567899');
+      expect(service.truncate(value, 10)).toBe('1234.567899');
+      expect(service.truncate(100.00, 4)).toBe('100');
+    });
   });
 
-  it('should convert 4815162342 to bytes', () => {
-    const bytes = service.toBytes('4815162342');
-    expect(bytes).toEqual(jasmine.any(Number));
-    expect(bytes).toBe(4815162342);
-  });
+  describe('format_number', () => {
+    const formats = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
 
-  it('should convert 100M to bytes', () => {
-    const bytes = service.toBytes('100M');
-    expect(bytes).toEqual(jasmine.any(Number));
-    expect(bytes).toBe(104857600);
-  });
+    it('should return minus for unsupported values', () => {
+      expect(service.format_number(service, 1024, formats)).toBe('-');
+      expect(service.format_number(undefined, 1024, formats)).toBe('-');
+      expect(service.format_number(null, 1024, formats)).toBe('-');
+      expect(service.format_number('0', 1024, formats)).toBe('-');
+    });
 
-  it('should convert 1.532KiB to bytes', () => {
-    const bytes = service.toBytes('1.532KiB');
-    expect(bytes).toEqual(jasmine.any(Number));
-    expect(bytes).toBe(Math.floor(1.532 * 1024));
+    it('should test some values', () => {
+      expect(service.format_number('1', 1024, formats)).toBe('1B');
+      expect(service.format_number('1024', 1024, formats)).toBe('1KiB');
+      expect(service.format_number(23.45678 * Math.pow(1024, 3), 1024, formats)).toBe('23.4568GiB');
+    });
   });
 
-  it('should convert 0.000000000001TiB to bytes', () => {
-    const bytes = service.toBytes('0.000000000001TiB');
-    expect(bytes).toEqual(jasmine.any(Number));
-    expect(bytes).toBe(1);
+  describe('toBytes', () => {
+    it('should not convert wrong values', () => {
+      expect(service.toBytes('10xyz')).toBeNull();
+      expect(service.toBytes('1.1.1KiB')).toBeNull();
+      expect(service.toBytes('1.1 KiloByte')).toBeNull();
+      expect(service.toBytes('1.1  kib')).toBeNull();
+      expect(service.toBytes('1.kib')).toBeNull();
+      expect(service.toBytes('1 ki')).toBeNull();
+    });
+
+    it('should convert values to bytes', () => {
+      expect(service.toBytes('4815162342')).toBe(4815162342);
+      expect(service.toBytes('100M')).toBe(104857600);
+      expect(service.toBytes('100 M')).toBe(104857600);
+      expect(service.toBytes('100 mIb')).toBe(104857600);
+      expect(service.toBytes('100 mb')).toBe(104857600);
+      expect(service.toBytes('100MIB')).toBe(104857600);
+      expect(service.toBytes('1.532KiB')).toBe(Math.round(1.532 * 1024));
+      expect(service.toBytes('0.000000000001TiB')).toBe(1);
+    });
+
+    it('should convert values to human readable again', () => {
+      convertToBytesAndBack('1.1MiB');
+      convertToBytesAndBack('1.0MiB', '1MiB');
+      convertToBytesAndBack('8.9GiB');
+      convertToBytesAndBack('123.456EiB');
+    });
   });
 });
index 7c6a95d3048a821810f1397ca79962a8de5f1f66..341e1ee25231a083ba56157644eb51b5bccc7ea9 100644 (file)
@@ -6,48 +6,26 @@ import * as _ from 'lodash';
 export class FormatterService {
   constructor() {}
 
-  truncate(n, maxWidth) {
-    const stringized = n.toString();
-    const parts = stringized.split('.');
+  truncate(n: number | string, decimals: number): string {
+    const value = n.toString();
+    const parts = value.split('.');
     if (parts.length === 1) {
-      // Just an int
-      return stringized;
+      return value; // integer
     } else {
-      const fractionalDigits = maxWidth - parts[0].length - 1;
-      if (fractionalDigits <= 0) {
-        // No width available for the fractional part, drop
-        // it and the decimal point
-        return parts[0];
-      } else {
-        return stringized.substring(0, maxWidth);
-      }
+      return Number.parseFloat(value).toPrecision(decimals + parts[0].length)
+        .toString().replace(/0+$/, '');
     }
   }
 
-  format_number(n, divisor, units) {
-    const width = 4;
-    let unit = 0;
-
-    if (n == null) {
-      // People shouldn't really be passing null, but let's
-      // do something sensible instead of barfing.
-      return '-';
-    }
-
-    while (Math.floor(n / divisor ** unit).toString().length > width - 1) {
-      unit = unit + 1;
+  format_number(n: any, divisor: number, units: string[], decimals: number = 4): string {
+    if (_.isString(n)) {
+      n = Number(n);
     }
-
-    let truncatedFloat;
-    if (unit > 0) {
-      truncatedFloat = this.truncate(
-        (n / Math.pow(divisor, unit)).toString(),
-        width
-      );
-    } else {
-      truncatedFloat = this.truncate(n, width);
+    if (!(_.isNumber(n)) || n === 0) {
+      return '-';
     }
-
+    const unit = Math.floor(Math.log(n) / Math.log(divisor));
+    const truncatedFloat = this.truncate((n / Math.pow(divisor, unit)), decimals);
     return truncatedFloat === '' ? '-' : (truncatedFloat + units[unit]);
   }
 
@@ -59,42 +37,15 @@ export class FormatterService {
    */
   toBytes(value: string): number | null {
     const base = 1024;
-    const units = {
-      'b': 1,
-      'k': Math.pow(base, 1),
-      'kb': Math.pow(base, 1),
-      'kib': Math.pow(base, 1),
-      'm': Math.pow(base, 2),
-      'mb': Math.pow(base, 2),
-      'mib': Math.pow(base, 2),
-      'g': Math.pow(base, 3),
-      'gb': Math.pow(base, 3),
-      'gib': Math.pow(base, 3),
-      't': Math.pow(base, 4),
-      'tb': Math.pow(base, 4),
-      'tib': Math.pow(base, 4),
-      'p': Math.pow(base, 5),
-      'pb': Math.pow(base, 5),
-      'pib': Math.pow(base, 5),
-      'e': Math.pow(base, 6),
-      'eb': Math.pow(base, 6),
-      'eib': Math.pow(base, 6),
-      'z': Math.pow(base, 7),
-      'zb': Math.pow(base, 7),
-      'zib': Math.pow(base, 7),
-      'y': Math.pow(base, 8),
-      'yb': Math.pow(base, 8),
-      'yib': Math.pow(base, 8)
-    };
-    const m = RegExp('^(\\d+(\.\\d+)?)\\s*(B|K(B|iB)?|M(B|iB)?|G(B|iB)?|T(B|iB)?|P(B|iB)?|' +
-      'E(B|iB)?|Z(B|iB)?|Y(B|iB)?)?$', 'i').exec(value);
+    const units = ['b', 'k', 'm', 'g', 't', 'p', 'e', 'z', 'y'];
+    const m = RegExp('^(\\d+(\.\\d+)?) ?(\[' + units.join('') + '\](b|ib)?)?$', 'i').exec(value);
     if (m === null) {
       return null;
     }
     let bytes = parseFloat(m[1]);
     if (_.isString(m[3])) {
-      bytes = bytes * units[m[3].toLowerCase()];
+      bytes = bytes * Math.pow(base, units.indexOf(m[3].toLowerCase()[0]));
     }
-    return Math.floor(bytes);
+    return Math.round(bytes);
   }
 }