Skip to content

Commit

Permalink
Added some JEST tests to cover skeleton basic features (cvat-ai#4865)
Browse files Browse the repository at this point in the history
* Added tests to create/get a task with skeletons

* Added some dummy data for tests

* Added a couple of tests

* Aborted extra changes

* Added a couple of put tests

* Removed extra lines

* Added test to check object state logic for complex objects

* Updated eslint config file

* Fixed config

* Removed extra space

* Fixed lint-staged config
  • Loading branch information
bsekachev committed Sep 2, 2022
1 parent 99a1be0 commit 95c20d1
Show file tree
Hide file tree
Showing 6 changed files with 570 additions and 15 deletions.
12 changes: 1 addition & 11 deletions cvat-core/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
// SPDX-License-Identifier: MIT

module.exports = {
env: {
'jest/globals': true,
},
ignorePatterns: [
'.eslintrc.js',
'webpack.config.js',
Expand All @@ -14,17 +11,10 @@ module.exports = {
'src/3rdparty/**',
'node_modules/**',
'dist/**',
'tests/**/*.js',
],
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: __dirname,
},
ignorePatterns: ['tests/**/*.js'],
plugins: ['jest'],
rules: {
'jest/no-disabled-tests': 'warn',
'jest/no-focused-tests': 'error',
'jest/no-identical-title': 'error',
'jest/prefer-to-have-length': 'warn',
}
};
99 changes: 99 additions & 0 deletions cvat-core/tests/api/annotations.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,28 @@ describe('Feature: get annotations', () => {
expect(annotations).toHaveLength(1);
expect(annotations[0].shapeType).toBe('ellipse');
});

test('get skeletons with a filter', async () => {
const job = (await window.cvat.jobs.get({ jobID: 40 }))[0];
const annotations = await job.annotations.get(0, false, JSON.parse('[{"and":[{"==":[{"var":"shape"},"skeleton"]}]}]'));
expect(Array.isArray(annotations)).toBeTruthy();
expect(annotations).toHaveLength(2);
for (const object of annotations) {
expect(object.shapeType).toBe('skeleton');
expect(object.elements).toBeInstanceOf(Array);
const label = object.label;
let points = [];
object.elements.forEach((element, idx) => {
expect(element).toBeInstanceOf(cvat.classes.ObjectState);
expect(element.label.id).toBe(label.structure.sublabels[idx].id);
expect(element.shapeType).toBe('points');
points = [...points, ...element.points];
});
expect(points).toEqual(object.points);
}

expect(annotations[0].shapeType).toBe('skeleton');
})
});

describe('Feature: get interpolated annotations', () => {
Expand Down Expand Up @@ -351,6 +373,68 @@ describe('Feature: put annotations', () => {
zOrder: 0,
})).toThrow(window.cvat.exceptions.ArgumentError);
});

test('put a skeleton shape to a job', async() => {
const job = (await window.cvat.jobs.get({ jobID: 40 }))[0];
const label = job.labels[0];
await job.annotations.clear(true);
await job.annotations.clear();
const skeleton = new window.cvat.classes.ObjectState({
frame: 0,
objectType: window.cvat.enums.ObjectType.SHAPE,
shapeType: window.cvat.enums.ShapeType.SKELETON,
points: [],
label,
elements: label.structure.sublabels.map((sublabel, idx) => ({
frame: 0,
objectType: window.cvat.enums.ObjectType.SHAPE,
shapeType: window.cvat.enums.ShapeType.POINTS,
points: [idx * 10, idx * 10],
label: sublabel,
}))
});

await job.annotations.put([skeleton]);
const annotations = await job.annotations.get(0);
expect(annotations.length).toBe(1);
expect(annotations[0].objectType).toBe(window.cvat.enums.ObjectType.SHAPE);
expect(annotations[0].shapeType).toBe(window.cvat.enums.ShapeType.SKELETON);
for (const element of annotations[0].elements) {
expect(element.objectType).toBe(window.cvat.enums.ObjectType.SHAPE);
expect(element.shapeType).toBe(window.cvat.enums.ShapeType.POINTS);
}
});

