Skip to content

Commit

Permalink
Tests for smartCover server object.
Browse files Browse the repository at this point in the history
  • Loading branch information
Neloreck committed Nov 12, 2023
1 parent 608376d commit 0b62dfa
Show file tree
Hide file tree
Showing 10 changed files with 229 additions and 46 deletions.
2 changes: 1 addition & 1 deletion src/engine/core/objects/LevelChanger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ describe("LevelChanger server class", () => {
EPacketDataType.STRING,
EPacketDataType.U16,
]);
expect(netProcessor.dataList).toEqual(["cse_alife_object", false, "another", 2]);
expect(netProcessor.dataList).toEqual(["LevelChanger", false, "another", 2]);

const anotherLevelChanger: LevelChanger = new LevelChanger("test-section");

Expand Down
2 changes: 1 addition & 1 deletion src/engine/core/objects/creature/Actor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe("Actor server object", () => {

expect(saveManager.serverSave).toHaveBeenCalledWith(netProcessor);
expect(netProcessor.writeDataOrder).toEqual([EPacketDataType.STRING, EPacketDataType.U16]);
expect(netProcessor.dataList).toEqual(["cse_alife_object", 0]);
expect(netProcessor.dataList).toEqual(["Actor", 0]);

actor.STATE_Read(mockNetPacket(netProcessor), 0);

Expand Down
8 changes: 4 additions & 4 deletions src/engine/core/objects/creature/Stalker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ describe("Stalker server object", () => {
EPacketDataType.STRING,
EPacketDataType.BOOLEAN,
]);
expect(netProcessor.dataList).toEqual(["cse_alife_object", NIL, NIL, false]);
expect(netProcessor.dataList).toEqual(["Stalker", NIL, NIL, false]);

const another: Stalker = new Stalker("stalker");

Expand Down Expand Up @@ -244,7 +244,7 @@ describe("Stalker server object", () => {
EPacketDataType.STRING,
EPacketDataType.BOOLEAN,
]);
expect(netProcessor.dataList).toEqual(["cse_alife_object", "435", "test_section", true]);
expect(netProcessor.dataList).toEqual(["Stalker", "435", "test_section", true]);

const another: Stalker = new Stalker("stalker");

Expand Down Expand Up @@ -281,7 +281,7 @@ describe("Stalker server object", () => {
EPacketDataType.STRING,
EPacketDataType.BOOLEAN,
]);
expect(netProcessor.dataList).toEqual(["cse_alife_object", "311", "test_section", true]);
expect(netProcessor.dataList).toEqual(["Stalker", "311", "test_section", true]);

const another: Stalker = new Stalker("stalker");

Expand Down Expand Up @@ -316,7 +316,7 @@ describe("Stalker server object", () => {
EPacketDataType.STRING,
EPacketDataType.BOOLEAN,
]);
expect(netProcessor.dataList).toEqual(["cse_alife_object", NIL, "test_section", true]);
expect(netProcessor.dataList).toEqual(["Stalker", NIL, "test_section", true]);

const another: Stalker = new Stalker("stalker");

Expand Down
178 changes: 173 additions & 5 deletions src/engine/core/objects/smart_cover/SmartCover.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import { describe, expect, it, jest } from "@jest/globals";
import { beforeEach, describe, expect, it, jest } from "@jest/globals";

import { registry } from "@/engine/core/database";
import { EGameEvent, EventsManager } from "@/engine/core/managers/events";
import { SmartCover } from "@/engine/core/objects/smart_cover/index";
import { SmartCover } from "@/engine/core/objects/smart_cover/SmartCover";
import { LuaArray, TStringId } from "@/engine/lib/types";
import { resetRegistry } from "@/fixtures/engine";
import { MockLuaTable } from "@/fixtures/lua";
import { EPacketDataType, mockIniFile, MockNetProcessor } from "@/fixtures/xray";

describe("SmartCover server object", () => {
beforeEach(() => {
resetRegistry();
});

it("should correctly emit lifecycle events", () => {
const eventsManager: EventsManager = EventsManager.getInstance();
const smartCover: SmartCover = new SmartCover("monster");
Expand All @@ -25,9 +34,168 @@ describe("SmartCover server object", () => {
expect(onSmartCoverUnregister).toHaveBeenCalledWith(smartCover);
});

it.todo("should handle registration events");
it("should handle registration events", () => {
const cover: SmartCover = new SmartCover("test_smart_cover");

jest.spyOn(cover, "spawn_ini").mockReturnValue(
mockIniFile("spawn.ini", {
story_object: {
story_id: "test-story-id",
},
})
);

expect(registry.smartCovers.length()).toBe(0);
expect(registry.storyLink.idBySid.length()).toBe(0);
expect(registry.storyLink.sidById.length()).toBe(0);

cover.on_before_register();

it.todo("should correctly fill props");
expect(registry.smartCovers.length()).toBe(1);
expect(registry.storyLink.idBySid.length()).toBe(0);
expect(registry.storyLink.sidById.length()).toBe(0);
expect(registry.smartCovers.get(cover.name())).toBe(cover);

cover.on_register();

expect(registry.storyLink.idBySid.get("test-story-id")).toBe(cover.id);
expect(registry.storyLink.sidById.get(cover.id)).toBe("test-story-id");

cover.on_unregister();

expect(registry.smartCovers.length()).toBe(0);
expect(registry.storyLink.idBySid.length()).toBe(0);
expect(registry.storyLink.sidById.length()).toBe(0);
});

it.todo("should correctly save and load data");
it("should correctly fill props when have description", () => {
const cover: SmartCover = new SmartCover("test_smart_cover");
const items: LuaArray<unknown> = new LuaTable();

jest.spyOn(cover, "description").mockImplementation(() => "combat_prone");

cover.FillProps("test-pref-1", items);

expect(cover.lastDescription).toBe("combat_prone");
expect(cover.loopholes).toEqualLuaTables({
prone: true,
});
expect(cover.set_loopholes_table_checker).toHaveBeenCalledWith([
items,
"test-pref-1\\test_smart_cover\\loopholes\\prone",
cover,
cover.loopholes,
"prone",
]);

cover.FillProps("test-pref-1", items);

expect(cover.loopholes).toEqualLuaTables({
prone: true,
});

jest.spyOn(cover, "description").mockImplementation(() => "combat_front");

cover.FillProps("test-pref-2", items);

expect(cover.lastDescription).toBe("combat_front");
expect(cover.loopholes).toEqualLuaTables({
crouch_front: true,
crouch_front_left: true,
crouch_front_right: true,
stand_front_left: true,
stand_front_right: true,
});
expect(cover.set_loopholes_table_checker).toHaveBeenCalledTimes(7);
for (const [key] of cover.loopholes) {
expect(cover.set_loopholes_table_checker).toHaveBeenCalledWith([
items,
`test-pref-2\\test_smart_cover\\loopholes\\${key}`,
cover,
cover.loopholes,
key,
]);
}
});

it("should correctly fill props when have no description", () => {
const cover: SmartCover = new SmartCover("test_smart_cover");
const items: LuaArray<unknown> = new LuaTable();

jest.spyOn(cover, "description").mockImplementation(() => null);

cover.FillProps("test-pref-2", items);

expect(cover.lastDescription).toBe("nil");
expect(cover.loopholes).toEqualLuaTables({});
expect(cover.set_loopholes_table_checker).not.toHaveBeenCalled();
});

it("should correctly save and load data", () => {
const cover: SmartCover = new SmartCover("test_smart_cover");
const netProcessor: MockNetProcessor = new MockNetProcessor();

cover.lastDescription = "combat_front";
cover.loopholes = MockLuaTable.mockFromObject<TStringId, boolean>({
crouch_front: true,
crouch_front_left: false,
crouch_front_right: true,
stand_front_left: false,
});

cover.STATE_Write(netProcessor.asMockNetPacket());

expect(netProcessor.writeDataOrder).toEqual([
EPacketDataType.STRING,
EPacketDataType.STRING,
EPacketDataType.U8,
EPacketDataType.STRING,
EPacketDataType.BOOLEAN,
EPacketDataType.STRING,
EPacketDataType.BOOLEAN,
EPacketDataType.STRING,
EPacketDataType.BOOLEAN,
EPacketDataType.STRING,
EPacketDataType.BOOLEAN,
]);
expect(netProcessor.dataList).toEqual([
"SmartCover",
"combat_front",
4,
"crouch_front",
true,
"crouch_front_left",
false,
"crouch_front_right",
true,
"stand_front_left",
false,
]);

const anotherCover: SmartCover = new SmartCover("test_smart_cover");

anotherCover.STATE_Read(netProcessor.asMockNetPacket(), 5001);

expect(netProcessor.dataList).toEqual([]);
expect(anotherCover.lastDescription).toBe("combat_front");
expect(cover.loopholes).toEqualLuaTables({
crouch_front: true,
crouch_front_left: false,
crouch_front_right: true,
stand_front_left: false,
});
});

it("should fail if description does not exist on load", () => {
const cover: SmartCover = new SmartCover("test_smart_cover");
const netProcessor: MockNetProcessor = new MockNetProcessor();

cover.lastDescription = "not_existing";

cover.STATE_Write(netProcessor.asMockNetPacket());

expect(() => cover.STATE_Read(netProcessor.asMockNetPacket(), 5001)).toThrow(
`SmartCover '${cover.name()}' has wrong description - 'not_existing'.`
);
});
});
54 changes: 26 additions & 28 deletions src/engine/core/objects/smart_cover/SmartCover.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { cse_smart_cover, LuabindClass, properties_helper } from "xray16";

import { ISmartCoverLoopholeDescriptor, smartCoversList } from "@/engine/core/animation/smart_covers";
import { smartCoversList } from "@/engine/core/animation/smart_covers/list";
import {
registerObjectStoryLinks,
registerSmartCover,
Expand All @@ -10,7 +10,9 @@ import {
import { EGameEvent, EventsManager } from "@/engine/core/managers/events";
import { assert } from "@/engine/core/utils/assertion";
import { LuaLogger } from "@/engine/core/utils/logging";
import { NetPacket, Optional, TCount, TLabel, TSection, TStringId } from "@/engine/lib/types";
import { resetTable } from "@/engine/core/utils/table";
import { NIL } from "@/engine/lib/constants/words";
import { LuaArray, NetPacket, Optional, TCount, TLabel, TSection, TStringId } from "@/engine/lib/types";

const logger: LuaLogger = new LuaLogger($filename);

Expand Down Expand Up @@ -39,47 +41,45 @@ export class SmartCover extends cse_smart_cover {
super.on_register();

registerObjectStoryLinks(this);

EventsManager.emitEvent(EGameEvent.SMART_COVER_REGISTER, this);
}

public override on_unregister(): void {
EventsManager.emitEvent(EGameEvent.SMART_COVER_UNREGISTER, this);

unregisterStoryLinkByObjectId(this.id);
unregisterSmartCover(this);

super.on_unregister();
}

public override FillProps(pref: string, items: LuaTable<number>): void {
public override FillProps(pref: string, items: LuaArray<unknown>): void {
super.FillProps(pref, items);

const smartCoverDescription: Optional<TLabel> = this.description();

if (smartCoverDescription !== this.lastDescription) {
for (const [key] of this.loopholes) {
this.loopholes.delete(key);
}
const nextDescription: TLabel = tostring(this.description());

this.lastDescription = tostring(smartCoverDescription);
if (nextDescription !== this.lastDescription) {
this.lastDescription = tostring(nextDescription);
resetTable(this.loopholes);
}

if (smartCoverDescription !== null) {
const loopholes = smartCoversList.get(smartCoverDescription).loopholes;

for (const [, descriptor] of loopholes) {
// todo: should this be part of memoized check?
if (nextDescription !== NIL) {
for (const [, descriptor] of smartCoversList.get(nextDescription).loopholes) {
if (this.loopholes.get(descriptor.id) === null) {
this.loopholes.set(descriptor.id, true);
}

const isLoopholesTableCheckerEnabled: boolean = new properties_helper().create_bool(
items,
`${pref}\\${this.section_name()}\\loopholes\\${descriptor.id}`,
this,
this.loopholes,
descriptor.id
this.set_loopholes_table_checker(
new properties_helper().create_bool(
items,
`${pref}\\${this.section_name()}\\loopholes\\${descriptor.id}`,
this,
this.loopholes,
descriptor.id
)
);

this.set_loopholes_table_checker(isLoopholesTableCheckerEnabled);
}
}
}
Expand All @@ -98,6 +98,7 @@ export class SmartCover extends cse_smart_cover {
public override STATE_Read(packet: NetPacket, size: TCount): void {
super.STATE_Read(packet, size);

// todo: Handle `nil` values with string casting.
this.lastDescription = packet.r_stringZ();

const smartCoverDescription: Optional<string> =
Expand All @@ -107,15 +108,12 @@ export class SmartCover extends cse_smart_cover {
if (smartCoverDescription !== null) {
assert(
smartCoversList.has(smartCoverDescription),
"SmartCover [%s] has wrong description [%s].",
"SmartCover '%s' has wrong description - '%s'.",
this.name(),
tostring(smartCoverDescription)
);

const loopholes: LuaTable<number, ISmartCoverLoopholeDescriptor> =
smartCoversList.get(smartCoverDescription).loopholes;

for (const [, descriptor] of loopholes) {
for (const [, descriptor] of smartCoversList.get(smartCoverDescription).loopholes) {
existingLoopholes.set(descriptor.id, true);
}

Expand All @@ -125,7 +123,7 @@ export class SmartCover extends cse_smart_cover {
const loopholeId: TStringId = packet.r_stringZ();
const isLoopholeExisting: boolean = packet.r_bool();

if (existingLoopholes.get(loopholeId) !== null) {
if (existingLoopholes.has(loopholeId)) {
this.loopholes.set(loopholeId, isLoopholeExisting);
}
}
Expand Down
6 changes: 2 additions & 4 deletions src/engine/core/objects/smart_terrain/SmartTerrain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ describe("SmartTerrain class generic logic", () => {
EPacketDataType.U16,
]);
expect(netProcessor.dataList).toEqual([
"cse_alife_object",
"SmartTerrain",
2,
firstArriving.id,
secondArriving.id,
Expand Down Expand Up @@ -300,7 +300,6 @@ describe("SmartTerrain class generic logic", () => {

const anotherSmartTerrain: SmartTerrain = mockSmartTerrainWithConfiguration("another_smart");

mockSmartTerrain();
anotherSmartTerrain.STATE_Read(netProcessor.asMockNetPacket(), 5001);

expect(anotherSmartTerrain.arrivingObjects).toEqualLuaTables({
Expand Down Expand Up @@ -355,11 +354,10 @@ describe("SmartTerrain class generic logic", () => {
EPacketDataType.U8,
EPacketDataType.U16,
]);
expect(netProcessor.dataList).toEqual(["cse_alife_object", 0, 0, 0, false, false, 0, 6]);
expect(netProcessor.dataList).toEqual(["SmartTerrain", 0, 0, 0, false, false, 0, 6]);

const anotherSmartTerrain: SmartTerrain = mockSmartTerrainWithConfiguration("another_smart");

mockSmartTerrain();
anotherSmartTerrain.STATE_Read(netProcessor.asMockNetPacket(), 5001);

expect(anotherSmartTerrain.arrivingObjects.length()).toBe(0);
Expand Down
Loading

0 comments on commit 0b62dfa

Please sign in to comment.