Skip to content

Commit dbe7f81

Browse files
author
Brian Hulette
committed
Add more Table unit tests
1 parent 1910962 commit dbe7f81

File tree

1 file changed

+143
-138
lines changed

1 file changed

+143
-138
lines changed

js/test/unit/table-tests.ts

+143-138
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,10 @@ const { predicate, Table } = Arrow;
2121

2222
const { col } = predicate;
2323

24-
describe(`Table`, () => {
25-
test(`can create an empty table`, () => {
26-
expect(Table.empty().length).toEqual(0);
27-
});
28-
29-
describe(`single record batch`, () => {
30-
const table = Table.from({
24+
const F32 = 0, I32 = 1, DICT = 2;
25+
const test_data = [
26+
{name: `single record batch`,
27+
table: Table.from({
3128
'schema': {
3229
'fields': [
3330
{
@@ -115,79 +112,19 @@ describe(`Table`, () => {
115112
}
116113
]
117114
}]
118-
});
119-
120-
// Wrap floating point values in a Float32Array and take them back out to
121-
// make sure that equality checks will pass
122-
const values = [
123-
[Math.fround(-0.3), -1, 'a'],
124-
[Math.fround(-0.2), 1, 'b'],
125-
[Math.fround(-0.1), -1, 'c'],
126-
[Math.fround( 0 ), 1, 'a'],
127-
[Math.fround( 0.1), -1, 'b'],
128-
[Math.fround( 0.2), 1, 'c'],
129-
[Math.fround( 0.3), -1, 'a']
130-
];
131-
test(`has the correct length`, () => {
132-
expect(table.length).toEqual(values.length);
133-
});
134-
test(`gets expected values`, () => {
135-
for (let i = -1; ++i < values.length;) {
136-
expect(table.get(i).toArray()).toEqual(values[i]);
137-
}
138-
});
139-
test(`iterates expected values`, () => {
140-
let i = 0;
141-
for (let row of table) {
142-
expect(row.toArray()).toEqual(values[i++]);
143-
}
144-
});
145-
test(`scans expected values`, () => {
146-
let expected_idx = 0;
147-
table.scan((idx, batch) => {
148-
const columns = batch.schema.fields.map((_, i) => batch.getChildAt(i)!);
149-
expect(columns.map((c) => c.get(idx))).toEqual(values[expected_idx++]);
150-
});
151-
});
152-
test(`count() returns the correct length`, () => {
153-
expect(table.count()).toEqual(values.length);
154-
});
155-
test(`filter on f32 >= 0 returns the correct length`, () => {
156-
expect(table.filter(col('f32').gteq(0)).count()).toEqual(4);
157-
});
158-
test(`filter on i32 <= 0 returns the correct length`, () => {
159-
expect(table.filter(col('i32').lteq(0)).count()).toEqual(4);
160-
});
161-
test(`filter on dictionary == 'a' returns the correct length`, () => {
162-
expect(table.filter(col('dictionary').eq('a')).count()).toEqual(3);
163-
});
164-
test(`countBy on dictionary returns the correct counts`, () => {
165-
// Make sure countBy works both with and without the Col wrapper
166-
// class
167-
expect(table.countBy(col('dictionary')).toJSON()).toEqual({
168-
'a': 3,
169-
'b': 2,
170-
'c': 2,
171-
});
172-
expect(table.countBy('dictionary').toJSON()).toEqual({
173-
'a': 3,
174-
'b': 2,
175-
'c': 2,
176-
});
177-
});
178-
test(`countBy on dictionary with filter returns the correct counts`, () => {
179-
expect(table.filter(col('i32').eq(1)).countBy('dictionary').toJSON()).toEqual({
180-
'a': 1,
181-
'b': 1,
182-
'c': 1,
183-
});
184-
});
185-
test(`countBy on non dictionary column throws error`, () => {
186-
expect(() => { table.countBy('i32'); }).toThrow();
187-
});
188-
});
189-
describe(`multiple record batches`, () => {
190-
const table = Table.from({
115+
}),
116+
// Use Math.fround to coerce to float32
117+
values: [
118+
[Math.fround(-0.3), -1, 'a'],
119+
[Math.fround(-0.2), 1, 'b'],
120+
[Math.fround(-0.1), -1, 'c'],
121+
[Math.fround( 0 ), 1, 'a'],
122+
[Math.fround( 0.1), -1, 'b'],
123+
[Math.fround( 0.2), 1, 'c'],
124+
[Math.fround( 0.3), -1, 'a']
125+
]},
126+
{name: `multiple record batches`,
127+
table: Table.from({
191128
'schema': {
192129
'fields': [
193130
{
@@ -319,11 +256,8 @@ describe(`Table`, () => {
319256
}
320257
]
321258
}]
322-
});
323-
324-
// Wrap floating point values in a Float32Array and take them back out to
325-
// make sure that equality checks will pass
326-
const values = [
259+
}),
260+
values: [
327261
[Math.fround(-0.3), -1, 'a'],
328262
[Math.fround(-0.2), 1, 'b'],
329263
[Math.fround(-0.1), -1, 'c'],
@@ -333,63 +267,134 @@ describe(`Table`, () => {
333267
[Math.fround( 0.3), -1, 'a'],
334268
[Math.fround( 0.2), 1, 'b'],
335269
[Math.fround( 0.1), -1, 'c'],
336-
];
337-
test(`has the correct length`, () => {
338-
expect(table.length).toEqual(values.length);
339-
});
340-
test(`gets expected values`, () => {
341-
for (let i = -1; ++i < values.length;) {
342-
expect(table.get(i).toArray()).toEqual(values[i]);
343-
}
344-
});
345-
test(`iterates expected values`, () => {
346-
let i = 0;
347-
for (let row of table) {
348-
expect(row.toArray()).toEqual(values[i++]);
270+
]}
271+
]
272+
273+
describe(`Table`, () => {
274+
test(`can create an empty table`, () => {
275+
expect(Table.empty().length).toEqual(0);
276+
});
277+
test(`Table.from([]) creates an empty table`, () => {
278+
expect(Table.from([]).length).toEqual(0);
279+
});
280+
test(`Table.from() creates an empty table`, () => {
281+
expect(Table.from().length).toEqual(0);
282+
});
283+
for (let datum of test_data) {
284+
describe(datum.name, () => {
285+
const table = datum.table;
286+
const values = datum.values;
287+
288+
test(`has the correct length`, () => {
289+
expect(table.length).toEqual(values.length);
290+
});
291+
test(`gets expected values`, () => {
292+
for (let i = -1; ++i < values.length;) {
293+
expect(table.get(i).toArray()).toEqual(values[i]);
294+
}
295+
});
296+
test(`iterates expected values`, () => {
297+
let i = 0;
298+
for (let row of table) {
299+
expect(row.toArray()).toEqual(values[i++]);
300+
}
301+
});
302+
test(`scans expected values`, () => {
303+
let expected_idx = 0;
304+
table.scan((idx, batch) => {
305+
const columns = batch.schema.fields.map((_, i) => batch.getChildAt(i)!);
306+
expect(columns.map((c) => c.get(idx))).toEqual(values[expected_idx++]);
307+
});
308+
});
309+
test(`count() returns the correct length`, () => {
310+
expect(table.count()).toEqual(values.length);
311+
});
312+
const filter_tests = [
313+
{
314+
name: `filter on f32 >= 0`,
315+
filtered: table.filter(col('f32').gteq(0)),
316+
expected: values.filter((row)=>row[F32] >= 0)
317+
}, {
318+
name: `filter on i32 <= 0 returns the correct length`,
319+
filtered: table.filter(col('i32').lteq(0)),
320+
expected: values.filter((row)=>row[I32] <= 0)
321+
}, {
322+
name: `filter method combines predicates (f32 >= 0 && i32 <= 0)`,
323+
filtered: table.filter(col('i32').lteq(0)).filter(col('f32').gteq(0)),
324+
expected: values.filter((row)=>row[I32] <= 0 && row[F32] >= 0)
325+
}, {
326+
name: `filter on dictionary == 'a'`,
327+
filtered: table.filter(col('dictionary').eq('a')),
328+
expected: values.filter((row)=>row[DICT] === 'a')
329+
}
330+
]
331+
for (let this_test of filter_tests) {
332+
describe(`filter on f32 >= 0`, () => {
333+
const filtered = this_test.filtered;
334+
const expected = this_test.expected;
335+
test(`count() returns the correct length`, () => {
336+
expect(filtered.count()).toEqual(expected.length);
337+
});
338+
test(`scans expected values`, () => {
339+
let expected_idx = 0;
340+
filtered.scan((idx, batch) => {
341+
const columns = batch.schema.fields.map((_, i) => batch.getChildAt(i)!);
342+
expect(columns.map((c) => c.get(idx))).toEqual(expected[expected_idx++]);
343+
});
344+
})
345+
});
349346
}
350-
});
351-
test(`scans expected values`, () => {
352-
let expected_idx = 0;
353-
table.scan((idx, batch) => {
354-
const columns = batch.schema.fields.map((_, i) => batch.getChildAt(i)!);
355-
expect(columns.map((c) => c.get(idx))).toEqual(values[expected_idx++]);
347+
test(`countBy on dictionary returns the correct counts`, () => {
348+
// Make sure countBy works both with and without the Col wrapper
349+
// class
350+
let expected: {[key: string]: number} = {'a': 0, 'b': 0, 'c': 0};
351+
for (let row of values) {
352+
expected[row[DICT]] += 1;
353+
}
354+
355+
expect(table.countBy(col('dictionary')).toJSON()).toEqual(expected);
356+
expect(table.countBy('dictionary').toJSON()).toEqual(expected);
356357
});
357-
});
358-
test(`count() returns the correct length`, () => {
359-
expect(table.count()).toEqual(values.length);
360-
});
361-
test(`filter on f32 >= 0 returns the correct length`, () => {
362-
expect(table.filter(col('f32').gteq(0)).count()).toEqual(6);
363-
});
364-
test(`filter on i32 <= 0 returns the correct length`, () => {
365-
expect(table.filter(col('i32').lteq(0)).count()).toEqual(5);
366-
});
367-
test(`filter on dictionary == 'a' returns the correct length`, () => {
368-
expect(table.filter(col('dictionary').eq('a')).count()).toEqual(3);
369-
});
370-
test(`countBy on dictionary returns the correct counts`, () => {
371-
// Make sure countBy works both with and without the Col wrapper
372-
// class
373-
expect(table.countBy(col('dictionary')).toJSON()).toEqual({
374-
'a': 3,
375-
'b': 3,
376-
'c': 3,
358+
test(`countBy on dictionary with filter returns the correct counts`, () => {
359+
let expected: {[key: string]: number} = {'a': 0, 'b': 0, 'c': 0};
360+
for (let row of values) {
361+
if(row[I32] === 1) { expected[row[DICT]] += 1; }
362+
}
363+
364+
expect(table.filter(col('i32').eq(1)).countBy('dictionary').toJSON()).toEqual(expected);
377365
});
378-
expect(table.countBy('dictionary').toJSON()).toEqual({
379-
'a': 3,
380-
'b': 3,
381-
'c': 3,
366+
test(`countBy on non dictionary column throws error`, () => {
367+
expect(() => { table.countBy('i32'); }).toThrow();
368+
expect(() => { table.filter(col('dict').eq('a')).countBy('i32'); }).toThrow();
382369
});
383-
});
384-
test(`countBy on dictionary with filter returns the correct counts`, () => {
385-
expect(table.filter(col('i32').eq(1)).countBy(col('dictionary')).toJSON()).toEqual({
386-
'a': 1,
387-
'b': 2,
388-
'c': 1,
370+
test(`table.select() basic tests`, () => {
371+
let selected = table.select('f32', 'dictionary');
372+
expect(selected.schema.fields.length).toEqual(2);
373+
expect(selected.schema.fields[0]).toEqual(table.schema.fields[0]);
374+
expect(selected.schema.fields[1]).toEqual(table.schema.fields[2]);
375+
376+
expect(selected.length).toEqual(values.length);
377+
let idx = 0, expected_row;
378+
for (let row of selected) {
379+
expected_row = values[idx++];
380+
expect(row.get(0)).toEqual(expected_row[F32]);
381+
expect(row.get(1)).toEqual(expected_row[DICT]);
382+
}
383+
});
384+
test(`table.toString()`, () => {
385+
let selected = table.select('i32', 'dictionary');
386+
let headers = [`"row_id"`, `"i32: Int32"`, `"dictionary: Dictionary<Utf8, Int8>"`]
387+
let expected = [headers.join(' | '), ...values.map((row, idx) => {
388+
return [`${idx}`, `${row[I32]}`, `"${row[DICT]}"`].map((str, col) => {
389+
return leftPad(str, ' ', headers[col].length);
390+
}).join(' | ');
391+
})].join('\n') + '\n';
392+
expect(selected.toString()).toEqual(expected);
389393
});
390394
});
391-
test(`countBy on non dictionary column throws error`, () => {
392-
expect(() => { table.countBy('i32'); }).toThrow();
393-
});
394-
});
395+
}
395396
});
397+
398+
function leftPad(str: string, fill: string, n: number) {
399+
return (new Array(n + 1).join(fill) + str).slice(-1 * n);
400+
}

0 commit comments

Comments
 (0)