From 05af1d3be5d689095ca70c01fe1547628abc6c17 Mon Sep 17 00:00:00 2001 From: Dattaprasad Mundada Date: Mon, 10 Jul 2023 12:23:41 +0530 Subject: [PATCH] ruby2cpg: Add ast tests for basic operations --- .../passes/ast/AssignCpgTests.scala | 176 ++++++++++++++++++ .../passes/ast/AttributeCpgTests.scala | 54 ++++++ .../passes/ast/BinOpCpgTests.scala | 54 ++++++ .../passes/ast/BoolOpCpgTests.scala | 69 +++++++ 4 files changed, 353 insertions(+) create mode 100644 joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/passes/ast/AssignCpgTests.scala create mode 100644 joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/passes/ast/AttributeCpgTests.scala create mode 100644 joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/passes/ast/BinOpCpgTests.scala create mode 100644 joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/passes/ast/BoolOpCpgTests.scala diff --git a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/passes/ast/AssignCpgTests.scala b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/passes/ast/AssignCpgTests.scala new file mode 100644 index 000000000000..cac07b91868f --- /dev/null +++ b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/passes/ast/AssignCpgTests.scala @@ -0,0 +1,176 @@ +package io.joern.rubysrc2cpg.passes.ast + +import io.joern.rubysrc2cpg.testfixtures.RubyCode2CpgFixture +import io.shiftleft.codepropertygraph.generated.{EvaluationStrategies, NodeTypes, DispatchTypes, Operators, nodes} +import io.shiftleft.semanticcpg.language.* +import io.shiftleft.semanticcpg.language.types.structure.NamespaceTraversal + +class AssignCpgTests extends RubyCode2CpgFixture { + "single target assign" should { + val cpg = code("""x = 2""".stripMargin) + + // TODO: .code property need to be fixed + "test assignment node properties" ignore { + val assignCall = cpg.call.methodFullName(Operators.assignment).head + assignCall.code shouldBe "x = 2" // "=" + assignCall.dispatchType shouldBe DispatchTypes.STATIC_DISPATCH + assignCall.lineNumber shouldBe Some(1) + assignCall.columnNumber shouldBe Some(1) + } + + "test assignment node ast children" in { + cpg.call + .methodFullName(Operators.assignment) + .astChildren + .order(1) + .isIdentifier + .head + .code shouldBe "x" + cpg.call + .methodFullName(Operators.assignment) + .astChildren + .order(2) + .isLiteral + .head + .code shouldBe "2" + } + + "test assignment node arguments" in { + cpg.call + .methodFullName(Operators.assignment) + .argument + .argumentIndex(1) + .isIdentifier + .head + .code shouldBe "x" + cpg.call + .methodFullName(Operators.assignment) + .argument + .argumentIndex(2) + .isLiteral + .head + .code shouldBe "2" + } + } + + "nested decomposing assign" should { + val cpg = code("""x, (y, z) = [1, [2, 3]]""".stripMargin) + + def getSurroundingBlock: nodes.Block = { + cpg.all.collect { case block: nodes.Block if block.code != "" => block }.head + } + + "test block exists" in { + // Throws if block does not exist. + getSurroundingBlock + } + + // TODO: .code property need to be fixed + "test block node properties" ignore { + val block = getSurroundingBlock + block.code shouldBe + """tmp0 = list + |x = tmp0[0] + |y = tmp0[1][0] + |z = tmp0[1][1]""".stripMargin + block.lineNumber shouldBe Some(1) + } + + // TODO: Need to fix the local variables + "test local node" ignore { + cpg.method.name("Test0.rb::program").local.name("tmp0").headOption should not be empty + } + + "test tmp variable assignment" in { + val block = getSurroundingBlock + val tmpAssignNode = block.astChildren.isCall.sortBy(_.order).head + // tmpAssignNode.code shouldBe "tmp0 = list" + tmpAssignNode.methodFullName shouldBe Operators.assignment + tmpAssignNode.lineNumber shouldBe Some(1) + } + + // TODO: Fix the code property of the Block node & the order too + "test assignments to targets" ignore { + val block = getSurroundingBlock + val assignNodes = block.astChildren.isCall.sortBy(_.order).tail + assignNodes.map(_.code) should contain theSameElementsInOrderAs List( + "x = tmp0[0]", + "y = tmp0[1][0]", + "z = tmp0[1][1]" + ) + assignNodes.map(_.lineNumber.get) should contain theSameElementsInOrderAs List(1, 1, 1) + } + + } + + "array destructuring assign" should { + val cpg = code("""x, *, y = [1, 2, 3, 5]""".stripMargin) + + def getSurroundingBlock: nodes.Block = { + cpg.all.collect { case block: nodes.Block if block.code != "" => block }.head + } + + "test block exists" in { + // Throws if block does not exist. + getSurroundingBlock + } + + // TODO: .code property need to be fixed + "test block node properties" ignore { + val block = getSurroundingBlock + block.code shouldBe + """tmp0 = list + |x = tmp0[0] + |y = tmp0[1][0] + |z = tmp0[1][1]""".stripMargin + block.astChildren.length shouldBe 4 + cpg.identifier("x").isEmpty shouldBe false + cpg.identifier("y").isEmpty shouldBe false + cpg.identifier("z").isEmpty shouldBe false + + } + + // TODO: Need to fix the local variables + "test local node" ignore { + cpg.method.name("Test0.rb::program").local.name("tmp0").headOption should not be empty + } + + } + + "multi target assign" should { + val cpg = code("""x = y = "abcd" """.stripMargin) + + def getSurroundingBlock: nodes.Block = { + cpg.all.collect { case block: nodes.Block if block.code != "" => block }.head + } + + "test block exists" in { + // Throws if block does not exist. + getSurroundingBlock + } + + // TODO: Fix the code property of the Block node + "test block node properties" ignore { + val block = getSurroundingBlock + block.code shouldBe + """tmp0 = list + |x = tmp0 + |y = tmp0""".stripMargin + block.lineNumber shouldBe Some(1) + } + + // TODO: Need to fix the local variables + "test local node" ignore { + cpg.method.name("Test0.rb::program").local.name("tmp0").headOption should not be empty + } + + // TODO: Need to fix the code property + "test tmp variable assignment" ignore { + val block = getSurroundingBlock + val tmpAssignNode = block.astChildren.isCall.sortBy(_.order).head + tmpAssignNode.code shouldBe "tmp0 = list" + tmpAssignNode.methodFullName shouldBe Operators.assignment + tmpAssignNode.lineNumber shouldBe Some(1) + } + } +} diff --git a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/passes/ast/AttributeCpgTests.scala b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/passes/ast/AttributeCpgTests.scala new file mode 100644 index 000000000000..1632912d3e19 --- /dev/null +++ b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/passes/ast/AttributeCpgTests.scala @@ -0,0 +1,54 @@ +package io.joern.rubysrc2cpg.passes.ast + +import io.joern.rubysrc2cpg.testfixtures.RubyCode2CpgFixture +import io.shiftleft.codepropertygraph.generated.{EvaluationStrategies, NodeTypes, DispatchTypes, Operators, nodes} +import io.shiftleft.semanticcpg.language.* +import io.shiftleft.semanticcpg.language.types.structure.NamespaceTraversal + +class AttributeCpgTests extends RubyCode2CpgFixture { + val cpg = code("""x.y""".stripMargin) + + // TODO: Class Modeling testcase + "test field access call node properties" ignore { + val callNode = cpg.call.methodFullName(Operators.fieldAccess).head + callNode.code shouldBe "x.y" + callNode.dispatchType shouldBe DispatchTypes.STATIC_DISPATCH + callNode.lineNumber shouldBe Some(1) + } + + // TODO: Class Modeling testcase + "test field access call ast children" ignore { + cpg.call + .methodFullName(Operators.fieldAccess) + .astChildren + .order(1) + .isIdentifier + .head + .code shouldBe "x" + cpg.call + .methodFullName(Operators.fieldAccess) + .astChildren + .order(2) + .isFieldIdentifier + .head + .code shouldBe "y" + } + + // TODO: Class Modeling testcase + "test field access call arguments" ignore { + cpg.call + .methodFullName(Operators.fieldAccess) + .argument + .argumentIndex(1) + .isIdentifier + .head + .code shouldBe "x" + cpg.call + .methodFullName(Operators.fieldAccess) + .argument + .argumentIndex(2) + .isFieldIdentifier + .head + .code shouldBe "y" + } +} diff --git a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/passes/ast/BinOpCpgTests.scala b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/passes/ast/BinOpCpgTests.scala new file mode 100644 index 000000000000..aa3bfadaeff7 --- /dev/null +++ b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/passes/ast/BinOpCpgTests.scala @@ -0,0 +1,54 @@ +package io.joern.rubysrc2cpg.passes.ast + +import io.joern.rubysrc2cpg.testfixtures.RubyCode2CpgFixture +import io.shiftleft.codepropertygraph.generated.{EvaluationStrategies, NodeTypes, DispatchTypes, Operators, nodes} +import io.shiftleft.semanticcpg.language.* +import io.shiftleft.semanticcpg.language.types.structure.NamespaceTraversal + +class BinOpCpgTests extends RubyCode2CpgFixture { + val cpg = code("""1 + 2""".stripMargin) + + "test binOp 'add' call node properties" in { + val additionCall = cpg.call.methodFullName(Operators.addition).head + additionCall.code shouldBe "1 + 2" + additionCall.dispatchType shouldBe DispatchTypes.STATIC_DISPATCH + additionCall.lineNumber shouldBe Some(1) + // TODO additionCall.columnNumber shouldBe Some(1) + } + + "test binOp 'add' ast children" in { + cpg.call + .methodFullName(Operators.addition) + .astChildren + .order(1) + .isLiteral + .head + .code shouldBe "1" + cpg.call + .methodFullName(Operators.addition) + .astChildren + .order(2) + .isLiteral + .head + .code shouldBe "2" + } + + "test binOp 'add' arguments" in { + cpg.call + .methodFullName(Operators.addition) + .argument + .argumentIndex(1) + .isLiteral + .head + .code shouldBe "1" + cpg.call + .methodFullName(Operators.addition) + .argument + .argumentIndex(2) + .isLiteral + .head + .code shouldBe "2" + } + +} + diff --git a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/passes/ast/BoolOpCpgTests.scala b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/passes/ast/BoolOpCpgTests.scala new file mode 100644 index 000000000000..c233b4f471dd --- /dev/null +++ b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/passes/ast/BoolOpCpgTests.scala @@ -0,0 +1,69 @@ +package io.joern.rubysrc2cpg.passes.ast + +import io.joern.rubysrc2cpg.testfixtures.RubyCode2CpgFixture +import io.shiftleft.codepropertygraph.generated.{EvaluationStrategies, NodeTypes, DispatchTypes, Operators, nodes} +import io.shiftleft.semanticcpg.language.* +import io.shiftleft.semanticcpg.language.types.structure.NamespaceTraversal + +class BoolOpCpgTests extends RubyCode2CpgFixture { + val cpg = code("""x or y or z""".stripMargin) + + "test boolOp 'or' call node properties" in { + val orCall = cpg.call.head +// val orCall = cpg.call.methodFullName(Operators.logicalOr).head + orCall.code shouldBe "x or y or z" + orCall.dispatchType shouldBe DispatchTypes.STATIC_DISPATCH + orCall.lineNumber shouldBe Some(1) + // TODO orCall.columnNumber shouldBe Some(3) + } + + // TODO: Fix this multi logicalOr operation + "test boolOp 'or' ast children" ignore { + cpg.call + .methodFullName(Operators.logicalOr) + .astChildren + .order(1) + .isIdentifier + .head + .code shouldBe "x" + cpg.call + .methodFullName(Operators.logicalOr) + .astChildren + .order(2) + .isIdentifier + .head + .code shouldBe "y" + cpg.call + .methodFullName(Operators.logicalOr) + .astChildren + .order(3) + .isIdentifier + .head + .code shouldBe "z" + } + + // TODO: Fix this multi logicalOr operation arguments + "test boolOp 'or' arguments" ignore { + cpg.call + .methodFullName(Operators.logicalOr) + .argument + .argumentIndex(1) + .isIdentifier + .head + .code shouldBe "x" + cpg.call + .methodFullName(Operators.logicalOr) + .argument + .argumentIndex(2) + .isIdentifier + .head + .code shouldBe "y" + cpg.call + .methodFullName(Operators.logicalOr) + .argument + .argumentIndex(3) + .isIdentifier + .head + .code shouldBe "z" + } +}