view src/app/components/jvm-info/jvm-io/jvm-io.controller.spec.js @ 242:2d2b8cfc8091

Query for denser sample sets when updating jvm-io data Reviewed-by: jkang Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-October/025283.html
author Andrew Azores <aazores@redhat.com>
date Wed, 11 Oct 2017 13:55:03 -0400
parents 0058493033d9
children
line wrap: on
line source

/**
 * Copyright 2012-2017 Red Hat, Inc.
 *
 * Thermostat is distributed under the GNU General Public License,
 * version 2 or any later version (with a special exception described
 * below, commonly known as the "Classpath Exception").
 *
 * A copy of GNU General Public License (GPL) is included in this
 * distribution, in the file COPYING.
 *
 * Linking Thermostat code with other modules is making a combined work
 * based on Thermostat.  Thus, the terms and conditions of the GPL
 * cover the whole combination.
 *
 * As a special exception, the copyright holders of Thermostat give you
 * permission to link this code with independent modules to produce an
 * executable, regardless of the license terms of these independent
 * modules, and to copy and distribute the resulting executable under
 * terms of your choice, provided that you also meet, for each linked
 * independent module, the terms and conditions of the license of that
 * module.  An independent module is a module which is not derived from
 * or based on Thermostat code.  If you modify Thermostat, you may
 * extend this exception to your version of the software, but you are
 * not obligated to do so.  If you do not wish to do so, delete this
 * exception statement from your version.
 */

import controllerModule from './jvm-io.controller.js';

