Adds unit tests covering tensor operations, type enums, client functionality, and serialization for all 12 SDK implementations: - JavaScript (Vitest): tensor, types, client tests - Python (pytest): tensor, types, client tests - Go: standard library tests with httptest - Flutter (flutter_test): tensor, types tests - Java (JUnit 5): tensor, types tests - Rust: embedded module tests - Ruby (minitest): tensor, types tests - C# (xUnit): tensor, types tests Tests cover: - Tensor creation (zeros, ones, random, randn, eye, arange, linspace) - Tensor operations (reshape, transpose, indexing) - Reductions (sum, mean, std, min, max) - Activations (relu, sigmoid, softmax) - Serialization/deserialization - Type enums and configuration - Client request building - Error handling
175 lines
5.4 KiB
TypeScript
175 lines
5.4 KiB
TypeScript
/**
|
|
* Unit tests for Tensor class
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import { Tensor } from '../tensor';
|
|
|
|
describe('Tensor', () => {
|
|
describe('creation', () => {
|
|
it('should create tensor from nested array', () => {
|
|
const t = Tensor.from([[1, 2, 3], [4, 5, 6]]);
|
|
expect(t.shape).toEqual([2, 3]);
|
|
expect(t.size).toBe(6);
|
|
expect(t.ndim).toBe(2);
|
|
});
|
|
|
|
it('should create tensor from flat array', () => {
|
|
const t = Tensor.from([1, 2, 3, 4]);
|
|
expect(t.shape).toEqual([4]);
|
|
expect(t.size).toBe(4);
|
|
expect(t.ndim).toBe(1);
|
|
});
|
|
|
|
it('should create tensor from Float32Array', () => {
|
|
const data = new Float32Array([1.0, 2.0, 3.0]);
|
|
const t = Tensor.from(data);
|
|
expect(t.dtype).toBe('fp32');
|
|
expect(t.size).toBe(3);
|
|
});
|
|
|
|
it('should create tensor from Float64Array', () => {
|
|
const data = new Float64Array([1.0, 2.0, 3.0]);
|
|
const t = Tensor.from(data);
|
|
expect(t.dtype).toBe('fp64');
|
|
});
|
|
});
|
|
|
|
describe('factory methods', () => {
|
|
it('should create zeros tensor', () => {
|
|
const t = Tensor.zeros([2, 3]);
|
|
expect(t.shape).toEqual([2, 3]);
|
|
expect(t.size).toBe(6);
|
|
expect(Array.from(t.data).every(v => v === 0)).toBe(true);
|
|
});
|
|
|
|
it('should create ones tensor', () => {
|
|
const t = Tensor.ones([3, 2]);
|
|
expect(t.shape).toEqual([3, 2]);
|
|
expect(Array.from(t.data).every(v => v === 1)).toBe(true);
|
|
});
|
|
|
|
it('should create random tensor', () => {
|
|
const t = Tensor.random([10, 10]);
|
|
expect(t.shape).toEqual([10, 10]);
|
|
expect(t.size).toBe(100);
|
|
// Values should be in [0, 1)
|
|
expect(Array.from(t.data).every(v => v >= 0 && v < 1)).toBe(true);
|
|
});
|
|
|
|
it('should create randn tensor', () => {
|
|
const t = Tensor.randn([100]);
|
|
expect(t.shape).toEqual([100]);
|
|
// Check approximate normal distribution properties
|
|
const values = Array.from(t.data);
|
|
const mean = values.reduce((a, b) => a + b) / values.length;
|
|
expect(Math.abs(mean)).toBeLessThan(0.5); // Should be close to 0
|
|
});
|
|
|
|
it('should support different dtypes', () => {
|
|
expect(Tensor.zeros([2, 2], 'fp64').dtype).toBe('fp64');
|
|
expect(Tensor.zeros([2, 2], 'fp32').dtype).toBe('fp32');
|
|
expect(Tensor.zeros([2, 2], 'int8').dtype).toBe('int8');
|
|
});
|
|
});
|
|
|
|
describe('operations', () => {
|
|
it('should reshape tensor', () => {
|
|
const t = Tensor.from([1, 2, 3, 4, 5, 6]);
|
|
const reshaped = t.reshape([2, 3]);
|
|
expect(reshaped.shape).toEqual([2, 3]);
|
|
expect(reshaped.size).toBe(6);
|
|
});
|
|
|
|
it('should throw on invalid reshape', () => {
|
|
const t = Tensor.from([1, 2, 3, 4]);
|
|
expect(() => t.reshape([2, 3])).toThrow();
|
|
});
|
|
|
|
it('should convert dtype', () => {
|
|
const t = Tensor.from([1.5, 2.5, 3.5], 'fp32');
|
|
const converted = t.to('fp64');
|
|
expect(converted.dtype).toBe('fp64');
|
|
expect(converted.data).toBeInstanceOf(Float64Array);
|
|
});
|
|
|
|
it('should get element at index', () => {
|
|
const t = Tensor.from([[1, 2, 3], [4, 5, 6]]);
|
|
expect(t.get(0, 0)).toBe(1);
|
|
expect(t.get(0, 2)).toBe(3);
|
|
expect(t.get(1, 1)).toBe(5);
|
|
});
|
|
|
|
it('should set element at index', () => {
|
|
const t = Tensor.from([[1, 2], [3, 4]]);
|
|
t.set(99, 0, 1);
|
|
expect(t.get(0, 1)).toBe(99);
|
|
});
|
|
|
|
it('should convert to array', () => {
|
|
const t = Tensor.from([[1, 2], [3, 4]]);
|
|
const arr = t.toArray();
|
|
expect(arr).toEqual([[1, 2], [3, 4]]);
|
|
});
|
|
});
|
|
|
|
describe('properties', () => {
|
|
it('should calculate byteSize correctly', () => {
|
|
const fp32 = Tensor.zeros([10], 'fp32');
|
|
expect(fp32.byteSize).toBe(40); // 10 * 4 bytes
|
|
|
|
const fp64 = Tensor.zeros([10], 'fp64');
|
|
expect(fp64.byteSize).toBe(80); // 10 * 8 bytes
|
|
});
|
|
});
|
|
|
|
describe('serialization', () => {
|
|
it('should serialize and deserialize tensor', () => {
|
|
const original = Tensor.from([[1, 2, 3], [4, 5, 6]]);
|
|
const serialized = original.serialize();
|
|
|
|
expect(serialized).toHaveProperty('data');
|
|
expect(serialized).toHaveProperty('shape');
|
|
expect(serialized).toHaveProperty('dtype');
|
|
expect(serialized.shape).toEqual([2, 3]);
|
|
|
|
const restored = Tensor.deserialize(serialized);
|
|
expect(restored.shape).toEqual(original.shape);
|
|
expect(restored.dtype).toBe(original.dtype);
|
|
expect(Array.from(restored.data)).toEqual(Array.from(original.data));
|
|
});
|
|
|
|
it('should preserve dtype during serialization', () => {
|
|
const fp64Tensor = Tensor.zeros([3], 'fp64');
|
|
fp64Tensor.set(1.234567890123, 0);
|
|
|
|
const serialized = fp64Tensor.serialize();
|
|
const restored = Tensor.deserialize(serialized);
|
|
|
|
expect(restored.dtype).toBe('fp64');
|
|
});
|
|
});
|
|
|
|
describe('edge cases', () => {
|
|
it('should handle scalar-like tensor', () => {
|
|
const t = Tensor.from([42]);
|
|
expect(t.shape).toEqual([1]);
|
|
expect(t.size).toBe(1);
|
|
});
|
|
|
|
it('should handle large tensors', () => {
|
|
const t = Tensor.zeros([1000, 1000]);
|
|
expect(t.size).toBe(1000000);
|
|
});
|
|
|
|
it('should throw on out-of-bounds access', () => {
|
|
const t = Tensor.from([1, 2, 3]);
|
|
expect(() => t.get(5)).toThrow();
|
|
});
|
|
|
|
it('should throw on wrong number of indices', () => {
|
|
const t = Tensor.from([[1, 2], [3, 4]]);
|
|
expect(() => t.get(0)).toThrow();
|
|
});
|
|
});
|
|
});
|