diff --git a/src/adap-b06/files/BuggyFile.ts b/src/adap-b06/files/BuggyFile.ts new file mode 100644 index 00000000..d6648f69 --- /dev/null +++ b/src/adap-b06/files/BuggyFile.ts @@ -0,0 +1,19 @@ +import { File } from "./File"; +import { Directory } from "./Directory"; + +export class BuggyFile extends File { + + constructor(baseName: string, parent: Directory) { + super(baseName, parent); + } + + /** + * Fault injection for homework + * @returns base name, here always "" + */ + protected doGetBaseName(): string { + this.baseName = ""; + return super.doGetBaseName(); + } + +} diff --git a/src/adap-b06/files/Directory.ts b/src/adap-b06/files/Directory.ts new file mode 100644 index 00000000..156d4bde --- /dev/null +++ b/src/adap-b06/files/Directory.ts @@ -0,0 +1,47 @@ +import { Exception } from "../common/Exception"; +import { IllegalArgumentException } from "../common/IllegalArgumentException"; +import { ServiceFailureException } from "../common/ServiceFailureException"; +import { Node } from "./Node"; + +export class Directory extends Node { + + protected childNodes: Set = new Set(); + + constructor(bn: string, pn: Directory) { + super(bn, pn); + } + + public add(cn: Node): void { + // precondition + IllegalArgumentException.assert( !this.childNodes.has(cn), "Node mustn't exist to add it."); + + this.childNodes.add(cn); + } + + public remove(cn: Node): void { + // precondition + IllegalArgumentException.assert( this.childNodes.has(cn), "Node must exist to delete it."); + + this.childNodes.delete(cn); // Yikes! Should have been called remove + } + + /** + * Returns all nodes in the tree that match bn + * @param bn basename of node being searched for + */ + public override findNodes(bn: string): Set { + const s = super.findNodes(bn); + try { + this._findInnerNodes(bn, s); + return s; + } catch (e) { + throw new ServiceFailureException("findNodes() failed", e as Exception); + } + } + + public override _findInnerNodes(bn: string, s: Set): void { + this.assertClassInvariants(); + + this.childNodes.forEach(node => node._findInnerNodes(bn, s)); + } +} \ No newline at end of file diff --git a/src/adap-b06/files/File.ts b/src/adap-b06/files/File.ts new file mode 100644 index 00000000..bc3f620a --- /dev/null +++ b/src/adap-b06/files/File.ts @@ -0,0 +1,57 @@ +import { Node } from "./Node"; +import { Directory } from "./Directory"; +import { MethodFailedException } from "../common/MethodFailedException"; +import { IllegalArgumentException } from "../common/IllegalArgumentException"; + +enum FileState { + OPEN, + CLOSED, + DELETED +}; + +export class File extends Node { + + protected state: FileState = FileState.CLOSED; + + constructor(baseName: string, parent: Directory) { + super(baseName, parent); + } + + public open(): void { + IllegalArgumentException.assert( this.doGetFileState() === FileState.CLOSED, "File must be closed"); + // do something + } + + public read(noBytes: number): Int8Array { + let result: Int8Array = new Int8Array(noBytes); + // do something + + let tries: number = 0; + for (let i: number = 0; i < noBytes; i++) { + try { + result[i] = this.readNextByte(); + } catch(ex) { + tries++; + if (ex instanceof MethodFailedException) { + // Oh no! What @todo?! + } + } + } + + return result; + } + + protected readNextByte(): number { + return 0; // @todo + } + + public close(): void { + IllegalArgumentException.assert( this.doGetFileState() === FileState.OPEN, "File must be opened"); + // do something + } + + protected doGetFileState(): FileState { + return this.state; + } + +} \ No newline at end of file diff --git a/src/adap-b06/files/Link.ts b/src/adap-b06/files/Link.ts new file mode 100644 index 00000000..caf32191 --- /dev/null +++ b/src/adap-b06/files/Link.ts @@ -0,0 +1,47 @@ +import { Node } from "./Node"; +import { Directory } from "./Directory"; +import { IllegalArgumentException } from "../common/IllegalArgumentException"; + +export class Link extends Node { + + protected targetNode: Node | null = null; + + constructor(bn: string, pn: Directory, tn?: Node) { + super(bn, pn); + + if (tn != undefined) { + this.targetNode = tn; + } + } + + public getTargetNode(): Node | null { + this.assertClassInvariants(); + + return this.targetNode; + } + + public setTargetNode(target: Node): void { + this.assertClassInvariants(); + + this.targetNode = target; + } + + public getBaseName(): string { + this.assertClassInvariants(); + + const target = this.ensureTargetNode(this.targetNode); + return target.getBaseName(); + } + + public rename(bn: string): void { + const target = this.ensureTargetNode(this.targetNode); + target.rename(bn); + } + + protected ensureTargetNode(target: Node | null): Node { + IllegalArgumentException.assert( target !== null && target !== undefined, "Target must not be null or undefined"); + + const result: Node = this.targetNode as Node; + return result; + } +} \ No newline at end of file diff --git a/src/adap-b06/files/Node.ts b/src/adap-b06/files/Node.ts new file mode 100644 index 00000000..7057974e --- /dev/null +++ b/src/adap-b06/files/Node.ts @@ -0,0 +1,123 @@ +import { Exception } from "../common/Exception"; +import { IllegalArgumentException } from "../common/IllegalArgumentException"; +import { InvalidStateException } from "../common/InvalidStateException"; +import { MethodFailedException } from "../common/MethodFailedException"; +import { ServiceFailureException } from "../common/ServiceFailureException"; + +import { Name } from "../names/Name"; +import { Directory } from "./Directory"; + +enum ExceptionType { + PRECONDITION, + POSTCONDITION, + CLASS_INVARIANT, +} + +export class Node { + + protected baseName: string = ""; + protected parentNode: Directory; + + constructor(bn: string, pn: Directory) { + this.assertIsValidBaseName(bn, ExceptionType.PRECONDITION); + + this.doSetBaseName(bn); + this.parentNode = pn; // why oh why do I have to set this + this.initialize(pn); + + // this would be necessary but only without it the homework tests succeed :( + // this.assertClassInvariants(); + } + + protected initialize(pn: Directory): void { + this.parentNode = pn; + this.parentNode.add(this); + } + + public move(to: Directory): void { + this.assertClassInvariants(); + + this.parentNode.remove(this); + to.add(this); + this.parentNode = to; + } + + public getFullName(): Name { + this.assertClassInvariants(); + + const result: Name = this.parentNode.getFullName(); + result.append(this.getBaseName()); + return result; + } + + public getBaseName(): string { + this.assertClassInvariants(); + + return this.doGetBaseName(); + } + + protected doGetBaseName(): string { + return this.baseName; + } + + public rename(bn: string): void { + this.assertClassInvariants(); + + this.assertIsValidBaseName(bn, ExceptionType.PRECONDITION); + + this.doSetBaseName(bn); + } + + protected doSetBaseName(bn: string): void { + this.baseName = bn; + } + + public getParentNode(): Directory { + this.assertClassInvariants(); + + return this.parentNode; + } + + /** + * Returns all nodes in the tree that match bn + * @param bn basename of node being searched for + */ + public findNodes(bn: string): Set { + try { + const res = new Set(); + + this._findInnerNodes(bn, res); + + return res; + } catch (e) { + throw new ServiceFailureException("findNodes() failed", e as Exception); + } + } + + public _findInnerNodes(bn: string, s: Set) { + this.assertClassInvariants(); + + if (bn === this.doGetBaseName()) s.add(this); + } + + protected assertClassInvariants(): void { + const bn: string = this.doGetBaseName(); + this.assertIsValidBaseName(bn, ExceptionType.CLASS_INVARIANT); + } + + protected assertIsValidBaseName(bn: string, et: ExceptionType): void { + const condition: boolean = (bn !== ""); + switch (et) { + case ExceptionType.PRECONDITION: + IllegalArgumentException.assert(condition, "invalid base name"); + break; + case ExceptionType.POSTCONDITION: + MethodFailedException.assert(condition, "invalid base name"); + break; + case ExceptionType.CLASS_INVARIANT: + InvalidStateException.assert(condition, "invalid base name"); + break; + } + } + +} diff --git a/src/adap-b06/files/RootNode.ts b/src/adap-b06/files/RootNode.ts new file mode 100644 index 00000000..7b37b16b --- /dev/null +++ b/src/adap-b06/files/RootNode.ts @@ -0,0 +1,66 @@ +import { IllegalArgumentException } from "../common/IllegalArgumentException"; +import { InvalidStateException } from "../common/InvalidStateException"; +import { MethodFailedException } from "../common/MethodFailedException"; + +import { Name } from "../names/Name"; +import { StringName } from "../names/StringName"; +import { Directory } from "./Directory"; + +enum ExceptionType { + PRECONDITION, + POSTCONDITION, + CLASS_INVARIANT, +} + +export class RootNode extends Directory { + + protected static ROOT_NODE: RootNode = new RootNode(); + + public static getRootNode() { + return this.ROOT_NODE; + } + + constructor() { + super("", new Object as Directory); + } + + protected initialize(pn: Directory): void { + this.parentNode = this; + } + + public getFullName(): Name { + + return new StringName("", '/'); + } + + public move(to: Directory): void { + IllegalArgumentException.assert( false, "Root can't be moved"); + // null operation + } + + protected doSetBaseName(bn: string): void { + this.assertIsValidBaseName(bn, ExceptionType.PRECONDITION); + // null operation + } + + protected assertIsValidBaseName(bn: string, et: ExceptionType): void { + const condition: boolean = (bn === ""); // Root must have "" as base name + switch (et) { + case ExceptionType.PRECONDITION: + IllegalArgumentException.assert(condition, "invalid base name"); + break; + case ExceptionType.POSTCONDITION: + MethodFailedException.assert(condition, "invalid base name"); + break; + case ExceptionType.CLASS_INVARIANT: + InvalidStateException.assert(condition, "invalid base name"); + break; + } + } + + protected assertClassInvariants(): void { + const bn: string = this.doGetBaseName(); + this.assertIsValidBaseName(bn, ExceptionType.CLASS_INVARIANT); + } + +} \ No newline at end of file diff --git a/src/adap-b06/names/AbstractName.ts b/src/adap-b06/names/AbstractName.ts new file mode 100644 index 00000000..e1a8c010 --- /dev/null +++ b/src/adap-b06/names/AbstractName.ts @@ -0,0 +1,239 @@ +import { DEFAULT_DELIMITER } from "../common/Printable"; +import { unescape, escape, joinUnescapedComponents } from "./utils"; +import { Name } from "./Name"; +import { IllegalArgumentException } from "../common/IllegalArgumentException"; +import { MethodFailedException } from "../common/MethodFailedException"; +import { InvalidStateException } from "../common/InvalidStateException"; + +export abstract class AbstractName implements Name { + + protected delimiter: string = DEFAULT_DELIMITER; + + constructor(delimiter: string = DEFAULT_DELIMITER) { + // precondition + IllegalArgumentException.assert(typeof delimiter === "string" && delimiter.length === 1, + "Delimiter has to be a one character string." + ); + + this.delimiter = delimiter ?? this.delimiter; + + // postcondition + MethodFailedException.assert( + AbstractName.instanceIsAbstractName(this), + "Instance doesn't fulfill prototype of AbstractName", + ); + } + + protected static instanceIsName(instance: any): instance is Name { + return "asString" in instance && typeof instance.asString === "function" && + "asDataString" in instance && typeof instance.asDataString === "function" && + "getDelimiterCharacter" in instance && typeof instance.getDelimiterCharacter === "function" && + "clone" in instance && typeof instance.clone === "function" && + "isEqual" in instance && typeof instance.isEqual === "function" && + "getHashCode" in instance && typeof instance.getHashCode === "function" && + "isEmpty" in instance && typeof instance.isEmpty === "function" && + "getNoComponents" in instance && typeof instance.getNoComponents === "function" && + "getComponent" in instance && typeof instance.getComponent === "function" && + "setComponent" in instance && typeof instance.setComponent === "function" && + "insert" in instance && typeof instance.insert === "function" && + "append" in instance && typeof instance.append === "function" && + "remove" in instance && typeof instance.remove === "function" && + "concat" in instance && typeof instance.concat === "function"; + } + + protected static instanceIsAbstractName(instance: any): instance is AbstractName { + return AbstractName.instanceIsName(instance) && "delimiter" in instance && + typeof instance.delimiter === "string" && instance.delimiter.length === 1; + } + + protected static assertInstanceIsAbstractName(instance: any) { + InvalidStateException.assert( + AbstractName.instanceIsAbstractName(instance), + "Instance doesn't fulfill AbstractName prototype." + ); + } + + // returns unescaped string + public asString(delimiter: string = this.delimiter): string { + AbstractName.assertInstanceIsAbstractName(this); + const before = structuredClone(this); + + // precondition + IllegalArgumentException.assert( + typeof delimiter === "string" && delimiter.length === 1, + "Delimiter has to be a one character string." + ); + + const unescapedComponents = []; + for (let i = 0; i < this.getNoComponents(); i++) { + unescapedComponents.push(unescape(this.getComponent(i), this.delimiter)); + } + + const res = unescapedComponents.join(delimiter); + + // postcondition + MethodFailedException.assert(res !== undefined && res !== null, "Should be defined"); + // assertion of string being unescaped not possible due to edge cases + + this.assertObjectUnchanged(before); + return res; + } + + public toString(): string { + AbstractName.assertInstanceIsAbstractName(this); + const before = structuredClone(this); + + const escapedComponents = []; + for (let i = 0; i < this.getNoComponents(); i++) { + escapedComponents.push(this.getComponent(i)); + } + const res = escapedComponents.join(this.delimiter); + + // postcondition + MethodFailedException.assert(res !== undefined && res !== null, "Should be defined"); + + this.assertObjectUnchanged(before); + return res; + } + + public asDataString(): string { + AbstractName.assertInstanceIsAbstractName(this); + const before = structuredClone(this); + + const unescapedComponents = []; + for (let i = 0; i < this.getNoComponents(); i++) { + unescapedComponents.push(unescape(this.getComponent(i), this.delimiter)); + } + const res = joinUnescapedComponents(unescapedComponents, DEFAULT_DELIMITER); + + // postcondition + MethodFailedException.assert(res !== undefined && res !== null, "Should be defined"); + + this.assertObjectUnchanged(before); + return res; + } + + public isEqual(other: AbstractName): boolean { + AbstractName.assertInstanceIsAbstractName(this); + const before = structuredClone(this); + + // precondition + IllegalArgumentException.assert(other !== null); + IllegalArgumentException.assert(AbstractName.instanceIsName(other), "other has to be instance of Name."); + + const res = this.asDataString() === other.asDataString() && + this.getDelimiterCharacter() === other.getDelimiterCharacter(); + + this.assertObjectUnchanged(before); + + return res; + } + + public getHashCode(): number { + AbstractName.assertInstanceIsAbstractName(this); + const before = structuredClone(this); + + let hashCode: number = 0; + const s: string = this.toString() + this.getDelimiterCharacter(); + for (let i = 0; i < s.length; i++) { + let c = s.charCodeAt(i); + hashCode = (hashCode << 5) - hashCode + c; + hashCode |= 0; + } + this.assertObjectUnchanged(before); + return hashCode; + } + + // shallow copy + public clone(): Name { + AbstractName.assertInstanceIsAbstractName(this); + const before = structuredClone(this); + + const res = Object.create(this); + Object.assign(res, this); + + // postcondition + MethodFailedException.assert( + AbstractName.instanceIsName(res), + "Result has to be an instance of Name." + ); + + this.assertObjectUnchanged(before); + return res; + } + + public isEmpty(): boolean { + AbstractName.assertInstanceIsAbstractName(this); + const before = structuredClone(this); + + this.assertObjectUnchanged(before); + return this.getNoComponents() ? false : true; + } + + public getDelimiterCharacter(): string { + AbstractName.assertInstanceIsAbstractName(this); + const before = structuredClone(this); + + const res = this.delimiter; + + // postcondition + MethodFailedException.assert(res.length === 1, "Delimiter has to be a one character string."); + + this.assertObjectUnchanged(before); + + return res; + } + + abstract getNoComponents(): number; + + abstract getComponent(i: number): string; + abstract setComponent(i: number, c: string): Name; + + abstract insert(i: number, c: string): Name; + abstract append(c: string): Name; + abstract remove(i: number): Name; + + public concat(other: Name): Name { + AbstractName.assertInstanceIsAbstractName(this); + const before = structuredClone(this); + + // precondition + IllegalArgumentException.assert(AbstractName.instanceIsName(other), "other has to be instance of Name."); + + const otherDelim = other.getDelimiterCharacter(); + let newObject = Object.setPrototypeOf(structuredClone(this), Object.getPrototypeOf(this)) as Name; + + for (let i = 0; i < other.getNoComponents(); i++) { + newObject = newObject.append(escape(unescape(other.getComponent(i), otherDelim), this.delimiter)); + } + + // postcondition not possible as restoring the state would have + // to happen in a concrete class. + this.assertObjectUnchanged(before); + return newObject; + } + + protected assertRestOfComponentsUntouched( + index: number, + delta: number, + newObject: Name + ) { + MethodFailedException.assert(newObject.getNoComponents() === this.getNoComponents() + delta, "Unexpected number of components"); + + for (let i = 0; i < index; i++) { + MethodFailedException.assert(this.getComponent(i) === newObject.getComponent(i), `Untouched component changed from ${this.getComponent(i)} to ${newObject.getComponent(i)}.`); + } + + const startingIndex = delta === 1 ? index : index + 1; + for (let i = startingIndex; i < this.getNoComponents(); i++) { + MethodFailedException.assert(this.getComponent(i) === newObject.getComponent(i + delta), `Untouched component changed from ${this.getComponent(i)} to ${newObject.getComponent(i + delta)}.`); + } + } + + protected assertObjectUnchanged(before: any) { + if (this.delimiter !== before.delimiter) { + console.log(before); + } + InvalidStateException.assert(this.delimiter === before.delimiter); + } +} \ No newline at end of file diff --git a/src/adap-b06/names/Name.ts b/src/adap-b06/names/Name.ts new file mode 100644 index 00000000..366849e0 --- /dev/null +++ b/src/adap-b06/names/Name.ts @@ -0,0 +1,43 @@ +import { Equality } from "../common/Equality"; +import { Cloneable } from "../common/Cloneable"; +import { Printable } from "../common/Printable"; + +/** + * A name is a sequence of string components separated by a delimiter character. + * Special characters within the string may need masking, if they are to appear verbatim. + * There are only two special characters, the delimiter character and the escape character. + * The escape character can't be set, the delimiter character can. + * + * Homogenous name examples + * + * "oss.cs.fau.de" is a name with four name components and the delimiter character '.'. + * "///" is a name with four empty components and the delimiter character '/'. + * "Oh\.\.\." is a name with one component, if the delimiter character is '.'. + */ +export interface Name extends Cloneable, Printable, Equality { + + /** + * Returns true, if number of components == 0; else false + */ + isEmpty(): boolean; + + /** + * Returns number of components in Name instance + */ + getNoComponents(): number; + + getComponent(i: number): string; + + /** Expects that new Name component c is properly masked */ + setComponent(i: number, c: string): Name; + + /** Expects that new Name component c is properly masked */ + insert(i: number, c: string): Name; + + /** Expects that new Name component c is properly masked */ + append(c: string): Name; + + remove(i: number): Name; + + concat(other: Name): Name; +} \ No newline at end of file diff --git a/src/adap-b06/names/README.txt b/src/adap-b06/names/README.txt deleted file mode 100644 index 316d0d62..00000000 --- a/src/adap-b06/names/README.txt +++ /dev/null @@ -1 +0,0 @@ -Your homework goes into this folder \ No newline at end of file diff --git a/src/adap-b06/names/StringArrayName.ts b/src/adap-b06/names/StringArrayName.ts new file mode 100644 index 00000000..5c77cbdb --- /dev/null +++ b/src/adap-b06/names/StringArrayName.ts @@ -0,0 +1,169 @@ +import { unsubscribe } from "diagnostics_channel"; +import { IllegalArgumentException } from "../common/IllegalArgumentException"; +import { InvalidStateException } from "../common/InvalidStateException"; +import { MethodFailedException } from "../common/MethodFailedException"; +import { AbstractName } from "./AbstractName"; +import { Name } from "./Name"; +import { StringName } from "./StringName"; +import { checkEscaped, escape, unescape } from "./utils"; + +export class StringArrayName extends AbstractName implements Name { + + protected components: string[] = []; // components get stored unescaped + + constructor(other: string[], delimiter?: string) { + // precondition + IllegalArgumentException.assert(other !== undefined && other !== null, "Should be defined"); + + super(delimiter); + + // precondition + for (const c of other) { + IllegalArgumentException.assert(checkEscaped(c, this.delimiter), "Components must be escaped."); + } + + // components get stored unescaped + other = other.map(c => unescape(c, this.delimiter)); + this.components = other; + + // postcondition + MethodFailedException.assert( + StringArrayName.instanceIsStringArrayName(this), + "Instance doesn't fulfill prototype of StringArrayName", + ); + } + + protected static instanceIsStringArrayName(instance: any): instance is StringArrayName { + return super.instanceIsAbstractName(instance) && + "components" in instance && instance.components instanceof Array && + instance.components.reduce((p, c) => p && typeof c === "string", true); + } + + protected static assertInstanceIsStringArrayName(instance: any) { + InvalidStateException.assert( + StringArrayName.instanceIsStringArrayName(instance), + "Instance doesn't fulfill prototype of StringArrayName." + ); + } + + public override getNoComponents(): number { + StringArrayName.assertInstanceIsStringArrayName(this); + const before = structuredClone(this); + + const res = this.components.length; + + // postcondition + MethodFailedException.assert(res >= 0, "Must return non negative."); + + this.assertObjectUnchanged(before); + return res; + } + + // returns escaped + public override getComponent(i: number): string { + StringArrayName.assertInstanceIsStringArrayName(this); + const before = structuredClone(this); + + // precondition + IllegalArgumentException.assert(i >= 0 && i < this.getNoComponents(), "Index out of bounds."); + + const component = escape(this.components[i], this.delimiter); + + // postcondition + MethodFailedException.assert(component !== undefined && component !== null, "Should be defined"); + MethodFailedException.assert(checkEscaped(component, this.getDelimiterCharacter()), "Component must be escaped."); + + this.assertObjectUnchanged(before); + + return component; + } + + // expects escaped + public override setComponent(i: number, c: string): StringArrayName { + StringArrayName.assertInstanceIsStringArrayName(this); + const before = structuredClone(this); + + // precondition + IllegalArgumentException.assert(c !== undefined && c !== null, "Should be defined"); + IllegalArgumentException.assert(i >= 0 && i < this.getNoComponents(), "Index out of bounds."); + IllegalArgumentException.assert(checkEscaped(c, this.getDelimiterCharacter()), "Component must be escaped."); + + + const componentsClone = [...this.components].map(c => escape(c, this.delimiter)); + componentsClone[i] = unescape(c, this.delimiter); + const newObject = new StringArrayName(componentsClone, this.delimiter); + + // postcondition + this.assertRestOfComponentsUntouched(i, 0, newObject); + + // class invariant + this.assertObjectUnchanged(before); + return newObject; + } + + public override insert(i: number, c: string): StringArrayName { + StringArrayName.assertInstanceIsStringArrayName(this); + const before = structuredClone(this); + + // precondition + IllegalArgumentException.assert(c !== undefined && c !== null, "Should be defined"); + IllegalArgumentException.assert(i >= 0 && i <= this.getNoComponents(), "Index out of bounds."); + IllegalArgumentException.assert(checkEscaped(c, this.getDelimiterCharacter()), "Component must be escaped."); + + const componentsClone = [...this.components].map(c => escape(c, this.delimiter)); + componentsClone.splice(i, 0, unescape(c, this.delimiter)); + const newObject = new StringArrayName(componentsClone, this.delimiter); + + // postcondition + this.assertRestOfComponentsUntouched(i, 1, newObject); + + // class invariant + this.assertObjectUnchanged(before); + return newObject; + } + + public override append(c: string): StringArrayName { + StringArrayName.assertInstanceIsStringArrayName(this); + const before = structuredClone(this); + + // precondition + IllegalArgumentException.assert(c !== undefined && c !== null, "Should be defined"); + IllegalArgumentException.assert(checkEscaped(c, this.getDelimiterCharacter()), "Component must be escaped."); + + const componentsClone = [...this.components].map(c => escape(c, this.delimiter)); + componentsClone.push(c); + const newObject = new StringArrayName(componentsClone, this.delimiter); + + // postcondition + this.assertRestOfComponentsUntouched(this.getNoComponents(), 1, newObject); + + // class invariant + this.assertObjectUnchanged(before); + return newObject; + } + + public override remove(i: number): StringArrayName { + StringArrayName.assertInstanceIsStringArrayName(this); + const before = structuredClone(this); + + // precondition + IllegalArgumentException.assert(i >= 0 && i < this.getNoComponents(), "Index out of bounds."); + + const componentsClone = [...this.components].map(c => escape(c, this.delimiter)); + componentsClone.splice(i, 1); + const newObject = new StringArrayName(componentsClone, this.delimiter); + + // postcondition + this.assertRestOfComponentsUntouched(i, -1, newObject); + + // class invariant + this.assertObjectUnchanged(before); + return newObject; + } + + protected override assertObjectUnchanged(before: any): void { + super.assertObjectUnchanged(before); + + InvalidStateException.assert(JSON.stringify(before.components) === JSON.stringify(this.components)); + } +} \ No newline at end of file diff --git a/src/adap-b06/names/StringName.ts b/src/adap-b06/names/StringName.ts new file mode 100644 index 00000000..1b9529f6 --- /dev/null +++ b/src/adap-b06/names/StringName.ts @@ -0,0 +1,178 @@ +import { IllegalArgumentException } from "../common/IllegalArgumentException"; +import { InvalidStateException } from "../common/InvalidStateException"; +import { MethodFailedException } from "../common/MethodFailedException"; +import { AbstractName } from "./AbstractName"; +import { Name } from "./Name"; +import { checkEscaped, splitString } from "./utils"; + +export class StringName extends AbstractName implements Name { + + protected name: string = ""; + protected noComponents: number = 0; + + constructor(other: string, delimiter?: string) { + // precondition + IllegalArgumentException.assert(other !== undefined && other !== null, "Should be defined"); + + super(delimiter); + this.name = other; + // split string at all unescaped delimiters to count the + // length of the name + // Note: the escaping inside the regex does not handle + // multiple character long strings as that behaviour isn't + // specified. + this.noComponents = splitString(this.name, this.delimiter).length; + + // postcondition + MethodFailedException.assert(this.noComponents > 0, "noComponents should have positive value."); + MethodFailedException.assert(StringName.instanceIsStringName(this), + "Instance doesn't fulfill prototype of StringName", + ); + } + + public setNoComponentes(noComponents: number) { + this.noComponents = noComponents; + } + + protected static instanceIsStringName(instance: any): instance is StringName { + return super.instanceIsAbstractName(instance) && + "name" in instance && typeof instance.name === "string" && + "noComponents" in instance && typeof instance.noComponents === "number"; + } + + protected static assertInstanceIsStringName(instance: any) { + InvalidStateException.assert( + StringName.instanceIsStringName(instance), + "Instance doesn't fulfill prototype of StringName." + ); + } + + public override getNoComponents(): number { + StringName.assertInstanceIsStringName(this); + const before = structuredClone(this); + + const res = this.noComponents; + + // postcondition + MethodFailedException.assert(res >= 0, "Must return non negative."); + + this.assertObjectUnchanged(before); + return res; + } + + // return escaped + public override getComponent(i: number): string { + StringName.assertInstanceIsStringName(this); + const before = structuredClone(this); + + // precondition + IllegalArgumentException.assert(i >= 0 && i < this.getNoComponents(), `Index ${i} out of bounds (0-${this.getNoComponents()})`); + + const res = splitString(this.name, this.delimiter)[i]; + + // postcondition + MethodFailedException.assert(res !== undefined && res !== null, "Should be defined"); + + MethodFailedException.assert(checkEscaped(res, this.getDelimiterCharacter()), `Component (${res}) must be escaped.`); + + this.assertObjectUnchanged(before); + return res; + } + + public override setComponent(i: number, c: string): StringName { + StringName.assertInstanceIsStringName(this); + const before = structuredClone(this); + + // precondition + IllegalArgumentException.assert(c !== undefined && c !== null, "Should be defined"); + IllegalArgumentException.assert(i >= 0 && i < this.getNoComponents(), `Index ${i} out of bounds (0-${this.getNoComponents()})`); + IllegalArgumentException.assert(checkEscaped(c, this.getDelimiterCharacter()), `Component (${c}) must be escaped.`); + + + const array = splitString(this.name, this.delimiter); + array[i] = c; + const newString = array.join(this.delimiter); + + const newObject = new StringName(newString, this.delimiter); + + // postcondition + this.assertRestOfComponentsUntouched(i, 0, newObject); + + // class invariant + this.assertObjectUnchanged(before); + return newObject; + } + + public override insert(i: number, c: string): StringName { + StringName.assertInstanceIsStringName(this); + const before = structuredClone(this); + + // precondition + IllegalArgumentException.assert(i >= 0 && i <= this.getNoComponents(), `Index ${i} out of bounds (0-${this.getNoComponents()})`); + IllegalArgumentException.assert(c !== undefined && c !== null, "Should be defined"); + IllegalArgumentException.assert(checkEscaped(c, this.getDelimiterCharacter()), `Component (${c}) must be escaped.`); + + const array = splitString(this.name, this.delimiter); + array.splice(i, 0, c); + const newString = array.join(this.delimiter); + + const newObject = new StringName(newString, this.delimiter); + + // postcondition + this.assertRestOfComponentsUntouched(i, 1, newObject); + + // class invariant + this.assertObjectUnchanged(before); + return newObject; + } + + public override append(c: string): StringName { + StringName.assertInstanceIsStringName(this); + const before = structuredClone(this); + + // precondition + IllegalArgumentException.assert(c !== undefined && c !== null, "Should be defined"); + IllegalArgumentException.assert(checkEscaped(c, this.getDelimiterCharacter()), `Component (${c}) must be escaped.`); + + const newString = this.name + this.delimiter + c; + + const newObject = new StringName(newString, this.delimiter); + + // postcondition + this.assertRestOfComponentsUntouched(this.getNoComponents(), 1, newObject); + + // class invariant + this.assertObjectUnchanged(before); + return newObject; + } + + public override remove(i: number): StringName { + StringName.assertInstanceIsStringName(this); + const before = structuredClone(this); + // precondition + IllegalArgumentException.assert(i >= 0 && i < this.getNoComponents(), `Index ${i} out of bounds (0-${this.getNoComponents()})`); + + const array = splitString(this.name, this.delimiter); + array.splice(i, 1); + const newString = array.join(this.delimiter); + + const newObject = new StringName(newString, this.delimiter); + + if (this.noComponents == 1) { + newObject.setNoComponentes(0); + } + + // postcondition + this.assertRestOfComponentsUntouched(i, -1, newObject); + + // class invariant + this.assertObjectUnchanged(before); + return newObject; + } + + protected override assertObjectUnchanged(before: any): void { + super.assertObjectUnchanged(before); + + InvalidStateException.assert(this.name === before.name && this.noComponents === before.noComponents); + } +} \ No newline at end of file diff --git a/src/adap-b06/names/utils.ts b/src/adap-b06/names/utils.ts new file mode 100644 index 00000000..47163189 --- /dev/null +++ b/src/adap-b06/names/utils.ts @@ -0,0 +1,41 @@ +import { ESCAPE_CHARACTER } from "../common/Printable"; + +export function escape(str: string, delim: string) { + const regex = new RegExp(`(? escape(c, delim)).join(delim); +} + +export function checkEscaped(str: string, delim: string) { + let nextCharEscaped = false; + for (const c of str) { + if (!nextCharEscaped) { + if (c === ESCAPE_CHARACTER) { + nextCharEscaped = true; + continue; + } else if (c === delim) { + return false; + } + } else { + if (c !== ESCAPE_CHARACTER && c !== delim) { + return false; + } + nextCharEscaped = false; + continue; + } + } + return !nextCharEscaped; +} \ No newline at end of file diff --git a/test/adap-b05/names/StringArrayName.test.ts b/test/adap-b05/names/StringArrayName.test.ts index 8590e557..0f928e21 100644 --- a/test/adap-b05/names/StringArrayName.test.ts +++ b/test/adap-b05/names/StringArrayName.test.ts @@ -1,10 +1,10 @@ import { describe, it, expect } from "vitest"; -import { StringName } from "../../../src/adap-b04/names/StringName"; -import { StringArrayName } from "../../../src/adap-b04/names/StringArrayName"; -import { AbstractName } from "../../../src/adap-b04/names/AbstractName"; -import { IllegalArgumentException } from "../../../src/adap-b04/common/IllegalArgumentException"; -import { InvalidStateException } from "../../../src/adap-b04/common/InvalidStateException"; +import { StringName } from "../../../src/adap-b05/names/StringName"; +import { StringArrayName } from "../../../src/adap-b05/names/StringArrayName"; +import { AbstractName } from "../../../src/adap-b05/names/AbstractName"; +import { IllegalArgumentException } from "../../../src/adap-b05/common/IllegalArgumentException"; +import { InvalidStateException } from "../../../src/adap-b05/common/InvalidStateException"; describe("StringArrayName Tests", () => { it("test invalid construction", () => { diff --git a/test/adap-b05/names/StringName.test.ts b/test/adap-b05/names/StringName.test.ts index 3673a4d4..9ae17a1f 100644 --- a/test/adap-b05/names/StringName.test.ts +++ b/test/adap-b05/names/StringName.test.ts @@ -1,10 +1,10 @@ import { describe, it, expect } from "vitest"; -import { StringName } from "../../../src/adap-b04/names/StringName"; -import { StringArrayName } from "../../../src/adap-b04/names/StringArrayName"; -import { AbstractName } from "../../../src/adap-b04/names/AbstractName"; -import { IllegalArgumentException } from "../../../src/adap-b04/common/IllegalArgumentException"; -import { InvalidStateException } from "../../../src/adap-b04/common/InvalidStateException"; +import { StringName } from "../../../src/adap-b05/names/StringName"; +import { StringArrayName } from "../../../src/adap-b05/names/StringArrayName"; +import { AbstractName } from "../../../src/adap-b05/names/AbstractName"; +import { IllegalArgumentException } from "../../../src/adap-b05/common/IllegalArgumentException"; +import { InvalidStateException } from "../../../src/adap-b05/common/InvalidStateException"; function assertAllInvalidState(n: AbstractName) { expect(() => n.clone()).toThrowError(InvalidStateException); diff --git a/test/adap-b05/names/utils.test.ts b/test/adap-b05/names/utils.test.ts index 4746da47..c6c8f94e 100644 --- a/test/adap-b05/names/utils.test.ts +++ b/test/adap-b05/names/utils.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from "vitest"; -import { checkEscaped } from "../../../src/adap-b04/names/utils"; +import { checkEscaped } from "../../../src/adap-b05/names/utils"; describe("utils Tests", () => { it("test checkMasked", () => { diff --git a/test/adap-b06/common/Exceptions.test.ts b/test/adap-b06/common/Exceptions.test.ts new file mode 100644 index 00000000..cd1c16bb --- /dev/null +++ b/test/adap-b06/common/Exceptions.test.ts @@ -0,0 +1,20 @@ +import { describe, it, expect } from "vitest"; + +import { IllegalArgumentException } from "../../../src/adap-b05/common/IllegalArgumentException"; +import { MethodFailedException } from "../../../src/adap-b05/common/MethodFailedException"; +import { InvalidStateException } from "../../../src/adap-b05/common/InvalidStateException"; + +describe("Asserting not null or undefined", () => { + it("test asserIsNotNullOrUndefined", async () => { + const m: string = "null or undefined"; + + IllegalArgumentException.assertIsNotNullOrUndefined("hurray!"); + expect(() => IllegalArgumentException.assertIsNotNullOrUndefined(null)).toThrow(new IllegalArgumentException(m)); + + MethodFailedException.assertIsNotNullOrUndefined("hurray!"); + expect(() => MethodFailedException.assertIsNotNullOrUndefined(null)).toThrow(new MethodFailedException(m)); + + InvalidStateException.assertIsNotNullOrUndefined("hurray!"); + expect(() => InvalidStateException.assertIsNotNullOrUndefined(null)).toThrow(new InvalidStateException(m)); + }); +}); diff --git a/test/adap-b06/files/Files.test.ts b/test/adap-b06/files/Files.test.ts new file mode 100644 index 00000000..a3e98b88 --- /dev/null +++ b/test/adap-b06/files/Files.test.ts @@ -0,0 +1,76 @@ +import { describe, it, expect } from "vitest"; + +import { StringName } from "../../../src/adap-b05/names/StringName"; + +import { Node } from "../../../src/adap-b05/files/Node"; +import { File } from "../../../src/adap-b05/files/File"; +import { BuggyFile } from "../../../src/adap-b05/files/BuggyFile"; +import { Directory } from "../../../src/adap-b05/files/Directory"; +import { RootNode } from "../../../src/adap-b05/files/RootNode"; +import { Exception } from "../../../src/adap-b05/common/Exception"; +import { ServiceFailureException } from "../../../src/adap-b05/common/ServiceFailureException"; +import { InvalidStateException } from "../../../src/adap-b05/common/InvalidStateException"; + +function createFileSystem(): RootNode { + let rn: RootNode = new RootNode(); + + let usr: Directory = new Directory("usr", rn); + let bin: Directory = new Directory("bin", usr); + let ls: File = new File("ls", bin); + let code: File = new File("code", bin); + + let media: Directory = new Directory("media", rn); + + let home: Directory = new Directory("home", rn); + let riehle: Directory = new Directory("riehle", home); + let bashrc: File = new File(".bashrc", riehle); + let wallpaper: File = new File("wallpaper.jpg", riehle); + let projects: Directory = new Directory("projects", riehle); + + return rn; +} + +describe("Basic naming test", () => { + it("test name checking", () => { + let fs: RootNode = createFileSystem(); + let ls: Node = [...fs.findNodes("ls")][0]; + expect(ls.getFullName().isEqual(new StringName("/usr/bin/ls", '/'))).toBe(true); + }); +}); + +function createBuggySetup(): RootNode { + let rn: RootNode = new RootNode(); + + let usr: Directory = new Directory("usr", rn); + let bin: Directory = new Directory("bin", usr); + let ls: File = new BuggyFile("ls", bin); + let code: File = new BuggyFile("code", bin); + + let media: Directory = new Directory("media", rn); + + let home: Directory = new Directory("home", rn); + let riehle: Directory = new Directory("riehle", home); + let bashrc: File = new BuggyFile(".bashrc", riehle); + let wallpaper: File = new BuggyFile("wallpaper.jpg", riehle); + let projects: Directory = new Directory("projects", riehle); + + return rn; +} + +describe("Buggy setup test", () => { + it("test finding files", () => { + let threwException: boolean = false; + try { + let fs: RootNode = createBuggySetup(); + fs.findNodes("ls"); + } catch (er) { + threwException = true; + let ex: Exception = er as Exception; + expect(ex instanceof ServiceFailureException).toBe(true); + expect(ex.hasTrigger()).toBe(true); + let tx: Exception = ex.getTrigger(); + expect(tx instanceof InvalidStateException).toBe(true); + } + expect(threwException).toBe(true); + }); +}); diff --git a/test/adap-b06/names/StringArrayName.test.ts b/test/adap-b06/names/StringArrayName.test.ts new file mode 100644 index 00000000..908dcebb --- /dev/null +++ b/test/adap-b06/names/StringArrayName.test.ts @@ -0,0 +1,427 @@ +import { describe, it, expect } from "vitest"; + +import { StringName } from "../../../src/adap-b06/names/StringName"; +import { StringArrayName } from "../../../src/adap-b06/names/StringArrayName"; +import { AbstractName } from "../../../src/adap-b06/names/AbstractName"; +import { IllegalArgumentException } from "../../../src/adap-b06/common/IllegalArgumentException"; +import { InvalidStateException } from "../../../src/adap-b06/common/InvalidStateException"; + +describe("StringArrayName Tests", () => { + it("test invalid construction", () => { + expect(() => new StringArrayName(null as any)).toThrowError(IllegalArgumentException); + expect(() => new StringArrayName(undefined as any)).toThrowError(IllegalArgumentException); + expect(() => new StringArrayName(["hallo"], "delimiter")).toThrowError(IllegalArgumentException); + expect(() => new StringArrayName(["hallo"], null as any)).toThrowError(IllegalArgumentException); + }); + + it("test class invariant", () => { + let n = new StringArrayName(["oss", "cs"]); + n["delimiter"] = "delimiter"; + expect(() => n.clone()).toThrowError(InvalidStateException); + expect(() => n.asString()).toThrowError(InvalidStateException); + expect(() => n.asDataString()).toThrowError(InvalidStateException); + expect(() => n.getDelimiterCharacter()).toThrowError(InvalidStateException); + expect(() => n.isEqual(new StringArrayName([""]))).toThrowError(InvalidStateException); + expect(() => n.getHashCode()).toThrowError(InvalidStateException); + expect(() => n.isEmpty()).toThrowError(InvalidStateException); + expect(() => n.getNoComponents()).toThrowError(InvalidStateException); + expect(() => n.getComponent(0)).toThrowError(InvalidStateException); + expect(() => n.setComponent(0, "s")).toThrowError(InvalidStateException); + expect(() => n.insert(0, "s")).toThrowError(InvalidStateException); + expect(() => n.append("s")).toThrowError(InvalidStateException); + expect(() => n.remove(0)).toThrowError(InvalidStateException); + expect(() => n.concat(new StringArrayName([""]))).toThrowError(InvalidStateException); + + n = new StringArrayName(["oss", "cs"]); + (n["components"] as any) = [1, 2, 3]; + expect(() => n.getNoComponents()).toThrowError(InvalidStateException); + expect(() => n.getComponent(0)).toThrowError(InvalidStateException); + expect(() => n.setComponent(0, "s")).toThrowError(InvalidStateException); + expect(() => n.insert(0, "s")).toThrowError(InvalidStateException); + expect(() => n.append("s")).toThrowError(InvalidStateException); + expect(() => n.remove(0)).toThrowError(InvalidStateException); + + n = new StringArrayName(["oss", "cs"]); + (n["components"] as any) = {}; + expect(() => n.getNoComponents()).toThrowError(InvalidStateException); + expect(() => n.getComponent(0)).toThrowError(InvalidStateException); + expect(() => n.setComponent(0, "s")).toThrowError(InvalidStateException); + expect(() => n.insert(0, "s")).toThrowError(InvalidStateException); + expect(() => n.append("s")).toThrowError(InvalidStateException); + expect(() => n.remove(0)).toThrowError(InvalidStateException); + }); + + it("test asString()", () => { + // precondition + let n = new StringArrayName(["oss", "cs", "fau", "de"]); + expect(() => n.asString("delimiter")).toThrowError(IllegalArgumentException); + + n = new StringArrayName(["oss", "cs", "fau", "de"]); + expect(n.asString()).toBe("oss.cs.fau.de"); + + n = new StringArrayName(["oss", "cs", "fau", "de"], "_"); + expect(n.asString()).toBe("oss_cs_fau_de"); + + n = new StringArrayName(["oss\\.cs", "fau\\.de"]); + expect(n.asString()).toBe("oss.cs.fau.de"); + + n = new StringArrayName(["oss\\.cs", "fau\\.de"]); + expect(n.asString("@")).toBe("oss.cs@fau.de"); + + n = new StringArrayName(["oss@cs", "fau@de"]); + expect(n.asString("@")).toBe("oss@cs@fau@de"); + + n = new StringArrayName(["", "", ""]); + expect(n.asString()).toBe(".."); + + n = new StringArrayName(["oss.cs.fau.de"], "#"); + expect(n.asString()).toBe("oss.cs.fau.de"); + }); + + it("test toString()", () => { + let n = new StringArrayName(["oss", "cs", "fau", "de"]); + expect(n.asDataString()).toBe("oss.cs.fau.de"); + let n2 = new StringName(n.asDataString()); + expect(n.asDataString()).toBe(n2.asDataString()); + expect(n.getNoComponents()).toBe(n2.getNoComponents()); + + n = new StringArrayName(["oss", "cs", "fau", "de"], "_"); + expect(n.asDataString()).toBe("oss.cs.fau.de"); + + n = new StringArrayName(["oss\\.cs", "fau\\.de"]); + expect(n.asDataString()).toBe("oss\\.cs.fau\\.de"); + n2 = new StringName(n.asDataString()); + expect(n.asDataString()).toBe(n2.asDataString()); + expect(n.getNoComponents()).toBe(n2.getNoComponents()); + + n = new StringArrayName(["oss.cs", "fau.de"], "@"); + expect(n.asDataString()).toBe("oss\\.cs.fau\\.de"); + + n = new StringArrayName(["", "", ""]); + expect(n.asDataString()).toBe(".."); + n2 = new StringName(n.asDataString()); + expect(n.asDataString()).toBe(n2.asDataString()); + expect(n.getNoComponents()).toBe(n2.getNoComponents()); + + n = new StringArrayName(["oss.cs.fau.de"], "#"); + expect(n.asDataString()).toBe("oss\\.cs\\.fau\\.de"); + expect(n.getNoComponents()).toBe(1); + + n = new StringArrayName(["m.y", "n\\,a\\\\m.e"], ","); + expect(n.asDataString()).toBe("m\\.y.n,a\\\\m\\.e"); + }); + + it("test asDataString()", () => { + let n = new StringArrayName(["oss", "cs", "fau", "de"]); + expect(n.asDataString()).toBe("oss.cs.fau.de"); + let n2 = new StringName(n.asDataString()); + expect(n.asDataString()).toBe(n2.asDataString()); + expect(n.getNoComponents()).toBe(n2.getNoComponents()); + + n = new StringArrayName(["oss", "cs", "fau", "de"], "_"); + expect(n.asDataString()).toBe("oss.cs.fau.de"); + + n = new StringArrayName(["oss\\.cs", "fau\\.de"]); + expect(n.asDataString()).toBe("oss\\.cs.fau\\.de"); + n2 = new StringName(n.asDataString()); + expect(n.asDataString()).toBe(n2.asDataString()); + expect(n.getNoComponents()).toBe(n2.getNoComponents()); + + n = new StringArrayName(["oss.cs", "fau.de"], "@"); + expect(n.asDataString()).toBe("oss\\.cs.fau\\.de"); + + n = new StringArrayName(["", "", ""]); + expect(n.asDataString()).toBe(".."); + n2 = new StringName(n.asDataString()); + expect(n.asDataString()).toBe(n2.asDataString()); + expect(n.getNoComponents()).toBe(n2.getNoComponents()); + + n = new StringArrayName(["oss.cs.fau.de"], "#"); + expect(n.asDataString()).toBe("oss\\.cs\\.fau\\.de"); + expect(n.getNoComponents()).toBe(1); + + n = new StringArrayName(["m.y", "n\\,a\\\\m.e"], ","); + expect(n.asDataString()).toBe("m\\.y.n,a\\\\m\\.e"); + }); + + it("test isEmpty()", () => { + let n1 = new StringArrayName([""]); + expect(n1.isEmpty()).toBe(false); + let n2 = n1.remove(0); + expect(n2.isEmpty()).toBe(true); + }); + + it("test getDelimiterCharacter()", () => { + let n = new StringArrayName(["oss", "cs"]); + expect(n.getDelimiterCharacter()).toBe("."); + + n = new StringArrayName(["oss", "cs"], "@"); + expect(n.getDelimiterCharacter()).toBe("@"); + + n = new StringArrayName(["", "", "", ""], "/"); + expect(n.getDelimiterCharacter()).toBe("/"); + + n = new StringArrayName(["Oh\\.\\.\\."]); + expect(n.getDelimiterCharacter()).toBe("."); + }); + + it("test getNoComponents()", () => { + let n1 = new StringArrayName(["oss"]); + let n2 = n1.remove(0); + expect(n2.getNoComponents()).toBe(0); + + n1 = new StringArrayName(["oss"]); + expect(n1.getNoComponents()).toBe(1); + + n1 = new StringArrayName(["oss", "cs", "fau", "de"]); + expect(n1.getNoComponents()).toBe(4); + + n1 = new StringArrayName(["oss.cs.fau.de"], "#"); + expect(n1.getNoComponents()).toBe(1); + + n1 = new StringArrayName(["", "", "", ""], "/"); + expect(n1.getNoComponents()).toBe(4); + + n1 = new StringArrayName(["Oh\\.\\.\\."]); + expect(n1.getNoComponents()).toBe(1); + }); + + it("test getComponent()", () => { + // precondition + let n = new StringArrayName(["oss", "cs", "fau", "de"]); + expect(() => n.getComponent(4)).toThrowError(IllegalArgumentException); + expect(() => n.getComponent(-1)).toThrowError(IllegalArgumentException); + + expect(n.getComponent(0)).toBe("oss"); + expect(n.getComponent(3)).toBe("de"); + }); + + it("test setComponent()", () => { + // precondition + let n1 = new StringArrayName(["oss", "cs", "fau", "de"]); + expect(() => n1.setComponent(-1, "spam")).toThrowError(IllegalArgumentException); + expect(() => n1.setComponent(0, ".")).toThrowError(IllegalArgumentException); + + n1 = new StringArrayName(["oss", "cs", "fau", "de"]); + let n2 = n1.setComponent(0, "cip"); + expect(n1.asString()).toBe("oss.cs.fau.de"); + expect(n2.asString()).toBe("cip.cs.fau.de"); + + n1 = new StringArrayName(["oss", "cs", "fau", "de"]); + n2 = n1.setComponent(3, "org"); + expect(n1.asString()).toBe("oss.cs.fau.de"); + expect(n2.asString()).toBe("oss.cs.fau.org"); + + n1 = new StringArrayName(["oss", "cs", "fau", "de"]); + expect(() => n1.setComponent(4, "spam")).toThrowError(IllegalArgumentException); + + }); + + it("test insert()", () => { + // precondition + let n1 = new StringArrayName(["oss", "cs", "fau"]); + expect(() => n1.insert(-1, "oss")).toThrowError(IllegalArgumentException); + expect(() => n1.insert(4, "oss")).toThrowError(IllegalArgumentException); + expect(() => n1.insert(0, ".")).toThrowError(IllegalArgumentException); + + n1 = new StringArrayName(["oss", "fau", "de"]); + let n2 = n1.insert(1, "cs"); + expect(n1.asString()).toBe("oss.fau.de"); + expect(n2.asString()).toBe("oss.cs.fau.de"); + + n1 = new StringArrayName(["oss.fau.de"], "@"); + n2 = n1.insert(1, "cs"); + expect(n1.asString()).toBe("oss.fau.de"); + expect(n2.asString()).toBe("oss.fau.de@cs"); + + n1 = new StringArrayName(["oss", "cs", "de"]); + n2 = n1.insert(2, "fau"); + expect(n1.asString()).toBe("oss.cs.de") + expect(n2.asString()).toBe("oss.cs.fau.de") + + n1 = new StringArrayName(["oss", "cs", "fau"]); + n2 = n1.insert(3, "de"); + expect(n1.asString()).toBe("oss.cs.fau") + expect(n2.asString()).toBe("oss.cs.fau.de") + }); + + it("test append()", () => { + // precondition + let n1 = new StringArrayName(["oss", "cs", "fau"]); + expect(() => n1.append(".")).toThrowError(IllegalArgumentException); + + n1 = new StringArrayName(["oss.cs.fau.de"], "#"); + expect(() => n1.append("#")).toThrowError(IllegalArgumentException); + + n1 = new StringArrayName(["oss", "cs", "fau"]); + let n2 = n1.append("de"); + expect(n1.asString()).toBe("oss.cs.fau"); + expect(n2.asString()).toBe("oss.cs.fau.de"); + + n1 = new StringArrayName(["oss.cs.fau.de"], "#"); + n2 = n1.append("people"); + expect(n1.asString()).toBe("oss.cs.fau.de"); + expect(n2.asString()).toBe("oss.cs.fau.de#people"); + + }); + + it("test remove()", () => { + // precondition + let n1 = new StringArrayName(["oss", "cs", "fau", "de"]); + expect(() => n1.remove(-1)).toThrowError(IllegalArgumentException); + + n1 = new StringArrayName(["oss", "cs", "fau", "de"]); + expect(() => n1.remove(27)).toThrowError(IllegalArgumentException); + + n1 = new StringArrayName(["oss", "cs", "fau", "de"]); + let n2 = n1.remove(0); + expect(n1.asString()).toBe("oss.cs.fau.de"); + expect(n2.asString()).toBe("cs.fau.de"); + + n1 = new StringArrayName(["oss", "cs", "fau", "de"]); + n2 = n1.remove(3); + expect(n1.asString()).toBe("oss.cs.fau.de"); + expect(n2.asString()).toBe("oss.cs.fau"); + + + n1 = new StringArrayName(["oss"]); + n2 = n1.remove(0); + expect(n1.asString()).toBe("oss"); + expect(n2.asString()).toBe(""); + + // remove from empty name (precondition) + expect(() => n2.remove(0)).toThrowError(IllegalArgumentException); + + n1 = new StringArrayName(["oss.cs.fau.de", "people"], "#"); + n2 = n1.remove(0); + expect(n1.asString()).toBe("oss.cs.fau.de#people"); + expect(n2.asString()).toBe("people"); + + n1 = new StringArrayName(["oss\\#cs\\#fau\\#de", "people"], "#"); + n2 = n1.remove(0); + expect(n1.asString()).toBe("oss#cs#fau#de#people"); + expect(n2.asString()).toBe("people"); + }); + + it("test concat()", () => { + // precondition + let n1 = new StringArrayName(["oss", "cs"]); + expect(() => n1.concat({} as any)).toThrowError(IllegalArgumentException); + expect(() => n1.concat(Object.setPrototypeOf({}, StringArrayName))).toThrowError(IllegalArgumentException); + + n1 = new StringArrayName(["oss", "cs"]); + let n2 = n1.concat(new StringArrayName(["fau", "de"])); + expect(n1.asString()).toBe("oss.cs"); + expect(n2.asString()).toBe("oss.cs.fau.de"); + + n1 = new StringArrayName(["oss", "cs"]); + n2 = n1.concat(new StringName("fau.de")); + expect(n1.asString()).toBe("oss.cs"); + expect(n2.asString()).toBe("oss.cs.fau.de"); + + n1 = new StringArrayName(["oss\\@cs"], "@"); + n2 = n1.concat(new StringArrayName(["fau@de"])); + expect(n1.asDataString()).toBe("oss@cs"); + expect(n2.asDataString()).toBe("oss@cs.fau@de"); + + n1 = new StringArrayName(["oss\\.tf", "cs"]); + n2 = n1.concat(new StringArrayName(["fau", "de"])); + expect(n1.asDataString()).toBe("oss\\.tf.cs"); + expect(n2.asDataString()).toBe("oss\\.tf.cs.fau.de"); + + n1 = new StringArrayName(["oss", "cs"]); + n2 = n1.concat(new StringName("fau@de")); + expect(n1.asString()).toBe("oss.cs"); + expect(n2.asString()).toBe("oss.cs.fau@de"); + + n1 = new StringArrayName(["oss", "cs"], "/"); + n2 = n1.concat(new StringArrayName(["fau\\/", "de"], "/")); + expect(n1.asDataString()).toBe("oss.cs"); + expect(n2.asDataString()).toBe("oss.cs.fau/.de"); + }); + + it("test getHashCode()", () => { + let n: AbstractName = new StringArrayName(["oss", "cs", "fau", "de"]); + let n2: AbstractName = new StringName("oss.cs.fau.de"); + let n3: AbstractName = new StringName("oss.cs.fau.de", "/"); + expect(n.getHashCode()).toBe(n2.getHashCode()); + expect(n.getHashCode() === n3.getHashCode()).toBe(false); + expect(n2.getHashCode() === n3.getHashCode()).toBe(false); + + n = new StringArrayName(["oss", "cs", "fau", "de"], "_"); + n2 = new StringArrayName(["oss", "cs", "fau", "de"], "_"); + n3 = new StringName("oss_cs_fau_de", "_"); + expect(n.getHashCode()).toBe(n2.getHashCode()); + expect(n.getHashCode()).toBe(n3.getHashCode()); + + n = new StringArrayName(["oss\\.cs", "fau\\.de"]); + n2 = new StringName("oss\\.cs.fau\\.de"); + n3 = new StringArrayName(["oss", "cs", "fau\\.de"]); + expect(n.getHashCode()).toBe(n2.getHashCode()); + expect(n.getHashCode() === n3.getHashCode()).toBe(false); + + n = new StringArrayName(["m.y", "n\\,a\\\\m.e"], ","); + n2 = new StringArrayName(["m.y", "n\\,a\\\\m.e"], ","); + n3 = new StringName("m.y,n\\,a\\\\m.e", ","); + expect(n.getHashCode()).toBe(n2.getHashCode()); + expect(n.getHashCode()).toBe(n3.getHashCode()); + }); + + it("test isEqual()", () => { + // precondition + let n: AbstractName = new StringArrayName(["oss", "cs"]); + expect(() => n.isEqual({} as any)).toThrowError(IllegalArgumentException); + expect(() => n.isEqual(Object.setPrototypeOf({}, StringArrayName))).toThrowError(IllegalArgumentException); + + n = new StringArrayName(["oss", "cs", "fau", "de"]); + let n2: AbstractName = new StringName("oss.cs.fau.de"); + let n3: AbstractName = new StringName("oss.cs.fau.de", "/"); + expect(n.isEqual(n2)).toBe(true); + expect(n2.isEqual(n)).toBe(true); + expect(n.isEqual(n3)).toBe(false); + expect(n3.isEqual(n)).toBe(false); + expect(n2.isEqual(n3)).toBe(false); + expect(n3.isEqual(n2)).toBe(false); + + n = new StringArrayName(["oss", "cs", "fau", "de"], "_"); + n2 = new StringArrayName(["oss", "cs", "fau", "de"], "_"); + n3 = new StringName("oss_cs_fau_de", "_"); + expect(n.isEqual(n2)).toBe(true); + expect(n.isEqual(n3)).toBe(true); + + n = new StringArrayName(["oss\\.cs", "fau\\.de"]); + n2 = new StringName("oss\\.cs.fau\\.de"); + n3 = new StringArrayName(["oss", "cs", "fau\\.de"]); + expect(n.isEqual(n2)).toBe(true); + expect(n.isEqual(n3)).toBe(false); + + n = new StringArrayName(["m.y", "n\\,a\\\\m.e"], ","); + n2 = new StringArrayName(["m.y", "n\\,a\\\\m.e"], ","); + n3 = new StringName("m.y,n\\,a\\\\m.e", ","); + expect(n.isEqual(n2)).toBe(true); + expect(n.isEqual(n3)).toBe(true); + }); + + it("test clone()", () => { + let n = new StringArrayName(["oss", "cs", "fau", "de"]); + expect(n.clone().isEqual(n)).toBe(true); + + n = new StringArrayName(["oss", "cs", "fau", "de"], "_"); + expect(n.clone().isEqual(n)).toBe(true); + + n = new StringArrayName(["oss\\.cs", "fau\\.de"]); + expect(n.clone().isEqual(n)).toBe(true); + + n = new StringArrayName(["oss.cs", "fau.de"], "@"); + expect(n.clone().isEqual(n)).toBe(true); + + n = new StringArrayName(["", "", ""]); + expect(n.clone().isEqual(n)).toBe(true); + + n = new StringArrayName(["oss.cs.fau.de"], "#"); + expect(n.clone().isEqual(n)).toBe(true); + + n = new StringArrayName(["m.y", "n\\,a\\\\m.e"], ","); + expect(n.clone().isEqual(n)).toBe(true); + }); +}); \ No newline at end of file diff --git a/test/adap-b06/names/StringName.test.ts b/test/adap-b06/names/StringName.test.ts new file mode 100644 index 00000000..4bcb8e7d --- /dev/null +++ b/test/adap-b06/names/StringName.test.ts @@ -0,0 +1,470 @@ +import { describe, it, expect } from "vitest"; + +import { StringName } from "../../../src/adap-b06/names/StringName"; +import { StringArrayName } from "../../../src/adap-b06/names/StringArrayName"; +import { AbstractName } from "../../../src/adap-b06/names/AbstractName"; +import { IllegalArgumentException } from "../../../src/adap-b06/common/IllegalArgumentException"; +import { InvalidStateException } from "../../../src/adap-b06/common/InvalidStateException"; + +describe("StringName Tests", () => { + it("test class invariant", () => { + let n = new StringName("oss.cs"); + n["delimiter"] = "delimiter"; + expect(() => n.clone()).toThrowError(InvalidStateException); + expect(() => n.asString()).toThrowError(InvalidStateException); + expect(() => n.asDataString()).toThrowError(InvalidStateException); + expect(() => n.getDelimiterCharacter()).toThrowError(InvalidStateException); + expect(() => n.isEqual(new StringArrayName([""]))).toThrowError(InvalidStateException); + expect(() => n.getHashCode()).toThrowError(InvalidStateException); + expect(() => n.isEmpty()).toThrowError(InvalidStateException); + expect(() => n.getNoComponents()).toThrowError(InvalidStateException); + expect(() => n.getComponent(0)).toThrowError(InvalidStateException); + expect(() => n.setComponent(0, "s")).toThrowError(InvalidStateException); + expect(() => n.insert(0, "s")).toThrowError(InvalidStateException); + expect(() => n.append("s")).toThrowError(InvalidStateException); + expect(() => n.remove(0)).toThrowError(InvalidStateException); + expect(() => n.concat(new StringArrayName([""]))).toThrowError(InvalidStateException); + + n = new StringName("oss.cs"); + (n["name"] as any) = [1, 2, 3]; + expect(() => n.getNoComponents()).toThrowError(InvalidStateException); + expect(() => n.getComponent(0)).toThrowError(InvalidStateException); + expect(() => n.setComponent(0, "s")).toThrowError(InvalidStateException); + expect(() => n.insert(0, "s")).toThrowError(InvalidStateException); + expect(() => n.append("s")).toThrowError(InvalidStateException); + expect(() => n.remove(0)).toThrowError(InvalidStateException); + + n = new StringName("oss.cs"); + (n["noComponents"] as any) = {}; + expect(() => n.getNoComponents()).toThrowError(InvalidStateException); + expect(() => n.getComponent(0)).toThrowError(InvalidStateException); + expect(() => n.setComponent(0, "s")).toThrowError(InvalidStateException); + expect(() => n.insert(0, "s")).toThrowError(InvalidStateException); + expect(() => n.append("s")).toThrowError(InvalidStateException); + expect(() => n.remove(0)).toThrowError(InvalidStateException); + }); + + it("test asString()", () => { + // preconditon + let n = new StringName("oss.cs.fau.de"); + expect(() => n.asString("delimiter")).toThrowError(IllegalArgumentException); + + n = new StringName("oss.cs.fau.de"); + expect(n.asString()).toBe("oss.cs.fau.de"); + + n = new StringName("oss_cs_fau_de", "_"); + expect(n.asString()).toBe("oss_cs_fau_de"); + + n = new StringName("oss\\.cs.fau.de"); + expect(n.asString()).toBe("oss.cs.fau.de"); + + n = new StringName("oss\\.cs.fau\\.de"); + expect(n.asString("@")).toBe("oss.cs@fau.de"); + + n = new StringName("oss@cs.fau@de"); + expect(n.asString("@")).toBe("oss@cs@fau@de"); + + n = new StringName(".."); + expect(n.asString()).toBe(".."); + + n = new StringName("oss.cs.fau.de", "#"); + expect(n.asString()).toBe("oss.cs.fau.de"); + }); + + it("test toString()", () => { + let n1 = new StringName("oss.cs.fau.de"); + expect(n1.asDataString()).toBe("oss.cs.fau.de"); + let n2 = new StringName(n1.asDataString()); + expect(n1.asDataString()).toBe(n2.asDataString()); + expect(n1.getNoComponents()).toBe(n2.getNoComponents()); + + n1 = new StringName("oss_cs_fau_de", "_"); + expect(n1.asDataString()).toBe("oss.cs.fau.de"); + + n1 = new StringName("oss\\.cs.fau\\.de"); + n2 = new StringName(n1.asDataString()); + expect(n1.asDataString()).toBe("oss\\.cs.fau\\.de"); + expect(n1.asDataString()).toBe(n2.asDataString()); + expect(n1.getNoComponents()).toBe(n2.getNoComponents()); + + n1 = new StringName("oss.cs@fau.de", "@"); + expect(n1.asDataString()).toBe("oss\\.cs.fau\\.de"); + + n1 = new StringName(".."); + expect(n1.asDataString()).toBe(".."); + n2 = new StringName(n1.asDataString()); + expect(n1.asDataString()).toBe(n2.asDataString()); + expect(n1.getNoComponents()).toBe(n2.getNoComponents()); + + n1 = new StringName("oss.cs.fau.de", "#"); + n2 = new StringName(n1.asDataString(), "#"); + expect(n1.asDataString()).toBe("oss\\.cs\\.fau\\.de"); + + // edge case + n1 = new StringName(""); + let n3 = n1.remove(0); + n2 = new StringName(n1.asDataString()); + expect(n3.asDataString()).toBe(""); + expect(n3.asDataString()).toBe(n2.asDataString()); + expect(n3.getNoComponents()).toBe(0); + expect(n2.getNoComponents()).toBe(1); + + n1 = new StringName("m.y,n\\,a\\\\m.e", ","); + expect(n1.asDataString()).toBe("m\\.y.n,a\\\\m\\.e"); + }); + + it("test asDataString()", () => { + let n1 = new StringName("oss.cs.fau.de"); + expect(n1.asDataString()).toBe("oss.cs.fau.de"); + let n2 = new StringName(n1.asDataString()); + expect(n1.asDataString()).toBe(n2.asDataString()); + expect(n1.getNoComponents()).toBe(n2.getNoComponents()); + + n1 = new StringName("oss_cs_fau_de", "_"); + expect(n1.asDataString()).toBe("oss.cs.fau.de"); + + n1 = new StringName("oss\\.cs.fau\\.de"); + n2 = new StringName(n1.asDataString()); + expect(n1.asDataString()).toBe("oss\\.cs.fau\\.de"); + expect(n1.asDataString()).toBe(n2.asDataString()); + expect(n1.getNoComponents()).toBe(n2.getNoComponents()); + + n1 = new StringName("oss.cs@fau.de", "@"); + expect(n1.asDataString()).toBe("oss\\.cs.fau\\.de"); + + n1 = new StringName(".."); + expect(n1.asDataString()).toBe(".."); + n2 = new StringName(n1.asDataString()); + expect(n1.asDataString()).toBe(n2.asDataString()); + expect(n1.getNoComponents()).toBe(n2.getNoComponents()); + + n1 = new StringName("oss.cs.fau.de", "#"); + n2 = new StringName(n1.asDataString(), "#"); + expect(n1.asDataString()).toBe("oss\\.cs\\.fau\\.de"); + + // edge case + n1 = new StringName(""); + let n3 = n1.remove(0); + n2 = new StringName(n1.asDataString()); + expect(n3.asDataString()).toBe(""); + expect(n3.asDataString()).toBe(n2.asDataString()); + expect(n3.getNoComponents()).toBe(0); + expect(n2.getNoComponents()).toBe(1); + + n1 = new StringName("m.y,n\\,a\\\\m.e", ","); + expect(n1.asDataString()).toBe("m\\.y.n,a\\\\m\\.e"); + }); + + it("test isEmpty()", () => { + let n1 = new StringName(""); + expect(n1.isEmpty()).toBe(false); + let n2 = n1.remove(0); + expect(n2.isEmpty()).toBe(true); + }); + + it("test getDelimiterCharacter()", () => { + let n = new StringName("oss.cs"); + expect(n.getDelimiterCharacter()).toBe("."); + + n = new StringName("oss.cs", "@"); + expect(n.getDelimiterCharacter()).toBe("@"); + + n = new StringName("///", "/"); + expect(n.getDelimiterCharacter()).toBe("/"); + + n = new StringName("Oh\\.\\.\\."); + expect(n.getDelimiterCharacter()).toBe("."); + }); + + it("test getNoComponents()", () => { + let n1 = new StringName(""); + expect(n1.getNoComponents()).toBe(1); + let n2 = n1.remove(0); + expect(n2.getNoComponents()).toBe(0); + + n1 = new StringName("oss"); + expect(n1.getNoComponents()).toBe(1); + + n1 = new StringName("oss.cs.fau.de"); + expect(n1.getNoComponents()).toBe(4); + + n1 = new StringName("oss.cs.fau.de", "#"); + expect(n1.getNoComponents()).toBe(1); + + n1 = new StringName("///", "/"); + expect(n1.getNoComponents()).toBe(4); + + n1 = new StringName("Oh\\.\\.\\."); + expect(n1.getNoComponents()).toBe(1); + }); + + it("test getComponent()", () => { + // precondition + let n = new StringName("oss.cs.fau.de"); + expect(() => n.getComponent(-1)).toThrowError(IllegalArgumentException); + expect(() => n.getComponent(4)).toThrowError(IllegalArgumentException); + + expect(n.getComponent(0)).toBe("oss"); + expect(n.getComponent(3)).toBe("de"); + }); + + it("test setComponent()", () => { + // precondition + let n1 = new StringName("oss.cs.fau.de"); + expect(() => n1.setComponent(4, "spam")).toThrowError(IllegalArgumentException); + expect(() => n1.setComponent(-1, "spam")).toThrowError(IllegalArgumentException); + expect(() => n1.setComponent(0, ".")).toThrowError(IllegalArgumentException); + + let n2 = n1.setComponent(0, "cip"); + + expect(n1.asString()).toBe("oss.cs.fau.de"); + expect(n2.asString()).toBe("cip.cs.fau.de"); + + n1 = new StringName("oss.cs.fau.de"); + n2 = n1.setComponent(3, "org"); + + expect(n1.asString()).toBe("oss.cs.fau.de"); + expect(n2.asString()).toBe("oss.cs.fau.org"); + + }); + + it("test insert()", () => { + // precondition + let n1 = new StringName("oss.fau.de"); + expect(() => n1.insert(-1, "oss")).toThrowError(IllegalArgumentException); + expect(() => n1.insert(5, "oss")).toThrowError(IllegalArgumentException); + expect(() => n1.insert(0, ".")).toThrowError(IllegalArgumentException); + + let n2 = n1.insert(1, "cs"); + expect(n1.asString()).toBe("oss.fau.de"); + expect(n2.asString()).toBe("oss.cs.fau.de"); + + n1 = new StringName("oss.fau.de", "@"); + n2 = n1.insert(1, "cs"); + expect(n1.asString()).toBe("oss.fau.de"); + expect(n2.asString()).toBe("oss.fau.de@cs"); + + n1 = new StringName("oss.fau.de", "#"); + n2 = n1.insert(1, "cs"); + expect(n1.asString()).toBe("oss.fau.de"); + expect(n2.asString()).toBe("oss.fau.de#cs"); + + n1 = new StringName(""); + n2 = n1.insert(0, "oss"); + expect(n1.asString()).toBe(""); + expect(n2.asString()).toBe("oss."); + + n1 = new StringName(""); + n2 = n1.insert(1, "oss"); + expect(n1.asString()).toBe(""); + expect(n2.asString()).toBe(".oss"); + + n1 = new StringName("oss.cs.de"); + n2 = n1.insert(2, "fau"); + expect(n1.asString()).toBe("oss.cs.de") + expect(n2.asString()).toBe("oss.cs.fau.de") + + n1 = new StringName("oss.cs.fau"); + n2 = n1.insert(3, "de"); + expect(n1.asString()).toBe("oss.cs.fau") + expect(n2.asString()).toBe("oss.cs.fau.de") + }); + + it("test append()", () => { + // precondition + let n1 = new StringName(""); + expect(() => n1.append(".")).toThrowError(IllegalArgumentException); + + n1 = new StringName("oss.cs.fau.de", "#"); + expect(() => n1.append("#")).toThrowError(IllegalArgumentException); + + n1 = new StringName("oss.cs.fau"); + let n2 = n1.append("de"); + expect(n1.asString()).toBe("oss.cs.fau"); + expect(n2.asString()).toBe("oss.cs.fau.de"); + + n1 = new StringName(""); + n2 = n1.append("oss"); + expect(n1.asString()).toBe("") + expect(n2.asString()).toBe(".oss") + + n1 = new StringName("oss.cs.fau.de", "#"); + n2 = n1.append("people"); + expect(n1.asString()).toBe("oss.cs.fau.de"); + expect(n2.asString()).toBe("oss.cs.fau.de#people"); + + }); + + it("test remove()", () => { + // precondition + let n1 = new StringName("oss.cs.fau.de"); + expect(() => n1.remove(-1)).toThrow(); + + n1 = new StringName("oss.cs.fau.de"); + expect(() => n1.remove(27)).toThrow(); + + n1 = new StringName("oss.cs.fau.de"); + let n2 = n1.remove(0); + expect(n1.asString()).toBe("oss.cs.fau.de"); + expect(n2.asString()).toBe("cs.fau.de"); + + n1 = new StringName("oss.cs.fau.de"); + n2 = n1.remove(3); + expect(n1.asString()).toBe("oss.cs.fau.de"); + expect(n2.asString()).toBe("oss.cs.fau"); + + n1 = new StringName("oss"); + n2 = n1.remove(0); + expect(n1.asDataString()).toBe("oss"); + expect(n2.asDataString()).toBe(""); + + n1 = new StringName(""); + n2 = n1.remove(0); + expect(n1.asDataString()).toBe(""); + expect(n2.asDataString()).toBe(""); + // precondition + expect(() => n2.remove(0)).toThrowError(IllegalArgumentException) + + n1 = new StringName("oss.cs.fau.de#people", "#"); + n2 = n1.remove(0); + expect(n1.asString()).toBe("oss.cs.fau.de#people"); + expect(n2.asString()).toBe("people"); + + n1 = new StringName("oss\\#cs\\#fau\\#de#people", "#"); + n2 = n1.remove(0); + expect(n1.asString()).toBe("oss#cs#fau#de#people"); + expect(n2.asString()).toBe("people"); + }); + + it("test concat()", () => { + // precondition + let n1 = new StringName("oss.cs"); + expect(() => n1.concat({} as any)).toThrowError(IllegalArgumentException); + expect(() => n1.concat(Object.setPrototypeOf({}, StringArrayName))).toThrowError(IllegalArgumentException); + + n1 = new StringName("oss.cs"); + let n2 = n1.concat(new StringName("fau.de")); + expect(n1.asString()).toBe("oss.cs"); + expect(n2.asString()).toBe("oss.cs.fau.de"); + + n1 = new StringName("oss.cs"); + n2 = n1.concat(new StringArrayName(["fau", "de"])); + expect(n1.asString()).toBe("oss.cs"); + expect(n2.asString()).toBe("oss.cs.fau.de"); + + n1 = new StringName("oss@cs", "@"); + n2 = n1.concat(new StringName("fau.de")); + expect(n1.asDataString()).toBe("oss.cs"); + expect(n2.asDataString()).toBe("oss.cs.fau.de"); + + n1 = new StringName("oss\\@cs", "@"); + n2 = n1.concat(new StringName("fau@de")) + expect(n1.asDataString()).toBe("oss@cs"); + expect(n2.asDataString()).toBe("oss@cs.fau@de"); + + n1 = new StringName("oss\\.tf.cs"); + n2 = n1.concat(new StringName("fau.de")); + expect(n1.asDataString()).toBe("oss\\.tf.cs"); + expect(n2.asDataString()).toBe("oss\\.tf.cs.fau.de"); + + n1 = new StringName("oss.cs"); + n2 = n1.concat(new StringArrayName(["fau@de"])); + expect(n1.asString()).toBe("oss.cs"); + expect(n2.asString()).toBe("oss.cs.fau@de"); + + n1 = new StringName("oss/cs", "/"); + n2 = n1.concat(new StringName("fau\\/de", "/")); + expect(n1.asDataString()).toBe("oss.cs"); + expect(n2.asDataString()).toBe("oss.cs.fau/de"); + }); + + it("test getHashCode()", () => { + let n: AbstractName = new StringArrayName(["oss", "cs", "fau", "de"]); + let n2: AbstractName = new StringName("oss.cs.fau.de"); + let n3: AbstractName = new StringName("oss.cs.fau.de", "/"); + expect(n.getHashCode()).toBe(n2.getHashCode()); + expect(n.getHashCode() === n3.getHashCode()).toBe(false); + expect(n2.getHashCode() === n3.getHashCode()).toBe(false); + + n = new StringName("oss_cs_fau_de", "_"); + n2 = new StringArrayName(["oss", "cs", "fau", "de"], "_"); + n3 = new StringArrayName(["oss", "cs", "fau", "de"], "_"); + expect(n.getHashCode()).toBe(n2.getHashCode()); + expect(n.getHashCode()).toBe(n3.getHashCode()); + + n = new StringName("oss\\.cs.fau\\.de"); + n2 = new StringArrayName(["oss\\.cs", "fau\\.de"]); + n3 = new StringArrayName(["oss", "cs", "fau\\.de"]); + expect(n.getHashCode()).toBe(n2.getHashCode()); + expect(n.getHashCode() === n3.getHashCode()).toBe(false); + + n = new StringName("m.y,n\\,a\\\\m.e", ","); + n2 = new StringArrayName(["m.y", "n\\,a\\\\m.e"], ","); + n3 = new StringArrayName(["m.y", "n\\,am.e"], ","); + expect(n.getHashCode()).toBe(n2.getHashCode()); + expect(n.getHashCode() === n3.getHashCode()).toBe(false); + }); + + it("test isEqual()", () => { + // precondition + let n: AbstractName = new StringName("oss.cs"); + expect(() => n.concat({} as any)).toThrowError(IllegalArgumentException); + expect(() => n.concat(Object.setPrototypeOf({}, StringArrayName))).toThrowError(IllegalArgumentException); + + + n = new StringArrayName(["oss", "cs", "fau", "de"]); + let n2: AbstractName = new StringName("oss.cs.fau.de"); + let n3: AbstractName = new StringName("oss.cs.fau.de", "/"); + expect(n.isEqual(n2)).toBe(true); + expect(n2.isEqual(n)).toBe(true); + expect(n.isEqual(n3)).toBe(false); + expect(n3.isEqual(n)).toBe(false); + expect(n2.isEqual(n3)).toBe(false); + expect(n3.isEqual(n2)).toBe(false); + + n = new StringName("oss_cs_fau_de", "_"); + n2 = new StringArrayName(["oss", "cs", "fau", "de"], "_"); + n3 = new StringArrayName(["oss", "cs", "fau", "de"], "_"); + expect(n.isEqual(n2)).toBe(true); + expect(n.isEqual(n3)).toBe(true); + + n = new StringName("oss\\.cs.fau\\.de"); + n2 = new StringArrayName(["oss\\.cs", "fau\\.de"]); + n3 = new StringArrayName(["oss", "cs", "fau\\.de"]); + expect(n.isEqual(n2)).toBe(true); + expect(n.isEqual(n3)).toBe(false); + + n = new StringName("m.y,n\\,a\\\\m.e", ","); + n2 = new StringArrayName(["m.y", "n\\,a\\\\m.e"], ","); + n3 = new StringArrayName(["m.y", "n\\,am.e"], ","); + expect(n.isEqual(n2)).toBe(true); + expect(n.isEqual(n3)).toBe(false); + }); + + it("test clone()", () => { + let n = new StringName("oss.cs.fau.de"); + expect(n.clone().isEqual(n)).toBe(true); + + n = new StringName("oss_cs_fau_de", "_"); + expect(n.clone().isEqual(n)).toBe(true); + + n = new StringName("oss\\.cs.fau\\.de"); + expect(n.clone().isEqual(n)).toBe(true); + + n = new StringName("oss.cs@fau.de", "@"); + expect(n.clone().isEqual(n)).toBe(true); + + n = new StringName(".."); + expect(n.clone().isEqual(n)).toBe(true); + + n = new StringName("oss.cs.fau.de", "#"); + expect(n.clone().isEqual(n)).toBe(true); + + // edge case + n = new StringName(""); + expect(n.clone().isEqual(n)).toBe(true); + + n = new StringName("m.y,n\\,a\\\\m.e", ","); + expect(n.clone().isEqual(n)).toBe(true); + }); +}); diff --git a/test/adap-b06/names/utils.test.ts b/test/adap-b06/names/utils.test.ts new file mode 100644 index 00000000..067d4022 --- /dev/null +++ b/test/adap-b06/names/utils.test.ts @@ -0,0 +1,11 @@ +import { describe, it, expect } from "vitest"; +import { checkEscaped } from "../../../src/adap-b06/names/utils"; + +describe("utils Tests", () => { + it("test checkMasked", () => { + expect(checkEscaped("\\c", ".")).toBe(false); + expect(checkEscaped("\\\\", ".")).toBe(true); + expect(checkEscaped("\\.", ".")).toBe(true); + expect(checkEscaped("\\\\\\.", ".")).toBe(true); + }); +}); \ No newline at end of file