Skip to content

Commit

Permalink
Added tests, fixed linting
Browse files Browse the repository at this point in the history
  • Loading branch information
karl-exini committed Oct 16, 2020
1 parent 1d56b14 commit 4bddb7c
Show file tree
Hide file tree
Showing 13 changed files with 250 additions and 30 deletions.
1 change: 1 addition & 0 deletions src/detour.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Transform, TransformOptions } from 'stream';

export abstract class Detour extends Transform {
Expand Down
13 changes: 3 additions & 10 deletions src/parse-flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { ByteParser, ByteReader, finishedParser, ParseResult, ParseStep } from './byte-parser';
import { Detour } from './detour';
import { Lookup } from './lookup';
import { dicomPreambleLength, isPreamble, readHeader, tryReadHeader } from './parsing';
import { dicomPreambleLength, isPreamble, readHeader, tryReadHeader, warnIfOdd } from './parsing';
import {
DeflatedChunk,
DicomPart,
Expand Down Expand Up @@ -171,9 +171,7 @@ class InFmiHeader extends DicomParseStep {

public parse(reader: ByteReader): ParseResult {
const header = readHeader(reader, this.state);
if (header.valueLength % 2 > 0) {
console.warn(`Element ${tagToString(header.tag)} has odd length`);
}
warnIfOdd(header.tag, header.vr, header.valueLength);
if (groupNumber(header.tag) !== 2) {
console.warn('Missing or wrong File Meta Information Group Length (0002,0000)');
return new ParseResult(undefined, this.toDatasetStep(reader, header.valueLength));
Expand Down Expand Up @@ -235,9 +233,7 @@ class InDatasetHeader extends DicomParseStep {

public readDatasetHeader(reader: ByteReader): DicomPart {
const header = readHeader(reader, this.state);
if (header.valueLength % 2 > 0) {
console.warn(`Element ${tagToString(header.tag)} has odd length`);
}
warnIfOdd(header.tag, header.vr, header.valueLength);
if (header.vr) {
const bytes = reader.take(header.headerLength);
if (header.vr === VR.SQ || (header.vr === VR.UN && header.valueLength === indeterminateLength)) {
Expand Down Expand Up @@ -355,9 +351,6 @@ class InFragments extends DicomParseStep {

public parse(reader: ByteReader): ParseResult {
const header = readHeader(reader, this.state);
if (header.valueLength % 2 > 0) {
console.warn(`Element ${tagToString(header.tag)} has odd length`);
}
if (header.tag === 0xfffee000) {
// begin fragment
const nextState =
Expand Down
17 changes: 16 additions & 1 deletion src/parsing.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { bytesToTag, bytesToUInt, bytesToUShort, bytesToVR, groupNumber, isFileMetaInformation } from './base';
import {
bytesToTag,
bytesToUInt,
bytesToUShort,
bytesToVR,
groupNumber,
indeterminateLength,
isFileMetaInformation,
tagToString,
} from './base';
import { ByteReader } from './byte-parser';
import { Lookup } from './lookup';
import { VR } from './vr';
Expand Down Expand Up @@ -95,3 +104,9 @@ export function readHeader(reader: ByteReader, state: any): AttributeInfo {
}
return new AttributeInfo(tagVr.tag, tagVr.vr, 8, bytesToUInt(tagVrBytes.slice(4), state.bigEndian));
}

export function warnIfOdd(tag: number, vr: VR, valueLength: number): void {
if (valueLength % 2 > 0 && valueLength != indeterminateLength && vr != null && vr != VR.SQ) {
console.warn(`Element ${tagToString(tag)} has odd length`);
}
}
1 change: 1 addition & 0 deletions src/sources.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Readable } from 'stream';

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function singleSource(element: any, objectMode = false): Readable {
return new Readable({
objectMode,
Expand Down
2 changes: 1 addition & 1 deletion src/value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ function dateTimeBytes(vr: VR, value: ZonedDateTime): Buffer {

function personNameBytes(vr: VR, value: PersonName): Buffer {
if (vr === VR.PN) {
return new Buffer(value.toString());
return Buffer.from(value.toString());
}
throw Error('Cannot create value of VR ' + vr + ' from person name');
}
Expand Down
4 changes: 2 additions & 2 deletions test/collect-flow-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,9 @@ describe('A collect elements flow', () => {
data.studyDate(),
data.pixeDataFragments(),
item(4),
new Buffer([1, 2, 3, 4]),
Buffer.from([1, 2, 3, 4]),
item(4),
new Buffer([5, 6, 7, 8]),
Buffer.from([5, 6, 7, 8]),
sequenceDelimitation(),
);

Expand Down
40 changes: 40 additions & 0 deletions test/dicom-flow-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,46 @@ describe('The guaranteed delimitation flow', () => {
assert.strictEqual(nSeqDelims, 1);
});
});

it('should gracefully handle determinate length sequences and items that end with a sequence delimiter', () => {
const bytes = concatv(
data.studyDate(),
data.sequence(Tag.DerivationCodeSequence, 92),
item(84),
data.studyDate(),
data.sequence(Tag.DerivationCodeSequence, 40),
item(24),
data.studyDate(),
itemDelimitation(),
sequenceDelimitation(),
itemDelimitation(),
sequenceDelimitation(),
data.patientNameJohnDoe(),
);

const testFlow = toIndeterminateLengthSequences();

return util.testParts(bytes, pipe(parseFlow(), testFlow), (parts) => {
util.partProbe(parts)
.expectHeader(Tag.StudyDate)
.expectValueChunk()
.expectSequence(Tag.DerivationCodeSequence)
.expectItem(1)
.expectHeader(Tag.StudyDate)
.expectValueChunk()
.expectSequence(Tag.DerivationCodeSequence)
.expectItem(1)
.expectHeader(Tag.StudyDate)
.expectValueChunk()
.expectItemDelimitation()
.expectSequenceDelimitation()
.expectItemDelimitation()
.expectSequenceDelimitation()
.expectHeader(Tag.PatientName)
.expectValueChunk()
.expectDicomComplete();
});
});
});

describe('The InSequence support', () => {
Expand Down
18 changes: 9 additions & 9 deletions test/dicom-flows-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -607,14 +607,14 @@ describe('The FMI group length flow', () => {
});

describe('The utf8 flow', () => {
it('should transform a japanese patient name encoded with multiple character sets to valid utf8', () => {
it('should transform a japanese person name encoded with multiple character sets to valid utf8', () => {
const specificCharacterSet = concatv(
tagToBytesLE(Tag.SpecificCharacterSet),
Buffer.from('CS'),
shortToBytesLE(0x0010),
padToEvenLength(Buffer.from('\\ISO 2022 IR 149'), VR.CS),
);
const patientName = concatv(
const personName = concatv(
tagToBytesLE(0x00100010),
Buffer.from('PN'),
shortToBytesLE(0x002c),
Expand Down Expand Up @@ -669,7 +669,7 @@ describe('The utf8 flow', () => {
),
);

const bytes = concat(specificCharacterSet, patientName);
const bytes = concat(specificCharacterSet, personName);

return util.testParts(bytes, pipe(parseFlow(), toUtf8Flow()), (parts) => {
util.partProbe(parts)
Expand Down Expand Up @@ -717,7 +717,7 @@ describe('The utf8 flow', () => {
shortToBytesLE(0x000a),
padToEvenLength(Buffer.from('ISO_IR 13'), VR.CS),
);
const patientName = concatv(
const personName = concatv(
tagToBytesLE(0x00100010),
Buffer.from('PN'),
shortToBytesLE(0x0008),
Expand All @@ -728,7 +728,7 @@ describe('The utf8 flow', () => {
specificCharacterSet,
data.sequence(Tag.DerivationCodeSequence),
item(),
patientName,
personName,
itemDelimitation(),
sequenceDelimitation(),
);
Expand All @@ -754,14 +754,14 @@ describe('The utf8 flow', () => {
shortToBytesLE(0x0010),
padToEvenLength(Buffer.from('\\ISO 2022 IR 149'), VR.CS),
);
const patientNameCS = concatv(
const personNameCS = concatv(
tagToBytesLE(0x00100010),
Buffer.from('CS'),
shortToBytesLE(0x0004),
padToEvenLength(Buffer.from([0xd4, 0xcf, 0xc0, 0xde]), VR.PN),
);

const bytes = concat(specificCharacterSet, patientNameCS);
const bytes = concat(specificCharacterSet, personNameCS);

return util.testParts(bytes, pipe(parseFlow(), toUtf8Flow()), (parts) => {
util.partProbe(parts)
Expand All @@ -780,13 +780,13 @@ describe('The utf8 flow', () => {
shortToBytesLE(0x000a),
Buffer.from('ISO_IR 192'),
);
const patientName = concatv(
const personName = concatv(
tagToBytesLE(Tag.PatientName),
Buffer.from('PN'),
shortToBytesLE(0x000c),
Buffer.from('ABC^ÅÖ^ヤ'),
);
const bytes = concat(specificCharacterSet, patientName);
const bytes = concat(specificCharacterSet, personName);

return util.testParts(bytes, pipe(parseFlow(), toUtf8Flow()), (parts) => {
const newBytes = parts.map((p) => p.bytes).reduce((b1, b2) => concat(b1, b2), emptyBuffer);
Expand Down
84 changes: 83 additions & 1 deletion test/element-sink-test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import assert from 'assert';
import { concatv, intToBytesLE, item, sequenceDelimitation } from '../src/base';
import { concatv, indeterminateLength, intToBytesLE, item, itemDelimitation, sequenceDelimitation } from '../src/base';
import { elementFlow } from '../src/element-flows';
import { elementSink } from '../src/element-sink';
import {
Expand Down Expand Up @@ -132,6 +132,88 @@ describe('An element sink', () => {
}),
);
});

it('should handle determinate length items and sequences', () => {
const elementList = [
new SequenceElement(Tag.DerivationCodeSequence, 68),
new ItemElement(1, 16),
new ValueElement(Tag.StudyDate, VR.DA, Value.fromString(VR.DA, '20040329')),
new ItemElement(2, 36),
new SequenceElement(Tag.DerivationCodeSequence, 24),
new ItemElement(1, 16),
new ValueElement(Tag.StudyDate, VR.DA, Value.fromString(VR.DA, '20040329')),
];

return util.streamPromise(
arraySource(elementList, true),
elementSink((elements) => {
assert.deepStrictEqual(elements.toElements(false), elementList);
}),
);
});

it('should "handle item and sequence delimitations in when items and sequences are of determinate length', () => {
const elementList = [
new SequenceElement(Tag.DerivationCodeSequence, 108),
new ItemElement(1, 24),
new ValueElement(Tag.StudyDate, VR.DA, Value.fromString(VR.DA, '20040329')),
new ItemDelimitationElement(1),
new ItemElement(2, 60),
new SequenceElement(Tag.DerivationCodeSequence, 40),
new ItemElement(1, 24),
new ValueElement(Tag.StudyDate, VR.DA, Value.fromString(VR.DA, '20040329')),
new ItemDelimitationElement(1),
new SequenceDelimitationElement(),
new ItemDelimitationElement(2),
new SequenceDelimitationElement(),
];

const expectedElementList = [
new SequenceElement(Tag.DerivationCodeSequence, 68),
new ItemElement(1, 16),
new ValueElement(Tag.StudyDate, VR.DA, Value.fromString(VR.DA, '20040329')),
new ItemElement(2, 36),
new SequenceElement(Tag.DerivationCodeSequence, 24),
new ItemElement(1, 16),
new ValueElement(Tag.StudyDate, VR.DA, Value.fromString(VR.DA, '20040329')),
];

return util.streamPromise(
arraySource(elementList, true),
elementSink((elements) => {
assert.deepStrictEqual(elements.toElements(false), expectedElementList);
}),
);
});

it('should "handle implicit VR encoding', () => {
const bytes = concatv(
data.preamble,
data.fmiGroupLength(data.transferSyntaxUID(UID.ImplicitVRLittleEndian)),
data.transferSyntaxUID(UID.ImplicitVRLittleEndian),
data.patientNameJohnDoe(false, false),
data.sequence(Tag.DerivationCodeSequence, indeterminateLength, false, false),
item(),
data.patientNameJohnDoe(false, false),
data.studyDate(false, false),
itemDelimitation(),
item(),
data.sequence(Tag.DerivationCodeSequence, 24, false, false),
item(16),
data.patientNameJohnDoe(false, false),
itemDelimitation(),
sequenceDelimitation(),
);

return util.streamPromise(
singleSource(bytes, true),
parseFlow(),
elementFlow(),
elementSink((elements) => {
assert.deepStrictEqual(elements.toBytes(), bytes);
}),
);
});
});

describe('Fragments', () => {
Expand Down
6 changes: 3 additions & 3 deletions test/elements-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,13 +343,13 @@ describe('Elements', () => {
});

it('should set bytes', () => {
const updated = elements.setBytes(Tag.SeriesDate, VR.DA, new Buffer('20100101'));
const updated = elements.setBytes(Tag.SeriesDate, VR.DA, Buffer.from('20100101'));
assert.deepStrictEqual(updated.dateByTag(Tag.SeriesDate), LocalDate.parse('2010-01-01'));
});

it('should set nested bytes', () => {
const tagPath = TagPath.fromItem(Tag.DerivationCodeSequence, 1).thenTag(Tag.SeriesDate);
const updated = elements.setNestedBytes(tagPath, VR.DA, new Buffer('20100101'));
const updated = elements.setNestedBytes(tagPath, VR.DA, Buffer.from('20100101'));
assert.deepStrictEqual(updated.dateByPath(tagPath), LocalDate.parse('2010-01-01'));
});

Expand Down Expand Up @@ -482,7 +482,7 @@ describe('Elements', () => {
});

it('should update character sets', () => {
const updatedCs1 = elements.setCharacterSets(CharacterSets.fromBytes(new Buffer('\\ISO 2022 IR 127')))
const updatedCs1 = elements.setCharacterSets(CharacterSets.fromBytes(Buffer.from('\\ISO 2022 IR 127')))
.characterSets;
assert.equal(updatedCs1.charsets.trim(), '\\ISO 2022 IR 127');
const updatedCs2 = elements.setElementSet(
Expand Down

0 comments on commit 4bddb7c

Please sign in to comment.