Skip to content

Commit

Permalink
Add tests for memoryCache, localStorageCache and geocoder
Browse files Browse the repository at this point in the history
  • Loading branch information
Iuliia Kulagina committed Apr 9, 2024
1 parent 8945f1d commit 6bda29d
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 42 deletions.
8 changes: 4 additions & 4 deletions src/cache/CacheManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ export class CacheManager {
private localStorageCache: LocalStorageCache;
private bingGeocoder: BingGeocoder;

constructor(localStorageService: IVisualLocalStorageV2Service) {
this.memoryCache = new MemoryCache(CacheSettings.MaxCacheSize, CacheSettings.MaxCacheSizeOverflow);
this.bingGeocoder = new BingGeocoder();
this.localStorageCache = new LocalStorageCache(localStorageService);
constructor(localStorageService: IVisualLocalStorageV2Service, memoryCache?: MemoryCache, localStorageCache?: LocalStorageCache, bingGeocoder?: BingGeocoder) {
this.memoryCache = memoryCache ?? new MemoryCache(CacheSettings.MaxCacheSize, CacheSettings.MaxCacheSizeOverflow);
this.bingGeocoder = bingGeocoder ?? new BingGeocoder();
this.localStorageCache = localStorageCache ?? new LocalStorageCache(localStorageService);
}

private createLocalStorageCache(): Promise<LocalStorageCache> {
Expand Down
46 changes: 22 additions & 24 deletions test/visualData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,28 @@ export class GlobeMapData extends TestDataViewBuilder {
80.237908
];

public coordinatesMock: ILocationDictionary = {
"addis ababa, ethiopia": {latitude: 9.03582859, longitude: 38.75241089},
"ahmedabad, india": {latitude: 23.0145092, longitude: 72.59175873},
"cairo, egypt": {latitude: 30.04348755, longitude: 31.23529243},
"cape town, south africa": {latitude: -33.92710876, longitude: 18.42006111},
"casablanca, morocco": {latitude: 33.59451294, longitude: -7.6200285},
"chennai, india": {latitude: 13.07209206, longitude: 80.20185852},
"durban, south africa": {latitude: -29.88188934, longitude: 30.98084259},
"jakarta, indonesia": {latitude: -6.17475653, longitude: 106.82707214},
"jeddah, saudi arabia": {latitude: 21.48730469, longitude: 39.18133545},
"lagos, nigeria": {latitude: 6.45505762, longitude: 3.39417958},
"lima, peru": {latitude: -12.06210613, longitude: -77.03652191},
"london, united kingdom": {latitude: 51.50740814, longitude: -0.12772401},
"mexico city, mexico": {latitude: 19.43267822, longitude: -99.13420868},
"new taipei city, republic of china": {latitude: 25.01170921, longitude: 121.46588135},
"riyadh, saudi arabia": {latitude: 24.69496918, longitude: 46.72412872},
"shanghai, china": {latitude: 31.23036957, longitude: 121.47370148},
"shenzhen, china": {latitude: 22.54368019, longitude: 114.0579071},
"surat, india": {latitude: 21.20350838, longitude: 72.83922577},
"tehran, iran": {latitude: 35.68925095, longitude: 51.38959885}
};

public valuesValue: number[] = getRandomNumbers(this.valuesSourceDestination.length, 10, 500);

public getDataView(columnNames?: string[]): DataView {
Expand Down Expand Up @@ -147,28 +169,4 @@ export class GlobeMapData extends TestDataViewBuilder {
}
], columnNames).build();
}

public getCoordinatesMock(): ILocationDictionary{
return {
"addis ababa, ethiopia": {latitude: 9.03582859, longitude: 38.75241089},
"ahmedabad, india": {latitude: 23.0145092, longitude: 72.59175873},
"cairo, egypt": {latitude: 30.04348755, longitude: 31.23529243},
"cape town, south africa": {latitude: -33.92710876, longitude: 18.42006111},
"casablanca, morocco": {latitude: 33.59451294, longitude: -7.6200285},
"chennai, india": {latitude: 13.07209206, longitude: 80.20185852},
"durban, south africa": {latitude: -29.88188934, longitude: 30.98084259},
"jakarta, indonesia": {latitude: -6.17475653, longitude: 106.82707214},
"jeddah, saudi arabia": {latitude: 21.48730469, longitude: 39.18133545},
"lagos, nigeria": {latitude: 6.45505762, longitude: 3.39417958},
"lima, peru": {latitude: -12.06210613, longitude: -77.03652191},
"london, united kingdom": {latitude: 51.50740814, longitude: -0.12772401},
"mexico city, mexico": {latitude: 19.43267822, longitude: -99.13420868},
"new taipei city, republic of china": {latitude: 25.01170921, longitude: 121.46588135},
"riyadh, saudi arabia": {latitude: 24.69496918, longitude: 46.72412872},
"shanghai, china": {latitude: 31.23036957, longitude: 121.47370148},
"shenzhen, china": {latitude: 22.54368019, longitude: 114.0579071},
"surat, india": {latitude: 21.20350838, longitude: 72.83922577},
"tehran, iran": {latitude: 35.68925095, longitude: 51.38959885}
};
}
}
144 changes: 130 additions & 14 deletions test/visualTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,15 @@ import { TileMap, ITileGapObject, IGlobeMapObject3DWithToolTipData } from "../sr

