isaacscript-common
Version:
Helper functions and features for IsaacScript mods.
551 lines (467 loc) • 15.5 kB
text/typescript
import { DefaultMap } from "../classes/DefaultMap";
import { SerializationBrand } from "../enums/private/SerializationBrand";
import { SerializationType } from "../enums/SerializationType";
import { arrayEquals } from "./array";
import { deepCopy } from "./deepCopy";
import { log } from "./log";
import { isDefaultMap, isTSTLMap, isTSTLSet } from "./tstlClass";
import { isNumber, isString, isTable } from "./types";
/**
* Run the suite of tests that prove that the "deepCopy" helper function works properly.
*
* This function is only useful if you are troubleshooting the "deepCopy" function.
*/
export function runDeepCopyTests(): void {
copiedObjectIsTable();
copiedObjectHasKeyAndValueString();
copiedTableHasKeyAndValueNumber();
copiedTableDoesNotCoerceTypes();
copiedObjectHasNoReferencesForPrimitivesForward();
copiedObjectHasNoReferencesForPrimitivesBackward();
copiedObjectHasNoReferencesForArray();
copiedObjectHasChildObject();
copiedMapIsMap();
copiedMapHasValue();
copiedSetIsSet();
copiedSetHasValue();
copiedMapHasChildMap();
copiedDefaultMapHasChildDefaultMap();
copiedDefaultMapHasBrand();
copiedSerializedMapHasStringKey();
copiedSerializedMapHasNumberKey();
copiedSerializedDefaultMapHasStringKey();
copiedSerializedDefaultMapHasNumberKey();
const successText = "All deep copy tests passed!";
log(successText);
print(successText);
}
function copiedObjectIsTable() {
const oldObject = {
abc: "def",
};
const newObject = deepCopy(
oldObject,
SerializationType.NONE,
"copiedObjectIsTable",
);
if (!isTable(newObject)) {
error(`The copied object had a type of: ${typeof newObject}`);
}
}
function copiedObjectHasKeyAndValueString() {
const keyToLookFor = "abc";
const valueToLookFor = "def";
const oldObject = {
abc: valueToLookFor,
};
const newObject = deepCopy(
oldObject,
SerializationType.NONE,
"copiedObjectHasKeyAndValueString",
);
const value = newObject[keyToLookFor] as string | undefined;
if (value === undefined) {
error(`The copied object did not have a key of: ${keyToLookFor}`);
}
if (!isString(value)) {
error(`The copied object had a value type of: ${typeof value}`);
}
if (value !== valueToLookFor) {
error(`The copied object had a value of: ${value}`);
}
}
function copiedTableHasKeyAndValueNumber() {
const keyToLookFor = 123;
const valueToLookFor = 456;
const oldTable = new LuaMap<AnyNotNil, unknown>();
oldTable.set(keyToLookFor, valueToLookFor);
const newTable = deepCopy(
oldTable,
SerializationType.NONE,
"copiedTableHasKeyAndValueNumber",
);
const value = newTable.get(keyToLookFor) as number | undefined;
if (value === undefined) {
error(`The copied object did not have a key of: ${keyToLookFor}`);
}
if (!isNumber(value)) {
error(`The copied object had a value type of: ${typeof value}`);
}
if (value !== valueToLookFor) {
error(`The copied object had a value of: ${value}`);
}
}
function copiedTableDoesNotCoerceTypes() {
const keyToLookFor = 123;
const valueToLookFor = 456;
const oldTable = new LuaMap<AnyNotNil, unknown>();
oldTable.set(keyToLookFor, valueToLookFor);
const newTable = deepCopy(
oldTable,
SerializationType.NONE,
"copiedTableDoesNotCoerceTypes",
);
const keyString = tostring(keyToLookFor);
const valueString = tostring(valueToLookFor);
const valueFromString = newTable.get(keyString);
if (valueFromString !== undefined) {
error(`The copied object had a string key of: ${keyString}`);
}
const value = newTable.get(keyToLookFor);
if (value === valueString) {
error(
`The copied object had a value that incorrectly matched the string of: ${valueString}`,
);
}
}
/** In this context, a reference is a pointer. */
function copiedObjectHasNoReferencesForPrimitivesForward() {
const originalStringValue = "abcdef";
const originalNumberValue = 123;
const oldObject = {
abc: originalStringValue,
def: originalNumberValue,
};
const newObject = deepCopy(
oldObject,
SerializationType.NONE,
"copiedObjectHasNoReferencesForPrimitivesForward",
);
oldObject.abc = "newValue";
if (oldObject.abc === newObject.abc) {
error("The copied object has a string reference going forward.");
}
oldObject.def = 456;
if (oldObject.def === newObject.def) {
error("The copied object has a number reference going forward.");
}
}
function copiedObjectHasNoReferencesForPrimitivesBackward() {
const originalStringValue = "abcdef";
const originalNumberValue = 123;
const oldObject = {
abc: originalStringValue,
def: originalNumberValue,
};
const newObject = deepCopy(
oldObject,
SerializationType.NONE,
"copiedObjectHasNoReferencesForPrimitivesBackward",
);
newObject.abc = "newValue";
if (newObject.abc === oldObject.abc) {
error("The copied object has a string reference going backward.");
}
newObject.def = 456;
if (newObject.def === oldObject.def) {
error("The copied object has a number reference going backward.");
}
}
/** In this context, a reference is a pointer. */
function copiedObjectHasNoReferencesForArray() {
const oldObject = {
abc: [1, 2, 3],
};
const newObject = deepCopy(
oldObject,
SerializationType.NONE,
"copiedObjectHasNoReferencesForArray",
);
if (oldObject.abc === newObject.abc) {
error("The copied object has the same point to the child array.");
}
if (!arrayEquals(oldObject.abc, newObject.abc)) {
error("The copied object does not have an equal array.");
}
oldObject.abc[0]!++; // eslint-disable-line @typescript-eslint/no-non-null-assertion
if (arrayEquals(oldObject.abc, newObject.abc)) {
error(
"The copied object has an equal array after a modification to the old array.",
);
}
oldObject.abc[0]!--; // eslint-disable-line @typescript-eslint/no-non-null-assertion
newObject.abc[0]!++; // eslint-disable-line @typescript-eslint/no-non-null-assertion
if (arrayEquals(oldObject.abc, newObject.abc)) {
error(
"The copied object has an equal array after a modification to the new array.",
);
}
newObject.abc[0]!--; // eslint-disable-line @typescript-eslint/no-non-null-assertion
}
function copiedObjectHasChildObject() {
const childObjectIndex = "abc";
const keyToLookFor = "def";
const valueToLookFor = "ghi";
const oldObject = {
abc: {
def: valueToLookFor,
},
};
const newObject = deepCopy(
oldObject,
SerializationType.NONE,
"copiedObjectHasChildObject",
);
const childObject = newObject[childObjectIndex] as
| (typeof oldObject)["abc"]
| undefined;
if (childObject === undefined) {
error(`Failed to find the child object at index: ${childObjectIndex}`);
}
if (!isTable(childObject)) {
error(`The copied child object had a type of: ${typeof childObject}`);
}
const value = childObject[keyToLookFor] as string | undefined;
if (value === undefined) {
error(`The child object did not have a key of: ${keyToLookFor}`);
}
if (!isString(value)) {
error(`The child object value had a type of: ${typeof value}`);
}
if (value !== valueToLookFor) {
error(`The child object value was: ${valueToLookFor}`);
}
}
function copiedMapIsMap() {
const keyToLookFor = "abc";
const valueToLookFor = "def";
const oldMap = new Map<string, string>();
oldMap.set(keyToLookFor, valueToLookFor);
const newMap = deepCopy(oldMap, SerializationType.NONE, "copiedMapIsMap");
if (!isTSTLMap(newMap)) {
error(`The copied Map was not a Map and has a type of: ${typeof newMap}`);
}
}
function copiedMapHasValue() {
const keyToLookFor = "abc";
const valueToLookFor = "def";
const oldMap = new Map<string, string>();
oldMap.set(keyToLookFor, valueToLookFor);
const newMap = deepCopy(oldMap, SerializationType.NONE, "copiedMapHasValue");
if (!isTSTLMap(newMap)) {
error(`The copied Map was not a Map and has a type of: ${typeof newMap}`);
}
const value = newMap.get(keyToLookFor);
if (value === undefined) {
error(`The copied Map did not have a key of: ${keyToLookFor}`);
}
if (value !== valueToLookFor) {
error(`The copied Map did not have a value of: ${valueToLookFor}`);
}
}
function copiedSetIsSet() {
const valueToLookFor = "abc";
const oldSet = new Set<string>();
oldSet.add(valueToLookFor);
const newSet = deepCopy(oldSet, SerializationType.NONE, "copiedSetIsSet");
if (!isTSTLSet(newSet)) {
error(`The copied Set was not a Set and has a type of: ${typeof newSet}`);
}
}
function copiedSetHasValue() {
const valueToLookFor = "abc";
const oldSet = new Set<string>();
oldSet.add(valueToLookFor);
const newSet = deepCopy(oldSet, SerializationType.NONE, "copiedSetHasValue");
if (!isTSTLSet(newSet)) {
error(`The copied Set was not a Set and has a type of: ${typeof newSet}`);
}
const hasValue = newSet.has(valueToLookFor);
if (!hasValue) {
error(`The copied Set did not have a value of: ${valueToLookFor}`);
}
}
function copiedMapHasChildMap() {
const childMapKey = 123;
const childMapValue = 456;
const oldChildMap = new Map<number, number>();
oldChildMap.set(childMapKey, childMapValue);
const keyToLookFor = "childMap";
const oldMap = new Map<string, Map<number, number>>();
oldMap.set(keyToLookFor, oldChildMap);
const newMap = deepCopy(
oldMap,
SerializationType.NONE,
"copiedMapHasChildMap",
);
if (!isTSTLMap(newMap)) {
error(`The copied Map was not a Map and had a type of: ${typeof newMap}`);
}
const newChildMap = newMap.get(keyToLookFor);
if (newChildMap === undefined) {
error(`The copied Map did not have a child map at key: ${keyToLookFor}`);
}
if (!isTSTLMap(newChildMap)) {
error(
`The copied child Map was not a Map and had a type of: ${typeof newChildMap}`,
);
}
const value = newChildMap.get(childMapKey);
if (value === undefined) {
error(`The copied child Map did not have a key of: ${childMapKey}`);
}
if (value !== childMapValue) {
error(`The copied child Map did not have a value of: ${childMapValue}`);
}
}
function copiedDefaultMapHasChildDefaultMap() {
const parentMapKey = "abc";
const childMapKey1 = 123;
const childMapKey2 = 456;
const childMapDefaultValue = 1;
const childMapCustomValue = 2;
const oldParentMap = new DefaultMap<string, DefaultMap<number, number>>(
() => new DefaultMap(childMapDefaultValue),
);
const oldChildMap = oldParentMap.getAndSetDefault(parentMapKey);
oldChildMap.getAndSetDefault(childMapKey1);
oldChildMap.set(childMapKey2, childMapCustomValue);
const newParentMap = deepCopy(
oldParentMap,
SerializationType.NONE,
"copiedDefaultMapHasChildDefaultMap",
);
if (!isDefaultMap(newParentMap)) {
error(
`The copied parent DefaultMap was not a DefaultMap and had a type of: ${typeof newParentMap}`,
);
}
const newChildMap = newParentMap.get(parentMapKey);
if (newChildMap === undefined) {
error(
`The copied DefaultMap did not have a child map at key: ${parentMapKey}`,
);
}
if (!isDefaultMap(newChildMap)) {
error(
`The copied child DefaultMap was not a DefaultMap and had a type of: ${typeof newChildMap}`,
);
}
const newChildMapValue1 = newChildMap.get(childMapKey1);
if (newChildMapValue1 === undefined) {
error(`The copied child DefaultMap did not have a key of: ${childMapKey1}`);
}
if (newChildMapValue1 !== childMapDefaultValue) {
error(
`The copied child Map did not have a default value of: ${childMapDefaultValue}`,
);
}
const newChildMapValue2 = newChildMap.get(childMapKey2);
if (newChildMapValue2 === undefined) {
error(`The copied child DefaultMap did not have a key of: ${childMapKey2}`);
}
if (newChildMapValue2 !== childMapCustomValue) {
error(
`The copied child Map did not have a custom value of: ${childMapCustomValue}`,
);
}
}
function copiedDefaultMapHasBrand() {
const oldDefaultValue = "foo";
const oldDefaultMap = new DefaultMap<string, string>(oldDefaultValue);
const newTable = deepCopy(
oldDefaultMap,
SerializationType.SERIALIZE,
"copiedDefaultMapHasBrand",
) as LuaMap<AnyNotNil, unknown>;
if (!isTable(newTable)) {
error(
`The copied DefaultMap was not a table and had a type of: ${typeof newTable}`,
);
}
if (!newTable.has(SerializationBrand.DEFAULT_MAP)) {
error(
`The copied DefaultMap does not have the brand: ${SerializationBrand.DEFAULT_MAP}`,
);
}
}
function copiedSerializedMapHasStringKey() {
const mapKey = "123";
const mapValue = 456;
const oldMap = new Map<string, number>();
oldMap.set(mapKey, mapValue);
const serializedOldMap = deepCopy(
oldMap,
SerializationType.SERIALIZE,
"copiedSerializedMapHasStringKey-serialize",
);
const newTable = deepCopy(
serializedOldMap,
SerializationType.DESERIALIZE,
"copiedSerializedMapHasStringKey-deserialize",
);
const newMap = newTable as Map<string, number>;
if (!newMap.has(mapKey)) {
const keyType = type(mapKey);
error(
`The copied Map did not have a key of: ${mapKey} with type ${keyType}`,
);
}
}
function copiedSerializedMapHasNumberKey() {
const mapKey = 123;
const mapValue = 456;
const oldMap = new Map<number, number>();
oldMap.set(mapKey, mapValue);
const serializedOldMap = deepCopy(
oldMap,
SerializationType.SERIALIZE,
"copiedSerializedMapHasNumberKey-serialize",
);
const newTable = deepCopy(
serializedOldMap,
SerializationType.DESERIALIZE,
"copiedSerializedMapHasNumberKey-deserialize",
);
const newMap = newTable as Map<number, number>;
if (!newMap.has(mapKey)) {
const keyType = type(mapKey);
error(
`The copied Map did not have a key of: ${mapKey} with type ${keyType}`,
);
}
}
function copiedSerializedDefaultMapHasStringKey() {
const mapKey = "123";
const oldDefaultMap = new DefaultMap<string, number>(456);
oldDefaultMap.getAndSetDefault(mapKey);
const serializedOldDefaultMap = deepCopy(
oldDefaultMap,
SerializationType.SERIALIZE,
"copiedSerializedDefaultMapHasStringKey-serialize",
);
const newTable = deepCopy(
serializedOldDefaultMap,
SerializationType.DESERIALIZE,
"copiedSerializedDefaultMapHasStringKey-deserialize",
);
const newDefaultMap = newTable as DefaultMap<string, number>;
if (!newDefaultMap.has(mapKey)) {
const keyType = type(mapKey);
error(
`The copied DefaultMap did not have a key of "${mapKey}" with type: ${keyType}`,
);
}
}
function copiedSerializedDefaultMapHasNumberKey() {
const mapKey = 123;
const oldDefaultMap = new DefaultMap<number, number>(456);
oldDefaultMap.getAndSetDefault(mapKey);
const serializedOldDefaultMap = deepCopy(
oldDefaultMap,
SerializationType.SERIALIZE,
"copiedSerializedDefaultMapHasNumberKey-serialize",
);
const newTable = deepCopy(
serializedOldDefaultMap,
SerializationType.DESERIALIZE,
"copiedSerializedDefaultMapHasNumberKey-deserialize",
);
const newDefaultMap = newTable as DefaultMap<number, number>;
if (!newDefaultMap.has(mapKey)) {
const keyType = type(mapKey);
error(
`The copied DefaultMap did not have a key of: ${mapKey} with type ${keyType}`,
);
}
}