using VersaGUI; static void Assert(bool condition, string message) { if (!condition) throw new InvalidOperationException(message); } static void TestDeviceConfigRoundTrip() { var source = new DeviceConfig(); source.Profiles[2].MxActions[4].Type = ActionType.HostCommand; source.Profiles[2].MxActions[4].Data = 0xBEEF; source.ActiveProfileIndex = 2; byte[] data = source.ToBytes(); Assert(data.Length == Protocol.ConfigSize, "Configgröße weicht ab."); ushort storedCrc = (ushort)(data[5] | (data[6] << 8)); Assert( storedCrc == DeviceConfig.Crc16(data, 7, data.Length - 7), "Config-CRC weicht ab."); var target = new DeviceConfig(); Assert(target.FromBytes(data), "Gültige Config wurde abgelehnt."); Assert(target.ActiveProfileIndex == 2, "Aktives Profil ging verloren."); Assert( target.Profiles[2].MxActions[4].Data == 0xBEEF, "Host-Command-ID ging verloren."); } static void TestInvalidConfigIsRejected() { byte[] data = new DeviceConfig().ToBytes(); data[7] = 3; ushort crc = DeviceConfig.Crc16(data, 7, data.Length - 7); data[5] = (byte)crc; data[6] = (byte)(crc >> 8); Assert(!new DeviceConfig().FromBytes(data), "Ungültiger Profilindex wurde akzeptiert."); var invalidPulse = new DeviceConfig(); invalidPulse.Profiles[0].LedAnim[0] = LedAnimType.Pulse; invalidPulse.Profiles[0].LedPeriod[0] = 1; Assert(!invalidPulse.TryValidate(out _), "Unsichere Pulse-Periode wurde akzeptiert."); } static void TestMacroValidation() { var macros = new MacroTable(); macros.Steps[31, 7].Keycode = 0x65; byte[] data = macros.ToBytes(); Assert(data.Length == Protocol.MacroSize, "Makrogröße weicht ab."); var copy = new MacroTable(); Assert(copy.FromBytes(data), "Gültige Makrotabelle wurde abgelehnt."); data[0] = 0x66; Assert(!copy.FromBytes(data), "Ungültiger HID-Keycode wurde akzeptiert."); } static void TestChunkValidation() { var receiver = new ChunkTransferBuffer(13, 3); Assert(receiver.Begin(3), "Gültige Chunkzahl wurde abgelehnt."); for (byte chunk = 0; chunk < 3; chunk++) { var packet = new SerialPacket(); packet.Data[1] = chunk; for (int i = 0; i < Protocol.PayloadSize; i++) packet.Data[2 + i] = (byte)(chunk * Protocol.PayloadSize + i); Assert(receiver.Add(packet), $"Chunk {chunk} wurde abgelehnt."); } Assert(receiver.TryComplete(3, out byte[] result), "Vollständiger Dump wurde abgelehnt."); Assert(result.Length == 13 && result[12] == 12, "Chunkdaten wurden falsch zusammengesetzt."); Assert(receiver.Begin(3), "Zweiter Transfer konnte nicht starten."); var duplicate = new SerialPacket(); Assert(receiver.Add(duplicate), "Erster Chunk wurde abgelehnt."); Assert(!receiver.Add(duplicate), "Doppelter Chunk wurde akzeptiert."); Assert(!receiver.TryComplete(3, out _), "Ungültiger Dump wurde abgeschlossen."); } static void TestHostCommandPayload() { var packet = new SerialPacket(Protocol.EvtEncCw, 2, 0x34, 0x12); Assert(packet.DataU16 == 0x1234, "16-Bit-Command-ID wurde falsch dekodiert."); } TestDeviceConfigRoundTrip(); TestInvalidConfigIsRejected(); TestMacroValidation(); TestChunkValidation(); TestHostCommandPayload(); Console.WriteLine("VersaGUI contract tests passed.");