describe('JvmIoController', () => {

  let ctrl, svc, interval, translations, translate,
    dateFilter, dateFormat, metricToNumberFilter;
  beforeEach(() => {
    angular.mock.module(controllerModule);
    angular.mock.inject($controller => {
      'ngInject';
      let svcPromise = sinon.stub();
      svc = {
        getJvmIoData: sinon.stub().returns({
          then: svcPromise
        }),
        promise: svcPromise
      };
      interval = sinon.stub().returns('intervalMock');
      interval.cancel = sinon.spy();

      translations = {
        'jvmIo.metrics.timestamp': 'date',
        'jvmIo.metrics.charactersRead': 'characters read',
        'jvmIo.metrics.charactersWritten': 'characters written',
        'jvmIo.metrics.readSyscalls': 'read sys calls',
        'jvmIo.metrics.writeSyscalls': 'write sys calls'
      };
      let translateThenThen = sinon.stub().yields();
      translate = sinon.stub().returns({
        then: sinon.stub().yields(translations).returns({
          then: translateThenThen
        })
      });

      dateFilter = sinon.stub().returns('dateFilterMock');
      dateFormat = {
        time: {
          medium: 'dateFormatMedium'
        }
      };
      metricToNumberFilter = sinon.stub().callsFake(v => parseInt(v.$numberLong));

      ctrl = $controller('JvmIoController', {
        jvmIoService: svc,
        $interval: interval,
        $translate: translate,
        dateFilter: dateFilter,
        DATE_FORMAT: dateFormat,
        metricToNumberFilter: metricToNumberFilter
      });
      ctrl.jvmId = 'foo-jvmId';
    });
  });

  it('should exist', () => {
    should.exist(ctrl);
  });

  describe('properties', () => {
    it('should set refresh rate to 10 seconds', () => {
      ctrl.refreshRate.should.equal('10000');
    });

    it('should set data age limit to 10 minutes', () => {
      ctrl.dataAgeLimit.should.equal('600000');
    });

    it('should use translations', () => {
      translate.should.be.calledWith([
        'jvmIo.chart.X_LABEL',
        'jvmIo.chart.Y1_LABEL',
        'jvmIo.chart.Y2_LABEL',

        'jvmIo.metrics.timestamp',
        'jvmIo.metrics.charactersRead',
        'jvmIo.metrics.charactersWritten',
        'jvmIo.metrics.readSyscalls',
        'jvmIo.metrics.writeSyscalls',
      ]);
    });
  });

  describe('$onInit ()', () => {
    beforeEach(() => {
      ctrl.$onInit();
    });

    it('should load historical data', () => {
      svc.getJvmIoData.should.be.calledOnce();
    });

    it('should start periodic live updates', () => {
      interval.should.be.calledOnce();
    });
  });

  describe('chart config', () => {
    it('should format x-axis ticks', () => {
      let fn = ctrl.config.axis.x.tick.format;
      fn(100).should.equal('dateFilterMock');
    });

    it('should provide an identity y-axis tick format function', () => {
      let fn = ctrl.config.axis.y.tick.format;
      fn(100).should.equal(100);
      fn(200).should.equal(200);
    });

    it('should provide an identity y2-axis tick format function', () => {
      let fn = ctrl.config.axis.y2.tick.format;
      fn(100).should.equal(100);
      fn(200).should.equal(200);
    });

    it('should provide identity tooltip format functions', () => {
      let title = ctrl.config.tooltip.format.title;
      title(100).should.equal(100);
      title(200).should.equal(200);
      let value = ctrl.config.tooltip.format.value;
      value(100).should.equal(100);
      value(200).should.equal(200);
    });
  });

  describe('$onDestroy', () => {
    it('should do nothing if controller is not started', () => {
      interval.cancel.should.not.be.called();
      ctrl.$onDestroy();
      interval.cancel.should.not.be.called();
    });

    it('should stop the controller if already started', () => {
      ctrl._start();
      interval.cancel.should.not.be.called();
      ctrl.$onDestroy();
      interval.cancel.should.be.calledOnce();
    });
  });

  describe('refreshRate', () => {
    it('should set interval and disable if <= 0', () => {
      interval.should.not.be.called();
      ctrl.refreshRate = 10000;
      interval.cancel.should.not.be.called();
      interval.should.be.calledOnce();
      interval.should.be.calledWith(sinon.match.func, 10000);
      ctrl.refreshRate = -1;
      interval.cancel.should.be.calledOnce();
      interval.should.be.calledOnce();
    });

    it('should reflect changes in getter', () => {
      ctrl.refreshRate.should.equal('10000');
      ctrl.refreshRate = 2000;
      ctrl.refreshRate.should.equal('2000');
    });
  });

  describe('dataAgeLimit', () => {
    it('should cause a data clear on change', () => {
      sinon.spy(ctrl, '_clearData');
      ctrl._clearData.should.not.be.called;
      ctrl.dataAgeLimit = 10000;
      ctrl._clearData.should.be.calledOnce();
      ctrl._clearData.restore();
    });

    it('should cause a historical data load on change', () => {
      sinon.spy(ctrl, '_loadHistoricalData');
      ctrl._loadHistoricalData.should.not.be.called;
      ctrl.dataAgeLimit = 10000;
      ctrl._loadHistoricalData.should.be.calledOnce();
      ctrl._loadHistoricalData.restore();
    });

    it('should reflect changes in getter', () => {
      ctrl.dataAgeLimit.should.equal('600000');
      ctrl.dataAgeLimit = 10000;
      ctrl.dataAgeLimit.should.equal('10000');
    });
  });

  describe('_start', () => {
    it('should perform updates on an interval', () => {
      interval.should.not.be.called();
      ctrl._start();
      interval.should.be.calledOnce();
      interval.should.be.calledWith(sinon.match.func, 10000);

      let func = interval.args[0][0];
      svc.getJvmIoData.should.be.calledOnce();
      func();
      svc.getJvmIoData.should.be.calledTwice();
    });
  });

  describe('_stop', () => {
    it('should do nothing if not started', () => {
      interval.cancel.should.not.be.called();
      ctrl._stop();
      interval.cancel.should.not.be.called();
    });
  });

  describe('_loadHistoricaldata', () => {
    it('should add update data to chart data', () => {
      ctrl._loadHistoricalData();
      svc.promise.should.be.calledOnce();
      svc.promise.should.be.calledWith(sinon.match.func);
      let stamp = Date.now();
      svc.promise.args[0][0](
        [
          {
            timeStamp: { $numberLong: (stamp - 10000).toString() },
            charactersRead: { $numberLong: '1000000' },
            charactersWritten: { $numberLong: '500000' },
            readSyscalls: { $numberLong: '100' },
            writeSyscalls: { $numberLong: '50' }
          },
          {
            timeStamp: { $numberLong: stamp.toString() },
            charactersRead: { $numberLong: '1000001' },
            charactersWritten: { $numberLong: '500001' },
            readSyscalls: { $numberLong: '101' },
            writeSyscalls: { $numberLong: '51' }
          }
        ]
      );
      ctrl.config.data.rows.should.deepEqual([
        ['date', 'characters read', 'characters written', 'read sys calls', 'write sys calls'],
        [(stamp - 10000), 1000000, 500000, 100, 50],
        [stamp, 1000001, 500001, 101, 51]
      ]);
    });
  });

  describe('_update', () => {
    it('should add update data to chart data', () => {
      ctrl._update();
      svc.promise.should.be.calledOnce();
      svc.promise.should.be.calledWith(sinon.match.func);
      let stamp = Date.now();
      svc.promise.args[0][0](
        [
          {
            timeStamp: { $numberLong: stamp.toString() },
            charactersRead: { $numberLong: '1000000' },
            charactersWritten: { $numberLong: '500000' },
            readSyscalls: { $numberLong: '100' },
            writeSyscalls: { $numberLong: '50' }
          }
        ]
      );
      ctrl.config.data.rows.should.deepEqual([
        ['date', 'characters read', 'characters written', 'read sys calls', 'write sys calls'],
        [stamp, 1000000, 500000, 100, 50]
      ]);
    });
  });

  describe('_trimData', () => {
    it('should remove datasets older than dataAgeLimit', () => {
      let now = Date.now();
      let expired = now - ctrl._dataAgeLimit - 1;
      ctrl.config.data.rows.push([
        expired, 1, 1, 1, 1
      ]);
      ctrl.config.data.rows.push([
        now, 2, 2, 2, 2
      ]);
      ctrl._trimData();
      ctrl.config.data.rows.should.deepEqual([
        ['date', 'characters read', 'characters written', 'read sys calls', 'write sys calls'],
        [now, 2, 2, 2, 2]
      ]);
    });
  });

});