< Summary

Class:ThreatTests
Assembly:bamlab.test.editmode
File(s):/github/workspace/Assets/Tests/EditMode/ThreatTests.cs
Covered lines:0
Uncovered lines:131
Coverable lines:131
Total lines:310
Line coverage:0% (0 of 131)
Covered branches:0
Total branches:0
Covered methods:0
Total methods:11
Method coverage:0% (0 of 11)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
Setup()0%2100%
Teardown()0%20400%
TestDirectAttack_LoadedCorrectly()0%2100%
Threat_IsNotAssignable()0%2100%
FixedWingThreat_CalculateAccelerationInput_RespectsMaxForwardAcceleration()0%2100%
FixedWingThreat_CalculateAccelerationInput_RespectsMaxNormalAcceleration()0%2100%
RotaryWingThreat_CalculateAccelerationToWaypoint_RespectsMaxForwardAcceleration()0%2100%
RotaryWingThreat_CalculateAccelerationToWaypoint_RespectsMaxNormalAcceleration()0%2100%
MockAttackBehavior(...)0%2100%
GetNextWaypoint(...)0%2100%
RotaryWingThreat_CalculateAccelerationToWaypoint_ComputesCorrectly()0%2100%

File(s)

/github/workspace/Assets/Tests/EditMode/ThreatTests.cs

