From 8349185f37592235c81f237a0633b39b0c9d1a30 Mon Sep 17 00:00:00 2001 From: manas-yu Date: Fri, 27 Dec 2024 23:51:11 +0530 Subject: [PATCH 1/6] fraction-subject-test --- .../testing/math/FractionSubjectTest.kt | 206 ++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 testing/src/test/java/org/oppia/android/testing/math/FractionSubjectTest.kt diff --git a/testing/src/test/java/org/oppia/android/testing/math/FractionSubjectTest.kt b/testing/src/test/java/org/oppia/android/testing/math/FractionSubjectTest.kt new file mode 100644 index 00000000000..fa864a2be11 --- /dev/null +++ b/testing/src/test/java/org/oppia/android/testing/math/FractionSubjectTest.kt @@ -0,0 +1,206 @@ +package org.oppia.android.testing.math + +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.oppia.android.app.model.Fraction + + +@RunWith(JUnit4::class) +class FractionSubjectTest { + + @Test + fun testHasNegativeProperty_withNegativeFraction_matchesTrue() { + val fraction = Fraction.newBuilder() + .setIsNegative(true) + .build() + + FractionSubject.assertThat(fraction).hasNegativePropertyThat().isTrue() + } + + @Test + fun testHasNegativeProperty_withPositiveFraction_matchesFalse() { + val fraction = Fraction.newBuilder() + .setIsNegative(false) + .build() + + FractionSubject.assertThat(fraction).hasNegativePropertyThat().isFalse() + } + + @Test + fun testHasNegativeProperty_defaultValue_matchesFalse() { + val fraction = Fraction.newBuilder().build() + + FractionSubject.assertThat(fraction).hasNegativePropertyThat().isFalse() + } + + @Test + fun testHasWholeNumber_withPositiveValue_matchesValue() { + val fraction = Fraction.newBuilder() + .setWholeNumber(5) + .build() + + FractionSubject.assertThat(fraction).hasWholeNumberThat().isEqualTo(5) + } + + @Test + fun testHasWholeNumber_withMaxUint32_matchesValue() { + val fraction = Fraction.newBuilder() + .setWholeNumber(Int.MAX_VALUE) + .build() + + FractionSubject.assertThat(fraction).hasWholeNumberThat().isEqualTo(Int.MAX_VALUE) + } + + @Test + fun testHasWholeNumber_defaultValue_matchesZero() { + val fraction = Fraction.newBuilder().build() + + FractionSubject.assertThat(fraction).hasWholeNumberThat().isEqualTo(0) + } + + @Test + fun testHasNumerator_withPositiveValue_matchesValue() { + val fraction = Fraction.newBuilder() + .setNumerator(3) + .build() + + FractionSubject.assertThat(fraction).hasNumeratorThat().isEqualTo(3) + } + + @Test + fun testHasNumerator_withMaxUint32_matchesValue() { + val fraction = Fraction.newBuilder() + .setNumerator(Int.MAX_VALUE) + .build() + + FractionSubject.assertThat(fraction).hasNumeratorThat().isEqualTo(Int.MAX_VALUE) + } + + @Test + fun testHasNumerator_defaultValue_matchesZero() { + val fraction = Fraction.newBuilder().build() + + FractionSubject.assertThat(fraction).hasNumeratorThat().isEqualTo(0) + } + + @Test + fun testHasDenominator_withPositiveValue_matchesValue() { + val fraction = Fraction.newBuilder() + .setDenominator(4) + .build() + + FractionSubject.assertThat(fraction).hasDenominatorThat().isEqualTo(4) + } + + @Test + fun testHasDenominator_withMaxUint32_matchesValue() { + val fraction = Fraction.newBuilder() + .setDenominator(Int.MAX_VALUE) + .build() + + FractionSubject.assertThat(fraction).hasDenominatorThat().isEqualTo(Int.MAX_VALUE) + } + + @Test + fun testHasDenominator_defaultValue_matchesZero() { + val fraction = Fraction.newBuilder().build() + + FractionSubject.assertThat(fraction).hasDenominatorThat().isEqualTo(0) + } + + @Test + fun testEvaluatesToDouble_withProperFraction_matchesExpectedValue() { + val fraction = Fraction.newBuilder() + .setNumerator(3) + .setDenominator(4) + .build() + + FractionSubject.assertThat(fraction).evaluatesToDoubleThat().isEqualTo(0.75) + } + + @Test + fun testEvaluatesToDouble_withImproperFraction_matchesExpectedValue() { + val fraction = Fraction.newBuilder() + .setNumerator(5) + .setDenominator(2) + .build() + + FractionSubject.assertThat(fraction).evaluatesToDoubleThat().isEqualTo(2.5) + } + + @Test + fun testEvaluatesToDouble_withMixedNumber_matchesExpectedValue() { + val fraction = Fraction.newBuilder() + .setWholeNumber(2) + .setNumerator(3) + .setDenominator(4) + .build() + + FractionSubject.assertThat(fraction).evaluatesToDoubleThat().isEqualTo(2.75) + } + + @Test + fun testEvaluatesToDouble_withNegativeValue_matchesExpectedValue() { + val fraction = Fraction.newBuilder() + .setIsNegative(true) + .setWholeNumber(2) + .setNumerator(1) + .setDenominator(2) + .build() + + FractionSubject.assertThat(fraction).evaluatesToDoubleThat().isEqualTo(-2.5) + } + + @Test + fun testProtoEquality_withIdenticalValues_areEqual() { + val fraction1 = Fraction.newBuilder() + .setIsNegative(true) + .setWholeNumber(3) + .setNumerator(2) + .setDenominator(5) + .build() + + val fraction2 = Fraction.newBuilder() + .setIsNegative(true) + .setWholeNumber(3) + .setNumerator(2) + .setDenominator(5) + .build() + + assertThat(fraction1).isEqualTo(fraction2) + } + + @Test + fun testProtoSerialization_withComplexFraction_maintainsValues() { + val originalFraction = Fraction.newBuilder() + .setIsNegative(true) + .setWholeNumber(3) + .setNumerator(2) + .setDenominator(5) + .build() + + val bytes = originalFraction.toByteArray() + val deserializedFraction = Fraction.parseFrom(bytes) + + FractionSubject.assertThat(deserializedFraction).apply { + hasNegativePropertyThat().isTrue() + hasWholeNumberThat().isEqualTo(3) + hasNumeratorThat().isEqualTo(2) + hasDenominatorThat().isEqualTo(5) + } + } + + @Test + fun testDefaultInstance_hasDefaultValues() { + val defaultFraction = Fraction.getDefaultInstance() + + FractionSubject.assertThat(defaultFraction).apply { + hasNegativePropertyThat().isFalse() + hasWholeNumberThat().isEqualTo(0) + hasNumeratorThat().isEqualTo(0) + hasDenominatorThat().isEqualTo(0) + } + } +} \ No newline at end of file From b9f4737fe5d77fcf4891b06fa01644e0fc71dbcc Mon Sep 17 00:00:00 2001 From: manas-yu Date: Sat, 28 Dec 2024 03:10:03 +0530 Subject: [PATCH 2/6] equation/expression tests --- .../testing/math/FractionSubjectTest.kt | 3 +- .../testing/math/MathEquationSubjectTest.kt | 199 ++++++++++++ .../testing/math/MathExpressionSubjectTest.kt | 282 ++++++++++++++++++ .../android/testing/math/RealSubjectTest.kt | 205 +++++++++++++ 4 files changed, 688 insertions(+), 1 deletion(-) create mode 100644 testing/src/test/java/org/oppia/android/testing/math/MathEquationSubjectTest.kt create mode 100644 testing/src/test/java/org/oppia/android/testing/math/MathExpressionSubjectTest.kt create mode 100644 testing/src/test/java/org/oppia/android/testing/math/RealSubjectTest.kt diff --git a/testing/src/test/java/org/oppia/android/testing/math/FractionSubjectTest.kt b/testing/src/test/java/org/oppia/android/testing/math/FractionSubjectTest.kt index fa864a2be11..0b3b211ed51 100644 --- a/testing/src/test/java/org/oppia/android/testing/math/FractionSubjectTest.kt +++ b/testing/src/test/java/org/oppia/android/testing/math/FractionSubjectTest.kt @@ -1,12 +1,13 @@ package org.oppia.android.testing.math +import android.annotation.SuppressLint import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.oppia.android.app.model.Fraction - +/** Tests for [FractionSubject]. */ @RunWith(JUnit4::class) class FractionSubjectTest { diff --git a/testing/src/test/java/org/oppia/android/testing/math/MathEquationSubjectTest.kt b/testing/src/test/java/org/oppia/android/testing/math/MathEquationSubjectTest.kt new file mode 100644 index 00000000000..92f84280ec6 --- /dev/null +++ b/testing/src/test/java/org/oppia/android/testing/math/MathEquationSubjectTest.kt @@ -0,0 +1,199 @@ +package org.oppia.android.testing.math + +import com.google.common.truth.Truth.assertThat +import org.junit.Assert.assertThrows +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.oppia.android.app.model.MathBinaryOperation +import org.oppia.android.app.model.MathEquation +import org.oppia.android.app.model.MathExpression +import org.oppia.android.app.model.Real + +/** Tests for [MathEquationSubject]. */ +@RunWith(JUnit4::class) +class MathEquationSubjectTest { + + @Test + fun testHasLeftHandSide_withValidExpression_matchesExpression() { + val equation = createEquation( + leftSide = createConstantExpression(5), + rightSide = createConstantExpression(0) + ) + + MathEquationSubject.assertThat(equation).hasLeftHandSideThat().hasStructureThatMatches { + constant { + withValueThat().isIntegerThat().isEqualTo(5) + } + } + } + + @Test + fun testHasLeftHandSide_withDefaultExpression_hasNoExpressionType() { + val equation = MathEquation.getDefaultInstance() + + // Default expression should have no type set + MathEquationSubject.assertThat(equation).hasLeftHandSideThat().isEqualTo( + MathExpression.getDefaultInstance() + ) + } + + @Test + fun testHasRightHandSide_withValidExpression_matchesExpression() { + val equation = createEquation( + leftSide = createConstantExpression(0), + rightSide = createConstantExpression(10) + ) + + MathEquationSubject.assertThat(equation).hasRightHandSideThat().hasStructureThatMatches { + constant { + withValueThat().isIntegerThat().isEqualTo(10) + } + } + } + + @Test + fun testHasRightHandSide_withDefaultExpression_hasNoExpressionType() { + val equation = MathEquation.getDefaultInstance() + + // Default expression should have no type set + MathEquationSubject.assertThat(equation).hasRightHandSideThat().isEqualTo( + MathExpression.getDefaultInstance() + ) + } + + @Test + fun testConvertsToLatex_simpleEquation_producesCorrectString() { + val equation = createEquation( + leftSide = createConstantExpression(5), + rightSide = createConstantExpression(10) + ) + + MathEquationSubject.assertThat(equation) + .convertsToLatexStringThat() + .isEqualTo("5 = 10") + } + + @Test + fun testConvertsToLatex_withDivision_retainsDivisionOperator() { + val equation = createEquation( + leftSide = createBinaryOperation( + MathBinaryOperation.Operator.DIVIDE, + createConstantExpression(10), + createConstantExpression(2) + ), + rightSide = createConstantExpression(5) + ) + + MathEquationSubject.assertThat(equation) + .convertsToLatexStringThat() + .isEqualTo("10 \\div 2 = 5") + } + + @Test + fun testConvertsToLatexWithFractions_withDivision_producesFractionNotation() { + val equation = createEquation( + leftSide = createBinaryOperation( + MathBinaryOperation.Operator.DIVIDE, + createConstantExpression(10), + createConstantExpression(2) + ), + rightSide = createConstantExpression(5) + ) + + MathEquationSubject.assertThat(equation) + .convertsWithFractionsToLatexStringThat() + .isEqualTo("\\frac{10}{2} = 5") + } + + @Test + fun testConvertsToLatex_complexExpression_producesCorrectString() { + val equation = createEquation( + leftSide = createBinaryOperation( + MathBinaryOperation.Operator.ADD, + createConstantExpression(3), + createBinaryOperation( + MathBinaryOperation.Operator.MULTIPLY, + createConstantExpression(4), + createVariableExpression("x") + ) + ), + rightSide = createConstantExpression(0) + ) + + MathEquationSubject.assertThat(equation) + .convertsToLatexStringThat() + .isEqualTo("3 + 4 \\times x = 0") + } + + @Test + fun testLeftHandSide_wrongExpression_failsWithAppropriateMessage() { + val equation = createEquation( + leftSide = createConstantExpression(5), + rightSide = createConstantExpression(0) + ) + + val exception = assertThrows(AssertionError::class.java) { + MathEquationSubject.assertThat(equation).hasLeftHandSideThat().hasStructureThatMatches { + constant { + withValueThat().isIntegerThat().isEqualTo(6) + } + } + } + assertThat(exception).hasMessageThat().contains("expected: 6") + } + + @Test + fun testRightHandSide_wrongExpression_failsWithAppropriateMessage() { + val equation = createEquation( + leftSide = createConstantExpression(0), + rightSide = createConstantExpression(10) + ) + + val exception = assertThrows(AssertionError::class.java) { + MathEquationSubject.assertThat(equation).hasRightHandSideThat().hasStructureThatMatches { + constant { + withValueThat().isIntegerThat().isEqualTo(11) + } + } + } + assertThat(exception).hasMessageThat().contains("expected: 11") + } + + private fun createEquation( + leftSide: MathExpression, + rightSide: MathExpression + ): MathEquation { + return MathEquation.newBuilder() + .setLeftSide(leftSide) + .setRightSide(rightSide) + .build() + } + + private fun createConstantExpression(value: Int): MathExpression { + return MathExpression.newBuilder() + .setConstant(Real.newBuilder().setInteger(value)) + .build() + } + + private fun createVariableExpression(name: String): MathExpression { + return MathExpression.newBuilder() + .setVariable(name) + .build() + } + + private fun createBinaryOperation( + operator: MathBinaryOperation.Operator, + left: MathExpression, + right: MathExpression + ): MathExpression { + return MathExpression.newBuilder() + .setBinaryOperation( + MathBinaryOperation.newBuilder() + .setOperator(operator) + .setLeftOperand(left) + .setRightOperand(right) + ) + .build() + } +} \ No newline at end of file diff --git a/testing/src/test/java/org/oppia/android/testing/math/MathExpressionSubjectTest.kt b/testing/src/test/java/org/oppia/android/testing/math/MathExpressionSubjectTest.kt new file mode 100644 index 00000000000..81f8e638dba --- /dev/null +++ b/testing/src/test/java/org/oppia/android/testing/math/MathExpressionSubjectTest.kt @@ -0,0 +1,282 @@ +package org.oppia.android.testing.math + +import com.google.common.truth.Truth.assertThat +import org.junit.Assert.assertThrows +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.oppia.android.app.model.MathBinaryOperation +import org.oppia.android.app.model.MathExpression +import org.oppia.android.app.model.MathFunctionCall +import org.oppia.android.app.model.MathUnaryOperation +import org.oppia.android.app.model.Real + +/** Tests for [MathExpressionSubject]. */ +@RunWith(JUnit4::class) +class MathExpressionSubjectTest { + + @Test + fun testConstantExpression_withInteger_matchesStructure() { + val expression = createConstantExpression(5) + + MathExpressionSubject.assertThat(expression).hasStructureThatMatches { + constant { + withValueThat().isIntegerThat().isEqualTo(5) + } + } + } + + @Test + fun testConstantExpression_withWrongValue_fails() { + val expression = createConstantExpression(5) + + val exception = assertThrows(AssertionError::class.java) { + MathExpressionSubject.assertThat(expression).hasStructureThatMatches { + constant { + withValueThat().isIntegerThat().isEqualTo(6) + } + } + } + assertThat(exception).hasMessageThat().contains("expected: 6") + } + + @Test + fun testVariableExpression_matchesStructure() { + val expression = createVariableExpression("x") + + MathExpressionSubject.assertThat(expression).hasStructureThatMatches { + variable { + withNameThat().isEqualTo("x") + } + } + } + + @Test + fun testVariableExpression_withWrongName_fails() { + val expression = createVariableExpression("x") + + val exception = assertThrows(AssertionError::class.java) { + MathExpressionSubject.assertThat(expression).hasStructureThatMatches { + variable { + withNameThat().isEqualTo("y") + } + } + } + assertThat(exception).hasMessageThat().contains("expected: y") + } + + @Test + fun testBinaryOperation_addition_matchesStructure() { + val expression = createBinaryOperation( + MathBinaryOperation.Operator.ADD, + createConstantExpression(3), + createConstantExpression(4) + ) + + MathExpressionSubject.assertThat(expression).hasStructureThatMatches { + addition { + leftOperand { + constant { + withValueThat().isIntegerThat().isEqualTo(3) + } + } + rightOperand { + constant { + withValueThat().isIntegerThat().isEqualTo(4) + } + } + } + } + } + + @Test + fun testBinaryOperation_multiplication_withImplicit_matchesStructure() { + val expression = createImplicitMultiplication( + createConstantExpression(2), + createConstantExpression(3) + ) + + MathExpressionSubject.assertThat(expression).hasStructureThatMatches { + multiplication(isImplicit = true) { + leftOperand { + constant { + withValueThat().isIntegerThat().isEqualTo(2) + } + } + rightOperand { + constant { + withValueThat().isIntegerThat().isEqualTo(3) + } + } + } + } + } + + @Test + fun testUnaryOperation_negation_matchesStructure() { + val expression = createUnaryOperation( + MathUnaryOperation.Operator.NEGATE, + createConstantExpression(5) + ) + + MathExpressionSubject.assertThat(expression).hasStructureThatMatches { + negation { + operand { + constant { + withValueThat().isIntegerThat().isEqualTo(5) + } + } + } + } + } + + @Test + fun testFunctionCall_squareRoot_matchesStructure() { + val expression = createFunctionCall( + MathFunctionCall.FunctionType.SQUARE_ROOT, + createConstantExpression(16) + ) + + MathExpressionSubject.assertThat(expression).hasStructureThatMatches { + functionCallTo(MathFunctionCall.FunctionType.SQUARE_ROOT) { + argument { + constant { + withValueThat().isIntegerThat().isEqualTo(16) + } + } + } + } + } + + @Test + fun testComplexExpression_matchesStructure() { + // Creates expression: 3 + 4 * (-5) + val expression = createBinaryOperation( + MathBinaryOperation.Operator.ADD, + createConstantExpression(3), + createBinaryOperation( + MathBinaryOperation.Operator.MULTIPLY, + createConstantExpression(4), + createUnaryOperation( + MathUnaryOperation.Operator.NEGATE, + createConstantExpression(5) + ) + ) + ) + + MathExpressionSubject.assertThat(expression).hasStructureThatMatches { + addition { + leftOperand { + constant { + withValueThat().isIntegerThat().isEqualTo(3) + } + } + rightOperand { + multiplication { + leftOperand { + constant { + withValueThat().isIntegerThat().isEqualTo(4) + } + } + rightOperand { + negation { + operand { + constant { + withValueThat().isIntegerThat().isEqualTo(5) + } + } + } + } + } + } + } + } + } + + @Test + fun testGroupExpression_matchesStructure() { + val expression = createGroupExpression(createConstantExpression(42)) + + MathExpressionSubject.assertThat(expression).hasStructureThatMatches { + group { + constant { + withValueThat().isIntegerThat().isEqualTo(42) + } + } + } + } + + private fun createConstantExpression(value: Int): MathExpression { + return MathExpression.newBuilder() + .setConstant(Real.newBuilder().setInteger(value)) + .build() + } + + private fun createVariableExpression(name: String): MathExpression { + return MathExpression.newBuilder() + .setVariable(name) + .build() + } + + private fun createBinaryOperation( + operator: MathBinaryOperation.Operator, + left: MathExpression, + right: MathExpression + ): MathExpression { + return MathExpression.newBuilder() + .setBinaryOperation( + MathBinaryOperation.newBuilder() + .setOperator(operator) + .setLeftOperand(left) + .setRightOperand(right) + ) + .build() + } + + private fun createImplicitMultiplication( + left: MathExpression, + right: MathExpression + ): MathExpression { + return MathExpression.newBuilder() + .setBinaryOperation( + MathBinaryOperation.newBuilder() + .setOperator(MathBinaryOperation.Operator.MULTIPLY) + .setIsImplicit(true) + .setLeftOperand(left) + .setRightOperand(right) + ) + .build() + } + + private fun createUnaryOperation( + operator: MathUnaryOperation.Operator, + operand: MathExpression + ): MathExpression { + return MathExpression.newBuilder() + .setUnaryOperation( + MathUnaryOperation.newBuilder() + .setOperator(operator) + .setOperand(operand) + ) + .build() + } + + private fun createFunctionCall( + functionType: MathFunctionCall.FunctionType, + argument: MathExpression + ): MathExpression { + return MathExpression.newBuilder() + .setFunctionCall( + MathFunctionCall.newBuilder() + .setFunctionType(functionType) + .setArgument(argument) + ) + .build() + } + + private fun createGroupExpression(inner: MathExpression): MathExpression { + return MathExpression.newBuilder() + .setGroup(inner) + .build() + } +} diff --git a/testing/src/test/java/org/oppia/android/testing/math/RealSubjectTest.kt b/testing/src/test/java/org/oppia/android/testing/math/RealSubjectTest.kt new file mode 100644 index 00000000000..44670b9c333 --- /dev/null +++ b/testing/src/test/java/org/oppia/android/testing/math/RealSubjectTest.kt @@ -0,0 +1,205 @@ +package org.oppia.android.testing.math + +import android.annotation.SuppressLint +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.oppia.android.app.model.Fraction +import org.oppia.android.app.model.Real +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +/** Tests for [RealSubject]. */ +@SuppressLint("CheckResult") +@RunWith(JUnit4::class) +class RealSubjectTest { + + @Test + fun testRational_withRationalValue_canAccessRationalSubject() { + val real = Real.newBuilder().setRational( + Fraction.newBuilder().setNumerator(1).setDenominator(2) + ).build() + + val subject = RealSubject.assertThat(real).isRationalThat() + + subject.hasNumeratorThat().isEqualTo(1) + subject.hasDenominatorThat().isEqualTo(2) + } + + @Test + fun testRational_withIrrationalValue_fails() { + val real = Real.newBuilder().setIrrational(3.14).build() + + val exception = assertThrows(AssertionError::class.java) { + RealSubject.assertThat(real).isRationalThat() + } + + assertThat(exception).hasMessageThat().contains( + "Expected real type to be RATIONAL, not: IRRATIONAL" + ) + } + + @Test + fun testRational_withIntegerValue_fails() { + val real = Real.newBuilder().setInteger(42).build() + + val exception = assertThrows(AssertionError::class.java) { + RealSubject.assertThat(real).isRationalThat() + } + + assertThat(exception).hasMessageThat().contains( + "Expected real type to be RATIONAL, not: INTEGER" + ) + } + + @Test + fun testIrrational_withIrrationalValue_canAccessDoubleSubject() { + val real = Real.newBuilder().setIrrational(3.14159).build() + + val subject = RealSubject.assertThat(real).isIrrationalThat() + + subject.isWithin(0.00001).of(3.14159) + } + + @Test + fun testIrrational_withRationalValue_fails() { + val real = Real.newBuilder().setRational( + Fraction.newBuilder().setNumerator(1).setDenominator(2) + ).build() + + val exception = assertThrows(AssertionError::class.java) { + RealSubject.assertThat(real).isIrrationalThat() + } + + assertThat(exception).hasMessageThat().contains( + "Expected real type to be IRRATIONAL, not: RATIONAL" + ) + } + + @Test + fun testIrrational_withIntegerValue_fails() { + val real = Real.newBuilder().setInteger(42).build() + + val exception = assertThrows(AssertionError::class.java) { + RealSubject.assertThat(real).isIrrationalThat() + } + + assertThat(exception).hasMessageThat().contains( + "Expected real type to be IRRATIONAL, not: INTEGER" + ) + } + + @Test + fun testInteger_withIntegerValue_canAccessIntegerSubject() { + val real = Real.newBuilder().setInteger(42).build() + + val subject = RealSubject.assertThat(real).isIntegerThat() + + subject.isEqualTo(42) + } + + @Test + fun testInteger_withRationalValue_fails() { + val real = Real.newBuilder().setRational( + Fraction.newBuilder().setNumerator(1).setDenominator(2) + ).build() + + val exception = assertThrows(AssertionError::class.java) { + RealSubject.assertThat(real).isIntegerThat() + } + + assertThat(exception).hasMessageThat().contains( + "Expected real type to be INTEGER, not: RATIONAL" + ) + } + + @Test + fun testInteger_withIrrationalValue_fails() { + val real = Real.newBuilder().setIrrational(3.14).build() + + val exception = assertThrows(AssertionError::class.java) { + RealSubject.assertThat(real).isIntegerThat() + } + + assertThat(exception).hasMessageThat().contains( + "Expected real type to be INTEGER, not: IRRATIONAL" + ) + } + + @Test + fun testNull_fails() { + val exception = assertThrows(IllegalStateException::class.java) { + RealSubject.assertThat(null).isRationalThat() + } + + assertThat(exception).hasMessageThat().contains("Expected real to be non-null") + } + + @Test + fun testUnsetType_asRational_fails() { + val real = Real.newBuilder().build() + + val exception = assertThrows(AssertionError::class.java) { + RealSubject.assertThat(real).isRationalThat() + } + + assertThat(exception).hasMessageThat().contains( + "Expected real type to be RATIONAL, not: REALTYPE_NOT_SET" + ) + } + + @Test + fun testUnsetType_asIrrational_fails() { + val real = Real.newBuilder().build() + + val exception = assertThrows(AssertionError::class.java) { + RealSubject.assertThat(real).isIrrationalThat() + } + + assertThat(exception).hasMessageThat().contains( + "Expected real type to be IRRATIONAL, not: REALTYPE_NOT_SET" + ) + } + + @Test + fun testUnsetType_asInteger_fails() { + val real = Real.newBuilder().build() + + val exception = assertThrows(AssertionError::class.java) { + RealSubject.assertThat(real).isIntegerThat() + } + + assertThat(exception).hasMessageThat().contains( + "Expected real type to be INTEGER, not: REALTYPE_NOT_SET" + ) + } + + @Test + fun testInheritedProtoMethods_work() { + val real = Real.newBuilder().setInteger(42).build() + + RealSubject.assertThat(real).isNotNull() + RealSubject.assertThat(real).isNotEqualTo(Real.getDefaultInstance()) + + } + + private fun assertThrows( + expectedType: Class, + runnable: () -> Unit + ): T { + try { + runnable() + } catch (t: Throwable) { + if (expectedType.isInstance(t)) { + @Suppress("UNCHECKED_CAST") + return t as T + } + throw AssertionError( + "Expected ${expectedType.simpleName} but got ${t.javaClass.simpleName}", + t + ) + } + throw AssertionError( + "Expected ${expectedType.simpleName} to be thrown but nothing was thrown" + ) + } +} From d9e7554f3bc3710b71d271b731f8245973dd4a4e Mon Sep 17 00:00:00 2001 From: manas-yu Date: Sat, 28 Dec 2024 03:11:06 +0530 Subject: [PATCH 3/6] formatting --- .../org/oppia/android/testing/math/FractionSubjectTest.kt | 3 +-- .../oppia/android/testing/math/MathEquationSubjectTest.kt | 2 +- .../java/org/oppia/android/testing/math/RealSubjectTest.kt | 5 ++--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/testing/src/test/java/org/oppia/android/testing/math/FractionSubjectTest.kt b/testing/src/test/java/org/oppia/android/testing/math/FractionSubjectTest.kt index 0b3b211ed51..79b2873f98e 100644 --- a/testing/src/test/java/org/oppia/android/testing/math/FractionSubjectTest.kt +++ b/testing/src/test/java/org/oppia/android/testing/math/FractionSubjectTest.kt @@ -1,6 +1,5 @@ package org.oppia.android.testing.math -import android.annotation.SuppressLint import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith @@ -204,4 +203,4 @@ class FractionSubjectTest { hasDenominatorThat().isEqualTo(0) } } -} \ No newline at end of file +} diff --git a/testing/src/test/java/org/oppia/android/testing/math/MathEquationSubjectTest.kt b/testing/src/test/java/org/oppia/android/testing/math/MathEquationSubjectTest.kt index 92f84280ec6..5260dbccfa2 100644 --- a/testing/src/test/java/org/oppia/android/testing/math/MathEquationSubjectTest.kt +++ b/testing/src/test/java/org/oppia/android/testing/math/MathEquationSubjectTest.kt @@ -196,4 +196,4 @@ class MathEquationSubjectTest { ) .build() } -} \ No newline at end of file +} diff --git a/testing/src/test/java/org/oppia/android/testing/math/RealSubjectTest.kt b/testing/src/test/java/org/oppia/android/testing/math/RealSubjectTest.kt index 44670b9c333..343321832f6 100644 --- a/testing/src/test/java/org/oppia/android/testing/math/RealSubjectTest.kt +++ b/testing/src/test/java/org/oppia/android/testing/math/RealSubjectTest.kt @@ -3,10 +3,10 @@ package org.oppia.android.testing.math import android.annotation.SuppressLint import com.google.common.truth.Truth.assertThat import org.junit.Test -import org.oppia.android.app.model.Fraction -import org.oppia.android.app.model.Real import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import org.oppia.android.app.model.Fraction +import org.oppia.android.app.model.Real /** Tests for [RealSubject]. */ @SuppressLint("CheckResult") @@ -179,7 +179,6 @@ class RealSubjectTest { RealSubject.assertThat(real).isNotNull() RealSubject.assertThat(real).isNotEqualTo(Real.getDefaultInstance()) - } private fun assertThrows( From c0e5e2af4ed7cecfca36043be197b9ea3ca2f5df Mon Sep 17 00:00:00 2001 From: manas-yu Date: Sat, 28 Dec 2024 20:56:47 +0530 Subject: [PATCH 4/6] bazel setup --- .../oppia/android/testing/math/BUILD.bazel | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 testing/src/test/java/org/oppia/android/testing/math/BUILD.bazel diff --git a/testing/src/test/java/org/oppia/android/testing/math/BUILD.bazel b/testing/src/test/java/org/oppia/android/testing/math/BUILD.bazel new file mode 100644 index 00000000000..354acf48077 --- /dev/null +++ b/testing/src/test/java/org/oppia/android/testing/math/BUILD.bazel @@ -0,0 +1,69 @@ +""" +Tests for math-related test utilities. +""" + +load("//:oppia_android_test.bzl", "oppia_android_test") + +oppia_android_test( + name = "FractionSubjectTest", + srcs = ["FractionSubjectTest.kt"], + custom_package = "org.oppia.android.testing.math", + test_class = "org.oppia.android.testing.math.FractionSubjectTest", + test_manifest = "//testing:test_manifest", + deps = [ + "//model/src/main/proto:math_java_proto_lite", + "//testing/src/main/java/org/oppia/android/testing/math:fraction_subject", + "//testing/src/main/java/org/oppia/android/testing/robolectric:test_module", + "//third_party:com_google_truth_truth", + "//third_party:junit_junit", + "//third_party:robolectric_android-all", + ], +) + +oppia_android_test( + name = "MathEquationSubjectTest", + srcs = ["MathEquationSubjectTest.kt"], + custom_package = "org.oppia.android.testing.math", + test_class = "org.oppia.android.testing.math.MathEquationSubjectTest", + test_manifest = "//testing:test_manifest", + deps = [ + "//model/src/main/proto:math_java_proto_lite", + "//testing/src/main/java/org/oppia/android/testing/math:math_equation_subject", + "//testing/src/main/java/org/oppia/android/testing/robolectric:test_module", + "//third_party:com_google_truth_truth", + "//third_party:junit_junit", + "//third_party:robolectric_android-all", + ], +) + +oppia_android_test( + name = "MathExpressionSubjectTest", + srcs = ["MathExpressionSubjectTest.kt"], + custom_package = "org.oppia.android.testing.math", + test_class = "org.oppia.android.testing.math.MathExpressionSubjectTest", + test_manifest = "//testing:test_manifest", + deps = [ + "//model/src/main/proto:math_java_proto_lite", + "//testing/src/main/java/org/oppia/android/testing/math:math_expression_subject", + "//testing/src/main/java/org/oppia/android/testing/robolectric:test_module", + "//third_party:com_google_truth_truth", + "//third_party:junit_junit", + "//third_party:robolectric_android-all", + ], +) + +oppia_android_test( + name = "RealSubjectTest", + srcs = ["RealSubjectTest.kt"], + custom_package = "org.oppia.android.testing.math", + test_class = "org.oppia.android.testing.math.RealSubjectTest", + test_manifest = "//testing:test_manifest", + deps = [ + "//model/src/main/proto:math_java_proto_lite", + "//testing/src/main/java/org/oppia/android/testing/math:real_subject", + "//testing/src/main/java/org/oppia/android/testing/robolectric:test_module", + "//third_party:com_google_truth_truth", + "//third_party:junit_junit", + "//third_party:robolectric_android-all", + ], +) From 04a5ce95f7ed96e4ca64b363f78086cbec544353 Mon Sep 17 00:00:00 2001 From: manas-yu Date: Sat, 28 Dec 2024 23:53:10 +0530 Subject: [PATCH 5/6] improved tests --- .../testing/math/MathEquationSubjectTest.kt | 193 ++++++++++++++++++ .../testing/math/MathExpressionSubjectTest.kt | 89 ++++++++ 2 files changed, 282 insertions(+) diff --git a/testing/src/test/java/org/oppia/android/testing/math/MathEquationSubjectTest.kt b/testing/src/test/java/org/oppia/android/testing/math/MathEquationSubjectTest.kt index 5260dbccfa2..87f35ec8cfc 100644 --- a/testing/src/test/java/org/oppia/android/testing/math/MathEquationSubjectTest.kt +++ b/testing/src/test/java/org/oppia/android/testing/math/MathEquationSubjectTest.kt @@ -8,6 +8,8 @@ import org.junit.runners.JUnit4 import org.oppia.android.app.model.MathBinaryOperation import org.oppia.android.app.model.MathEquation import org.oppia.android.app.model.MathExpression +import org.oppia.android.app.model.MathFunctionCall +import org.oppia.android.app.model.MathUnaryOperation import org.oppia.android.app.model.Real /** Tests for [MathEquationSubject]. */ @@ -160,6 +162,197 @@ class MathEquationSubjectTest { assertThat(exception).hasMessageThat().contains("expected: 11") } + @Test + fun testConvertsToLatex_withNestedOperations_producesCorrectString() { + val equation = createEquation( + leftSide = createBinaryOperation( + MathBinaryOperation.Operator.ADD, + createUnaryOperation( + MathUnaryOperation.Operator.NEGATE, + createConstantExpression(2) + ), + createBinaryOperation( + MathBinaryOperation.Operator.MULTIPLY, + createConstantExpression(3), + createVariableExpression("x") + ) + ), + rightSide = createConstantExpression(0) + ) + + MathEquationSubject.assertThat(equation) + .convertsToLatexStringThat() + .isEqualTo("-2 + 3 \\times x = 0") + } + + @Test + fun testConvertsToLatexWithFractions_nestedFractions_producesCorrectString() { + val equation = createEquation( + leftSide = createBinaryOperation( + MathBinaryOperation.Operator.DIVIDE, + createConstantExpression(1), + createBinaryOperation( + MathBinaryOperation.Operator.DIVIDE, + createConstantExpression(2), + createVariableExpression("x") + ) + ), + rightSide = createConstantExpression(0) + ) + + MathEquationSubject.assertThat(equation) + .convertsWithFractionsToLatexStringThat() + .isEqualTo("\\frac{1}{\\frac{2}{x}} = 0") + } + + @Test + fun testConvertsToLatex_withUnaryOperationInFraction_producesCorrectString() { + val equation = createEquation( + leftSide = createBinaryOperation( + MathBinaryOperation.Operator.DIVIDE, + createUnaryOperation( + MathUnaryOperation.Operator.NEGATE, + createConstantExpression(1) + ), + createConstantExpression(2) + ), + rightSide = createConstantExpression(0) + ) + + MathEquationSubject.assertThat(equation) + .convertsWithFractionsToLatexStringThat() + .isEqualTo("\\frac{-1}{2} = 0") + } + + @Test + fun testConvertsToLatex_withFunctionCallInComplexExpression_producesCorrectString() { + val equation = createEquation( + leftSide = createBinaryOperation( + MathBinaryOperation.Operator.ADD, + createConstantExpression(1), + createFunctionCall( + MathFunctionCall.FunctionType.SQUARE_ROOT, + createBinaryOperation( + MathBinaryOperation.Operator.ADD, + createConstantExpression(4), + createVariableExpression("x") + ) + ) + ), + rightSide = createConstantExpression(0) + ) + + MathEquationSubject.assertThat(equation) + .convertsToLatexStringThat() + .isEqualTo("1 + \\sqrt{4 + x} = 0") + } + + @Test + fun testConvertsToLatex_withInvalidExpression_fails() { + val equation = MathEquation.getDefaultInstance() + + val exception = assertThrows(AssertionError::class.java) { + MathEquationSubject.assertThat(equation) + .convertsToLatexStringThat() + .isEqualTo("5 = 0") + } + assertThat(exception).hasMessageThat().contains("expected: 5 = 0\n" + + "but was : =") + } + + @Test + fun testConvertsWithFractionsToLatex_withInvalidExpression_fails() { + val equation = MathEquation.getDefaultInstance() + + val exception = assertThrows(AssertionError::class.java) { + MathEquationSubject.assertThat(equation) + .convertsWithFractionsToLatexStringThat() + .isEqualTo("\\frac{1}{2} = 0") + } + assertThat(exception).hasMessageThat().contains("expected: \\frac{1}{2} = 0\n" + + "but was : =") + } + + @Test + fun testHasLeftHandSide_withComplexNestedExpression_matchesExpression() { + val equation = createEquation( + leftSide = createBinaryOperation( + MathBinaryOperation.Operator.ADD, + createFunctionCall( + MathFunctionCall.FunctionType.SQUARE_ROOT, + createBinaryOperation( + MathBinaryOperation.Operator.MULTIPLY, + createConstantExpression(4), + createVariableExpression("x") + ) + ), + createUnaryOperation( + MathUnaryOperation.Operator.NEGATE, + createConstantExpression(3) + ) + ), + rightSide = createConstantExpression(0) + ) + + MathEquationSubject.assertThat(equation).hasLeftHandSideThat().hasStructureThatMatches { + addition { + leftOperand { + functionCallTo(MathFunctionCall.FunctionType.SQUARE_ROOT) { + argument { + multiplication { + leftOperand { + constant { + withValueThat().isIntegerThat().isEqualTo(4) + } + } + rightOperand { + variable { + withNameThat().isEqualTo("x") + } + } + } + } + } + } + rightOperand { + negation { + operand { + constant { + withValueThat().isIntegerThat().isEqualTo(3) + } + } + } + } + } + } + } + + private fun createFunctionCall( + functionType: MathFunctionCall.FunctionType, + argument: MathExpression + ): MathExpression { + return MathExpression.newBuilder() + .setFunctionCall( + MathFunctionCall.newBuilder() + .setFunctionType(functionType) + .setArgument(argument) + ) + .build() + } + + private fun createUnaryOperation( + operator: MathUnaryOperation.Operator, + operand: MathExpression + ): MathExpression { + return MathExpression.newBuilder() + .setUnaryOperation( + MathUnaryOperation.newBuilder() + .setOperator(operator) + .setOperand(operand) + ) + .build() + } + private fun createEquation( leftSide: MathExpression, rightSide: MathExpression diff --git a/testing/src/test/java/org/oppia/android/testing/math/MathExpressionSubjectTest.kt b/testing/src/test/java/org/oppia/android/testing/math/MathExpressionSubjectTest.kt index 81f8e638dba..d5e29fc9759 100644 --- a/testing/src/test/java/org/oppia/android/testing/math/MathExpressionSubjectTest.kt +++ b/testing/src/test/java/org/oppia/android/testing/math/MathExpressionSubjectTest.kt @@ -206,18 +206,103 @@ class MathExpressionSubjectTest { } } + @Test + fun testExpression_withUnsetType_fails() { + val expression = MathExpression.getDefaultInstance() + + val exception = assertThrows(AssertionError::class.java) { + MathExpressionSubject.assertThat(expression).hasStructureThatMatches { + constant { + withValueThat().isIntegerThat().isEqualTo(5) + } + } + } + assertThat(exception).hasMessageThat().contains("EXPRESSIONTYPE_NOT_SET") + } + + @Test + fun testBinaryOperation_withUnsetOperator_fails() { + val expression = MathExpression.newBuilder() + .setBinaryOperation( + MathBinaryOperation.newBuilder() + .setLeftOperand(createConstantExpression(3)) + .setRightOperand(createConstantExpression(4)) + ) + .build() + + val exception = assertThrows(AssertionError::class.java) { + MathExpressionSubject.assertThat(expression).hasStructureThatMatches { + addition { + leftOperand { + constant { + withValueThat().isIntegerThat().isEqualTo(3) + } + } + rightOperand { + constant { + withValueThat().isIntegerThat().isEqualTo(4) + } + } + } + } + } + assertThat(exception).hasMessageThat().contains("Expected binary operation with operator") + } + + @Test + fun testVariableExpression_withNullName_fails() { + val expression = MathExpression.newBuilder() + .setVariable("") + .build() + + val exception = assertThrows(AssertionError::class.java) { + MathExpressionSubject.assertThat(expression).hasStructureThatMatches { + variable { + withNameThat().isNotEmpty() + } + } + } + assertThat(exception).hasMessageThat().contains("expected not to be empty") + } + + @Test + fun testFunctionCall_withMissingArgument_fails() { + val expression = MathExpression.newBuilder() + .setFunctionCall( + MathFunctionCall.newBuilder() + .setFunctionType(MathFunctionCall.FunctionType.SQUARE_ROOT) + ) + .build() + + val exception = assertThrows(AssertionError::class.java) { + MathExpressionSubject.assertThat(expression).hasStructureThatMatches { + functionCallTo(MathFunctionCall.FunctionType.SQUARE_ROOT) { + argument { + constant { + withValueThat().isIntegerThat().isEqualTo(16) + } + } + } + } + } + assertThat(exception).hasMessageThat().contains("EXPRESSIONTYPE_NOT_SET") + } + + /** Creates a constant [MathExpression] with the specified integer value. */ private fun createConstantExpression(value: Int): MathExpression { return MathExpression.newBuilder() .setConstant(Real.newBuilder().setInteger(value)) .build() } + /** Creates a variable [MathExpression] with the specified variable name. */ private fun createVariableExpression(name: String): MathExpression { return MathExpression.newBuilder() .setVariable(name) .build() } + /** Creates a binary operation [MathExpression] with the specified operator and operands. */ private fun createBinaryOperation( operator: MathBinaryOperation.Operator, left: MathExpression, @@ -233,6 +318,7 @@ class MathExpressionSubjectTest { .build() } + /** Creates an implicit multiplication [MathExpression] between two operands. */ private fun createImplicitMultiplication( left: MathExpression, right: MathExpression @@ -248,6 +334,7 @@ class MathExpressionSubjectTest { .build() } + /** Creates a unary operation [MathExpression] with the specified operator and operand. */ private fun createUnaryOperation( operator: MathUnaryOperation.Operator, operand: MathExpression @@ -261,6 +348,7 @@ class MathExpressionSubjectTest { .build() } + /** Creates a function call [MathExpression] with the specified function type and argument. */ private fun createFunctionCall( functionType: MathFunctionCall.FunctionType, argument: MathExpression @@ -274,6 +362,7 @@ class MathExpressionSubjectTest { .build() } + /** Creates a group [MathExpression] that wraps the specified inner expression. */ private fun createGroupExpression(inner: MathExpression): MathExpression { return MathExpression.newBuilder() .setGroup(inner) From 5a3ffa203c136257184c686d623e38379664ef4f Mon Sep 17 00:00:00 2001 From: manas-yu Date: Sat, 28 Dec 2024 23:58:11 +0530 Subject: [PATCH 6/6] formatting --- .../testing/math/MathEquationSubjectTest.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/testing/src/test/java/org/oppia/android/testing/math/MathEquationSubjectTest.kt b/testing/src/test/java/org/oppia/android/testing/math/MathEquationSubjectTest.kt index 87f35ec8cfc..7a9a6b8b62f 100644 --- a/testing/src/test/java/org/oppia/android/testing/math/MathEquationSubjectTest.kt +++ b/testing/src/test/java/org/oppia/android/testing/math/MathEquationSubjectTest.kt @@ -34,7 +34,6 @@ class MathEquationSubjectTest { fun testHasLeftHandSide_withDefaultExpression_hasNoExpressionType() { val equation = MathEquation.getDefaultInstance() - // Default expression should have no type set MathEquationSubject.assertThat(equation).hasLeftHandSideThat().isEqualTo( MathExpression.getDefaultInstance() ) @@ -58,7 +57,6 @@ class MathEquationSubjectTest { fun testHasRightHandSide_withDefaultExpression_hasNoExpressionType() { val equation = MathEquation.getDefaultInstance() - // Default expression should have no type set MathEquationSubject.assertThat(equation).hasRightHandSideThat().isEqualTo( MathExpression.getDefaultInstance() ) @@ -256,8 +254,10 @@ class MathEquationSubjectTest { .convertsToLatexStringThat() .isEqualTo("5 = 0") } - assertThat(exception).hasMessageThat().contains("expected: 5 = 0\n" + - "but was : =") + assertThat(exception).hasMessageThat().contains( + "expected: 5 = 0\n" + + "but was : =" + ) } @Test @@ -269,8 +269,10 @@ class MathEquationSubjectTest { .convertsWithFractionsToLatexStringThat() .isEqualTo("\\frac{1}{2} = 0") } - assertThat(exception).hasMessageThat().contains("expected: \\frac{1}{2} = 0\n" + - "but was : =") + assertThat(exception).hasMessageThat().contains( + "expected: \\frac{1}{2} = 0\n" + + "but was : =" + ) } @Test