mirror of
https://github.com/mapillary/mapillary-js.git
synced 2025-12-08 17:35:55 +00:00
629 lines
20 KiB
TypeScript
629 lines
20 KiB
TypeScript
import { Subject } from "rxjs";
|
|
|
|
import { Image } from "../../../src/graph/Image";
|
|
import { ClusterContract } from "../../../src/api/contracts/ClusterContract";
|
|
import { SpatialCache } from "../../../src/component/spatial/SpatialCache";
|
|
import { GraphService } from "../../../src/graph/GraphService";
|
|
import { GraphServiceMockCreator } from "../../helper/GraphServiceMockCreator";
|
|
import { ImageHelper } from "../../helper/ImageHelper";
|
|
import { DataProvider } from "../../helper/ProviderHelper";
|
|
import { APIWrapper } from "../../../src/api/APIWrapper";
|
|
|
|
const cacheTile: (
|
|
hash: string,
|
|
cache: SpatialCache,
|
|
graphService: GraphService,
|
|
images: Image[]) => void = (
|
|
hash: string,
|
|
cache: SpatialCache,
|
|
graphService: GraphService,
|
|
images: Image[]): void => {
|
|
|
|
const cacheCell$: Subject<Image[]> = new Subject<Image[]>();
|
|
const cacheCellSpy: jasmine.Spy = <jasmine.Spy>graphService.cacheCell$;
|
|
cacheCellSpy.and.returnValue(cacheCell$);
|
|
|
|
cache.cacheCell$(hash)
|
|
.subscribe();
|
|
|
|
cacheCell$.next(images);
|
|
|
|
expect(cache.hasCell(hash)).toBe(true);
|
|
};
|
|
|
|
describe("SpatialCache.ctor", () => {
|
|
it("should be defined", () => {
|
|
const cache: SpatialCache =
|
|
new SpatialCache(
|
|
new GraphServiceMockCreator().create(),
|
|
undefined);
|
|
|
|
expect(cache).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe("SpatialCache.cacheTile$", () => {
|
|
it("should call cache bounding box", () => {
|
|
const graphService: GraphService = new GraphServiceMockCreator().create();
|
|
const cacheCell$: Subject<Image[]> = new Subject<Image[]>();
|
|
const cacheCellSpy: jasmine.Spy = <jasmine.Spy>graphService.cacheCell$;
|
|
cacheCellSpy.and.returnValue(cacheCell$);
|
|
|
|
const dataProvider = new DataProvider();
|
|
const api = new APIWrapper(dataProvider);
|
|
const cache: SpatialCache = new SpatialCache(
|
|
graphService, api);
|
|
|
|
const hash: string = "12345678";
|
|
cache.cacheCell$(hash);
|
|
|
|
expect(cacheCellSpy.calls.count()).toBe(1);
|
|
expect(cacheCellSpy.calls.first().args[0]).toBe(hash);
|
|
});
|
|
|
|
it("should be caching tile", () => {
|
|
const graphService: GraphService = new GraphServiceMockCreator().create();
|
|
const cacheCell$: Subject<Image[]> = new Subject<Image[]>();
|
|
const cacheCellSpy: jasmine.Spy = <jasmine.Spy>graphService.cacheCell$;
|
|
cacheCellSpy.and.returnValue(cacheCell$);
|
|
|
|
const dataProvider = new DataProvider();
|
|
const api = new APIWrapper(dataProvider);
|
|
const cache: SpatialCache = new SpatialCache(
|
|
graphService, api);
|
|
|
|
const hash: string = "00000000";
|
|
|
|
expect(cache.isCachingCell(hash)).toBe(false);
|
|
|
|
cache.cacheCell$(hash);
|
|
|
|
expect(cache.isCachingCell(hash)).toBe(true);
|
|
});
|
|
|
|
it("should cache tile", () => {
|
|
const graphService: GraphService = new GraphServiceMockCreator().create();
|
|
const cacheCell$: Subject<Image[]> = new Subject<Image[]>();
|
|
const cacheCellSpy: jasmine.Spy = <jasmine.Spy>graphService.cacheCell$;
|
|
cacheCellSpy.and.returnValue(cacheCell$);
|
|
|
|
const dataProvider = new DataProvider();
|
|
const api = new APIWrapper(dataProvider);
|
|
const cache: SpatialCache = new SpatialCache(
|
|
graphService, api);
|
|
|
|
const hash: string = "00000000";
|
|
|
|
expect(cache.hasCell(hash)).toBe(false);
|
|
|
|
cache.cacheCell$(hash)
|
|
.subscribe();
|
|
|
|
const image: Image = new ImageHelper().createImage();
|
|
cacheCell$.next([image]);
|
|
|
|
expect(cache.isCachingCell(hash)).toBe(false);
|
|
expect(cache.hasCell(hash)).toBe(true);
|
|
expect(cache.getCell(hash).length).toBe(1);
|
|
expect(cache.getCell(hash)[0].id).toBe(image.id);
|
|
});
|
|
|
|
it("should catch error", (done: Function) => {
|
|
spyOn(console, "error").and.stub();
|
|
|
|
const graphService: GraphService = new GraphServiceMockCreator().create();
|
|
const cacheCell$: Subject<Image[]> = new Subject<Image[]>();
|
|
const cacheCellSpy: jasmine.Spy = <jasmine.Spy>graphService.cacheCell$;
|
|
cacheCellSpy.and.returnValue(cacheCell$);
|
|
|
|
const dataProvider = new DataProvider();
|
|
const api = new APIWrapper(dataProvider);
|
|
const cache: SpatialCache = new SpatialCache(
|
|
graphService, api);
|
|
|
|
const hash: string = "00000000";
|
|
|
|
cache.cacheCell$(hash)
|
|
.subscribe();
|
|
|
|
cacheCell$.error(new Error());
|
|
|
|
expect(cache.isCachingCell(hash)).toBe(false);
|
|
expect(cache.hasCell(hash)).toBe(false);
|
|
|
|
let tileEmitCount: number = 0;
|
|
cache.cacheCell$(hash)
|
|
.subscribe(
|
|
(): void => {
|
|
tileEmitCount++;
|
|
},
|
|
undefined,
|
|
(): void => {
|
|
expect(tileEmitCount).toBe(0);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("SpatialCache.cacheReconstructions$", () => {
|
|
it("should cache a reconstruction", (done: Function) => {
|
|
const image: Image = new ImageHelper().createImage();
|
|
const hash: string = "00000000";
|
|
|
|
let resolver: Function;
|
|
const promise: any = {
|
|
then: (resolve: (result: any) => void): void => {
|
|
resolver = resolve;
|
|
},
|
|
};
|
|
|
|
const dataProvider = new DataProvider();
|
|
spyOn(dataProvider, "getCluster").and.returnValue(promise);
|
|
|
|
const graphService: GraphService = new GraphServiceMockCreator().create();
|
|
const api = new APIWrapper(dataProvider);
|
|
const cache: SpatialCache = new SpatialCache(graphService, api);
|
|
|
|
cacheTile(hash, cache, graphService, [image]);
|
|
|
|
let emitCount: number = 0;
|
|
cache.cacheClusters$(hash)
|
|
.subscribe(
|
|
(r: ClusterContract): void => {
|
|
expect(r.id).toBe(image.clusterId);
|
|
emitCount++;
|
|
},
|
|
undefined,
|
|
(): void => {
|
|
expect(emitCount).toBe(1);
|
|
expect(cache.hasClusters(hash)).toBe(true);
|
|
done();
|
|
});
|
|
|
|
resolver({ key: image.clusterId });
|
|
});
|
|
|
|
it("should not have an errored reconstruction", (done: Function) => {
|
|
spyOn(console, "error").and.stub();
|
|
|
|
const image: Image = new ImageHelper().createImage();
|
|
const hash: string = "00000000";
|
|
|
|
let rejecter: Function;
|
|
const promise: any = {
|
|
then: (_: (result: any) => void, reject: (error: Error) => void): void => {
|
|
rejecter = reject;
|
|
},
|
|
};
|
|
|
|
const dataProvider = new DataProvider();
|
|
spyOn(dataProvider, "getCluster").and.returnValue(promise);
|
|
|
|
const graphService: GraphService = new GraphServiceMockCreator().create();
|
|
const api = new APIWrapper(dataProvider);
|
|
const cache: SpatialCache = new SpatialCache(graphService, api);
|
|
|
|
cacheTile(hash, cache, graphService, [image]);
|
|
|
|
let emitCount: number = 0;
|
|
cache.cacheClusters$(hash)
|
|
.subscribe(
|
|
(): void => {
|
|
emitCount++;
|
|
},
|
|
undefined,
|
|
(): void => {
|
|
expect(emitCount).toBe(0);
|
|
expect(cache.hasClusters(hash)).toBe(false);
|
|
expect(cache.getClusters(hash).length).toBe(0);
|
|
done();
|
|
});
|
|
|
|
rejecter(new Error("reject"));
|
|
});
|
|
|
|
it("should abort on uncache", (done: Function) => {
|
|
const image: Image = new ImageHelper().createImage();
|
|
const hash: string = "00000000";
|
|
|
|
const promise: any = {
|
|
then: (): void => { /*noop*/ },
|
|
};
|
|
|
|
const dataProvider = new DataProvider();
|
|
const clusterSpy: jasmine.Spy = spyOn(dataProvider, "getCluster");
|
|
clusterSpy.and.returnValue(promise);
|
|
|
|
const graphService: GraphService = new GraphServiceMockCreator().create();
|
|
const api = new APIWrapper(dataProvider);
|
|
const cache: SpatialCache = new SpatialCache(graphService, api);
|
|
|
|
cacheTile(hash, cache, graphService, [image]);
|
|
|
|
cache.cacheClusters$(hash)
|
|
.subscribe();
|
|
|
|
expect(clusterSpy.calls.count()).toBe(1);
|
|
const abort: Promise<void> = clusterSpy.calls.mostRecent().args[1];
|
|
|
|
abort.catch(
|
|
(): void => {
|
|
done();
|
|
});
|
|
|
|
cache.uncache();
|
|
});
|
|
|
|
it("should only request reconstruction once if called twice before completing", () => {
|
|
const image: Image = new ImageHelper().createImage();
|
|
const hash: string = "00000000";
|
|
|
|
let resolver: Function;
|
|
const promise: any = {
|
|
then: (resolve: (value: ClusterContract) => void): void => {
|
|
resolver = resolve;
|
|
},
|
|
};
|
|
|
|
const dataProvider = new DataProvider();
|
|
const clusterSpy: jasmine.Spy = spyOn(dataProvider, "getCluster");
|
|
clusterSpy.and.returnValue(promise);
|
|
|
|
const graphService: GraphService = new GraphServiceMockCreator().create();
|
|
const api = new APIWrapper(dataProvider);
|
|
const cache: SpatialCache = new SpatialCache(graphService, api);
|
|
|
|
cacheTile(hash, cache, graphService, [image]);
|
|
|
|
let emitCount1: number = 0;
|
|
cache.cacheClusters$(hash)
|
|
.subscribe(
|
|
(): void => {
|
|
emitCount1++;
|
|
});
|
|
|
|
expect(clusterSpy.calls.count()).toBe(1);
|
|
|
|
let emitCount2: number = 0;
|
|
cache.cacheClusters$(hash)
|
|
.subscribe(
|
|
(): void => {
|
|
emitCount2++;
|
|
});
|
|
|
|
expect(emitCount1).toBe(0);
|
|
expect(emitCount2).toBe(0);
|
|
|
|
resolver({
|
|
key: image.clusterId,
|
|
points: [],
|
|
refererence_lla: { altitude: 0, latitude: 0, longitude: 0 },
|
|
});
|
|
|
|
expect(emitCount1).toBe(1);
|
|
expect(emitCount2).toBe(1);
|
|
|
|
expect(clusterSpy.calls.count()).toBe(1);
|
|
|
|
expect(cache.isCachingClusters(hash)).toBe(false);
|
|
expect(cache.hasClusters(hash)).toBe(true);
|
|
expect(cache.getClusters(hash).length).toBe(1);
|
|
expect(cache.getClusters(hash)[0].id).toBe(image.clusterId);
|
|
});
|
|
});
|
|
|
|
describe("SpatialCache.updateCell$", () => {
|
|
it("should throw if cell does not exist", () => {
|
|
const graphService = new GraphServiceMockCreator().create();
|
|
const cacheCell$ = new Subject<Image[]>();
|
|
const cacheCellSpy = <jasmine.Spy>graphService.cacheCell$;
|
|
cacheCellSpy.and.returnValue(cacheCell$);
|
|
|
|
|
|
const dataProvider = new DataProvider();
|
|
const api = new APIWrapper(dataProvider);
|
|
const cache: SpatialCache = new SpatialCache(
|
|
graphService, api);
|
|
|
|
const cellId = "1";
|
|
|
|
expect(() => cache.updateCell$(cellId)).toThrowError(Error);
|
|
});
|
|
|
|
it("should call to update cell", () => {
|
|
const graphService: GraphService = new GraphServiceMockCreator().create();
|
|
const cacheCell1$: Subject<Image[]> = new Subject<Image[]>();
|
|
const cacheCellSpy: jasmine.Spy = <jasmine.Spy>graphService.cacheCell$;
|
|
cacheCellSpy.and.returnValue(cacheCell1$);
|
|
|
|
const dataProvider = new DataProvider();
|
|
const api = new APIWrapper(dataProvider);
|
|
const cache: SpatialCache = new SpatialCache(
|
|
graphService, api);
|
|
|
|
const cellId = "1";
|
|
cache.cacheCell$(cellId).subscribe();
|
|
|
|
const image = new ImageHelper().createImage();
|
|
cacheCell1$.next([image]);
|
|
cacheCell1$.complete();
|
|
|
|
expect(cache.hasCell(cellId)).toBe(true);
|
|
|
|
const cacheCell2$: Subject<Image[]> = new Subject<Image[]>();
|
|
cacheCellSpy.and.returnValue(cacheCell2$);
|
|
|
|
cache.updateCell$(cellId).subscribe();
|
|
|
|
cacheCell2$.next([image]);
|
|
|
|
expect(cache.hasCell(cellId)).toBe(true);
|
|
expect(cacheCellSpy.calls.count()).toBe(2);
|
|
expect(cache.getCell(cellId).length).toBe(1);
|
|
expect(cache.getCell(cellId)[0].id).toBe(image.id);
|
|
});
|
|
|
|
it("should add new images to cell", () => {
|
|
const graphService: GraphService = new GraphServiceMockCreator().create();
|
|
const cacheCell1$: Subject<Image[]> = new Subject<Image[]>();
|
|
const cacheCellSpy: jasmine.Spy = <jasmine.Spy>graphService.cacheCell$;
|
|
cacheCellSpy.and.returnValue(cacheCell1$);
|
|
|
|
const dataProvider = new DataProvider();
|
|
const api = new APIWrapper(dataProvider);
|
|
const cache: SpatialCache = new SpatialCache(
|
|
graphService, api);
|
|
|
|
const cellId = "1";
|
|
cache.cacheCell$(cellId).subscribe();
|
|
|
|
cacheCell1$.next([]);
|
|
cacheCell1$.complete();
|
|
|
|
expect(cache.hasCell(cellId)).toBe(true);
|
|
expect(cache.getCell(cellId).length).toBe(0);
|
|
|
|
const cacheCell2$: Subject<Image[]> = new Subject<Image[]>();
|
|
cacheCellSpy.and.returnValue(cacheCell2$);
|
|
|
|
cache.updateCell$(cellId).subscribe();
|
|
|
|
const image = new ImageHelper().createImage();
|
|
cacheCell2$.next([image]);
|
|
|
|
expect(cache.getCell(cellId).length).toBe(1);
|
|
expect(cache.getCell(cellId)[0].id).toBe(image.id);
|
|
});
|
|
});
|
|
|
|
describe("SpatialCache.updateReconstructions$", () => {
|
|
const createCluster = (key: string): ClusterContract => {
|
|
return {
|
|
colors: [],
|
|
coordinates: [],
|
|
id: key,
|
|
pointIds: [],
|
|
reference: { lat: 0, lng: 0, alt: 0 },
|
|
rotation: [0, 0, 0]
|
|
};
|
|
};
|
|
|
|
it("should throw when tile not cached", () => {
|
|
const dataProvider = new DataProvider();
|
|
spyOn(dataProvider, "getCluster").and
|
|
.returnValue(
|
|
new Promise<ClusterContract>(() => { /* noop */ }));
|
|
|
|
const graphService = new GraphServiceMockCreator().create();
|
|
const api = new APIWrapper(dataProvider);
|
|
const cache = new SpatialCache(graphService, api);
|
|
|
|
expect(() => cache.updateClusters$("123")).toThrowError();
|
|
});
|
|
|
|
it("should not request an existing reconstruction", done => {
|
|
const image = new ImageHelper().createImage();
|
|
const cellId = "123";
|
|
|
|
let resolver: Function;
|
|
const promise: any = {
|
|
then: (resolve: (value: ClusterContract) => void): void => {
|
|
resolver = resolve;
|
|
},
|
|
};
|
|
|
|
const dataProvider = new DataProvider();
|
|
const clusterSpy = spyOn(dataProvider, "getCluster").and
|
|
.returnValue(promise);
|
|
|
|
const graphService = new GraphServiceMockCreator().create();
|
|
const api = new APIWrapper(dataProvider);
|
|
const cache = new SpatialCache(graphService, api);
|
|
|
|
cacheTile(cellId, cache, graphService, [image]);
|
|
|
|
cache.cacheClusters$(cellId).subscribe();
|
|
|
|
const cluster = createCluster(image.clusterId);
|
|
resolver(cluster);
|
|
|
|
expect(cache.hasClusters(cellId)).toBe(true);
|
|
expect(clusterSpy.calls.count()).toBe(1);
|
|
|
|
cache.updateClusters$("123")
|
|
.subscribe(
|
|
undefined,
|
|
undefined,
|
|
() => {
|
|
expect(cache.hasClusters(cellId)).toBe(true);
|
|
expect(clusterSpy.calls.count()).toBe(1);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("should request a new cluster reconstruction", done => {
|
|
const cellId = "123";
|
|
|
|
let resolver: Function;
|
|
const promise: any = {
|
|
then: (resolve: (value: ClusterContract) => void): void => {
|
|
resolver = resolve;
|
|
},
|
|
};
|
|
|
|
const dataProvider = new DataProvider();
|
|
const clusterSpy = spyOn(dataProvider, "getCluster").and
|
|
.returnValue(promise);
|
|
|
|
const graphService = new GraphServiceMockCreator().create();
|
|
const api = new APIWrapper(dataProvider);
|
|
const cache = new SpatialCache(graphService, api);
|
|
|
|
cacheTile(cellId, cache, graphService, []);
|
|
|
|
expect(cache.getCell(cellId).length).toBe(0);
|
|
|
|
cache.cacheClusters$(cellId).subscribe();
|
|
|
|
expect(cache.hasClusters(cellId)).toBe(true);
|
|
expect(cache.getClusters(cellId).length).toBe(0);
|
|
expect(clusterSpy.calls.count()).toBe(0);
|
|
|
|
const cacheCell$ = new Subject<Image[]>();
|
|
const cacheCellSpy = <jasmine.Spy>graphService.cacheCell$;
|
|
cacheCellSpy.and.returnValue(cacheCell$);
|
|
|
|
cache.updateCell$(cellId)
|
|
.subscribe();
|
|
|
|
const image = new ImageHelper().createImage();
|
|
cacheCell$.next([image]);
|
|
|
|
expect(cache.getCell(cellId).length).toBe(1);
|
|
|
|
cache.updateClusters$("123")
|
|
.subscribe(
|
|
undefined,
|
|
undefined,
|
|
() => {
|
|
expect(cache.hasClusters(cellId)).toBe(true);
|
|
const cs = cache.getClusters(cellId);
|
|
expect(cs.length).toBe(1);
|
|
expect(cs[0].id).toBe(image.clusterId);
|
|
expect(clusterSpy.calls.count()).toBe(1);
|
|
done();
|
|
});
|
|
|
|
const cluster = createCluster(image.clusterId);
|
|
resolver(cluster);
|
|
});
|
|
});
|
|
|
|
describe("SpatialCache.removeCluster", () => {
|
|
const createCluster = (id: string): ClusterContract => {
|
|
return {
|
|
colors: [],
|
|
coordinates: [],
|
|
id,
|
|
pointIds: [],
|
|
reference: { lat: 0, lng: 0, alt: 0 },
|
|
rotation: [0, 0, 0]
|
|
};
|
|
};
|
|
|
|
it("should have cell and cell clusters after remove", () => {
|
|
const image = new ImageHelper().createImage();
|
|
const cellId = "123";
|
|
|
|
let resolver: Function;
|
|
const promise: any = {
|
|
then: (resolve: (value: ClusterContract) => void): void => {
|
|
resolver = resolve;
|
|
},
|
|
};
|
|
|
|
const dataProvider = new DataProvider();
|
|
spyOn(dataProvider, "getCluster").and.returnValue(promise);
|
|
const graphService = new GraphServiceMockCreator().create();
|
|
const api = new APIWrapper(dataProvider);
|
|
const cache = new SpatialCache(graphService, api);
|
|
|
|
cacheTile(cellId, cache, graphService, [image]);
|
|
|
|
cache.cacheClusters$(cellId).subscribe();
|
|
|
|
const cluster = createCluster(image.clusterId);
|
|
resolver(cluster);
|
|
|
|
expect(cache.hasCell(cellId)).toBe(true);
|
|
expect(cache.hasClusters(cellId)).toBe(true);
|
|
|
|
cache.removeCluster(cluster.id);
|
|
|
|
expect(cache.hasCell(cellId)).toBe(true);
|
|
expect(cache.hasClusters(cellId)).toBe(true);
|
|
});
|
|
|
|
it("should remove cluster", () => {
|
|
const image = new ImageHelper().createImage();
|
|
const cellId = "123";
|
|
|
|
let resolver: Function;
|
|
const promise: any = {
|
|
then: (resolve: (value: ClusterContract) => void): void => {
|
|
resolver = resolve;
|
|
},
|
|
};
|
|
|
|
const dataProvider = new DataProvider();
|
|
spyOn(dataProvider, "getCluster").and.returnValue(promise);
|
|
const graphService = new GraphServiceMockCreator().create();
|
|
const api = new APIWrapper(dataProvider);
|
|
const cache = new SpatialCache(graphService, api);
|
|
|
|
cacheTile(cellId, cache, graphService, [image]);
|
|
|
|
cache.cacheClusters$(cellId).subscribe();
|
|
|
|
const cluster = createCluster(image.clusterId);
|
|
resolver(cluster);
|
|
|
|
expect(cache.getClusters(cellId).length).toBe(1);
|
|
|
|
cache.removeCluster(cluster.id);
|
|
|
|
expect(cache.getClusters(cellId).length).toBe(0);
|
|
});
|
|
|
|
it("should remove images", () => {
|
|
const image = new ImageHelper().createImage();
|
|
const cellId = "123";
|
|
|
|
let resolver: Function;
|
|
const promise: any = {
|
|
then: (resolve: (value: ClusterContract) => void): void => {
|
|
resolver = resolve;
|
|
},
|
|
};
|
|
|
|
const dataProvider = new DataProvider();
|
|
spyOn(dataProvider, "getCluster").and.returnValue(promise);
|
|
const graphService = new GraphServiceMockCreator().create();
|
|
const api = new APIWrapper(dataProvider);
|
|
const cache = new SpatialCache(graphService, api);
|
|
|
|
cacheTile(cellId, cache, graphService, [image]);
|
|
|
|
cache.cacheClusters$(cellId).subscribe();
|
|
|
|
const cluster = createCluster(image.clusterId);
|
|
resolver(cluster);
|
|
|
|
expect(cache.getCell(cellId).length).toBe(1);
|
|
|
|
cache.removeCluster(cluster.id);
|
|
|
|
expect(cache.getCell(cellId).length).toBe(0);
|
|
});
|
|
});
|