// Copyright (c) StellaOps. All rights reserved. // Licensed under AGPL-3.0-or-later. See LICENSE in the project root. using StellaOps.BinaryIndex.Decompiler; using Xunit; namespace StellaOps.BinaryIndex.Decompiler.Tests; [Trait("Category", "Unit")] public sealed class CodeNormalizerTests { private readonly CodeNormalizer _normalizer = new(); [Fact] public void Normalize_WithWhitespace_NormalizesWhitespace() { // Arrange var code = "int x = 1;"; var options = new NormalizationOptions { NormalizeWhitespace = true }; // Act var normalized = _normalizer.Normalize(code, options); // Assert Assert.DoesNotContain(" ", normalized); } [Fact] public void Normalize_WithVariables_NormalizesVariableNames() { // Arrange var code = "int myVar = 1; int otherVar = myVar;"; var options = new NormalizationOptions { NormalizeVariables = true }; // Act var normalized = _normalizer.Normalize(code, options); // Assert // Original variable names should be replaced with canonical names Assert.DoesNotContain("myVar", normalized); Assert.DoesNotContain("otherVar", normalized); Assert.Contains("var_", normalized); } [Fact] public void Normalize_WithConstants_NormalizesLargeNumbers() { // Arrange var code = "int x = 1234567890;"; var options = new NormalizationOptions { NormalizeConstants = true }; // Act var normalized = _normalizer.Normalize(code, options); // Assert Assert.DoesNotContain("1234567890", normalized); } [Fact] public void Normalize_PreservesKeywords_DoesNotRenameKeywords() { // Arrange var code = "int foo() { return 1; }"; var options = new NormalizationOptions { NormalizeVariables = true }; // Act var normalized = _normalizer.Normalize(code, options); // Assert Assert.Contains("return", normalized); Assert.Contains("int", normalized); } [Fact] public void Normalize_PreservesStandardLibraryFunctions() { // Arrange var code = "printf(\"hello\"); malloc(100); free(ptr);"; var options = new NormalizationOptions { NormalizeFunctionCalls = true }; // Act var normalized = _normalizer.Normalize(code, options); // Assert Assert.Contains("printf", normalized); Assert.Contains("malloc", normalized); Assert.Contains("free", normalized); } [Fact] public void ComputeCanonicalHash_SameCode_ReturnsSameHash() { // Arrange var code1 = "int foo() { return 1; }"; var code2 = "int foo() { return 1; }"; // Act var hash1 = _normalizer.ComputeCanonicalHash(code1); var hash2 = _normalizer.ComputeCanonicalHash(code2); // Assert Assert.Equal(hash1, hash2); } [Fact] public void ComputeCanonicalHash_DifferentWhitespace_ReturnsSameHash() { // Arrange var code1 = "int foo(){return 1;}"; var code2 = "int foo() { return 1; }"; // Act var hash1 = _normalizer.ComputeCanonicalHash(code1); var hash2 = _normalizer.ComputeCanonicalHash(code2); // Assert Assert.Equal(hash1, hash2); } [Fact] public void ComputeCanonicalHash_DifferentVariableNames_ReturnsSameHash() { // Arrange var code1 = "int foo(int x) { return x + 1; }"; var code2 = "int foo(int y) { return y + 1; }"; // Act var hash1 = _normalizer.ComputeCanonicalHash(code1); var hash2 = _normalizer.ComputeCanonicalHash(code2); // Assert Assert.Equal(hash1, hash2); } [Fact] public void ComputeCanonicalHash_DifferentLogic_ReturnsDifferentHash() { // Arrange var code1 = "int foo(int x) { return x + 1; }"; var code2 = "int foo(int x) { return x - 1; }"; // Act var hash1 = _normalizer.ComputeCanonicalHash(code1); var hash2 = _normalizer.ComputeCanonicalHash(code2); // Assert Assert.NotEqual(hash1, hash2); } [Fact] public void ComputeCanonicalHash_Returns32Bytes() { // Arrange var code = "int foo() { return 1; }"; // Act var hash = _normalizer.ComputeCanonicalHash(code); // Assert (SHA256 = 32 bytes) Assert.Equal(32, hash.Length); } [Fact] public void Normalize_RemovesComments() { // Arrange var code = @" int foo() { // This is a comment return 1; /* inline comment */ }"; var options = NormalizationOptions.Default; // Act var normalized = _normalizer.Normalize(code, options); // Assert Assert.DoesNotContain("//", normalized); Assert.DoesNotContain("/*", normalized); } [Fact] public void NormalizeAst_WithParser_NormalizesAstNodes() { // Arrange var parser = new DecompiledCodeParser(); var code = @" int foo(int myVar) { return myVar + 1; }"; var ast = parser.Parse(code); var options = new NormalizationOptions { NormalizeVariables = true }; // Act var normalizedAst = _normalizer.NormalizeAst(ast, options); // Assert Assert.NotNull(normalizedAst); Assert.Equal(ast.NodeCount, normalizedAst.NodeCount); } }