Skip to content

Commit

Permalink
[rubysrc2cpg] Add AST tests for basic operations (#3076)
Browse files Browse the repository at this point in the history
Added assign, attribute, binop, and boolop tests.
  • Loading branch information
dbMundada authored Jul 10, 2023
1 parent 3ef1614 commit f87519b
Show file tree
Hide file tree
Showing 4 changed files with 352 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Original file line number Diff line number Diff line change
@@ -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"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
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"
}

}
Original file line number Diff line number Diff line change
@@ -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"
}
}

0 comments on commit f87519b

Please sign in to comment.