model-explorer / js /tests /tensorShapeInspector.test.js
mr4's picture
Upload 71 files
9bd422a verified
/**
* Unit tests for TensorShapeInspector
* Validates: Requirements 21.1, 21.2, 21.3, 21.4, 21.5
*/
import { describe, it, expect, beforeEach } from 'vitest';
/**
* Extract the pure tensor lookup logic for testing (mirrors TensorShapeInspector._buildTensorInfoMap + lookupTensor).
* @param {Object} parsedModel
* @returns {{ lookup: (name: string) => Object|null, size: number }}
*/
function buildTensorLookup(parsedModel) {
const map = new Map();
// 1. Inputs
if (Array.isArray(parsedModel.inputs)) {
for (const inp of parsedModel.inputs) {
if (inp.name) {
map.set(inp.name, {
name: inp.name,
shape: inp.shape || [],
dataType: inp.dataType || 'UNKNOWN'
});
}
}
}
// 2. Outputs
if (Array.isArray(parsedModel.outputs)) {
for (const out of parsedModel.outputs) {
if (out.name) {
map.set(out.name, {
name: out.name,
shape: out.shape || [],
dataType: out.dataType || 'UNKNOWN'
});
}
}
}
// 3. value_info (intermediate tensors)
const valueInfo = parsedModel.graph && parsedModel.graph.valueInfo;
if (valueInfo && typeof valueInfo === 'object') {
const entries = Object.entries(valueInfo);
for (const [key, vi] of entries) {
if (key && !map.has(key)) {
map.set(key, {
name: vi.name || key,
shape: vi.shape || [],
dataType: vi.dataType || 'UNKNOWN'
});
}
}
}
return {
lookup: (name) => map.get(name) || null,
size: map.size
};
}
/**
* Build tooltip text (mirrors TensorShapeInspector._buildTooltipHTML logic, text-only).
*/
function buildTooltipText(tensorName, tensorInfo) {
const displayName = tensorName || 'Unnamed tensor';
if (tensorInfo) {
const shapeStr = tensorInfo.shape && tensorInfo.shape.length > 0
? '[' + tensorInfo.shape.join(', ') + ']'
: 'unknown';
return { name: displayName, shape: shapeStr, dataType: tensorInfo.dataType };
}
return { name: displayName, shape: 'unknown', dataType: null };
}
describe('TensorShapeInspector - Tensor Lookup', () => {
let model;
beforeEach(() => {
model = {
inputs: [
{ name: 'input_0', shape: [1, 3, 224, 224], dataType: 'FLOAT' }
],
outputs: [
{ name: 'output_0', shape: [1, 1000], dataType: 'FLOAT' }
],
graph: {
nodes: [],
edges: [],
valueInfo: {
'conv1_out': { name: 'conv1_out', shape: [1, 64, 112, 112], dataType: 'FLOAT' },
'relu1_out': { name: 'relu1_out', shape: [1, 64, 112, 112], dataType: 'FLOAT' }
}
},
initializers: []
};
});
it('should find input tensors by name', () => {
const { lookup } = buildTensorLookup(model);
const info = lookup('input_0');
expect(info).not.toBeNull();
expect(info.name).toBe('input_0');
expect(info.shape).toEqual([1, 3, 224, 224]);
expect(info.dataType).toBe('FLOAT');
});
it('should find output tensors by name', () => {
const { lookup } = buildTensorLookup(model);
const info = lookup('output_0');
expect(info).not.toBeNull();
expect(info.shape).toEqual([1, 1000]);
expect(info.dataType).toBe('FLOAT');
});
it('should find intermediate tensors from valueInfo', () => {
const { lookup } = buildTensorLookup(model);
const info = lookup('conv1_out');
expect(info).not.toBeNull();
expect(info.shape).toEqual([1, 64, 112, 112]);
expect(info.dataType).toBe('FLOAT');
});
it('should return null for unknown tensor names', () => {
const { lookup } = buildTensorLookup(model);
expect(lookup('nonexistent_tensor')).toBeNull();
});
it('should return null for empty/null name', () => {
const { lookup } = buildTensorLookup(model);
expect(lookup('')).toBeNull();
expect(lookup(null)).toBeNull();
expect(lookup(undefined)).toBeNull();
});
it('should count all tensors from all sources', () => {
const { size } = buildTensorLookup(model);
// 1 input + 1 output + 2 valueInfo = 4
expect(size).toBe(4);
});
it('should handle model with no valueInfo', () => {
model.graph.valueInfo = {};
const { lookup, size } = buildTensorLookup(model);
expect(size).toBe(2); // only input + output
expect(lookup('conv1_out')).toBeNull();
});
it('should handle model with no inputs or outputs', () => {
const emptyModel = { inputs: [], outputs: [], graph: { valueInfo: {} }, initializers: [] };
const { size } = buildTensorLookup(emptyModel);
expect(size).toBe(0);
});
it('should prioritize inputs/outputs over valueInfo for same name', () => {
model.graph.valueInfo['input_0'] = { name: 'input_0', shape: [999], dataType: 'INT32' };
const { lookup } = buildTensorLookup(model);
const info = lookup('input_0');
// Input should take priority
expect(info.shape).toEqual([1, 3, 224, 224]);
expect(info.dataType).toBe('FLOAT');
});
it('should handle tensors with missing shape', () => {
model.graph.valueInfo['no_shape'] = { name: 'no_shape', dataType: 'FLOAT' };
const { lookup } = buildTensorLookup(model);
const info = lookup('no_shape');
expect(info).not.toBeNull();
expect(info.shape).toEqual([]);
});
it('should handle tensors with missing dataType', () => {
model.graph.valueInfo['no_dtype'] = { name: 'no_dtype', shape: [1, 10] };
const { lookup } = buildTensorLookup(model);
const info = lookup('no_dtype');
expect(info.dataType).toBe('UNKNOWN');
});
});
describe('TensorShapeInspector - Tooltip Content', () => {
it('should show shape and type for known tensor', () => {
const tensorInfo = { name: 'conv1_out', shape: [1, 64, 112, 112], dataType: 'FLOAT' };
const result = buildTooltipText('conv1_out', tensorInfo);
expect(result.name).toBe('conv1_out');
expect(result.shape).toBe('[1, 64, 112, 112]');
expect(result.dataType).toBe('FLOAT');
});
it('should show "Shape: unknown" when tensor info is null', () => {
const result = buildTooltipText('mystery_tensor', null);
expect(result.name).toBe('mystery_tensor');
expect(result.shape).toBe('unknown');
expect(result.dataType).toBeNull();
});
it('should show "Shape: unknown" when tensor has empty shape', () => {
const tensorInfo = { name: 'empty', shape: [], dataType: 'FLOAT' };
const result = buildTooltipText('empty', tensorInfo);
expect(result.shape).toBe('unknown');
});
it('should handle dynamic dimensions in shape', () => {
const tensorInfo = { name: 'dynamic', shape: ['batch', 3, 224, 224], dataType: 'FLOAT' };
const result = buildTooltipText('dynamic', tensorInfo);
expect(result.shape).toBe('[batch, 3, 224, 224]');
});
it('should show "Unnamed tensor" when name is empty', () => {
const result = buildTooltipText('', null);
expect(result.name).toBe('Unnamed tensor');
});
});