import capabilities from '../capabilities.json';
import PrimitiveValue = powerbi.PrimitiveValue;
import { ILocationKeyDictionary } from "../src/interfaces/locationInterfaces";
import { PointerType, createSelectionId, d3MouseDown, renderTimeout } from "powerbi-visuals-utils-testutils";
import { ILocationDictionary, ILocationKeyDictionary } from "../src/interfaces/locationInterfaces";
import { PointerType, createSelectionId, d3MouseDown, renderTimeout, MockIStorageV2Service } from "powerbi-visuals-utils-testutils";
import SubSelectionOutlineVisibility = powerbi.visuals.SubSelectionOutlineVisibility;
import ArcSubSelectionOutline = powerbi.visuals.ArcSubSelectionOutline;
import { DataPointReferences } from "../src/settings";
import { CacheSettings, DataPointReferences } from "../src/settings";
import { BingGeocoder } from "../src/geocoder";
import { LocalStorageCache } from "../src/cache/LocalStorageCache";
import { MemoryCache } from "../src/cache/MemoryCache";
import { CacheManager } from "../src/cache/CacheManager";

describe("GlobeMap", () => {
let visualBuilder: GlobeMapBuilder,
Expand All @@ -61,16 +65,8 @@ describe("GlobeMap", () => {

let categoricalColumns = GlobeMapColumns.getCategoricalColumns(dataView);

let locations = categoricalColumns.Location.values as PrimitiveValue[];
let locationsNeedToBeLoaded = {} as ILocationKeyDictionary;

locations.forEach((locationName) => {
const name = (locationName as string).toLowerCase();
locationsNeedToBeLoaded[name] = {
place: name,
locationType: ""
};
});
let locations = categoricalColumns.Location.values as string[];
let locationsNeedToBeLoaded = convertLocationsForCacheManager(locations);

const coordinates = await visualInstance.cacheManager.loadCoordinates(locationsNeedToBeLoaded);

Expand Down Expand Up @@ -244,11 +240,119 @@ describe("GlobeMap", () => {
}
});
});

it("geocoder should return locations for all valid input", async () => {
const bingGeocoder = new BingGeocoder();
const locations = Object.assign(defaultDataViewBuilder.valuesSourceDestination);
// set invalid location
locations[0] = 'lll, lll';
const coordinatesFromBing: ILocationDictionary = await bingGeocoder.geocode(locations);

expect(Object.keys(coordinatesFromBing).length).toBe(locations.length - 1);
expect(coordinatesFromBing[locations[0]]).toBeUndefined;
});

it("cacheManager should load coordinates for all input from bing, when memory cache is empty and local storage is empty", async () => {
const locations = Object.assign(defaultDataViewBuilder.valuesSourceDestination);
const locationsNeedToBeLoaded = convertLocationsForCacheManager(locations);

const { memoryCacheMock, localStorageMock, geocoderMock } = setUpMocks(0, 0);
const cacheManager = new CacheManager(new MockIStorageV2Service(), memoryCacheMock, localStorageMock, geocoderMock);

const result = await cacheManager.loadCoordinates(locationsNeedToBeLoaded);
expect(geocoderMock.geocode).toHaveBeenCalled();
expect(Object.keys(result).length).toBe(Object.keys(locationsNeedToBeLoaded).length);
});

it("cacheManager should load coordinates for all input, when memory cache is empty and local storage has half of data", async () => {
const locations = Object.assign(defaultDataViewBuilder.valuesSourceDestination);
const locationsNeedToBeLoaded = convertLocationsForCacheManager(locations);

const { memoryCacheMock, localStorageMock, geocoderMock } = setUpMocks(0, 10);
const cacheManager = new CacheManager(new MockIStorageV2Service(), memoryCacheMock, localStorageMock, geocoderMock);

const result = await cacheManager.loadCoordinates(locationsNeedToBeLoaded);
expect(geocoderMock.geocode).toHaveBeenCalled();
expect(Object.keys(result).length).toBe(Object.keys(locationsNeedToBeLoaded).length);
});

it("cacheManager should load coordinates for all input, when memory cache has half of data and local storage is empty", async () => {
const locations = Object.assign(defaultDataViewBuilder.valuesSourceDestination);
const locationsNeedToBeLoaded = convertLocationsForCacheManager(locations);

const { memoryCacheMock, localStorageMock, geocoderMock } = setUpMocks(10, 0);
const cacheManager = new CacheManager(new MockIStorageV2Service(), memoryCacheMock, localStorageMock, geocoderMock);

const result = await cacheManager.loadCoordinates(locationsNeedToBeLoaded);
expect(geocoderMock.geocode).toHaveBeenCalled();
expect(Object.keys(result).length).toBe(Object.keys(locationsNeedToBeLoaded).length);
});

it("cacheManager should load coordinates for all input, when memory cache has 1/3 of data and local storage has 1/3 of data", async () => {
const locations = Object.assign(defaultDataViewBuilder.valuesSourceDestination);
const locationsNeedToBeLoaded = convertLocationsForCacheManager(locations);

const { memoryCacheMock, localStorageMock, geocoderMock } = setUpMocks(5, 5);
const cacheManager = new CacheManager(new MockIStorageV2Service(), memoryCacheMock, localStorageMock, geocoderMock);

const result = await cacheManager.loadCoordinates(locationsNeedToBeLoaded);
expect(geocoderMock.geocode).toHaveBeenCalled();
expect(Object.keys(result).length).toBe(Object.keys(locationsNeedToBeLoaded).length);
});

it("cacheManager should load coordinates for all input, when memory cache has 1/2 of data and local storage has 1/2 of data", async () => {
const locations = Object.assign(defaultDataViewBuilder.valuesSourceDestination);
const locationsNeedToBeLoaded = convertLocationsForCacheManager(locations);

const { memoryCacheMock, localStorageMock, geocoderMock } = setUpMocks(10, 10);
const cacheManager = new CacheManager(new MockIStorageV2Service(), memoryCacheMock, localStorageMock, geocoderMock);

const result = await cacheManager.loadCoordinates(locationsNeedToBeLoaded);
expect(geocoderMock.geocode).not.toHaveBeenCalled();
expect(Object.keys(result).length).toBe(Object.keys(locationsNeedToBeLoaded).length);
});

function setUpMocks(locationsInMemoryCache: number, locationsInLocalStorageCache: number) {
const fullResult = defaultDataViewBuilder.coordinatesMock;

const locationsFromMemoryCache: ILocationDictionary = {};
const locationsFromLocalStorageCache: ILocationDictionary = {};
const locationsFromBing: ILocationDictionary = {};
let counter = 0;
for (let key in fullResult) {
if (counter < locationsInMemoryCache){
locationsFromMemoryCache[key] = fullResult[key];
}
else if (counter < locationsInLocalStorageCache + locationsInMemoryCache){
locationsFromLocalStorageCache[key] = fullResult[key];
}
else {
locationsFromBing[key] = fullResult[key];
}
counter++;
}

// mock for local storage
const localStorageMock = new LocalStorageCache(new MockIStorageV2Service());
const locationsInPromise = new Promise<ILocationDictionary>((resolve)=> resolve(locationsFromLocalStorageCache));
spyOn(localStorageMock, 'loadCoordinates').and.returnValue(locationsInPromise);

// mock for MemoryCache
const memoryCacheMock = new MemoryCache(CacheSettings.MaxCacheSize, CacheSettings.MaxCacheSizeOverflow);
spyOn(memoryCacheMock, 'loadCoordinates').and.returnValue(locationsFromMemoryCache);

// mock for geocoder
const geocoderMock = new BingGeocoder();
const locationsGeocoderInPromise = new Promise<ILocationDictionary>((resolve)=> resolve(locationsFromBing));
spyOn(geocoderMock, 'geocode').and.returnValue(locationsGeocoderInPromise);

return {memoryCacheMock, localStorageMock, geocoderMock};
}
});

describe("OnObject tests", () => {
beforeAll((done) => {
const coordinates = defaultDataViewBuilder.getCoordinatesMock();
const coordinates = defaultDataViewBuilder.coordinatesMock;
if (Object.keys(coordinates).length > 0) {
visualInstance.cacheManager.saveCoordinates(coordinates)
.then(() => {
Expand Down Expand Up @@ -739,4 +843,16 @@ describe("GlobeMap", () => {
}, powerbi.VisualUpdateType.Data, true, 1500, subselectionsFromContextMenu);
});
});

function convertLocationsForCacheManager(locations: string[]): ILocationKeyDictionary{
const locationsNeedToBeLoaded = {} as ILocationKeyDictionary;
locations.forEach((locationName) => {
const name = (locationName as string).toLowerCase();
locationsNeedToBeLoaded[name] = {
place: name,
locationType: ""
};
});
return locationsNeedToBeLoaded;
}
});

0 comments on commit 6bda29d

Please sign in to comment.