提交 d85d7770 编写于 作者: B Boris Sekachev 提交者: Nikita Manovich

CVAT.js API Tests (#578)

* Added annotations dummy data

* Added test file

* Weak maps instead of regular dict

* Added 14 API tests

* Fixed put tests

* Some written tests and some additional checks

* Merge tests

* Split & group tests

* Clear annotations tests

* Statistics tests

* Added frames meta

* Selection tests & bug fixes

* Tests for frame

* ObjectState tests, many fixed bugs

* Object state tests

* Renamed method FrameData.frame() => FrameData.data()
上级 d939b9c3
......@@ -104,8 +104,12 @@
}
class Collection {
constructor(labels) {
this.labels = labels.reduce((labelAccumulator, label) => {
constructor(data) {
this.startFrame = data.startFrame;
this.stopFrame = data.stopFrame;
this.frameMeta = data.frameMeta;
this.labels = data.labels.reduce((labelAccumulator, label) => {
labelAccumulator[label.id] = label;
return labelAccumulator;
}, {});
......@@ -124,6 +128,7 @@
labels: this.labels,
collectionZ: this.collectionZ,
groups: this.groups,
frameMeta: this.frameMeta,
};
}
......@@ -208,7 +213,7 @@
const object = this.objects[state.clientID];
if (typeof (object) === 'undefined') {
throw new window.cvat.exceptions.ArgumentError(
'The object has not been saved yet. Call ObjectState.save() before you can merge it',
'The object has not been saved yet. Call ObjectState.put([state]) before you can merge it',
);
}
return object;
......@@ -216,6 +221,18 @@
const keyframes = {}; // frame: position
const { label, shapeType } = objectStates[0];
if (!(label.id in this.labels)) {
throw new window.cvat.exceptions.ArgumentError(
`Unknown label for the task: ${label.id}`,
);
}
if (!Object.values(window.cvat.enums.ObjectShape).includes(shapeType)) {
throw new window.cvat.exceptions.ArgumentError(
`Got unknown shapeType "${shapeType}"`,
);
}
const labelAttributes = label.attributes.reduce((accumulator, attribute) => {
accumulator[attribute.id] = attribute;
return accumulator;
......@@ -365,7 +382,9 @@
// Remove other shapes
for (const object of objectsForMerge) {
object.removed = true;
object.resetCache();
if (typeof (object.resetCache) === 'function') {
object.resetCache();
}
}
}
......@@ -376,7 +395,7 @@
const object = this.objects[objectState.clientID];
if (typeof (object) === 'undefined') {
throw new window.cvat.exceptions.ArgumentError(
'The object has not been saved yet. Call annotations.put(state) before',
'The object has not been saved yet. Call annotations.put([state]) before',
);
}
......@@ -385,7 +404,7 @@
}
const keyframes = Object.keys(object.shapes).sort((a, b) => +a - +b);
if (frame <= +keyframes[0]) {
if (frame <= +keyframes[0] || frame > keyframes[keyframes.length - 1]) {
return;
}
......@@ -463,7 +482,7 @@
const object = this.objects[state.clientID];
if (typeof (object) === 'undefined') {
throw new window.cvat.exceptions.ArgumentError(
'The object has not been saved yet. Call annotations.put(state) before',
'The object has not been saved yet. Call annotations.put([state]) before',
);
}
return object;
......@@ -472,8 +491,12 @@
const groupIdx = reset ? 0 : ++this.groups.max;
for (const object of objectsForGroup) {
object.group = groupIdx;
object.resetCache();
if (typeof (object.resetCache) === 'function') {
object.resetCache();
}
}
return groupIdx;
}
clear() {
......@@ -526,7 +549,7 @@
} else if (object instanceof Tag) {
objectType = 'tag';
} else {
throw window.cvat.exceptions.ScriptingError(
throw new window.cvat.exceptions.ScriptingError(
`Unexpected object type: "${objectType}"`,
);
}
......@@ -543,6 +566,7 @@
if (objectType === 'track') {
const keyframes = Object.keys(object.shapes)
.sort((a, b) => +a - +b).map(el => +el);
let prevKeyframe = keyframes[0];
let visible = false;
......@@ -560,6 +584,13 @@
labels[label].total++;
}
}
const lastKey = keyframes[keyframes.length - 1];
if (lastKey !== this.stopFrame && !object.shapes[lastKey].outside) {
const interpolated = this.stopFrame - lastKey;
labels[label].interpolated += interpolated;
labels[label].total += interpolated;
}
} else {
labels[label].manually++;
labels[label].total++;
......@@ -648,7 +679,7 @@
frame: state.frame,
group: 0,
label_id: state.label.id,
occluded: state.occluded,
occluded: state.occluded || false,
points: [...state.points],
type: state.shapeType,
z_order: 0,
......@@ -664,7 +695,7 @@
attributes: attributes
.filter(attr => labelAttributes[attr.spec_id].mutable),
frame: state.frame,
occluded: state.occluded,
occluded: state.occluded || false,
outside: false,
points: [...state.points],
type: state.shapeType,
......@@ -698,7 +729,7 @@
const object = this.objects[state.clientID];
if (typeof (object) === 'undefined') {
throw new window.cvat.exceptions.ArgumentError(
'The object has not been saved yet. Call annotations.put(state) before',
'The object has not been saved yet. Call annotations.put([state]) before',
);
}
......
此差异已折叠。
......@@ -102,7 +102,7 @@
} else if (typeof (object.id) === 'undefined') {
splitted.created[type].push(object);
} else {
throw window.cvat.exceptions.ScriptingError(
throw new window.cvat.exceptions.ScriptingError(
`Id of object is defined "${object.id}"`
+ 'but it absents in initial state',
);
......@@ -140,7 +140,7 @@
+ indexes.shapes.length + indexes.tags.length;
if (indexesLength !== savedLength) {
throw window.cvat.exception.ScriptingError(
throw new window.cvat.exception.ScriptingError(
'Number of indexes is differed by number of saved objects'
+ `${indexesLength} vs ${savedLength}`,
);
......
......@@ -11,9 +11,10 @@
const serverProxy = require('./server-proxy');
const Collection = require('./annotations-collection');
const AnnotationsSaver = require('./annotations-saver');
const { checkObjectType } = require('./common');
const jobCache = {};
const taskCache = {};
const jobCache = new WeakMap();
const taskCache = new WeakMap();
function getCache(sessionType) {
if (sessionType === 'task') {
......@@ -33,17 +34,32 @@
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
if (!(session.id in cache)) {
if (!cache.has(session)) {
const rawAnnotations = await serverProxy.annotations
.getAnnotations(sessionType, session.id);
const collection = new Collection(session.labels || session.task.labels)
.import(rawAnnotations);
// Get meta information about frames
const startFrame = sessionType === 'job' ? session.startFrame : 0;
const stopFrame = sessionType === 'job' ? session.stopFrame : session.size - 1;
const frameMeta = {};
for (let i = startFrame; i <= stopFrame; i++) {
frameMeta[i] = await session.frames.get(i);
}
const collection = new Collection({
labels: session.labels || session.task.labels,
startFrame,
stopFrame,
frameMeta,
}).import(rawAnnotations);
const saver = new AnnotationsSaver(rawAnnotations.version, collection, session);
cache[session.id] = {
cache.set(session, {
collection,
saver,
};
});
}
}
......@@ -51,15 +67,15 @@
await getAnnotationsFromServer(session);
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
return cache[session.id].collection.get(frame, filter);
return cache.get(session).collection.get(frame, filter);
}
async function saveAnnotations(session, onUpdate) {
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
if (session.id in cache) {
await cache[session.id].saver.save(onUpdate);
if (cache.has(session)) {
await cache.get(session).saver.save(onUpdate);
}
// If a collection wasn't uploaded, than it wasn't changed, finally we shouldn't save it
......@@ -69,11 +85,11 @@
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
if (session.id in cache) {
return cache[session.id].collection.merge(objectStates);
if (cache.has(session)) {
return cache.get(session).collection.merge(objectStates);
}
throw window.cvat.exceptions.DataError(
throw new window.cvat.exceptions.DataError(
'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
);
}
......@@ -82,11 +98,11 @@
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
if (session.id in cache) {
return cache[session.id].collection.split(objectState, frame);
if (cache.has(session)) {
return cache.get(session).collection.split(objectState, frame);
}
throw window.cvat.exceptions.DataError(
throw new window.cvat.exceptions.DataError(
'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
);
}
......@@ -95,11 +111,11 @@
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
if (session.id in cache) {
return cache[session.id].collection.group(objectStates, reset);
if (cache.has(session)) {
return cache.get(session).collection.group(objectStates, reset);
}
throw window.cvat.exceptions.DataError(
throw new window.cvat.exceptions.DataError(
'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
);
}
......@@ -108,23 +124,24 @@
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
if (session.id in cache) {
return cache[session.id].saver.hasUnsavedChanges();
if (cache.has(session)) {
return cache.get(session).saver.hasUnsavedChanges();
}
return false;
}
async function clearAnnotations(session, reload) {
checkObjectType('reload', reload, 'boolean', null);
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
if (session.id in cache) {
cache[session.id].collection.clear();
if (cache.has(session)) {
cache.get(session).collection.clear();
}
if (reload) {
delete cache[session.id];
cache.delete(session);
await getAnnotationsFromServer(session);
}
}
......@@ -133,11 +150,11 @@
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
if (session.id in cache) {
return cache[session.id].collection.statistics();
if (cache.has(session)) {
return cache.get(session).collection.statistics();
}
throw window.cvat.exceptions.DataError(
throw new window.cvat.exceptions.DataError(
'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
);
}
......@@ -146,11 +163,11 @@
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
if (session.id in cache) {
return cache[session.id].collection.put(objectStates);
if (cache.has(session)) {
return cache.get(session).collection.put(objectStates);
}
throw window.cvat.exceptions.DataError(
throw new window.cvat.exceptions.DataError(
'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
);
}
......@@ -159,11 +176,11 @@
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
if (session.id in cache) {
return cache[session.id].collection.select(objectStates, x, y);
if (cache.has(session)) {
return cache.get(session).collection.select(objectStates, x, y);
}
throw window.cvat.exceptions.DataError(
throw new window.cvat.exceptions.DataError(
'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
);
}
......
......@@ -348,14 +348,12 @@
*/
config: {
/**
* @memberof module:API.cvat.config
* @property {string} backendAPI host with a backend api
* @memberof module:API.cvat.config
* @property {string} proxy Axios proxy settings.
* For more details please read <a href="https://github.com/axios/axios"> here </a>
* @memberof module:API.cvat.config
* @property {integer} preloadFrames the number of subsequent frames which are
* loaded in background
* @memberof module:API.cvat.config
* @property {integer} taskID this value is displayed in a logs if available
* @memberof module:API.cvat.config
* @property {integer} jobID this value is displayed in a logs if available
......@@ -364,7 +362,6 @@
* value which is displayed in a logs
* @memberof module:API.cvat.config
*/
preloadFrames: 300,
backendAPI: 'http://localhost:7000/api/v1',
proxy: false,
taskID: undefined,
......@@ -451,5 +448,9 @@
require('browser-env')();
}
Math.clamp = function (value, min, max) {
return Math.min(Math.max(value, min), max);
};
window.cvat = Object.freeze(implementAPI(cvat));
})();
......@@ -38,7 +38,7 @@
);
} else if (!fields[prop](filter[prop])) {
throw new window.cvat.exceptions.ArgumentError(
`Received filter property ${prop} is not satisfied for checker`,
`Received filter property "${prop}" is not satisfied for checker`,
);
}
}
......@@ -61,7 +61,7 @@
if (!(value instanceof instance)) {
if (value !== undefined) {
throw new window.cvat.exceptions.ArgumentError(
`${name} is expected to be ${instance.name}, but `
`"${name}" is expected to be ${instance.name}, but `
+ `"${value.constructor.name}" has been got`,
);
}
......
......@@ -59,7 +59,7 @@
/**
* Method returns URL encoded image which can be placed in the img tag
* @method frame
* @method data
* @returns {string}
* @memberof module:API.cvat.classes.FrameData
* @instance
......@@ -67,16 +67,16 @@
* @throws {module:API.cvat.exception.ServerError}
* @throws {module:API.cvat.exception.PluginError}
*/
async frame() {
async data() {
const result = await PluginRegistry
.apiWrapper.call(this, FrameData.prototype.frame);
.apiWrapper.call(this, FrameData.prototype.data);
return result;
}
}
FrameData.prototype.frame.implementation = async function () {
FrameData.prototype.data.implementation = async function () {
if (!(this.number in frameCache[this.tid])) {
const frame = await serverProxy.frames.getFrame(this.tid, this.number);
const frame = await serverProxy.frames.getData(this.tid, this.number);
if (window.URL.createObjectURL) { // browser env
const url = window.URL.createObjectURL(new Blob([frame]));
......
......@@ -153,12 +153,21 @@
* @name points
* @type {number[]}
* @memberof module:API.cvat.classes.ObjectState
* @throws {module:API.cvat.exceptions.ArgumentError}
* @instance
*/
get: () => data.points,
set: (points) => {
data.updateFlags.points = true;
data.points = [...points];
if (Array.isArray(points)) {
data.updateFlags.points = true;
data.points = [...points];
} else {
throw new window.cvat.exceptions.ArgumentError(
'Points are expected to be an array '
+ `but got ${typeof (points) === 'object'
? points.constructor.name : typeof (points)}`,
);
}
},
},
group: {
......@@ -252,14 +261,10 @@
get: () => data.attributes,
set: (attributes) => {
if (typeof (attributes) !== 'object') {
if (typeof (attributes) === 'undefined') {
throw new window.cvat.exceptions.ArgumentError(
'Expected attributes are object, but got undefined',
);
}
throw new window.cvat.exceptions.ArgumentError(
`Expected attributes are object, but got ${attributes.constructor.name}`,
'Attributes are expected to be an object '
+ `but got ${typeof (attributes) === 'object'
? attributes.constructor.name : typeof (attributes)}`,
);
}
......@@ -277,11 +282,17 @@
this.outside = serialized.outside;
this.keyframe = serialized.keyframe;
this.occluded = serialized.occluded;
this.attributes = serialized.attributes;
this.points = serialized.points;
this.color = serialized.color;
this.lock = serialized.lock;
// It can be undefined in a constructor and it can be defined later
if (typeof (serialized.points) !== 'undefined') {
this.points = serialized.points;
}
if (typeof (serialized.attributes) !== 'undefined') {
this.attributes = serialized.attributes;
}
data.updateFlags.reset();
}
......
......@@ -421,7 +421,7 @@
return response.data;
}
async function getFrame(tid, frame) {
async function getData(tid, frame) {
const { backendAPI } = window.cvat.config;
let response = null;
......@@ -629,7 +629,7 @@
frames: {
value: Object.freeze({
getFrame,
getData,
getMeta,
}),
writable: false,
......
......@@ -259,6 +259,7 @@
* @param {module:API.cvat.classes.ObjectState[]} data
* array of objects on the specific frame
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.DataError}
* @throws {module:API.cvat.exceptions.ArgumentError}
* @instance
* @async
......@@ -302,11 +303,13 @@
* @async
*/
/**
* Select shape under a cursor using math alghorithms
* Select shape under a cursor by using minimal distance
* between a cursor and a shape edge or a shape point
* For closed shapes a cursor is placed inside a shape
* @method select
* @memberof Session.annotations
* @param {module:API.cvat.classes.ObjectState[]} objectStates
* object which can be selected
* objects which can be selected
* @param {float} x horizontal coordinate
* @param {float} y vertical coordinate
* @returns {Object}
......@@ -659,7 +662,7 @@
return this;
}
throw window.cvat.exceptions.ArgumentError(
throw new window.cvat.exceptions.ArgumentError(
'Can not save job without and id',
);
};
......@@ -673,7 +676,7 @@
if (frame < this.startFrame || frame > this.stopFrame) {
throw new window.cvat.exceptions.ArgumentError(
`Frame ${frame} does not exist in the job`,
`The frame with number ${frame} is out of the job`,
);
}
......@@ -1262,9 +1265,15 @@
};
Task.prototype.frames.get.implementation = async function (frame) {
if (!Number.isInteger(frame) || frame < 0) {
throw new window.cvat.exceptions.ArgumentError(
`Frame must be a positive integer. Got: "${frame}"`,
);
}
if (frame >= this.size) {
throw new window.cvat.exceptions.ArgumentError(
`Frame ${frame} does not exist in the task`,
`The frame with number ${frame} is out of the task`,
);
}
......
此差异已折叠。
/*
* Copyright (C) 2018 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
jest:false
describe:false
*/
// Setup mock for a server
jest.mock('../../src/server-proxy', () => {
const mock = require('../mocks/server-proxy.mock');
return mock;
});
// Initialize api
require('../../src/api');
const { FrameData } = require('../../src/frames');
describe('Feature: get frame meta', () => {
test('get meta for a task', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
const frame = await task.frames.get(0);
expect(frame).toBeInstanceOf(FrameData);
});
test('get meta for a job', async () => {
const job = (await window.cvat.jobs.get({ jobID: 100 }))[0];
const frame = await job.frames.get(0);
expect(frame).toBeInstanceOf(FrameData);
});
test('pass frame number out of a task', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
expect(task.frames.get(100))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
expect(task.frames.get(-1))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('pass bad frame number', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
expect(task.frames.get('5'))
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('do not pass any frame number', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
expect(task.frames.get())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
});
describe('Feature: get frame data', () => {
test('get frame data for a task', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
const frame = await task.frames.get(0);
const frameData = await frame.data();
expect(typeof (frameData)).toBe('string');
});
test('get frame data for a job', async () => {
const job = (await window.cvat.jobs.get({ jobID: 100 }))[0];
const frame = await job.frames.get(0);
const frameData = await frame.data();
expect(typeof (frameData)).toBe('string');
});
});
......@@ -64,26 +64,26 @@ describe('Feature: get a list of jobs', () => {
});
test('get jobs by invalid filter with both taskID and jobID', async () => {
await expect(window.cvat.jobs.get({
expect(window.cvat.jobs.get({
taskID: 1,
jobID: 1,
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('get jobs by invalid job id', async () => {
await expect(window.cvat.jobs.get({
expect(window.cvat.jobs.get({
jobID: '1',
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('get jobs by invalid task id', async () => {
await expect(window.cvat.jobs.get({
expect(window.cvat.jobs.get({
taskID: '1',
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('get jobs by unknown filter', async () => {
await expect(window.cvat.jobs.get({
expect(window.cvat.jobs.get({
unknown: 50,
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
......
/*
* Copyright (C) 2018 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
jest:false
describe:false
*/
// Setup mock for a server
jest.mock('../../src/server-proxy', () => {
const mock = require('../mocks/server-proxy.mock');
return mock;
});
// Initialize api
require('../../src/api');
describe('Feature: set attributes for an object state', () => {
test('set a valid value', () => {
const state = new window.cvat.classes.ObjectState({
objectType: window.cvat.enums.ObjectType.SHAPE,
shapeType: window.cvat.enums.ObjectShape.RECTANGLE,
frame: 5,
});
const attributes = {
5: 'man',
6: 'glasses',
};
state.attributes = attributes;
expect(state.attributes).toEqual(attributes);
});
test('trying to set a bad value', () => {
const state = new window.cvat.classes.ObjectState({
objectType: window.cvat.enums.ObjectType.SHAPE,
shapeType: window.cvat.enums.ObjectShape.RECTANGLE,
frame: 5,
});
let attributes = 'bad attribute';
expect(() => {
state.attributes = attributes;
}).toThrow(window.cvat.exceptions.ArgumentError);
attributes = 5;
expect(() => {
state.attributes = attributes;
}).toThrow(window.cvat.exceptions.ArgumentError);
attributes = false;
expect(() => {
state.attributes = attributes;
}).toThrow(window.cvat.exceptions.ArgumentError);
});
});
describe('Feature: set points for an object state', () => {
test('set a valid value', () => {
const state = new window.cvat.classes.ObjectState({
objectType: window.cvat.enums.ObjectType.SHAPE,
shapeType: window.cvat.enums.ObjectShape.RECTANGLE,
frame: 5,
});
const points = [1, 2, 3, 4];
state.points = points;
expect(state.points).toEqual(points);
});
test('trying to set a bad value', () => {
const state = new window.cvat.classes.ObjectState({
objectType: window.cvat.enums.ObjectType.SHAPE,
shapeType: window.cvat.enums.ObjectShape.RECTANGLE,
frame: 5,
});
let points = 'bad points';
expect(() => {
state.points = points;
}).toThrow(window.cvat.exceptions.ArgumentError);
points = 5;
expect(() => {
state.points = points;
}).toThrow(window.cvat.exceptions.ArgumentError);
points = false;
expect(() => {
state.points = points;
}).toThrow(window.cvat.exceptions.ArgumentError);
points = {};
expect(() => {
state.points = points;
}).toThrow(window.cvat.exceptions.ArgumentError);
});
});
describe('Feature: save object from its state', () => {
test('save valid values for a shape', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
const annotations = await task.annotations.get(0);
let state = annotations[0];
expect(state.objectType).toBe(window.cvat.enums.ObjectType.SHAPE);
expect(state.shapeType).toBe(window.cvat.enums.ObjectShape.RECTANGLE);
state.points = [0, 0, 100, 100];
state.occluded = true;
[, state.label] = task.labels;
state.lock = true;
state = await state.save();
expect(state).toBeInstanceOf(window.cvat.classes.ObjectState);
expect(state.label.id).toBe(task.labels[1].id);
expect(state.lock).toBe(true);
expect(state.occluded).toBe(true);
expect(state.points).toEqual([0, 0, 100, 100]);
});
test('save valid values for a track', async () => {
const task = (await window.cvat.tasks.get({ id: 101 }))[0];
const annotations = await task.annotations.get(10);
let state = annotations[1];
expect(state.objectType).toBe(window.cvat.enums.ObjectType.TRACK);
expect(state.shapeType).toBe(window.cvat.enums.ObjectShape.RECTANGLE);
state.occluded = true;
state.lock = true;
state.points = [100, 200, 200, 400];
state.attributes = {
1: 'sitting',
3: 'female',
2: '10',
4: 'true',
};
state = await state.save();
expect(state).toBeInstanceOf(window.cvat.classes.ObjectState);
expect(state.lock).toBe(true);
expect(state.occluded).toBe(true);
expect(state.points).toEqual([100, 200, 200, 400]);
expect(state.attributes[1]).toBe('sitting');
expect(state.attributes[2]).toBe('10');
expect(state.attributes[3]).toBe('female');
expect(state.attributes[4]).toBe('true');
state.lock = false;
[state.label] = task.labels;
state = await state.save();
expect(state.label.id).toBe(task.labels[0].id);
state.outside = true;
state = await state.save();
expect(state.lock).toBe(false);
expect(state.outside).toBe(true);
state.keyframe = false;
state = await state.save();
expect(state.keyframe).toBe(false);
});
test('save bad values for a shape', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
const annotations = await task.annotations.get(0);
const state = annotations[0];
state.occluded = 'false';
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
const oldPoints = state.points;
state.occluded = false;
state.points = ['100', '50', '100', {}];
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
state.points = oldPoints;
state.lock = 'true';
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
const oldLabel = state.label;
state.lock = false;
state.label = 1;
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
state.label = oldLabel;
state.attributes = { 1: {}, 2: false, 3: () => {} };
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('save bad values for a track', async () => {
const task = (await window.cvat.tasks.get({ id: 101 }))[0];
const annotations = await task.annotations.get(0);
const state = annotations[0];
state.occluded = 'false';
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
const oldPoints = state.points;
state.occluded = false;
state.points = ['100', '50', '100', {}];
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
state.points = oldPoints;
state.lock = 'true';
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
const oldLabel = state.label;
state.lock = false;
state.label = 1;
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
state.label = oldLabel;
state.outside = 5;
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
state.outside = false;
state.keyframe = '10';
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
state.keyframe = true;
state.attributes = { 1: {}, 2: false, 3: () => {} };
await expect(state.save())
.rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('trying to change locked shape', async () => {
const task = (await window.cvat.tasks.get({ id: 101 }))[0];
const annotations = await task.annotations.get(0);
let state = annotations[0];
state.lock = true;
state = await state.save();
const { points } = state;
state.points = [0, 0, 500, 500];
state = await state.save();
expect(state.points).toEqual(points);
});
test('trying to set too small area of a shape', async () => {
const task = (await window.cvat.tasks.get({ id: 101 }))[0];
const annotations = await task.annotations.get(0);
let state = annotations[0];
const { points } = state;
state.points = [0, 0, 2, 2]; // area is 4
state = await state.save();
expect(state.points).toEqual(points);
});
test('trying to set too small area of a track', async () => {
const task = (await window.cvat.tasks.get({ id: 101 }))[0];
const annotations = await task.annotations.get(0);
let state = annotations[0];
const { points } = state;
state.points = [0, 0, 2, 2]; // area is 4
state = await state.save();
expect(state.points).toEqual(points);
});
test('trying to set too small length of a shape', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
const annotations = await task.annotations.get(0);
let state = annotations[8];
const { points } = state;
state.points = [0, 0, 2, 2]; // length is 2
state = await state.save();
expect(state.points).toEqual(points);
});
});
describe('Feature: delete object', () => {
test('delete a shape', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
const annotationsBefore = await task.annotations.get(0);
const { length } = annotationsBefore;
await annotationsBefore[0].delete();
const annotationsAfter = await task.annotations.get(0);
expect(annotationsAfter).toHaveLength(length - 1);
});
test('delete a track', async () => {
const task = (await window.cvat.tasks.get({ id: 101 }))[0];
const annotationsBefore = await task.annotations.get(0);
const { length } = annotationsBefore;
await annotationsBefore[0].delete();
const annotationsAfter = await task.annotations.get(0);
expect(annotationsAfter).toHaveLength(length - 1);
});
});
describe('Feature: change z order of an object', () => {
test('up z order for a shape', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
const annotations = await task.annotations.get(0);
const state = annotations[0];
const { zOrder } = state;
await state.up();
expect(state.zOrder).toBeGreaterThan(zOrder);
});
test('up z order for a track', async () => {
const task = (await window.cvat.tasks.get({ id: 101 }))[0];
const annotations = await task.annotations.get(0);
const state = annotations[0];
const { zOrder } = state;
await state.up();
expect(state.zOrder).toBeGreaterThan(zOrder);
});
test('down z order for a shape', async () => {
const task = (await window.cvat.tasks.get({ id: 100 }))[0];
const annotations = await task.annotations.get(0);
const state = annotations[0];
const { zOrder } = state;
await state.down();
expect(state.zOrder).toBeLessThan(zOrder);
});
test('down z order for a track', async () => {
const task = (await window.cvat.tasks.get({ id: 101 }))[0];
const annotations = await task.annotations.get(0);
const state = annotations[0];
const { zOrder } = state;
await state.down();
expect(state.zOrder).toBeLessThan(zOrder);
});
});
/*
* Copyright (C) 2018 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
jest:false
describe:false
*/
// Setup mock for a server
jest.mock('../../src/server-proxy', () => {
const mock = require('../mocks/server-proxy.mock');
return mock;
});
// Initialize api
require('../../src/api');
describe('Feature: dummy feature', () => {
test('dummy test', async () => {
// TODO: Write test after design of plugin system
});
});
/*
const plugin = {
name: 'Example Plugin',
description: 'This example plugin demonstrates how plugin system in CVAT works',
cvat: {
server: {
about: {
async leave(self, result) {
result.plugins = await self.internal.getPlugins();
return result;
},
},
},
classes: {
Job: {
prototype: {
annotations: {
put: {
enter(self, objects) {
for (const obj of objects) {
if (obj.type !== 'tag') {
const points = obj.position.map((point) => {
const roundPoint = {
x: Math.round(point.x),
y: Math.round(point.y),
};
return roundPoint;
});
obj.points = points;
}
}
},
},
},
},
},
},
},
internal: {
async getPlugins() {
const plugins = await window.cvat.plugins.list();
return plugins.map((el) => {
const obj = {
name: el.name,
description: el.description,
};
return obj;
});
},
},
};
async function test() {
await window.cvat.plugins.register(plugin);
await window.cvat.server.login('admin', 'nimda760');
try {
console.log(JSON.stringify(await window.cvat.server.about()));
console.log(await window.cvat.users.get({ self: false }));
console.log(await window.cvat.users.get({ self: true }));
console.log(JSON.stringify(await window.cvat.jobs.get({ taskID: 8 })));
console.log(JSON.stringify(await window.cvat.jobs.get({ jobID: 10 })));
console.log(await window.cvat.tasks.get());
console.log(await window.cvat.tasks.get({ id: 8 }));
console.log('Done.');
} catch (exception) {
console.log(exception.constructor.name);
console.log(exception.message);
}
}
*/
......@@ -44,7 +44,7 @@ describe('Feature: get share storage info', () => {
});
test('get files in a some unknown dir of a share storage', async () => {
await expect(window.cvat.server.share(
expect(window.cvat.server.share(
'Unknown Directory',
)).rejects.toThrow(window.cvat.exceptions.ServerError);
});
......
......@@ -26,7 +26,7 @@ describe('Feature: get a list of tasks', () => {
test('get all tasks', async () => {
const result = await window.cvat.tasks.get();
expect(Array.isArray(result)).toBeTruthy();
expect(result).toHaveLength(3);
expect(result).toHaveLength(5);
for (const el of result) {
expect(el).toBeInstanceOf(Task);
}
......@@ -51,7 +51,7 @@ describe('Feature: get a list of tasks', () => {
});
test('get a task by an invalid id', async () => {
await expect(window.cvat.tasks.get({
expect(window.cvat.tasks.get({
id: '50',
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
......@@ -61,7 +61,7 @@ describe('Feature: get a list of tasks', () => {
mode: 'interpolation',
});
expect(Array.isArray(result)).toBeTruthy();
expect(result).toHaveLength(2);
expect(result).toHaveLength(3);
for (const el of result) {
expect(el).toBeInstanceOf(Task);
expect(el.mode).toBe('interpolation');
......@@ -69,7 +69,7 @@ describe('Feature: get a list of tasks', () => {
});
test('get tasks by invalid filters', async () => {
await expect(window.cvat.tasks.get({
expect(window.cvat.tasks.get({
unknown: '5',
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
......@@ -184,77 +184,3 @@ describe('Feature: delete a task', () => {
expect(result).toHaveLength(0);
});
});
/*
const plugin = {
name: 'Example Plugin',
description: 'This example plugin demonstrates how plugin system in CVAT works',
cvat: {
server: {
about: {
async leave(self, result) {
result.plugins = await self.internal.getPlugins();
return result;
},
},
},
classes: {
Job: {
prototype: {
annotations: {
put: {
enter(self, objects) {
for (const obj of objects) {
if (obj.type !== 'tag') {
const points = obj.position.map((point) => {
const roundPoint = {
x: Math.round(point.x),
y: Math.round(point.y),
};
return roundPoint;
});
obj.points = points;
}
}
},
},
},
},
},
},
},
internal: {
async getPlugins() {
const plugins = await window.cvat.plugins.list();
return plugins.map((el) => {
const obj = {
name: el.name,
description: el.description,
};
return obj;
});
},
},
};
async function test() {
await window.cvat.plugins.register(plugin);
await window.cvat.server.login('admin', 'nimda760');
try {
console.log(JSON.stringify(await window.cvat.server.about()));
console.log(await window.cvat.users.get({ self: false }));
console.log(await window.cvat.users.get({ self: true }));
console.log(JSON.stringify(await window.cvat.jobs.get({ taskID: 8 })));
console.log(JSON.stringify(await window.cvat.jobs.get({ jobID: 10 })));
console.log(await window.cvat.tasks.get());
console.log(await window.cvat.tasks.get({ id: 8 }));
console.log('Done.');
} catch (exception) {
console.log(exception.constructor.name);
console.log(exception.message);
}
}
*/
......@@ -41,13 +41,13 @@ describe('Feature: get a list of users', () => {
});
test('get users with unknown filter key', async () => {
await expect(window.cvat.users.get({
expect(window.cvat.users.get({
unknown: '50',
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
test('get users with invalid filter key', async () => {
await expect(window.cvat.users.get({
expect(window.cvat.users.get({
self: 1,
})).rejects.toThrow(window.cvat.exceptions.ArgumentError);
});
......
......@@ -14,6 +14,9 @@ const {
aboutDummyData,
shareDummyData,
usersDummyData,
taskAnnotationsDummyData,
jobAnnotationsDummyData,
frameMetaDummyData,
} = require('./dummy-data.mock');
......@@ -185,19 +188,50 @@ class ServerProxy {
return JSON.parse(JSON.stringify(usersDummyData)).results[0];
}
async function getFrame() {
return null;
async function getData() {
return 'DUMMY_IMAGE';
}
async function getMeta() {
return null;
async function getMeta(tid) {
return JSON.parse(JSON.stringify(frameMetaDummyData[tid]));
}
async function getAnnotations() {
async function getAnnotations(session, id) {
if (session === 'task') {
return JSON.parse(JSON.stringify(taskAnnotationsDummyData[id]));
}
if (session === 'job') {
return JSON.parse(JSON.stringify(jobAnnotationsDummyData[id]));
}
return null;
}
async function updateAnnotations() {
async function updateAnnotations(session, id, data, action) {
// Actually we do not change our dummy data
// We just update the argument in some way and return it
data.version += 1;
if (action === 'create') {
let idGenerator = 1000;
data.tracks.concat(data.tags).concat(data.shapes).map((el) => {
el.id = ++idGenerator;
return el;
});
return data;
}
if (action === 'update') {
return data;
}
if (action === 'delete') {
return data;
}
return null;
}
......@@ -241,7 +275,7 @@ class ServerProxy {
frames: {
value: Object.freeze({
getFrame,
getData,
getMeta,
}),
writable: false,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册