test('put a skeleton track to a task', async() => {
const task = (await window.cvat.tasks.get({ id: 40 }))[0];
const label = task.labels[0];
await task.annotations.clear(true);
await task.annotations.clear();
const skeleton = new window.cvat.classes.ObjectState({
frame: 0,
objectType: window.cvat.enums.ObjectType.TRACK,
shapeType: window.cvat.enums.ShapeType.SKELETON,
points: [],
label,
elements: label.structure.sublabels.map((sublabel, idx) => ({
frame: 0,
objectType: window.cvat.enums.ObjectType.TRACK,
shapeType: window.cvat.enums.ShapeType.POINTS,
points: [idx * 10, idx * 10],
label: sublabel,
}))
});

await task.annotations.put([skeleton]);
const annotations = await task.annotations.get(2);
expect(annotations.length).toBe(1);
expect(annotations[0].objectType).toBe(window.cvat.enums.ObjectType.TRACK);
expect(annotations[0].shapeType).toBe(window.cvat.enums.ShapeType.SKELETON);
for (const element of annotations[0].elements) {
expect(element.objectType).toBe(window.cvat.enums.ObjectType.TRACK);
expect(element.shapeType).toBe(window.cvat.enums.ShapeType.POINTS);
}
});
});

describe('Feature: check unsaved changes', () => {
Expand Down Expand Up @@ -772,6 +856,21 @@ describe('Feature: get statistics', () => {
expect(statistics).toBeInstanceOf(window.cvat.classes.Statistics);
expect(statistics.total.total).toBe(1012);
});

test('get statistics from a job with skeletons', async () => {
const job = (await window.cvat.jobs.get({ jobID: 40 }))[0];
await job.annotations.clear(true);
const statistics = await job.annotations.statistics();
expect(statistics).toBeInstanceOf(window.cvat.classes.Statistics);
expect(statistics.total.total).toBe(30);
const labelName = job.labels[0].name;
expect(statistics.label[labelName].skeleton.shape).toBe(1);
expect(statistics.label[labelName].skeleton.track).toBe(1);
expect(statistics.label[labelName].manually).toBe(2);
expect(statistics.label[labelName].interpolated).toBe(3);
expect(statistics.label[labelName].total).toBe(5);

});
});

describe('Feature: select object', () => {
Expand Down
37 changes: 37 additions & 0 deletions cvat-core/tests/api/object-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,3 +282,40 @@ describe('Feature: delete object', () => {
expect(annotationsAfter).toHaveLength(length - 1);
});
});

describe('Feature: skeletons', () => {
test('lock, hide, occluded, outside for skeletons', async () => {
const job = (await window.cvat.jobs.get({ jobID: 40 }))[0];
let [skeleton] = await job.annotations.get(0, false, JSON.parse('[{"and":[{"==":[{"var":"shape"},"skeleton"]}]}]'));
expect(skeleton.shapeType).toBe('skeleton');
skeleton.lock = true;
skeleton.outside = true;
skeleton.occluded = true;
skeleton.hidden = true;
skeleton = await skeleton.save();
expect(skeleton.lock).toBe(true);
expect(skeleton.outside).toBe(true);
expect(skeleton.occluded).toBe(true);
expect(skeleton.hidden).toBe(true);
expect(skeleton.elements).toBeInstanceOf(Array);
expect(skeleton.elements.length).toBe(skeleton.label.structure.sublabels.length);
for (const element of skeleton.elements) {
expect(element.lock).toBe(true);
expect(element.outside).toBe(true);
expect(element.occluded).toBe(true);
expect(element.hidden).toBe(true);
}

skeleton.elements[0].lock = false;
skeleton.elements[0].outside = false;
skeleton.elements[0].occluded = false;
skeleton.elements[0].hidden = false;
skeleton.elements[0].save();

[skeleton] = await job.annotations.get(0, false, JSON.parse('[{"and":[{"==":[{"var":"shape"},"skeleton"]}]}]'));
expect(skeleton.lock).toBe(false);
expect(skeleton.outside).toBe(false);
expect(skeleton.occluded).toBe(false);
expect(skeleton.hidden).toBe(false);
});
});
80 changes: 78 additions & 2 deletions cvat-core/tests/api/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,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(6);
expect(result).toHaveLength(7);
for (const el of result) {
expect(el).toBeInstanceOf(Task);
}
Expand All @@ -34,6 +34,33 @@ describe('Feature: get a list of tasks', () => {
expect(result[0].id).toBe(3);
});