#LineLine coverage
 1using System;
 2using NUnit.Framework;
 3using UnityEngine;
 4using System.Collections.Generic;
 5using System.Linq;
 6using System.IO;
 7
 8public class ThreatTests : AgentTestBase {
 9  private FixedWingThreat fixedWingThreat;
 10  private RotaryWingThreat rotaryWingThreat;
 11
 12  private const string TestDirectAttackJson =
 13      @"
 14    {
 15        ""name"": ""TestDirectAttack"",
 16        ""attackBehaviorType"": ""DIRECT_ATTACK"",
 17        ""targetPosition"": {
 18            ""x"": 0.01,
 19            ""y"": 0.0,
 20            ""z"": 0.0
 21        },
 22        ""targetVelocity"": {
 23            ""x"": 0.0001,
 24            ""y"": 0.0,
 25            ""z"": 0.0
 26        },
 27        ""targetColliderSize"": {
 28            ""x"": 20.0,
 29            ""y"": 20.0,
 30            ""z"": 20.0
 31        },
 32        ""flightPlan"": {
 33            ""type"": ""DistanceToTarget"",
 34            ""waypoints"": [
 35                {
 36                    ""distance"": 10000.0,
 37                    ""altitude"": 500.0,
 38                    ""power"": ""MIL""
 39                },
 40                {
 41                    ""distance"": 5000.0,
 42                    ""altitude"": 100.0,
 43                    ""power"": ""MAX""
 44                }
 45            ]
 46        }
 47    }
 48    ";
 49
 050  public override void Setup() {
 051    base.Setup();
 52    // Write the hard-coded JSON to the file
 053    string attackConfigPath = Path.Combine(Application.streamingAssetsPath,
 54                                           "Configs/Behaviors/Attack/test_direct_attack.json");
 055    Directory.CreateDirectory(Path.GetDirectoryName(attackConfigPath));
 056    File.WriteAllText(attackConfigPath, TestDirectAttackJson);
 57    // Load configurations using ConfigLoader
 58    // Create dynamic configurations for threats
 059    var ucavConfig = new DynamicAgentConfig {
 60      agent_model = "ucav.json", attack_behavior = "test_direct_attack.json",
 61      initial_state = new InitialState { position = new Vector3(2000, 100, 4000),
 62                                         rotation = new Vector3(90, 0, 0),
 63                                         velocity = new Vector3(-50, 0, -100) },
 64      standard_deviation = new StandardDeviation { position = new Vector3(400, 30, 400),
 65                                                   velocity = new Vector3(0, 0, 15) },
 66      dynamic_config =
 67          new DynamicConfig { launch_config = new LaunchConfig { launch_time = 0 },
 68                              sensor_config =
 69                                  new SensorConfig { type = SensorType.IDEAL, frequency = 100 } }
 70    };
 71
 072    var quadcopterConfig = new DynamicAgentConfig {
 73      agent_model = "quadcopter.json", attack_behavior = "test_direct_attack.json",
 74      initial_state =
 75          new InitialState { position = new Vector3(0, 600, 6000), rotation = new Vector3(90, 0, 0),
 76                             velocity = new Vector3(0, 0, -50) },
 77      standard_deviation = new StandardDeviation { position = new Vector3(1000, 200, 100),
 78                                                   velocity = new Vector3(0, 0, 25) },
 79      dynamic_config =
 80          new DynamicConfig { launch_config = new LaunchConfig { launch_time = 0 },
 81                              sensor_config =
 82                                  new SensorConfig { type = SensorType.IDEAL, frequency = 100 } }
 83    };
 84
 085    Agent threatAgent = CreateTestThreat(ucavConfig);
 086    Assert.IsNotNull(threatAgent);
 087    Assert.IsTrue(threatAgent is FixedWingThreat);
 088    fixedWingThreat = (FixedWingThreat)threatAgent;
 089    Assert.IsNotNull(fixedWingThreat);
 90
 091    threatAgent = CreateTestThreat(quadcopterConfig);
 092    Assert.IsNotNull(threatAgent);
 093    Assert.IsTrue(threatAgent is RotaryWingThreat);
 094    rotaryWingThreat = (RotaryWingThreat)threatAgent;
 095    Assert.IsNotNull(rotaryWingThreat);
 096  }
 97
 098  public override void Teardown() {
 099    base.Teardown();
 100    // Delete the attack configuration file
 0101    string attackConfigPath = Path.Combine(Application.streamingAssetsPath,
 102                                           "Configs/Behaviors/Attack/test_direct_attack.json");
 0103    if (File.Exists(attackConfigPath)) {
 0104      File.Delete(attackConfigPath);
 0105    }
 106
 0107    if (fixedWingThreat != null) {
 0108      GameObject.DestroyImmediate(fixedWingThreat.gameObject);
 0109    }
 110
 0111    if (rotaryWingThreat != null) {
 0112      GameObject.DestroyImmediate(rotaryWingThreat.gameObject);
 0113    }
 0114  }
 115
 116  [Test]
 0117  public void TestDirectAttack_LoadedCorrectly() {
 118    // Arrange
 0119    try {
 120      // Act
 0121      DynamicAgentConfig config = new DynamicAgentConfig {
 122        agent_model = "ucav.json", attack_behavior = "test_direct_attack.json",
 123        initial_state = new InitialState(), standard_deviation = new StandardDeviation(),
 124        dynamic_config = new DynamicConfig()
 125      };
 126
 0127      Threat threat = CreateTestThreat(config);
 128
 129      // Assert
 0130      Assert.IsNotNull(threat, "Threat should not be null");
 131
 0132      AttackBehavior attackBehavior = GetPrivateField<AttackBehavior>(threat, "_attackBehavior");
 0133      Assert.IsNotNull(attackBehavior, "Attack behavior should not be null");
 0134      Assert.AreEqual("TestDirectAttack", attackBehavior.name);
 0135      Assert.AreEqual(AttackBehavior.AttackBehaviorType.DIRECT_ATTACK,
 136                      attackBehavior.attackBehaviorType);
 137
 0138      Assert.IsTrue(attackBehavior is DirectAttackBehavior,
 139                    "Attack behavior should be a DirectAttackBehavior");
 0140      DirectAttackBehavior directAttackBehavior = (DirectAttackBehavior)attackBehavior;
 141
 0142      Vector3 targetPosition = directAttackBehavior.targetPosition;
 0143      Assert.AreEqual(new Vector3(0.01f, 0, 0), targetPosition);
 144
 0145      DTTFlightPlan flightPlan = directAttackBehavior.flightPlan;
 0146      Assert.IsNotNull(flightPlan, "Flight plan should not be null");
 0147      Assert.AreEqual("DistanceToTarget", flightPlan.type);
 148
 0149      List<DTTWaypoint> dttWaypoints = flightPlan.waypoints;
 0150      Assert.IsNotNull(dttWaypoints, "Waypoints should not be null");
 0151      Assert.AreEqual(2, dttWaypoints.Count, "There should be 2 waypoints");
 152
 0153      Assert.AreEqual(5000f, dttWaypoints[0].distance);
 0154      Assert.AreEqual(100f, dttWaypoints[0].altitude);
 0155      Assert.AreEqual(PowerSetting.MAX, dttWaypoints[0].power);
 156
 0157      Assert.AreEqual(10000f, dttWaypoints[1].distance);
 0158      Assert.AreEqual(500f, dttWaypoints[1].altitude);
 0159      Assert.AreEqual(PowerSetting.MIL, dttWaypoints[1].power);
 160
 161      // Check targetVelocity
 0162      Vector3 targetVelocity = directAttackBehavior.targetVelocity;
 0163      Assert.AreEqual(new Vector3(0.0001f, 0f, 0f), targetVelocity,
 164                      "Target velocity should match the config");
 165
 166      // Check targetColliderSize
 0167      Vector3 targetColliderSize = directAttackBehavior.targetColliderSize;
 0168      Assert.AreEqual(new Vector3(20f, 20f, 20f), targetColliderSize,
 169                      "Target collider size should match the config");
 170
 171      // Check targetPosition (more precise check)
 0172      Assert.AreEqual(0.01f, targetPosition.x, 0.0001f, "Target position X should be 0.01");
 0173      Assert.AreEqual(0f, targetPosition.y, 0.0001f, "Target position Y should be 0");
 0174      Assert.AreEqual(0f, targetPosition.z, 0.0001f, "Target position Z should be 0");
 175
 176      // Clean up
 0177      GameObject.DestroyImmediate(simManager.gameObject);
 0178    } catch (AssertionException e) {
 0179      throw new AssertionException(
 180          e.Message + "\n" + "This test likely failed because you have edited " +
 181          "The test string at the top of the test. Please update the test with the new values.\n" +
 182          "If you need to change the test values, please update the test string at the top of the test.");
 183    }
 0184  }
 185
 186  [Test]
 0187  public void Threat_IsNotAssignable() {
 0188    Assert.IsFalse(fixedWingThreat.IsAssignable());
 0189    Assert.IsFalse(rotaryWingThreat.IsAssignable());
 0190  }
 191
 192  [Test]
 0193  public void FixedWingThreat_CalculateAccelerationInput_RespectsMaxForwardAcceleration() {
 0194    SetPrivateField(fixedWingThreat, "_currentWaypoint", Vector3.one * 1000f);
 0195    Vector3 acceleration =
 196        InvokePrivateMethod<Vector3>(fixedWingThreat, "CalculateAccelerationInput");
 0197    float maxForwardAcceleration =
 198        InvokePrivateMethod<float>(fixedWingThreat, "CalculateMaxForwardAcceleration");
 199    const float epsilon = 1e-5f;
 0200    Assert.LessOrEqual((Vector3.Project(acceleration, fixedWingThreat.transform.forward)).magnitude,
 201                       maxForwardAcceleration + epsilon);
 0202  }
 203
 204  [Test]
 0205  public void FixedWingThreat_CalculateAccelerationInput_RespectsMaxNormalAcceleration() {
 0206    SetPrivateField(fixedWingThreat, "_currentWaypoint", Vector3.one * 1000f);
 0207    Vector3 acceleration =
 208        InvokePrivateMethod<Vector3>(fixedWingThreat, "CalculateAccelerationInput");
 0209    float maxNormalAcceleration =
 210        InvokePrivateMethod<float>(fixedWingThreat, "CalculateMaxNormalAcceleration");
 211    const float epsilon = 1e-5f;
 0212    Assert.LessOrEqual(
 213        acceleration.magnitude, maxNormalAcceleration + epsilon,
 214        $"Acceleration magnitude {acceleration.magnitude} should be less than or equal to max normal acceleration {maxNo
 0215  }
 216
 217  [Test]
 0218  public void RotaryWingThreat_CalculateAccelerationToWaypoint_RespectsMaxForwardAcceleration() {
 0219    SetPrivateField(rotaryWingThreat, "_currentWaypoint", Vector3.one * 1000f);
 0220    Vector3 acceleration =
 221        InvokePrivateMethod<Vector3>(rotaryWingThreat, "CalculateAccelerationToWaypoint");
 0222    float maxForwardAcceleration =
 223        InvokePrivateMethod<float>(rotaryWingThreat, "CalculateMaxForwardAcceleration");
 224    const float epsilon = 1e-5f;
 0225    Assert.LessOrEqual((Vector3.Project(acceleration, fixedWingThreat.transform.forward)).magnitude,
 226                       maxForwardAcceleration + epsilon);
 0227  }
 228
 229  [Test]
 0230  public void RotaryWingThreat_CalculateAccelerationToWaypoint_RespectsMaxNormalAcceleration() {
 0231    SetPrivateField(rotaryWingThreat, "_currentWaypoint", Vector3.one * 1000f);
 0232    Vector3 acceleration =
 233        InvokePrivateMethod<Vector3>(rotaryWingThreat, "CalculateAccelerationToWaypoint");
 0234    float maxNormalAcceleration =
 235        InvokePrivateMethod<float>(rotaryWingThreat, "CalculateMaxNormalAcceleration");
 236    const float epsilon = 1e-5f;
 237    // Calculate the normal component of acceleration
 0238    Vector3 forwardComponent = Vector3.Project(acceleration, rotaryWingThreat.transform.forward);
 0239    Vector3 normalComponent = acceleration - forwardComponent;
 240
 0241    Assert.LessOrEqual(
 242        normalComponent.magnitude, maxNormalAcceleration + epsilon,
 243        $"Normal acceleration magnitude {normalComponent.magnitude} should be less than or equal to max normal accelerat
 0244  }
 245
 246  private class MockAttackBehavior : AttackBehavior {
 247    private Vector3 waypoint;
 248    private PowerSetting powerSetting;
 249
 0250    public MockAttackBehavior(Vector3 waypoint, PowerSetting powerSetting) {
 0251      this.waypoint = waypoint;
 0252      this.powerSetting = powerSetting;
 0253      this.name = "MockAttackBehavior";
 0254    }
 255
 256    public override (Vector3, PowerSetting)
 0257        GetNextWaypoint(Vector3 currentPosition, Vector3 targetPosition) {
 0258      return (waypoint, powerSetting);
 0259    }
 260  }
 261
 262  [Test]
 0263  public void RotaryWingThreat_CalculateAccelerationToWaypoint_ComputesCorrectly() {
 264    // Arrange
 0265    Vector3 initialPosition = new Vector3(0, 0, 0);
 0266    Vector3 waypoint = new Vector3(1000, 0, 0);
 0267    Vector3 initialVelocity = new Vector3(0, 0, 0);
 0268    float desiredSpeed = 50f;
 269
 0270    rotaryWingThreat.SetPosition(initialPosition);
 0271    SetPrivateField(rotaryWingThreat, "_currentWaypoint", waypoint);
 0272    rotaryWingThreat.SetVelocity(initialVelocity);
 0273    SetPrivateField(rotaryWingThreat, "_currentPowerSetting", PowerSetting.MIL);
 274
 275    // Assume PowerTableLookup returns 50 for PowerSetting.MIL
 276    // Assert that its true using PowerTableLookup
 0277    float powerSetting =
 278        InvokePrivateMethod<float>(rotaryWingThreat, "PowerTableLookup", PowerSetting.MIL);
 0279    Assert.AreEqual(desiredSpeed, powerSetting);
 280
 281    // Act
 0282    Vector3 accelerationInput =
 283        InvokePrivateMethod<Vector3>(rotaryWingThreat, "CalculateAccelerationToWaypoint");
 284
 285    // Assert
 0286    Vector3 toWaypoint = waypoint - initialPosition;
 0287    Vector3 expectedAccelerationDir = toWaypoint.normalized;
 0288    float expectedAccelerationMag = desiredSpeed / (float)Time.fixedDeltaTime;
 0289    Vector3 expectedAcceleration = expectedAccelerationDir * expectedAccelerationMag;
 290
 291    // Decompose acceleration into forward and normal acceleration components
 0292    Vector3 forwardAcceleration =
 293        Vector3.Project(expectedAcceleration, rotaryWingThreat.transform.forward);
 0294    Vector3 normalAcceleration = expectedAcceleration - forwardAcceleration;
 295
 296    // Limit acceleration magnitude
 0297    float maxForwardAcceleration =
 298        InvokePrivateMethod<float>(rotaryWingThreat, "CalculateMaxForwardAcceleration");
 0299    forwardAcceleration = Vector3.ClampMagnitude(forwardAcceleration, maxForwardAcceleration);
 0300    float maxNormalAcceleration =
 301        InvokePrivateMethod<float>(rotaryWingThreat, "CalculateMaxNormalAcceleration");
 0302    normalAcceleration = Vector3.ClampMagnitude(normalAcceleration, maxNormalAcceleration);
 0303    expectedAcceleration = forwardAcceleration + normalAcceleration;
 304
 0305    Assert.AreEqual(expectedAcceleration.magnitude, accelerationInput.magnitude, 0.1f,
 306                    "Acceleration magnitude should match expected");
 0307    Assert.AreEqual(expectedAcceleration.normalized, accelerationInput.normalized,
 308                    "Acceleration direction should be towards waypoint");
 0309  }
 310}