Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions managed/CounterStrikeSharp.Tests.Native/CommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,6 @@ public async Task CanTriggerCommandsWithPublicChatTrigger()
NativeAPI.IssueServerCommand("css_test_public_chat 1 2 3");
await WaitOneFrame();
mock.Verify(s => s(It.IsAny<int>(), It.IsAny<IntPtr>()), Times.Once);

NativeAPI.IssueServerCommand("say \"!test_public_chat 1 2 3\"");
await WaitOneFrame();
mock.Verify(s => s(It.IsAny<int>(), It.IsAny<IntPtr>()), Times.Exactly(2));
NativeAPI.RemoveCommand("css_test_public_chat", methodCallback);
}

[Fact]
Expand Down
133 changes: 133 additions & 0 deletions managed/CounterStrikeSharp.Tests.Native/EngineTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using System.Threading.Tasks;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using Xunit;

namespace NativeTestsPlugin;

public class EngineTests
{
[Fact]
public void GetMapName_ReturnsValidMapName()
{
var mapName = Server.MapName;

Assert.NotNull(mapName);
Assert.NotEmpty(mapName);
}

[Fact]
public void GetGameDirectory_ReturnsValidPath()
{
var gameDir = Server.GameDirectory;

Assert.NotNull(gameDir);
Assert.NotEmpty(gameDir);
}

[Fact]
public void IsMapValid_ReturnsTrueForCurrentMap()
{
var mapName = Server.MapName;
var isValid = Server.IsMapValid(mapName);

Assert.True(isValid, $"Current map '{mapName}' should be valid");
}

[Fact]
public void IsMapValid_ReturnsFalseForInvalidMap()
{
var isValid = Server.IsMapValid("nonexistent_map_xyz_12345");

Assert.False(isValid, "Nonexistent map should be invalid");
}

[Fact]
public void GetTickCount_ReturnsPositiveValue()
{
var tickCount = Server.TickCount;

Assert.True(tickCount > 0, $"TickCount should be positive, got {tickCount}");
}

[Fact]
public async Task GetTickCount_IncrementsOverTime()
{
var tickCount1 = Server.TickCount;
await WaitOneFrame();
var tickCount2 = Server.TickCount;

Assert.True(tickCount2 > tickCount1, $"TickCount should increment: {tickCount1} -> {tickCount2}");
}

[Fact]
public void GetEngineTime_ReturnsPositiveValue()
{
var engineTime = Server.EngineTime;

Assert.True(engineTime > 0, $"EngineTime should be positive, got {engineTime}");
}

[Fact]
public async Task GetEngineTime_IncrementsOverTime()
{
var time1 = Server.EngineTime;
await WaitOneFrame();
var time2 = Server.EngineTime;

Assert.True(time2 > time1, $"EngineTime should increment: {time1} -> {time2}");
}

[Fact]
public void GetCurrentTime_ReturnsPositiveValue()
{
var currentTime = Server.CurrentTime;

Assert.True(currentTime > 0, $"CurrentTime should be positive, got {currentTime}");
}

[Fact]
public async Task GetCurrentTime_IncrementsOverTime()
{
var time1 = Server.CurrentTime;
await WaitOneFrame();
var time2 = Server.CurrentTime;

Assert.True(time2 > time1, $"CurrentTime should increment: {time1} -> {time2}");
}

[Fact]
public void GetMaxClients_ReturnsExpectedValue()
{
var maxPlayers = Server.MaxPlayers;

Assert.True(maxPlayers > 0, $"MaxPlayers should be positive, got {maxPlayers}");
Assert.True(maxPlayers <= 64, $"MaxPlayers should be <= 64 for CS2, got {maxPlayers}");
}

[Fact]
public void GetTickInterval_ReturnsCorrectValue()
{
var tickInterval = NativeAPI.GetTickInterval();

// CS2 is a 64 tick server, so tick interval should be 1/64 = 0.015625
Assert.True(tickInterval > 0, $"TickInterval should be positive, got {tickInterval}");
Assert.Equal(0.015625f, tickInterval, 6);
}

[Fact]
public void GetGameFrameTime_ReturnsPositiveValue()
{
var frameTime = Server.FrameTime;

Assert.True(frameTime > 0, $"FrameTime should be positive, got {frameTime}");
}

[Fact]
public void GetTickedTime_ReturnsNonNegativeValue()
{
var tickedTime = Server.TickedTime;

Assert.True(tickedTime >= 0, $"TickedTime should be non-negative, got {tickedTime}");
}
}
73 changes: 73 additions & 0 deletions managed/CounterStrikeSharp.Tests.Native/EntityIOTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System.Linq;
using System.Threading.Tasks;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using Moq;
using Xunit;