test('get a task with skeletons by an id', async () => {
const result = await window.cvat.tasks.get({
id: 40,
});

expect(Array.isArray(result)).toBeTruthy();
expect(result).toHaveLength(1);
expect(result[0]).toBeInstanceOf(Task);
expect(result[0].id).toBe(40);
expect(result[0].labels).toBeInstanceOf(Array);

for (const label of result[0].labels) {
expect(label).toBeInstanceOf(window.cvat.classes.Label);
if (label.type === 'skeleton') {
expect(label.hasParent).toBe(false);
expect(label.structure.sublabels).toBeInstanceOf(Array);
expect(typeof label.structure.svg).toBe('string');
expect(label.structure.svg.length).not.toBe(0);

for (const sublabel of label.structure.sublabels) {
expect(sublabel).toBeInstanceOf(window.cvat.classes.Label);
expect(sublabel.hasParent).toBe(true);
}
}
}
});

test('get a task by an unknown id', async () => {
const result = await window.cvat.tasks.get({
id: 50,
Expand Down Expand Up @@ -154,12 +181,61 @@ describe('Feature: save a task', () => {
project_id: 2,
bug_tracker: 'bug tracker value',
image_quality: 50,
z_order: true,
});

const result = await task.save();
expect(result.projectId).toBe(2);
});

test('create a new task with skeletons', async () => {
const svgSpec = `
<line x1="65.11705780029297" y1="18.267141342163086" x2="27.49163818359375" y2="39.504600524902344" stroke="black" data-type="edge" data-node-from="3" stroke-width="0.5" data-node-to="5"></line>
<line x1="21.806020736694336" y1="18.099916458129883" x2="65.11705780029297" y2="18.267141342163086" stroke="black" data-type="edge" data-node-from="1" stroke-width="0.5" data-node-to="3"></line>
<line x1="61.10367965698242" y1="40.00627136230469" x2="21.806020736694336" y2="18.099916458129883" stroke="black" data-type="edge" data-node-from="4" stroke-width="0.5" data-node-to="1"></line>
<line x1="44.38127136230469" y1="7.397575378417969" x2="61.10367965698242" y2="40.00627136230469" stroke="black" data-type="edge" data-node-from="2" stroke-width="0.5" data-node-to="4">
</line><line x1="27.49163818359375" y1="39.504600524902344" x2="44.38127136230469" y2="7.397575378417969" stroke="black" data-type="edge" data-node-from="5" stroke-width="0.5" data-node-to="2"></line>
<circle r="1.5" stroke="black" fill="#b3b3b3" cx="21.806020736694336" cy="18.099916458129883" stroke-width="0.1" data-type="element node" data-element-id="1" data-node-id="1" data-label-name="1"></circle>
<circle r="1.5" stroke="black" fill="#b3b3b3" cx="44.38127136230469" cy="7.397575378417969" stroke-width="0.1" data-type="element node" data-element-id="2" data-node-id="2" data-label-name="2"></circle>
<circle r="1.5" stroke="black" fill="#b3b3b3" cx="65.11705780029297" cy="18.267141342163086" stroke-width="0.1" data-type="element node" data-element-id="3" data-node-id="3" data-label-name="3"></circle>
<circle r="1.5" stroke="black" fill="#b3b3b3" cx="61.10367965698242" cy="40.00627136230469" stroke-width="0.1" data-type="element node" data-element-id="4" data-node-id="4" data-label-name="4"></circle>
<circle r="1.5" stroke="black" fill="#b3b3b3" cx="27.49163818359375" cy="39.504600524902344" stroke-width="0.1" data-type="element node" data-element-id="5" data-node-id="5" data-label-name="5"></circle>
`;

const task = new window.cvat.classes.Task({
name: 'task with skeletons',
labels: [{
name: 'star skeleton',
type: 'skeleton',
attributes: [],
svg: svgSpec,
sublabels: [{
name: '1',
type: 'points',
attributes: []
}, {
name: '2',
type: 'points',
attributes: []
}, {
name: '3',
type: 'points',
attributes: []
}, {
name: '4',
type: 'points',
attributes: []
}, {
name: '5',
type: 'points',
attributes: []
}]
}],
project_id: null,
});

const result = await task.save();
expect(typeof result.id).toBe('number');
});
});

describe('Feature: delete a task', () => {
Expand Down
Loading

0 comments on commit 95c20d1

Please sign in to comment.