namespace NativeTestsPlugin;

public class EntityIOTests
{
[Fact]
public void GetDesignerName_ReturnsCorrectName()
{
var world = Utilities.FindAllEntitiesByDesignerName<CWorld>("worldent").FirstOrDefault();

Assert.NotNull(world);
Assert.Equal("worldent", world.DesignerName);
}

[Fact]
public void GetEntityFromIndex_ReturnsValidPointer()
{
var world = Utilities.GetEntityFromIndex<CWorld>(0);
Assert.NotNull(world);

Assert.NotNull(world);
Assert.Equal("worldent", world.DesignerName);
}

[Fact]
public void GetEntityFromHandle_Works()
{
var world = Utilities.FindAllEntitiesByDesignerName<CWorld>("worldent").FirstOrDefault();

Assert.NotNull(world);

var worldFromHandle = world.EntityHandle.Get().As<CWorld>();

Assert.Equal(world.Handle, worldFromHandle.Handle);
}

[Fact]
public async Task AcceptInput_DoesNotThrow()
{
var entity = Utilities.CreateEntityByName<CBaseModelEntity>("prop_dynamic");

Assert.NotNull(entity);

var mock = new Mock<Action>();
var callback = FunctionReference.Create(mock.Object);

try
{
NativeAPI.HookEntityOutput("prop_dynamic", "OnUser1", callback, HookMode.Pre);
NativeAPI.AcceptInput(entity.Handle, "FireUser1", IntPtr.Zero, IntPtr.Zero, "", 0);
await WaitOneFrame();

Assert.Single(mock.Invocations);

// Test unhook
NativeAPI.UnhookEntityOutput("prop_dynamic", "OnUser1", callback, HookMode.Pre);
NativeAPI.AcceptInput(entity.Handle, "FireUser1", IntPtr.Zero, IntPtr.Zero, "", 0);

await WaitOneFrame();
Assert.Single(mock.Invocations);
}
finally
{
entity.Remove();
}
}
}
98 changes: 98 additions & 0 deletions managed/CounterStrikeSharp.Tests.Native/FrameSchedulingTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using System.Threading.Tasks;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using Moq;
using Xunit;

namespace NativeTestsPlugin;

public class FrameSchedulingTests
{
[Fact]
public async Task QueueTaskForNextFrame_ExecutesCallback()
{
var mock = new Mock<Action>();
var callback = FunctionReference.Create(mock.Object);

NativeAPI.QueueTaskForNextFrame(callback);
await WaitOneFrame();

mock.Verify(s => s(), Times.Once);
}

[Fact]
public async Task QueueTaskForFrame_ExecutesAtSpecifiedTick()
{
var mock = new Mock<Action>();
var callback = FunctionReference.Create(mock.Object);
var targetTick = Server.TickCount + 5;

NativeAPI.QueueTaskForFrame(targetTick, callback);

mock.Verify(s => s(), Times.Never);

// Wait for the tick to pass
await Server.RunOnTickAsync(targetTick + 1, () => { });

mock.Verify(s => s(), Times.Once);
}

[Fact]
public async Task QueueTaskForNextWorldUpdate_ExecutesCallback()
{
var mock = new Mock<Action>();
var callback = FunctionReference.Create(mock.Object);

NativeAPI.QueueTaskForNextWorldUpdate(callback);
await WaitOneFrame();

mock.Verify(s => s(), Times.Once);
}

[Fact]
public async Task NextFrame_HighLevelApi_ExecutesCallback()
{
bool called = false;
Server.NextFrame(() => { called = true; });

await WaitOneFrame();

Assert.True(called, "NextFrame callback should have been called");
}

[Fact]
public async Task NextFrameAsync_ReturnsCompletedTask()
{
bool called = false;
var task = Server.NextFrameAsync(() => { called = true; });

await task;

Assert.True(called, "NextFrameAsync callback should have been called");
Assert.True(task.IsCompleted, "Task should be completed");
}

[Fact]
public async Task RunOnTickAsync_ReturnsCompletedTask()
{
bool called = false;
var targetTick = Server.TickCount + 3;
var task = Server.RunOnTickAsync(targetTick, () => { called = true; });

await task;

Assert.True(called, "RunOnTickAsync callback should have been called");
Assert.True(task.IsCompleted, "Task should be completed");
}

[Fact]
public async Task NextWorldUpdate_HighLevelApi_ExecutesCallback()
{
bool called = false;
Server.NextWorldUpdate(() => { called = true; });

await WaitOneFrame();

Assert.True(called, "NextWorldUpdate callback should have been called");
}
}
Loading
Loading