Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for numeric & algebraic expressions/equations interactions support #4044

Closed
BenHenning opened this issue Dec 14, 2021 · 0 comments · Fixed by #2173
Closed

Add support for numeric & algebraic expressions/equations interactions support #4044

BenHenning opened this issue Dec 14, 2021 · 0 comments · Fixed by #2173
Assignees
Labels
Z-ibt Temporary label for Ben to keep track of issues he's triaged.

Comments

@BenHenning
Copy link
Member

Project tracking issue for implementing math-based interactions (https://github.com/oppia/oppia/tree/develop/extensions/interactions -- AlgebraicExpressionInput, MathEquationInput, NumericExpressionInput) per the PRD https://docs.google.com/document/d/1x2vcSjocJUXkwwlce5Gjjq_Z83ykVIn2Fp0BcnrOrTg/edit# and the accessibility version of this feature in Android.

@BenHenning BenHenning self-assigned this Dec 14, 2021
@BenHenning BenHenning added this to the Alpha MR4 milestone Dec 14, 2021
BenHenning added a commit that referenced this issue Mar 25, 2022
…4045)

## Explanation
Fix part of #4044
Fix part of #1617
This PR makes #1543 obsolete once merged.
Originally copied from #2173 when it was in proof-of-concept form

This PR replaces the proto formatting mechanism (or, rather, makes it obsolete) by leveraging a feature of Bazel's proto library that allows the import prefix to be stripped. This ensures that the generated protos are treated as though they're in the same directory (which is how they were set up before per Gradle's requirement). The main benefit of this slightly cleaner approach is it allows the textproto conversion pipeline to work for protos that import other protos (otherwise the path doesn't match up, and it ends up with a Bazel error).

This required introducing a new proto library to ensure the prefix is properly stripped, and thus this PR introduces a regex check + test to force this check. As part of introducing this check, file matching & exemption checking now allows for more flexible regex (via checks).

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
N/A -- this only changes build infrastructure & has no direct impact on user interfaces.

Commit history:

* Copy proto-based changes from #2173.

* Post-merge fix.

* Add regex check, docs, and resolve TODOs.

This also changes regex handling in the check to be more generic for
better flexibility when matching files.

* Lint fix.

* Fix broken build.

* Post-merge fix.
BenHenning added a commit that referenced this issue Mar 26, 2022
…ity refactor) (#4046)

## Explanation
Fix part of #4044
Originally copied from #2173 when it was in proof-of-concept form

This PR introduces a new math.proto and math utility package for upcoming protos & utilities coming in later PRs as part of the broader math expressions project. This includes moving some pure math protos & utilities to the new package, but not quite everything can be moved yet due to resource dependencies for RatioExtensions and the ratio/fraction parsers. These could be split up and partly moved to the new package in a future PR, but it's not a prerequisite for any of the math expressions work.

This PR also moves most of fraction parsing to utility and extracts just the UI-specific portion (i.e. accessing app strings). Consequently, there are now two test suites to verify the fraction parsing functionality. StringExtensions needed to be moved out of app due to it being needed in FractionParser. This move is needed for a downstream PR that will utilize the fraction parser in tests.

The new test file exemption changes are just updates since there are moved files.

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
N/A refactor with no side effects to affect user flows.

Commit history:

* Copy proto-based changes from #2173.

* Introduce math.proto & refactor math extensions.

Much of this is copied from #2173.

* Migrate tests & remove unneeded prefix.

* Add needed newline.

* Some needed Fraction changes.

* Lint fix.

* Fix broken test post-refactor.

* Post-merge fix.

* Add regex check, docs, and resolve TODOs.

This also changes regex handling in the check to be more generic for
better flexibility when matching files.

* Lint fix.

* Fix failing static checks.

* Move StringExtensions & fraction parsing.

This splits fraction parsing between UI & utility components.

* Add missing KDocs.

* Fix broken build.

* Fix broken build post-merge.

* Post-merge fix.

* More post-merge fixes.
BenHenning added a commit that referenced this issue Mar 26, 2022
…s/equations (#4047)

## Explanation
Fix part of #4044
Originally copied from #2173 when it was in proof-of-concept form

This PR introduces several new math protos:
- Real
- MathExpression (& its constituent pieces: MathBinaryOperation, MathUnaryOperation, and MathFunctionCall)
- MathEquation

The expression & equation structures are necessary for downstream numeric expression, algebraic expression, and algebraic equation parsing, as well as for implementing the MatchesExactlyWith classifiers for each interaction. For specific details on this classifier, see [the PRD](https://docs.google.com/document/d/1x2vcSjocJUXkwwlce5Gjjq_Z83ykVIn2Fp0BcnrOrTg/edit#heading=h.9mdskev1k1ax). None of that code is introduced in this PR, but will be in future PRs.

The ``Real`` structure being introduced is to help with full and partial expression evaluation that will be introduced in a future PR to ensure infinite precision can be kept for as long as possible (i.e. by keeping values in the form of integers or fractions).

Some caveats regarding the expression structure:
- Parsing information is included for token & subexpression extraction which is needed for certain user errors that will be implemented as part of the interaction UI implementations in a future PR
- A single generic structure is used to represent both numeric and algebraic expressions to simplify a lot of code (this will be more evident in future PRs)

Beyond the protos, new Truth test subject classes are being introduced to make verifying these values easier. Part of this includes a custom Kotlin DSL for verifying expressions & equations in a clean way, e.g.:

```kotlin
    val equation = parseAlgebraicEquationSuccessfully("y = (x+1)(x-1)")
    assertThat(equation).hasLeftHandSideThat().hasStructureThatMatches {
      variable {
        withNameThat().isEqualTo("y")
      }
    }
    assertThat(equation).hasRightHandSideThat().hasStructureThatMatches {
      multiplication {
        leftOperand {
          group {
            addition {
              leftOperand {
                variable {
                  withNameThat().isEqualTo("x")
                }
              }
              rightOperand {
                constant {
                  withValueThat().isIntegerThat().isEqualTo(1)
                }
              }
            }
          }
        }
        rightOperand {
          group {
            subtraction {
              leftOperand {
                variable {
                  withNameThat().isEqualTo("x")
                }
              }
              rightOperand {
                constant {
                  withValueThat().isIntegerThat().isEqualTo(1)
                }
              }
            }
          }
        }
      }
    }
```

The main benefit to the DSL is to avoid cascading checks using subsequent assertion lines that can become quite difficult to read, e.g.:

```kotlin
    val rootExpression = result.getExpectedSuccessfulExpression()
    val mulOp = rootExpression.getExpectedBinaryOperationWithOperator(MULTIPLY)
    val leftConstant = mulOp.leftOperand.getExpectedRationalConstant()
    val rightAddOp = mulOp.rightOperand.getExpectedBinaryOperationWithOperator(ADD)
    val rightAddLeftVar = rightAddOp.leftOperand.getExpectedVariable()
    val rightAddRightConstant = rightAddOp.rightOperand.getExpectedRationalConstant()
    assertThat(leftConstant).isEqualTo(createWholeNumberFraction(2))
    assertThat(rightAddLeftVar).isEqualTo("x")
    assertThat(rightAddRightConstant).isEqualTo(createWholeNumberFraction(1))
```

The above is for the expression "2(x+1)" and was a previous attempt. It leverages helpers and to me seems much harder to read as compared with the DSL. The DSL does result in more verbosity, but I think that actually helps readability in this case.

### Test exemptions
Note that I did not add tests for these. Given that they're test utilities & the importance of getting the downstream work completed, I decided to file #4097 to track adding tests for each. I've already manually verified the subjects work in actual tests downstream, and the code is simple enough that I'm confident in its correctness by inspection. Nevertheless, we should eventually add automated tests for this. Reviewers: please let me know if you have any concerns with this approach.

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
N/A -- proto & testing library only change, and the proto changes are only additions. No UI functionality is yet affected by these changes.

Commit history:

* Copy proto-based changes from #2173.

* Introduce math.proto & refactor math extensions.

Much of this is copied from #2173.

* Migrate tests & remove unneeded prefix.

* Add needed newline.

* Some needed Fraction changes.

* Introduce math expression + equation protos.

Also adds testing libraries for both + fractions & reals (new
structure).

Most of this is copied from #2173.

* Lint fix.

* Fix broken test post-refactor.

* Post-merge fix.

* Add regex check, docs, and resolve TODOs.

This also changes regex handling in the check to be more generic for
better flexibility when matching files.

* Lint fix.

* Fix failing static checks.

* Fix broken CI checks.

Adds missing KDocs, test file exemptions, and fixes the Gradle build.

* Lint fixes.

* Address reviewer comments + other stuff.

This also fixes a typo and incorrectly ordered exemptions list I noticed
during development of downstream PRs.

* Move StringExtensions & fraction parsing.

This splits fraction parsing between UI & utility components.

* Add missing KDocs.

* Fix broken build.

* Fix broken build post-merge.

* Post-merge fix.

* More post-merge fixes.
BenHenning added a commit that referenced this issue Mar 26, 2022
…ssions/operations (#4049)

## Explanation
Fix part of #4044
Originally copied from #2173 when it was in proof-of-concept form

This PR introduces the proto & Truth subject for representing math expressions in a way that makes them trivial to compare without commutative or associativity changing the meaning of the operation. See #4047 for details about the test exemptions being made here, and the custom Kotlin DSL. Note that #4098 is tracking adding tests for the new test subject.

The way the structure introduced here works is that it provides a structure for math expressions that collects "accumulations" (i.e. products or summations). Since both multiplication and addition are commutative operators, elements of these accumulation lists can be rearranged (and thus sorted for deterministic sorting that doesn't fail upon commutative reordering). Associativity complicates this since we actually want expressions like 3-4+7 to be equivalent to 7+3-4. In order for that to be the case, the structure treats subtraction as negative addition and division as inverted multiplication so that division and subtraction terms can also be rearranged. This leaves only square roots & exponentiations as non-commutative operations (which are more or less retained in the same hierarchical structure as math expressions).

This structure will be utilized in a later PR when a routine for converting ``MathExpression``s to ``ComparableOperation``s, and then later in MatchesUpToTrivialManipulations classifier implementations for each numeric expression, algebraic expression, and math equation interaction. For specific details on this classifier, see [the PRD](https://docs.google.com/document/d/1x2vcSjocJUXkwwlce5Gjjq_Z83ykVIn2Fp0BcnrOrTg/edit#heading=h.g0bd549g079d).

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
N/A -- proto & testing library only change, and the proto changes are only additions. No UI functionality is yet affected by these changes.

Commit history:

* Copy proto-based changes from #2173.

* Introduce math.proto & refactor math extensions.

Much of this is copied from #2173.

* Migrate tests & remove unneeded prefix.

* Add needed newline.

* Some needed Fraction changes.

* Introduce math expression + equation protos.

Also adds testing libraries for both + fractions & reals (new
structure).

Most of this is copied from #2173.

* Add protos + testing lib for commutative exprs.

* Lint fix.

* Fix broken test post-refactor.

* Post-merge fix.

* Add regex check, docs, and resolve TODOs.

This also changes regex handling in the check to be more generic for
better flexibility when matching files.

* Lint fix.

* Fix failing static checks.

* Fix broken CI checks.

Adds missing KDocs, test file exemptions, and fixes the Gradle build.

* Lint fixes.

* Add docs & exempted tests.

* Remove blank line.

* Address reviewer comments + other stuff.

This also fixes a typo and incorrectly ordered exemptions list I noticed
during development of downstream PRs.

* Move StringExtensions & fraction parsing.

This splits fraction parsing between UI & utility components.

* Add missing KDocs.

* Remove the ComparableOperationList wrapper.

* Fix broken build.

* Fix broken build post-merge.

* Post-merge fix.

* More post-merge fixes.
BenHenning added a commit that referenced this issue Mar 26, 2022
## Explanation
Fix part of #4044
Originally copied from #2173 when it was in proof-of-concept form

This PR introduces the protos and test subject to represent polynomials.

For the purposes of upcoming classifier work, a polynomial is defined as a sum of terms where each term has a coefficient and zero or more variables with positive integer powers. This representation provides support for all real polynomials, and keeps a nicely structured representation for coefficients that actually allows for retaining integers and rational values (this will become more evident when math expression -> polynomial conversion is added).

Furthermore, various extensions files are added to help support some of the fundamental operations (mainly needed by test subjects). These operations are being thoroughly tested, and will be augmented with a lot more functionality in upcoming PRs.

The new polynomial test subject doesn't have new tests to keep this PR focused on production code, and since it's relatively easy to verify as correct via review. #4100 is tracking adding tests.

This structure will be utilized in a later PR in IsEquivalentTo classifier implementations for each numeric expression, algebraic expression, and math equation interaction. For specific details on this classifier, see [the PRD](https://docs.google.com/document/d/1x2vcSjocJUXkwwlce5Gjjq_Z83ykVIn2Fp0BcnrOrTg/edit#heading=h.1q3av9yssyi5). Polynomials are the ideal structure for verifying equivalence since they fully collapse expressions regardless of associativity, commutativity, and distributivity (to some extent--caveats will be noted in the future PR that converts expressions to this new polynomial structure).

Slightly separate from polynomials, ``FloatExtensions`` was updated to include better epsilon values for comparing both floats and doubles (and a separate one is used for doubles). These are loosely based on the computed machine epsilon value listed here: https://en.wikipedia.org/wiki/Machine_epsilon, but it uses a higher order of magnitude and rounding for a bit more "wiggle room" for equality (so that it's not essentially replicating '==' for values close to 1).

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
N/A -- proto & testing library only change, and the proto changes are only additions. No UI functionality is yet affected by these changes.

Commit history:

* Copy proto-based changes from #2173.

* Introduce math.proto & refactor math extensions.

Much of this is copied from #2173.

* Migrate tests & remove unneeded prefix.

* Add needed newline.

* Some needed Fraction changes.

* Introduce math expression + equation protos.

Also adds testing libraries for both + fractions & reals (new
structure).

Most of this is copied from #2173.

* Add protos + testing lib for commutative exprs.

* Add protos & test libs for polynomials.

* Lint fix.

* Lint fixes.

* Fix broken test post-refactor.

* Post-merge fix.

* Add regex check, docs, and resolve TODOs.

This also changes regex handling in the check to be more generic for
better flexibility when matching files.

* Lint fix.

* Fix failing static checks.

* Fix broken CI checks.

Adds missing KDocs, test file exemptions, and fixes the Gradle build.

* Lint fixes.

* Add docs & exempted tests.

* Remove blank line.

* Add docs + tests.

* Address reviewer comments + other stuff.

This also fixes a typo and incorrectly ordered exemptions list I noticed
during development of downstream PRs.

* Move StringExtensions & fraction parsing.

This splits fraction parsing between UI & utility components.

* Address reviewer comments.

* Alphabetize test exemptions.

* Add missing KDocs.

* Remove the ComparableOperationList wrapper.

* Use more intentional epsilons for float comparing.

* Remove failing test.

* Fix broken build.

* Fix broken build post-merge.

* Post-merge fix.

* More post-merge fixes.
BenHenning added a commit that referenced this issue Mar 26, 2022
…4051)

## Explanation
Fix part of #4044
Originally copied from #2173 when it was in proof-of-concept form

This PR principally introduces the lexical tokenizer (lexer) for math expressions, as defined by the [formal grammar](https://docs.google.com/document/d/1JMpbjqRqdEpye67HvDoqBo_rtScY9oEaB7SwKBBspss/edit). The tokenizer converts a string into a sequence of ``Token``s which provide context to the characters available in the string. While the tokenizer is not yet hooked up, it will be leveraged in an upcoming PR to add support for parsing math expressions and equations. Note that special care was taken to ensure the parser is an LL(1) parser to simplify implementation, ease future grammar additions, and to allow for a table-based parsing method in the future if increased performance is necessary.

Some caveats of the tokenizer:
- It's implemented on top of a sequence with lazy iteration to ensure that no processing time is spent parsing more than necessary (i.e. if the parser short-circuits due to an error then no additional tokenization cost is paid)
- Due to a limitation in the grammar being LL(1), function name parsing is a bit more nuanced and limiting. The lexer has to assume that the start of a function name is actually a name and not a series of variables. To account for this, a special token exists for invalid function names. See the tokenizer's test suite for more details on valid and invalid character combinations.
- Care is taken to ensure that overflowed integers & doubles result in failing tokens
- The lexer takes on a bit of the actual parsing (for integers & reals) to simplify the parser, but the majority of parsing is not done in the lexer

This PR introduces a new Truth subject for the tokenizer's ``Token`` class, though similar to subjects introduced in previous PRs this one does not include tests. #4121 is tracking adding them in the future.

To enforce the LL(1) grammar, the tokenizer makes use of a PeekableIterator that doesn't allow for lookahead beyond one character. This iterator will also be used by the upcoming parser to keep the LL(1) property.

To enable better testing for the tokenizer, I decided to add parameterized tests. Unfortunately, there wasn't an obvious way on how to exactly do this. While parameterized JUnit tests work, there are some limitations:
- They won't work with instrumentation or Robolectric
- They don't support combining parameterized & non-parameterized tests

Instead, I decided to implement a custom parameterized test runner that addresses both of the points above. I used ``AndroidJUnit4`` and ``ParameterizedRobolectricTestRunner`` as key references into understanding how to actually build this. While the implementation we're now using is quite different than both, they serve as parts of the basis. ``AndroidJUnit4`` is final, so we needed to implement a similar switching mechanism to select Robolectric or Espresso test helpers based on the platform (this is controlled via a test class-level annotation). Further, Robolectric's runner was important to understand how to even set up a parameterized runner, and to better understand how to make Robolectric work in this environment.

The result seems to be a very clean test runner, but I'm keen to hear reviewer's thoughts on this. Note a couple things:
- While there's an Espresso-supported runner, I didn't actually verify that it works since we haven't yet gotten instrumentation tests working natively with Bazel (though I do expect that it'll work)
- I didn't test this on Gradle, but per the CI run it appears to work just fine
- There are no tests for any of the new runners or their utilities. The utilities are fairly trivial, and the runners are difficult to test. I decided not to test them or file a tracking issue as they'll likely be the last components we add tests for when we aim to raise the codebase's code coverage (I didn't feel the difficulty was worth overcoming here over vs. manual verification).
- The runner has very robust error detection to try and reduce potential errors, including enforcing parameter order consistency
- I ran into a little snag with ktlint--see #4122
- The previous iteration of this led to platform selection between Espresso & Robolectric based on Bazel dependencies, but I decided to change this to be an explicit annotation. This has a number of considerations:
  - It leads to automatic build fixes (i.e. you can't compile a reference to a class without the Bazel dependency being complete)
  - It allowed me to introduce a JUnit-specific test suite (after changing MathTokenizerTest to this, I saw an almost 40x decrease in runtime; I plan to use this for all future parameterized math tests since Robolectric isn't actually needed for these and is **much** slower)
  - It doesn't facilitate shared tests like AndroidJUnit4, but we could conceivably create a shared runner in the future that uses either the instrumentation or Robolectric runner based on the current platform (similar to how AndroidJUnit4 works--see the commit history for the old code that did this)
- Finally, see the KDoc for ``OppiaParameterizedTestRunner`` for much more details on the runner, including both a code example and suggestions on when to actually use the runner. Further, ``MathTokenizerTest`` demonstrates real uses of the runner.

The way that the runner works is it generates a test suite that combines one runner for all non-parameterized tests with a runner for each parameterized iteration (so the total number of tests in a suite is ``number_of_non_parameterized_tests + sum(parameterized_iterations_in_all_parameterized_tests)``. The iteration names are appended to parameterized test's names to provide both Bazel filtering support and direct error reporting when tests fail (so that the exact iteration is known & can be run/debug in isolation). Android Studio will run all iterations when filtering on a method (since that should match against all), but this might be environment-specific (I verified this with Bazel in Android Studio, but not with Gradle).

### Script asset changes
Test file exemptions were added for the new JUnit rules & the TokenSubject class--see above for the rationale.

Regex content exemptions were added for ``ParameterizedMethod`` since it uses ``Locale`` and ``capitalize``. It doesn't have access to ``MachineLocale`` (at least, currently), and is only running in tests (so localization correctness isn't as important). Finally, the chosen locale to use for capitalization is ``US`` which should more or less match field names in tests for the Oppia codebase.

Further, a new regex check was added to require all uses of the new test runner to require approval (i.e. by adding an exemption). This will help ensure it doesn't get abused/used too broadly since parameterization should be an exceptional case rather than the norm.

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
N/A -- This PR introduces a non-user facing utility, and the utility isn't yet hooked up (it will be in a subsequent PR).

Commit history:

* Copy proto-based changes from #2173.

* Introduce math.proto & refactor math extensions.

Much of this is copied from #2173.

* Migrate tests & remove unneeded prefix.

* Add needed newline.

* Some needed Fraction changes.

* Introduce math expression + equation protos.

Also adds testing libraries for both + fractions & reals (new
structure).

Most of this is copied from #2173.

* Add protos + testing lib for commutative exprs.

* Add protos & test libs for polynomials.

* Lint fix.

* Lint fixes.

* Add math tokenizer + utility & tests.

This is mostly copied from #2173.

* Fix broken test post-refactor.

* Post-merge fix.

* Add regex check, docs, and resolve TODOs.

This also changes regex handling in the check to be more generic for
better flexibility when matching files.

* Lint fix.

* Fix failing static checks.

* Fix broken CI checks.

Adds missing KDocs, test file exemptions, and fixes the Gradle build.

* Lint fixes.

* Add docs & exempted tests.

* Remove blank line.

* Add docs + tests.

* Add parameterized test runner.

This commit introduces a new parameterized test runner that allows
proper combinations of parameterized & non-parameterized tests in the
same suite, and in a way that should work on both Robolectric & Espresso
(though the latter isn't currently verified).

Further, this commit also introduces a TokenSubject that will be used
more explicitly by the follow-up commit for verifying MathTokenizer.

* Add & update tests.

This introduces tests for PeekableIterator, and reimplements all of
MathTokenizer's tests to be more structured, thorough, and a bit more
maintainable (i.e. by leveraging parameterized tests).

* Lint fixes.

This includes a fix for 'fun interface' not working with ktlint (see #4122).

* Remove internals that broke things.

* Add regex exemptions.

* Address reviewer comments + other stuff.

This also fixes a typo and incorrectly ordered exemptions list I noticed
during development of downstream PRs.

* Move StringExtensions & fraction parsing.

This splits fraction parsing between UI & utility components.

* Address reviewer comments.

* Alphabetize test exemptions.

* Fix typo & add regex check.

The new regex check makes it so that all parameterized testing can be
more easily tracked by the Android TL.

* Add missing KDocs.

* Remove the ComparableOperationList wrapper.

* Change parameterized method delimiter.

* Use more intentional epsilons for float comparing.

* Treat en-dash as a subtraction symbol.

* Add explicit platform selection for paramerized.

This adds explicit platform selection support rather than it being
automatic based on deps. While less flexible for shared tests, this
offers better control for tests that don't want to to use Robolectric
for local tests.

This also adds a JUnit-only test runner, and updates MathTokenizerTest
to use it (which led to an almost 40x decrease in runtime).

* Exemption fixes.

Also, fix name for the AndroidJUnit4 runner.

* Remove failing test.

* Address reviewer comment.

Clarifies the documentation in the test runner around parameter
injection.

* Fix broken build.

* Fix broken build post-merge.

* Post-merge fix.

* More post-merge fixes.
BenHenning added a commit that referenced this issue Mar 26, 2022
…tection) (#4052)

## Explanation
Fix part of #4044
Originally copied from #2173 when it was in proof-of-concept form

This PR introduces an LL(1) recursive descent parser for numeric expressions, algebraic expressions, and algebraic equations (per [this grammar specification](https://docs.google.com/document/d/1JMpbjqRqdEpye67HvDoqBo_rtScY9oEaB7SwKBBspss/edit#bookmark=id.wtmim9gp20a6)).

Some details about the implementation:
- Error handling is managed through a MathParsingResult sealed class with corresponding transformations and errors specifically tied to MathParsingError. I actually quite like this pattern since it avoids relying on exceptions yet still provides as much context as the frontend needs (this will be more apparent in a future PR that leverages these errors for user-visible errors). I might try to adopt this as the more broad error handling pattern in the future.
- Error detection is split into two levels of strictness: required-only (i.e. irrecoverable) and all errors. 'Optional' errors are ones that can still be represented by the ``MathExpression`` proto, but are likely learner errors. While classifiers will never disable these, tests do in order to test certain scenarios that are possible per the grammar.
- The parser implementation is intentionally generic so that both numeric & algebraic expressions can share the same parser (for simplicity)
- A specific effort is made to force LL(1) parsing for simpler grammar & implementation and long-term maintainability (since it's likely this grammar will need to be changed in the future). This is done by leveraging an LL(1) tokenizer and wrapping the ``Sequence`` provided by the tokenizer in a ``PeekableIterator`` (which doesn't allow a lookahead farther than 1). As a result, the parser will short-circuit and fail at the first encounter of an irrecoverable error (optional errors are processed after parsing is complete).
- The original implementation leveraged the [Shunting-yard algorithm](https://en.wikipedia.org/wiki/Shunting-yard_algorithm) (see implementation: https://github.com/oppia/oppia-android/blob/33d62aaa415bcab92dbecd9d64eda199f2676532/utility/src/main/java/org/oppia/android/util/math/MathExpressionParser.kt#L59). This implementation was far smaller, but it was much harder to make specific complex cases work correctly (such as implicit multiplication, and specialized error detection). A recursive descent parser was picked, instead, for better long-term maintainability and flexibility.
- An attempt was made to leverage a Kotlin DSL to define a generic grammar language to try and more declaratively define the implementation (i.e. by sort of just writing out the grammar and inferring a parser from it), but it ended up being more complex than the direct implementation and the syntax wasn't much better. See: 0ddcd2e.
- Specific effort was made toward maintainability in the implementation by utilizing exhaustive when statements so that new token types trigger many compiler errors (to ensure most cases that handle tokens can correctly manage the new token). This is intentional.

Some details about testing (including testing exemptions):
- Due to the size and complexity of the parser, there are actually four test suites for it: MathExpressionParserTest (verifies that parsing works at a high-level, and thoroughly tests error cases), NumericExpressionParserTest (verifies thoroughly that numeric expressions are parsed correctly), AlgebraicExpressionParserTest (thoroughly verifies variable-based parsing), and AlgebraicEquationTest (verifies equation parsing). This approach helps reduce the size of the overall suites and makes them easier to work with (Android Studio seems to have issues with Kotlin files much larger than 1k LOC).
- Note that the tests are intentionally non-redundant; they leak the implementation detail that a common implementation is used for all three types of math constructs and rely on each other for verification (i.e. AlgebraicExpressionTest assumes that most of the tests in NumericExpressionTest also apply to it, just with a variable added).
- Note that error testing does not include ordering. While it does matter to an extent, it didn't seem important enough to add targeted tests for it right now.
- I used code coverage checking locally (as part of verifying the possibility of #1497) to add a few more tests for the parser, but all the remaining uncovered lines are either impossible to cover (due to cascading when statements where earlier calls already cover such cases; this is a limitation in Kotlin) or are incorrectly instrumented by JaCoCo. The current code coverage for the parser is around 93.5% (but realistically is closer to 100%; though there are many logical cases not covered in current tests).
- MathParsingError was exempted from tests since it's a simple data structure definition.
- A custom Truth subject was added for MathParsingError.
- MathParsingErrorSubject was exempted to keep this PR smaller, and because it's trivially verifiable. #4132 was filed to track adding tests in the future.

A regex exemption check was added to enable parameterized testing in MathExpressionParserTest. These are tests which are verifying the same failure behaviors under many different conditions, so it seems like a good utilization of parameterization.

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
The parser introduced in this PR is not yet available via any UI features, so it doesn't directly affect UIs yet.

Commit history:

* Copy proto-based changes from #2173.

* Introduce math.proto & refactor math extensions.

Much of this is copied from #2173.

* Migrate tests & remove unneeded prefix.

* Add needed newline.

* Some needed Fraction changes.

* Introduce math expression + equation protos.

Also adds testing libraries for both + fractions & reals (new
structure).

Most of this is copied from #2173.

* Add protos + testing lib for commutative exprs.

* Add protos & test libs for polynomials.

* Lint fix.

* Lint fixes.

* Add math tokenizer + utility & tests.

This is mostly copied from #2173.

* Add math expression/equation parsing support.

This includes full error detection, and specific test suites for each
parsing case. Much revisement is needed in the tests, and some
additional issues may yet need to be fixed in the parser and/or
error-detection logic.

This is copied from #2173 with revisement & reduction since it's part of
a multi-PR split.

* Remove unneeded comment lines.

* Fix broken test post-refactor.

* Fix equals errors for equations.

This splits the current error into two: one for no equals being present
(& adds it), and one for too many equals. This better supports the UI
errors that need to be displayed to the user in these cases.

* Post-merge fix.

* Add regex check, docs, and resolve TODOs.

This also changes regex handling in the check to be more generic for
better flexibility when matching files.

* Lint fix.

* Fix failing static checks.

* Fix broken CI checks.

Adds missing KDocs, test file exemptions, and fixes the Gradle build.

* Lint fixes.

* Add docs & exempted tests.

* Remove blank line.

* Add docs + tests.

* Add parameterized test runner.

This commit introduces a new parameterized test runner that allows
proper combinations of parameterized & non-parameterized tests in the
same suite, and in a way that should work on both Robolectric & Espresso
(though the latter isn't currently verified).

Further, this commit also introduces a TokenSubject that will be used
more explicitly by the follow-up commit for verifying MathTokenizer.

* Add & update tests.

This introduces tests for PeekableIterator, and reimplements all of
MathTokenizer's tests to be more structured, thorough, and a bit more
maintainable (i.e. by leveraging parameterized tests).

* Lint fixes.

This includes a fix for 'fun interface' not working with ktlint (see #4122).

* Remove internals that broke things.

* Add regex exemptions.

* Post-merge fix.

* Add error tests.

These tests are more or less comprehensive based on existing tests and
some new ideas. All errors are now covered by MathExpressionParserTest.

Error ordering is not tested.

A new Truth subject was added for easier testing, as well (for
MathParsingError).

* Finish algebraic equation tests.

* Reimplement numeric expression tests.

This is almost a full replacement. The new tests are more structured and
intentional to cover key high-level concepts. More tests may be added in
the future, but this is a sensible initial test offering.

This also updates MathExpressionSubject to support checking specifically
for implicit multiplication (and it's now required for such cases since
explicit is otherwise assumed).

* Finish algebraic expression tests.

These largely rely on numeric expression tests (since they focus on
verifying specific variable scenarios).

* Add missing tests for better coverage.

* Add KDocs & test exemptions.

* Lint fixes.

* Remove temporary TODOs.

* Address reviewer comments + other stuff.

This also fixes a typo and incorrectly ordered exemptions list I noticed
during development of downstream PRs.

* Move StringExtensions & fraction parsing.

This splits fraction parsing between UI & utility components.

* Address reviewer comments.

* Alphabetize test exemptions.

* Fix typo & add regex check.

The new regex check makes it so that all parameterized testing can be
more easily tracked by the Android TL.

* Add missing KDocs.

* Post-merge cleanups.

Also, fix text file exemption ordering.

* Add new test for negation with math symbol.

* Remove the ComparableOperationList wrapper.

* Change parameterized method delimiter.

* Use more intentional epsilons for float comparing.

* Treat en-dash as a subtraction symbol.

* Add explicit platform selection for paramerized.

This adds explicit platform selection support rather than it being
automatic based on deps. While less flexible for shared tests, this
offers better control for tests that don't want to to use Robolectric
for local tests.

This also adds a JUnit-only test runner, and updates MathTokenizerTest
to use it (which led to an almost 40x decrease in runtime).

* Exemption fixes.

Also, fix name for the AndroidJUnit4 runner.

* Remove failing test.

* Fix unary expression precedence.

Also, use ParameterizedJunitTestRunner for MathExpressionParserTest.

* Fixes & add more test cases.

* Address reviewer comment.

Clarifies the documentation in the test runner around parameter
injection.

* Fix broken build.

* Fix broken build post-merge.

* Post-merge fix.

* More post-merge fixes.

* Fix TODO comment.
BenHenning added a commit that referenced this issue Mar 26, 2022
… to LaTeX (#4054)

## Explanation
Fix part of #4044
Originally copied from #2173 when it was in proof-of-concept form

This PR introduces support for:
- Converting math expressions & equations to renderable LaTeX
- Evaluating numeric expressions (i.e. turning them into a ``Real`` value)

The LaTeX support is fairly barebones, but it covers all cases that can be represented by the math expression and equation protos. Note that it has a special mode for treating divisions as fractions as this is a requirement for the upcoming math interactions that are being added (as a creator-specified configuration argument).

Some specifics regarding evaluation:
- The expression evaluation is limited to numeric expressions since variables can't be evaluated without values being provided for those variables. This is something we could trivially add in the future, but we don't yet have any applications for such functionality.
- Evaluation is meant to be used mainly in tests for verifying structural correctness of parsed expressions (see the changes in NumericExpressionParserTest). All of the complexity added for computing parts of expressions won't just be limited to evaluation, though; it's also needed for polynomial conversion (which is being added in an upcoming PR) and for downstream classifiers work (one of the classifier types compares the results of evaluation for numeric expressions).
- The constituent computation pieces of evaluation are designed to maximize precision by trying keep integers for as long as possible. For example, adding two integers yields an integer, but dividing two might not (e.g. if the integers correctly divide to another integer, then an integer will be returned; otherwise, a fraction will be returned). When precision cannot accurately be kept, then a double is computed. This approach has nice benefits for polynomial computations since it ensures perfect operations when possible (there are less concerns around carrying rounding errors).
- 0^0 is special cased to be 1 for consistency with the system pow() method and other languages (see https://stackoverflow.com/a/19955996).
- Failures are a bit inconsistent (they can sometimes throw and sometimes return NaN/infinity). We can revise this in the future if needed, but I wanted to err on the side of "failures probably won't happen" since we have parser-based guards in place to prevent them.
- The pow function supports negative powers, non-integer powers, and root powers (e.g. powers whose absolute value is less than 1) where the root computation is the real value root rather than the principal root (i.e. it will take roots of negative values when it can). This actually is inconsistent with the system power function, but matches *some* calculator's behaviors. We can make this configurable or remove the negative support in the future, if needed.

Some notes on testing:
- ``MathEquationSubject`` and ``MathExpressionSubject`` were updated to include convenience functions for evaluation & LaTeX. These aren't directly tested since they're expected to be tested as part of the future test suites for each of these subjects.
- ``RealSubject.assertThat`` was updated to accept a null value (since this can come up with evaluation test cases).
- The fractions & real values tests are extremely detailed to cover a large variety of cases for each operation. This is exacerbated by the fact that each ``Real`` operation has a matrix of different combinations with different outcomes. This test coverage is important since these are the fundamental operational building blocks of polynomial calculations and evaluation.
- RealExtensionsTest is being exempted for parameterized testing. This is a rare case where these tests aren't actually verifying single behaviors, but it's necessary since the test matrix is too large to reasonably fit within a single test suite without maintenance concerns. Fortunately, it seems like the parameterized representation for these tests are mostly readable, and quite concise.

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
N/A -- This change is introducing test-only functionality (numeric expression evaluation) and support for something that will later be shown on the UI (so it doesn't yet affect any UI screens or flows).

Commit history:

* Copy proto-based changes from #2173.

* Introduce math.proto & refactor math extensions.

Much of this is copied from #2173.

* Migrate tests & remove unneeded prefix.

* Add needed newline.

* Some needed Fraction changes.

* Introduce math expression + equation protos.

Also adds testing libraries for both + fractions & reals (new
structure).

Most of this is copied from #2173.

* Add protos + testing lib for commutative exprs.

* Add protos & test libs for polynomials.

* Lint fix.

* Lint fixes.

* Add math tokenizer + utility & tests.

This is mostly copied from #2173.

* Add math expression/equation parsing support.

This includes full error detection, and specific test suites for each
parsing case. Much revisement is needed in the tests, and some
additional issues may yet need to be fixed in the parser and/or
error-detection logic.

This is copied from #2173 with revisement & reduction since it's part of
a multi-PR split.

* Add exp evaluation & LaTeX conversion support.

This is mainly copied from #2173.

* Remove unneeded comment lines.

* Fix broken test post-refactor.

* Add reasonable import for abs().

* Fix equals errors for equations.

This splits the current error into two: one for no equals being present
(& adds it), and one for too many equals. This better supports the UI
errors that need to be displayed to the user in these cases.

* Fix rational^rational powers.

This generifies the sqrt algorithm to support n-roots so that rationals
raised by rationals can actually work & retain the rational (in cases
where the root can actually be taken).

* Post-merge fix.

* Add regex check, docs, and resolve TODOs.

This also changes regex handling in the check to be more generic for
better flexibility when matching files.

* Lint fix.

* Fix failing static checks.

* Fix broken CI checks.

Adds missing KDocs, test file exemptions, and fixes the Gradle build.

* Lint fixes.

* Add docs & exempted tests.

* Remove blank line.

* Add docs + tests.

* Add parameterized test runner.

This commit introduces a new parameterized test runner that allows
proper combinations of parameterized & non-parameterized tests in the
same suite, and in a way that should work on both Robolectric & Espresso
(though the latter isn't currently verified).

Further, this commit also introduces a TokenSubject that will be used
more explicitly by the follow-up commit for verifying MathTokenizer.

* Add & update tests.

This introduces tests for PeekableIterator, and reimplements all of
MathTokenizer's tests to be more structured, thorough, and a bit more
maintainable (i.e. by leveraging parameterized tests).

* Lint fixes.

This includes a fix for 'fun interface' not working with ktlint (see #4122).

* Remove internals that broke things.

* Add regex exemptions.

* Post-merge fix.

* Add error tests.

These tests are more or less comprehensive based on existing tests and
some new ideas. All errors are now covered by MathExpressionParserTest.

Error ordering is not tested.

A new Truth subject was added for easier testing, as well (for
MathParsingError).

* Finish algebraic equation tests.

* Reimplement numeric expression tests.

This is almost a full replacement. The new tests are more structured and
intentional to cover key high-level concepts. More tests may be added in
the future, but this is a sensible initial test offering.

This also updates MathExpressionSubject to support checking specifically
for implicit multiplication (and it's now required for such cases since
explicit is otherwise assumed).

* Finish algebraic expression tests.

These largely rely on numeric expression tests (since they focus on
verifying specific variable scenarios).

* Add missing tests for better coverage.

* Add KDocs & test exemptions.

* Lint fixes.

* Remove temporary TODOs.

* Add tests.

* Split StringToFractionParser.

This is a temporary change that will be finished upstream (since there's
an earlier PR that's a better fit for this change).

* Address reviewer comments + other stuff.

This also fixes a typo and incorrectly ordered exemptions list I noticed
during development of downstream PRs.

* Move StringExtensions & fraction parsing.

This splits fraction parsing between UI & utility components.

* Address reviewer comments.

* Alphabetize test exemptions.

* Fix typo & add regex check.

The new regex check makes it so that all parameterized testing can be
more easily tracked by the Android TL.

* Add missing KDocs.

* Post-merge cleanups.

Also, fix text file exemption ordering.

* Add new test for negation with math symbol.

* Post-merge fixes.

* Add KDocs.

Also, add new regex exemption for new parameterized tests in this
branch.

* Refactor & simplify real ext impl.

Also, fix/clarify some KDocs.

* Lint fixes.

* Remove the ComparableOperationList wrapper.

* Change parameterized method delimiter.

* Use utility directly in test.

* Use more intentional epsilons for float comparing.

* Treat en-dash as a subtraction symbol.

* Add explicit platform selection for paramerized.

This adds explicit platform selection support rather than it being
automatic based on deps. While less flexible for shared tests, this
offers better control for tests that don't want to to use Robolectric
for local tests.

This also adds a JUnit-only test runner, and updates MathTokenizerTest
to use it (which led to an almost 40x decrease in runtime).

* Exemption fixes.

Also, fix name for the AndroidJUnit4 runner.

* Remove failing test.

* Fix unary expression precedence.

Also, use ParameterizedJunitTestRunner for MathExpressionParserTest.

* Fixes & add more test cases.

* Post-merge fixes & test changes.

Also, update RealExtensionsTest to use the faster JUnit runner.

* Use utility directly in LaTeX tests.

* Address reviewer comment.

Clarifies the documentation in the test runner around parameter
injection.

* Fix broken build.

* Fix broken build post-merge.

* Post-merge fix.

* More post-merge fixes.

* Fix TODO comment.
BenHenning added a commit that referenced this issue Mar 26, 2022
…mmutativity & associativity (#4055)

## Explanation
Fix part of #4044
Originally copied from #2173 when it was in proof-of-concept form

Introduces support for comparable operations.

Comparable operations are representations of math expressions that can be compared such that commutativity and associativity is ignored. This is done by combining arithmetic operations into summations and products, correctly redistribute negations as needed, keep track of non-combinable operations (such as exponentiation and square roots), and finally using a deterministic method for sorting the full operation list to ensure subsequent calls result in the same list even if items are rearranged per associativity and commutativity. There's a lot of nuance behind the scenarios where this can occur, so the corresponding test suite is complex and long.

The sorting is done by checking all protos that are part of ``ComparableOperation`` and its constituent protos, even if they aren't defined. This isn't the most efficient, but it's not too bad (and it's much simpler than the alternative--see past commits in this PR for what was present before). Some ``Comparator`` extensions were added to simplify sorting (and it's expected both will be needed for the upcoming PR that will introduce expression->polynomial conversion).

Regarding testing, ``ExpressionToComparableOperationConverterTest`` was whitelisted to be parameterized since it had a bunch of common operations to verify with just slightly different arrangement. The suite is still quite long, and may benefit from being split up in the future (similar to how ``MathExpressionParser``'s tests are split into 4 suites).

Finally, more tests were added to ``RealExtensionsTest`` for its new comparator (these follow the same less-than-ideal format as other tests in that suite to fold many test cases into a few using parameterization to reduce the overall suite length).

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
N/A -- This does not have user-facing changes, though it will be used as the base logic for future domain classifiers that will be added.

Commit history:

* Copy proto-based changes from #2173.

* Introduce math.proto & refactor math extensions.

Much of this is copied from #2173.

* Migrate tests & remove unneeded prefix.

* Add needed newline.

* Some needed Fraction changes.

* Introduce math expression + equation protos.

Also adds testing libraries for both + fractions & reals (new
structure).

Most of this is copied from #2173.

* Add protos + testing lib for commutative exprs.

* Add protos & test libs for polynomials.

* Lint fix.

* Lint fixes.

* Add math tokenizer + utility & tests.

This is mostly copied from #2173.

* Add math expression/equation parsing support.

This includes full error detection, and specific test suites for each
parsing case. Much revisement is needed in the tests, and some
additional issues may yet need to be fixed in the parser and/or
error-detection logic.

This is copied from #2173 with revisement & reduction since it's part of
a multi-PR split.

* Add exp evaluation & LaTeX conversion support.

This is mainly copied from #2173.

* Remove unneeded comment lines.

* Add expr->comparable operation list conv support.

This enables the ability to compare two expressions such that operation
associativity and commutativity is considered (i.e. items can be
rearranged using those rules without breaking expression equality).

This is mostly copied from #2173.

* Fix broken test post-refactor.

* Add reasonable import for abs().

* Fix equals errors for equations.

This splits the current error into two: one for no equals being present
(& adds it), and one for too many equals. This better supports the UI
errors that need to be displayed to the user in these cases.

* Fix rational^rational powers.

This generifies the sqrt algorithm to support n-roots so that rationals
raised by rationals can actually work & retain the rational (in cases
where the root can actually be taken).

* Post-merge fix.

* Add regex check, docs, and resolve TODOs.

This also changes regex handling in the check to be more generic for
better flexibility when matching files.

* Lint fix.

* Fix failing static checks.

* Fix broken CI checks.

Adds missing KDocs, test file exemptions, and fixes the Gradle build.

* Lint fixes.

* Add docs & exempted tests.

* Remove blank line.

* Add docs + tests.

* Add parameterized test runner.

This commit introduces a new parameterized test runner that allows
proper combinations of parameterized & non-parameterized tests in the
same suite, and in a way that should work on both Robolectric & Espresso
(though the latter isn't currently verified).

Further, this commit also introduces a TokenSubject that will be used
more explicitly by the follow-up commit for verifying MathTokenizer.

* Add & update tests.

This introduces tests for PeekableIterator, and reimplements all of
MathTokenizer's tests to be more structured, thorough, and a bit more
maintainable (i.e. by leveraging parameterized tests).

* Lint fixes.

This includes a fix for 'fun interface' not working with ktlint (see #4122).

* Remove internals that broke things.

* Add regex exemptions.

* Post-merge fix.

* Add error tests.

These tests are more or less comprehensive based on existing tests and
some new ideas. All errors are now covered by MathExpressionParserTest.

Error ordering is not tested.

A new Truth subject was added for easier testing, as well (for
MathParsingError).

* Finish algebraic equation tests.

* Reimplement numeric expression tests.

This is almost a full replacement. The new tests are more structured and
intentional to cover key high-level concepts. More tests may be added in
the future, but this is a sensible initial test offering.

This also updates MathExpressionSubject to support checking specifically
for implicit multiplication (and it's now required for such cases since
explicit is otherwise assumed).

* Finish algebraic expression tests.

These largely rely on numeric expression tests (since they focus on
verifying specific variable scenarios).

* Add missing tests for better coverage.

* Add KDocs & test exemptions.

* Lint fixes.

* Remove temporary TODOs.

* Add tests.

* Split StringToFractionParser.

This is a temporary change that will be finished upstream (since there's
an earlier PR that's a better fit for this change).

* Address reviewer comments + other stuff.

This also fixes a typo and incorrectly ordered exemptions list I noticed
during development of downstream PRs.

* Move StringExtensions & fraction parsing.

This splits fraction parsing between UI & utility components.

* Address reviewer comments.

* Alphabetize test exemptions.

* Fix typo & add regex check.

The new regex check makes it so that all parameterized testing can be
more easily tracked by the Android TL.

* Add missing KDocs.

* Post-merge cleanups.

Also, fix text file exemption ordering.

* Add new test for negation with math symbol.

* Post-merge fixes.

* Add KDocs.

Also, add new regex exemption for new parameterized tests in this
branch.

* Refactor & simplify real ext impl.

Also, fix/clarify some KDocs.

* Lint fixes.

* Simplify operation list converter a lot.

This inlines three recursive operations to be done during the actual
computation to simplify the overall converter complexity (and to make
determining the test matrix easier).

* Prepare for new tests.

* Remove the ComparableOperationList wrapper.

* Change parameterized method delimiter.

* Use utility directly in test.

* Post-merge fixes.

This adjusts for the removal of ComparableOperationList (i.e. no wrapper
proto).

* Add first round of tests.

This includes fixes to the converter itself as it wasn't distributing
both product inversions and negation correctly in several cases. Tests
should now be covering these cases.

* Finish initial test suite.

Still needs to be cleaned up, but after converter refactoring attempts.

* Simplify operation sorting comparators.

* Remove old tests.

* Add remaining missing tests.

* KDocs & test exemption.

* Renames & lint fixes.

* Use more intentional epsilons for float comparing.

* Treat en-dash as a subtraction symbol.

* Add explicit platform selection for paramerized.

This adds explicit platform selection support rather than it being
automatic based on deps. While less flexible for shared tests, this
offers better control for tests that don't want to to use Robolectric
for local tests.

This also adds a JUnit-only test runner, and updates MathTokenizerTest
to use it (which led to an almost 40x decrease in runtime).

* Exemption fixes.

Also, fix name for the AndroidJUnit4 runner.

* Remove failing test.

* Fix unary expression precedence.

Also, use ParameterizedJunitTestRunner for MathExpressionParserTest.

* Fixes & add more test cases.

* Post-merge fixes & test changes.

Also, update RealExtensionsTest to use the faster JUnit runner.

* Use utility directly in LaTeX tests.

* Post-merge fixes.

Also, update ExpressionToComparableOperationConverterTest to use the
fast JUnit-only runner.

* Address reviewer comment.

Clarifies the documentation in the test runner around parameter
injection.

* Fix broken build.

* Fix broken build post-merge.

* Post-merge fix.

* More post-merge fixes.

* Fix TODO comment.
BenHenning added a commit that referenced this issue Mar 26, 2022
…pressions (#4056)

## Explanation
Fix part of #4044
Originally copied from #2173 when it was in proof-of-concept form

Introduces support for computing polynomials from algebraic expressions (represented using the ``MathExpression`` proto).

At a high-level, most algebraic expression representations of polynomials should be convertible to the proto ``Polynomial`` structure by the converter, but there's a main limitation: cases when polynomial factoring is needed (such as a root power) isn't currently supported, though single term polynomials can be rooted if they result in a valid root. Beyond that, any case where a non-integer or negative power occurs (such as variables in denominators or remainders during polynomial division) will result in a failure. Otherwise, full polynomial arithmetic is supported including: negation, addition, subtraction, multiplication, division (via long division), and exponentiation.

Like past PRs, the converter attempts to retain maximum precision by attempting to keep integers and fraction representation as long as possible. There are also a lot of edge cases which is why the tests are so long.

Some general notes:
- PolynomialExtensionsTest was exempted to utilize parameterization for a few test cases
- ``RealExtensions`` was updated to return a nullable result for square roots & powers to replace exceptional cases with null results so that they can be ignored similar to other failure cases
- A new reverse sort function was added for iterables in ComparatorExtensions (to ensure polynomial sorting is correct)

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
N/A -- This does not have user-facing changes, though it will be used as the base logic for future domain classifiers that will be added.

Commit history:

* Copy proto-based changes from #2173.

* Introduce math.proto & refactor math extensions.

Much of this is copied from #2173.

* Migrate tests & remove unneeded prefix.

* Add needed newline.

* Some needed Fraction changes.

* Introduce math expression + equation protos.

Also adds testing libraries for both + fractions & reals (new
structure).

Most of this is copied from #2173.

* Add protos + testing lib for commutative exprs.

* Add protos & test libs for polynomials.

* Lint fix.

* Lint fixes.

* Add math tokenizer + utility & tests.

This is mostly copied from #2173.

* Add math expression/equation parsing support.

This includes full error detection, and specific test suites for each
parsing case. Much revisement is needed in the tests, and some
additional issues may yet need to be fixed in the parser and/or
error-detection logic.

This is copied from #2173 with revisement & reduction since it's part of
a multi-PR split.

* Add exp evaluation & LaTeX conversion support.

This is mainly copied from #2173.

* Remove unneeded comment lines.

* Add expr->comparable operation list conv support.

This enables the ability to compare two expressions such that operation
associativity and commutativity is considered (i.e. items can be
rearranged using those rules without breaking expression equality).

This is mostly copied from #2173.

* Add support for expression->polynomial conversion.

This is mostly copied from #2173.

* Fix broken test post-refactor.

* Add reasonable import for abs().

* Fix equals errors for equations.

This splits the current error into two: one for no equals being present
(& adds it), and one for too many equals. This better supports the UI
errors that need to be displayed to the user in these cases.

* Fix rational^rational powers.

This generifies the sqrt algorithm to support n-roots so that rationals
raised by rationals can actually work & retain the rational (in cases
where the root can actually be taken).

* Ensure rational terms reduce to ints.

This ensures cases like 8/1 become just '8' coefficients rather than
staying as an irrational (for simplification).

* Post-merge fix.

* Add regex check, docs, and resolve TODOs.

This also changes regex handling in the check to be more generic for
better flexibility when matching files.

* Lint fix.

* Fix failing static checks.

* Fix broken CI checks.

Adds missing KDocs, test file exemptions, and fixes the Gradle build.

* Lint fixes.

* Add docs & exempted tests.

* Remove blank line.

* Add docs + tests.

* Add parameterized test runner.

This commit introduces a new parameterized test runner that allows
proper combinations of parameterized & non-parameterized tests in the
same suite, and in a way that should work on both Robolectric & Espresso
(though the latter isn't currently verified).

Further, this commit also introduces a TokenSubject that will be used
more explicitly by the follow-up commit for verifying MathTokenizer.

* Add & update tests.

This introduces tests for PeekableIterator, and reimplements all of
MathTokenizer's tests to be more structured, thorough, and a bit more
maintainable (i.e. by leveraging parameterized tests).

* Lint fixes.

This includes a fix for 'fun interface' not working with ktlint (see #4122).

* Remove internals that broke things.

* Add regex exemptions.

* Post-merge fix.

* Add error tests.

These tests are more or less comprehensive based on existing tests and
some new ideas. All errors are now covered by MathExpressionParserTest.

Error ordering is not tested.

A new Truth subject was added for easier testing, as well (for
MathParsingError).

* Finish algebraic equation tests.

* Reimplement numeric expression tests.

This is almost a full replacement. The new tests are more structured and
intentional to cover key high-level concepts. More tests may be added in
the future, but this is a sensible initial test offering.

This also updates MathExpressionSubject to support checking specifically
for implicit multiplication (and it's now required for such cases since
explicit is otherwise assumed).

* Finish algebraic expression tests.

These largely rely on numeric expression tests (since they focus on
verifying specific variable scenarios).

* Add missing tests for better coverage.

* Add KDocs & test exemptions.

* Lint fixes.

* Remove temporary TODOs.

* Add tests.

* Split StringToFractionParser.

This is a temporary change that will be finished upstream (since there's
an earlier PR that's a better fit for this change).

* Address reviewer comments + other stuff.

This also fixes a typo and incorrectly ordered exemptions list I noticed
during development of downstream PRs.

* Move StringExtensions & fraction parsing.

This splits fraction parsing between UI & utility components.

* Address reviewer comments.

* Alphabetize test exemptions.

* Fix typo & add regex check.

The new regex check makes it so that all parameterized testing can be
more easily tracked by the Android TL.

* Add missing KDocs.

* Post-merge cleanups.

Also, fix text file exemption ordering.

* Add new test for negation with math symbol.

* Post-merge fixes.

* Add KDocs.

Also, add new regex exemption for new parameterized tests in this
branch.

* Refactor & simplify real ext impl.

Also, fix/clarify some KDocs.

* Lint fixes.

* Simplify operation list converter a lot.

This inlines three recursive operations to be done during the actual
computation to simplify the overall converter complexity (and to make
determining the test matrix easier).

* Prepare for new tests.

* Remove the ComparableOperationList wrapper.

* Change parameterized method delimiter.

* Use utility directly in test.

* Post-merge fixes.

This adjusts for the removal of ComparableOperationList (i.e. no wrapper
proto).

* Add first round of tests.

This includes fixes to the converter itself as it wasn't distributing
both product inversions and negation correctly in several cases. Tests
should now be covering these cases.

* Finish initial test suite.

Still needs to be cleaned up, but after converter refactoring attempts.

* Simplify operation sorting comparators.

* Remove old tests.

* Add remaining missing tests.

* KDocs & test exemption.

* Renames & lint fixes.

* Post-merge fixes.

* Add tests.

* KDocs + exemptions.

Also, clean up polynomial sorting.

* Lint fixes.

* Use more intentional epsilons for float comparing.

* Treat en-dash as a subtraction symbol.

* Add explicit platform selection for paramerized.

This adds explicit platform selection support rather than it being
automatic based on deps. While less flexible for shared tests, this
offers better control for tests that don't want to to use Robolectric
for local tests.

This also adds a JUnit-only test runner, and updates MathTokenizerTest
to use it (which led to an almost 40x decrease in runtime).

* Exemption fixes.

Also, fix name for the AndroidJUnit4 runner.

* Remove failing test.

* Fix unary expression precedence.

Also, use ParameterizedJunitTestRunner for MathExpressionParserTest.

* Fixes & add more test cases.

* Post-merge fixes & test changes.

Also, update RealExtensionsTest to use the faster JUnit runner.

* Use utility directly in LaTeX tests.

* Post-merge fixes.

Also, update ExpressionToComparableOperationConverterTest to use the
fast JUnit-only runner.

* Post-merge fixes.

Also, update PolynomialExtensionsTest to use fast JUnit-only runner.

* Address reviewer comment.

Clarifies the documentation in the test runner around parameter
injection.

* Fix broken build.

* Fix broken build post-merge.

* Post-merge fix.

* More post-merge fixes.

* Fix TODO comment.
BenHenning added a commit that referenced this issue Mar 26, 2022
## Explanation
Fix part of #4044

Introduce all domain classifiers for the NumericExpressionInput interaction.

Specific classifiers being introduced:
- MatchesExactlyWith: this performs an exact comparison check between ``MathExpression``s to match the answer (only whitespace can be ignored, even groups are checked)
- MatchesUpToTrivialManipulations: this compares ``ComparableOperation`` representations of the interaction input and answer (which allows rearranging around commutative operators, and changing of associativity)
- IsEquivalentTo: this evaluates a ``Real`` value for each expression and compares them. Note that a polynomial isn't used like later PRs because direct evaluation is equivalent and a bit more performant. Furthermore, we need the evaluator for test purposes so it might as well be used here.

More details on these classifiers can be seen in the [PRD](https://docs.google.com/document/d/1x2vcSjocJUXkwwlce5Gjjq_Z83ykVIn2Fp0BcnrOrTg/edit#heading=h.8za8vrjxlg51), [technical specification](https://docs.google.com/document/d/1JMpbjqRqdEpye67HvDoqBo_rtScY9oEaB7SwKBBspss/edit#bookmark=id.hqwadl44tk37), and [test matrix breakdown](https://docs.google.com/spreadsheets/d/1u1fQdah2WsmdYKWKGmuXy5TPT7Ot-b8A7O9iZF-j5XE/edit#gid=0).

Other changes:
- This changes the double comparison epsilon value to be a bit more flexible for testing (since it's a small change, the impact on user behavior should be negligible).
- This introduces the InteractionObject proto change for math expressions and equations (which is just a string that's parsed during classifier matching time).
- This PR also introduces support for comparing ``MathExpression``s, ``ComparableOperation``s, and ``Polynomial``s that are used here & will be used in later PRs. The reason we can't just use standard proto equality checking is because ``Real``s can contain doubles and thus need an epsilon check. Very thorough tests were added to verify these equality operations separately from where they're used.
- This PR introduces exemptions for 4 test suites to enable parameterization. In all of these cases, effectively a single behavior (or two closely related behaviors) are being tested across a bunch of examples which is a really good fit for parameterization.

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
N/A -- This is introducing a new classifier, but it isn't yet being hooked up.

Commit history:

* Copy proto-based changes from #2173.

* Introduce math.proto & refactor math extensions.

Much of this is copied from #2173.

* Migrate tests & remove unneeded prefix.

* Add needed newline.

* Some needed Fraction changes.

* Introduce math expression + equation protos.

Also adds testing libraries for both + fractions & reals (new
structure).

Most of this is copied from #2173.

* Add protos + testing lib for commutative exprs.

* Add protos & test libs for polynomials.

* Lint fix.

* Lint fixes.

* Add math tokenizer + utility & tests.

This is mostly copied from #2173.

* Add math expression/equation parsing support.

This includes full error detection, and specific test suites for each
parsing case. Much revisement is needed in the tests, and some
additional issues may yet need to be fixed in the parser and/or
error-detection logic.

This is copied from #2173 with revisement & reduction since it's part of
a multi-PR split.

* Add exp evaluation & LaTeX conversion support.

This is mainly copied from #2173.

* Remove unneeded comment lines.

* Add expr->comparable operation list conv support.

This enables the ability to compare two expressions such that operation
associativity and commutativity is considered (i.e. items can be
rearranged using those rules without breaking expression equality).

This is mostly copied from #2173.

* Add support for expression->polynomial conversion.

This is mostly copied from #2173.

* Add NumericExpressionInput classifiers.

This doesn't hook them up to the application or tests yet (that will
happen in a later PR).

* Add missing annotation for new interaction.

* Fix provider name.

* Fix broken test post-refactor.

* Add reasonable import for abs().

* Fix equals errors for equations.

This splits the current error into two: one for no equals being present
(& adds it), and one for too many equals. This better supports the UI
errors that need to be displayed to the user in these cases.

* Fix rational^rational powers.

This generifies the sqrt algorithm to support n-roots so that rationals
raised by rationals can actually work & retain the rational (in cases
where the root can actually be taken).

* Ensure rational terms reduce to ints.

This ensures cases like 8/1 become just '8' coefficients rather than
staying as an irrational (for simplification).

* Add partial equivalence checking for new rules.

This ensures irrational constants don't fail to compare when computed
due to rounding inconsistencies in FPUs.

* Post-merge fix.

* Add regex check, docs, and resolve TODOs.

This also changes regex handling in the check to be more generic for
better flexibility when matching files.

* Lint fix.

* Fix failing static checks.

* Fix broken CI checks.

Adds missing KDocs, test file exemptions, and fixes the Gradle build.

* Lint fixes.

* Add docs & exempted tests.

* Remove blank line.

* Add docs + tests.

* Add parameterized test runner.

This commit introduces a new parameterized test runner that allows
proper combinations of parameterized & non-parameterized tests in the
same suite, and in a way that should work on both Robolectric & Espresso
(though the latter isn't currently verified).

Further, this commit also introduces a TokenSubject that will be used
more explicitly by the follow-up commit for verifying MathTokenizer.

* Add & update tests.

This introduces tests for PeekableIterator, and reimplements all of
MathTokenizer's tests to be more structured, thorough, and a bit more
maintainable (i.e. by leveraging parameterized tests).

* Lint fixes.

This includes a fix for 'fun interface' not working with ktlint (see #4122).

* Remove internals that broke things.

* Add regex exemptions.

* Post-merge fix.

* Add error tests.

These tests are more or less comprehensive based on existing tests and
some new ideas. All errors are now covered by MathExpressionParserTest.

Error ordering is not tested.

A new Truth subject was added for easier testing, as well (for
MathParsingError).

* Finish algebraic equation tests.

* Reimplement numeric expression tests.

This is almost a full replacement. The new tests are more structured and
intentional to cover key high-level concepts. More tests may be added in
the future, but this is a sensible initial test offering.

This also updates MathExpressionSubject to support checking specifically
for implicit multiplication (and it's now required for such cases since
explicit is otherwise assumed).

* Finish algebraic expression tests.

These largely rely on numeric expression tests (since they focus on
verifying specific variable scenarios).

* Add missing tests for better coverage.

* Add KDocs & test exemptions.

* Lint fixes.

* Remove temporary TODOs.

* Add tests.

* Split StringToFractionParser.

This is a temporary change that will be finished upstream (since there's
an earlier PR that's a better fit for this change).

* Address reviewer comments + other stuff.

This also fixes a typo and incorrectly ordered exemptions list I noticed
during development of downstream PRs.

* Move StringExtensions & fraction parsing.

This splits fraction parsing between UI & utility components.

* Address reviewer comments.

* Alphabetize test exemptions.

* Fix typo & add regex check.

The new regex check makes it so that all parameterized testing can be
more easily tracked by the Android TL.

* Add missing KDocs.

* Post-merge cleanups.

Also, fix text file exemption ordering.

* Add new test for negation with math symbol.

* Post-merge fixes.

* Add KDocs.

Also, add new regex exemption for new parameterized tests in this
branch.

* Refactor & simplify real ext impl.

Also, fix/clarify some KDocs.

* Lint fixes.

* Simplify operation list converter a lot.

This inlines three recursive operations to be done during the actual
computation to simplify the overall converter complexity (and to make
determining the test matrix easier).

* Prepare for new tests.

* Remove the ComparableOperationList wrapper.

* Change parameterized method delimiter.

* Use utility directly in test.

* Post-merge fixes.

This adjusts for the removal of ComparableOperationList (i.e. no wrapper
proto).

* Add first round of tests.

This includes fixes to the converter itself as it wasn't distributing
both product inversions and negation correctly in several cases. Tests
should now be covering these cases.

* Finish initial test suite.

Still needs to be cleaned up, but after converter refactoring attempts.

* Simplify operation sorting comparators.

* Remove old tests.

* Add remaining missing tests.

* KDocs & test exemption.

* Renames & lint fixes.

* Post-merge fixes.

* Add tests.

* KDocs + exemptions.

Also, clean up polynomial sorting.

* Lint fixes.

* Post-merge fixes.

Also, mark methods/classes that need tests.

* Add extension tests.

* Add classifier tests.

* Use more intentional epsilons for float comparing.

* Treat en-dash as a subtraction symbol.

* Add explicit platform selection for paramerized.

This adds explicit platform selection support rather than it being
automatic based on deps. While less flexible for shared tests, this
offers better control for tests that don't want to to use Robolectric
for local tests.

This also adds a JUnit-only test runner, and updates MathTokenizerTest
to use it (which led to an almost 40x decrease in runtime).

* Exemption fixes.

Also, fix name for the AndroidJUnit4 runner.

* Remove failing test.

* Fix unary expression precedence.

Also, use ParameterizedJunitTestRunner for MathExpressionParserTest.

* Fixes & add more test cases.

* Post-merge fixes & test changes.

Also, update RealExtensionsTest to use the faster JUnit runner.

* Use utility directly in LaTeX tests.

* Post-merge fixes.

Also, update ExpressionToComparableOperationConverterTest to use the
fast JUnit-only runner.

* Post-merge fixes.

Also, update PolynomialExtensionsTest to use fast JUnit-only runner.

* Post-merge fixes.

Also, update float interval per new tests.

* Lint & other check fixes.

* Replace deprecated term.

* Fix test on Gradle.

* Address reviewer comment.

Clarifies the documentation in the test runner around parameter
injection.

* Fix broken build.

* Fix broken build post-merge.

* Fix broken post-merge classifier.

* Post-merge fix.

* More post-merge fixes.

* Fix TODO comment.
BenHenning added a commit that referenced this issue Mar 26, 2022
## Explanation
Fix part of #4044

Introduce all domain classifiers for the AlgebraicExpressionInput interaction.

This PR has the same classifiers and behaviors as described in #4057 except these classifiers support variables. These classifiers' tests are also being exempted for parameterization so that they can more easily implement the tests defined in the technical spec test matrix.

Beyond the classifiers, this PR also introduces a new ``ClassificationContext`` data class which now encapsulates ``WrittenTranslationContext`` and adds the interaction customization arguments (since there's an argument that affects both AlgebraicExpressionInput and MathEquationInput--customOskLetters which specifies variables allowed for the answers). This class unfortunately leads to broad changes due to all classifiers being impacted. Further, the data class is being exempted from requiring a test since it isn't needed.

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
N/A -- This is introducing a new classifier, but it isn't yet being hooked up.

Commit history:

* Add support for expression->polynomial conversion.

This is mostly copied from #2173.

* Add NumericExpressionInput classifiers.

This doesn't hook them up to the application or tests yet (that will
happen in a later PR).

* Introduce ClassificationContext.

This refactor introduces support for passing customization arguments
down to classifiers (which is needed for algebraic expression input).

* Add classifiers for AlgebraicExpressionInput.

* Add missing annotation for new interaction.

* Add missing annotation for new interaction.

* Lint fixes.

* Fix provider name.

* Fix provider name.

* Fix broken test post-refactor.

* Add reasonable import for abs().

* Fix equals errors for equations.

This splits the current error into two: one for no equals being present
(& adds it), and one for too many equals. This better supports the UI
errors that need to be displayed to the user in these cases.

* Fix rational^rational powers.

This generifies the sqrt algorithm to support n-roots so that rationals
raised by rationals can actually work & retain the rational (in cases
where the root can actually be taken).

* Ensure rational terms reduce to ints.

This ensures cases like 8/1 become just '8' coefficients rather than
staying as an irrational (for simplification).

* Add partial equivalence checking for new rules.

This ensures irrational constants don't fail to compare when computed
due to rounding inconsistencies in FPUs.

* Add partial constant checking for new rules.

* Post-merge fix.

* Add regex check, docs, and resolve TODOs.

This also changes regex handling in the check to be more generic for
better flexibility when matching files.

* Lint fix.

* Fix failing static checks.

* Fix broken CI checks.

Adds missing KDocs, test file exemptions, and fixes the Gradle build.

* Lint fixes.

* Add docs & exempted tests.

* Remove blank line.

* Add docs + tests.

* Add parameterized test runner.

This commit introduces a new parameterized test runner that allows
proper combinations of parameterized & non-parameterized tests in the
same suite, and in a way that should work on both Robolectric & Espresso
(though the latter isn't currently verified).

Further, this commit also introduces a TokenSubject that will be used
more explicitly by the follow-up commit for verifying MathTokenizer.

* Add & update tests.

This introduces tests for PeekableIterator, and reimplements all of
MathTokenizer's tests to be more structured, thorough, and a bit more
maintainable (i.e. by leveraging parameterized tests).

* Lint fixes.

This includes a fix for 'fun interface' not working with ktlint (see #4122).

* Remove internals that broke things.

* Add regex exemptions.

* Post-merge fix.

* Add error tests.

These tests are more or less comprehensive based on existing tests and
some new ideas. All errors are now covered by MathExpressionParserTest.

Error ordering is not tested.

A new Truth subject was added for easier testing, as well (for
MathParsingError).

* Finish algebraic equation tests.

* Reimplement numeric expression tests.

This is almost a full replacement. The new tests are more structured and
intentional to cover key high-level concepts. More tests may be added in
the future, but this is a sensible initial test offering.

This also updates MathExpressionSubject to support checking specifically
for implicit multiplication (and it's now required for such cases since
explicit is otherwise assumed).

* Finish algebraic expression tests.

These largely rely on numeric expression tests (since they focus on
verifying specific variable scenarios).

* Add missing tests for better coverage.

* Add KDocs & test exemptions.

* Lint fixes.

* Remove temporary TODOs.

* Add tests.

* Split StringToFractionParser.

This is a temporary change that will be finished upstream (since there's
an earlier PR that's a better fit for this change).

* Address reviewer comments + other stuff.

This also fixes a typo and incorrectly ordered exemptions list I noticed
during development of downstream PRs.

* Move StringExtensions & fraction parsing.

This splits fraction parsing between UI & utility components.

* Address reviewer comments.

* Alphabetize test exemptions.

* Fix typo & add regex check.

The new regex check makes it so that all parameterized testing can be
more easily tracked by the Android TL.

* Add missing KDocs.

* Post-merge cleanups.

Also, fix text file exemption ordering.

* Add new test for negation with math symbol.

* Post-merge fixes.

* Add KDocs.

Also, add new regex exemption for new parameterized tests in this
branch.

* Refactor & simplify real ext impl.

Also, fix/clarify some KDocs.

* Lint fixes.

* Simplify operation list converter a lot.

This inlines three recursive operations to be done during the actual
computation to simplify the overall converter complexity (and to make
determining the test matrix easier).

* Prepare for new tests.

* Remove the ComparableOperationList wrapper.

* Change parameterized method delimiter.

* Use utility directly in test.

* Post-merge fixes.

This adjusts for the removal of ComparableOperationList (i.e. no wrapper
proto).

* Add first round of tests.

This includes fixes to the converter itself as it wasn't distributing
both product inversions and negation correctly in several cases. Tests
should now be covering these cases.

* Finish initial test suite.

Still needs to be cleaned up, but after converter refactoring attempts.

* Simplify operation sorting comparators.

* Remove old tests.

* Add remaining missing tests.

* KDocs & test exemption.

* Renames & lint fixes.

* Post-merge fixes.

* Add tests.

* KDocs + exemptions.

Also, clean up polynomial sorting.

* Lint fixes.

* Post-merge fixes.

Also, mark methods/classes that need tests.

* Add extension tests.

* Add classifier tests.

* Use more intentional epsilons for float comparing.

* Treat en-dash as a subtraction symbol.

* Add explicit platform selection for paramerized.

This adds explicit platform selection support rather than it being
automatic based on deps. While less flexible for shared tests, this
offers better control for tests that don't want to to use Robolectric
for local tests.

This also adds a JUnit-only test runner, and updates MathTokenizerTest
to use it (which led to an almost 40x decrease in runtime).

* Exemption fixes.

Also, fix name for the AndroidJUnit4 runner.

* Remove failing test.

* Fix unary expression precedence.

Also, use ParameterizedJunitTestRunner for MathExpressionParserTest.

* Fixes & add more test cases.

* Post-merge fixes & test changes.

Also, update RealExtensionsTest to use the faster JUnit runner.

* Use utility directly in LaTeX tests.

* Post-merge fixes.

Also, update ExpressionToComparableOperationConverterTest to use the
fast JUnit-only runner.

* Post-merge fixes.

Also, update PolynomialExtensionsTest to use fast JUnit-only runner.

* Post-merge fixes.

Also, update float interval per new tests.

* Lint & other check fixes.

* Replace deprecated term.

* Post-merge fixes.

* Add full test suites for alg exp classifiers.

* Lint & static check fixes.

* Fix test on Gradle.

* Fix test for Gradle.

* Address reviewer comment.

Clarifies the documentation in the test runner around parameter
injection.

* Fix broken build.

* Fix broken build post-merge.

* Fix broken post-merge classifier.

* Address reviewer comment.

* Post-merge build fixes.

* Post-merge fix.

* More post-merge fixes.

* Fix TODO comment.
BenHenning added a commit that referenced this issue Mar 26, 2022
## Explanation
Fix part of #4044

Introduce all domain classifiers for the MathEquationInput interaction.

This PR has the same classifiers and behaviors as described in #4057 except these classifiers support variables and must be formed into an equation. These classifiers' tests are also being exempted for parameterization so that they can more easily implement the tests defined in the technical spec test matrix.

This PR includes a change to the parameterized test runner to add support for variables including the '=' sign (since it was before splitting on '=' rather than treating it as a single delimiter).

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
N/A -- This is introducing a new classifier, but it isn't yet being hooked up.

Commit history:

* Add reasonable import for abs().

* Fix equals errors for equations.

This splits the current error into two: one for no equals being present
(& adds it), and one for too many equals. This better supports the UI
errors that need to be displayed to the user in these cases.

* Fix rational^rational powers.

This generifies the sqrt algorithm to support n-roots so that rationals
raised by rationals can actually work & retain the rational (in cases
where the root can actually be taken).

* Ensure rational terms reduce to ints.

This ensures cases like 8/1 become just '8' coefficients rather than
staying as an irrational (for simplification).

* Add partial equivalence checking for new rules.

This ensures irrational constants don't fail to compare when computed
due to rounding inconsistencies in FPUs.

* Add partial constant checking for new rules.

* Add partial equivalence checking for new rules.

* Post-merge fix.

* Add regex check, docs, and resolve TODOs.

This also changes regex handling in the check to be more generic for
better flexibility when matching files.

* Lint fix.

* Fix failing static checks.

* Fix broken CI checks.

Adds missing KDocs, test file exemptions, and fixes the Gradle build.

* Lint fixes.

* Add docs & exempted tests.

* Remove blank line.

* Add docs + tests.

* Add parameterized test runner.

This commit introduces a new parameterized test runner that allows
proper combinations of parameterized & non-parameterized tests in the
same suite, and in a way that should work on both Robolectric & Espresso
(though the latter isn't currently verified).

Further, this commit also introduces a TokenSubject that will be used
more explicitly by the follow-up commit for verifying MathTokenizer.

* Add & update tests.

This introduces tests for PeekableIterator, and reimplements all of
MathTokenizer's tests to be more structured, thorough, and a bit more
maintainable (i.e. by leveraging parameterized tests).

* Lint fixes.

This includes a fix for 'fun interface' not working with ktlint (see #4122).

* Remove internals that broke things.

* Add regex exemptions.

* Post-merge fix.

* Add error tests.

These tests are more or less comprehensive based on existing tests and
some new ideas. All errors are now covered by MathExpressionParserTest.

Error ordering is not tested.

A new Truth subject was added for easier testing, as well (for
MathParsingError).

* Finish algebraic equation tests.

* Reimplement numeric expression tests.

This is almost a full replacement. The new tests are more structured and
intentional to cover key high-level concepts. More tests may be added in
the future, but this is a sensible initial test offering.

This also updates MathExpressionSubject to support checking specifically
for implicit multiplication (and it's now required for such cases since
explicit is otherwise assumed).

* Finish algebraic expression tests.

These largely rely on numeric expression tests (since they focus on
verifying specific variable scenarios).

* Add missing tests for better coverage.

* Add KDocs & test exemptions.

* Lint fixes.

* Remove temporary TODOs.

* Add tests.

* Split StringToFractionParser.

This is a temporary change that will be finished upstream (since there's
an earlier PR that's a better fit for this change).

* Address reviewer comments + other stuff.

This also fixes a typo and incorrectly ordered exemptions list I noticed
during development of downstream PRs.

* Move StringExtensions & fraction parsing.

This splits fraction parsing between UI & utility components.

* Address reviewer comments.

* Alphabetize test exemptions.

* Fix typo & add regex check.

The new regex check makes it so that all parameterized testing can be
more easily tracked by the Android TL.

* Add missing KDocs.

* Post-merge cleanups.

Also, fix text file exemption ordering.

* Add new test for negation with math symbol.

* Post-merge fixes.

* Add KDocs.

Also, add new regex exemption for new parameterized tests in this
branch.

* Refactor & simplify real ext impl.

Also, fix/clarify some KDocs.

* Lint fixes.

* Simplify operation list converter a lot.

This inlines three recursive operations to be done during the actual
computation to simplify the overall converter complexity (and to make
determining the test matrix easier).

* Prepare for new tests.

* Remove the ComparableOperationList wrapper.

* Change parameterized method delimiter.

* Use utility directly in test.

* Post-merge fixes.

This adjusts for the removal of ComparableOperationList (i.e. no wrapper
proto).

* Add first round of tests.

This includes fixes to the converter itself as it wasn't distributing
both product inversions and negation correctly in several cases. Tests
should now be covering these cases.

* Finish initial test suite.

Still needs to be cleaned up, but after converter refactoring attempts.

* Simplify operation sorting comparators.

* Remove old tests.

* Add remaining missing tests.

* KDocs & test exemption.

* Renames & lint fixes.

* Post-merge fixes.

* Add tests.

* KDocs + exemptions.

Also, clean up polynomial sorting.

* Lint fixes.

* Post-merge fixes.

Also, mark methods/classes that need tests.

* Add extension tests.

* Add classifier tests.

* Use more intentional epsilons for float comparing.

* Treat en-dash as a subtraction symbol.

* Add explicit platform selection for paramerized.

This adds explicit platform selection support rather than it being
automatic based on deps. While less flexible for shared tests, this
offers better control for tests that don't want to to use Robolectric
for local tests.

This also adds a JUnit-only test runner, and updates MathTokenizerTest
to use it (which led to an almost 40x decrease in runtime).

* Exemption fixes.

Also, fix name for the AndroidJUnit4 runner.

* Remove failing test.

* Fix unary expression precedence.

Also, use ParameterizedJunitTestRunner for MathExpressionParserTest.

* Fixes & add more test cases.

* Post-merge fixes & test changes.

Also, update RealExtensionsTest to use the faster JUnit runner.

* Use utility directly in LaTeX tests.

* Post-merge fixes.

Also, update ExpressionToComparableOperationConverterTest to use the
fast JUnit-only runner.

* Post-merge fixes.

Also, update PolynomialExtensionsTest to use fast JUnit-only runner.

* Post-merge fixes.

Also, update float interval per new tests.

* Lint & other check fixes.

* Replace deprecated term.

* Post-merge fixes.

* Add full test suites for alg exp classifiers.

* Lint & static check fixes.

* Fix test on Gradle.

* Fix test for Gradle.

* Add tests for math equations.

And, post-merge fixes.

* Static check & lint fixes.

* Address reviewer comment.

Clarifies the documentation in the test runner around parameter
injection.

* Fix broken build.

* Fix broken build post-merge.

* Fix broken post-merge classifier.

* Address reviewer comment.

* Post-merge build fixes.

* Post-merge build fixes for new classifiers.

* Post-merge fix.

* More post-merge fixes.

* Fix TODO comment.
BenHenning added a commit that referenced this issue Mar 27, 2022
## Explanation
Fix part of #4044

This PR enables all of the new classifiers introduced in #4057, #4058, and #4059 which comprise the three new interactions being added as part of the math expressions project.

This mainly entails adding the modules to ApplicationComponent, the instrumentation test-only ApplicationComponent, and all tests that require interactions. This doesn't actually enable an UI-related changes, or even enable support for loading explorations with these interactions yet. It just enables the domain classifiers for use in a future PR.

This PR also adds a Bazel target DrawableBindingAdaptersTest since it was missed in the PR in which the suite was introduced.

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
N/A -- While this hooks up the new classifiers, they can't yet be loaded by any explorations (hence there're no affected UIs due to this change, yet).

Commit history:

* Fix rational^rational powers.

This generifies the sqrt algorithm to support n-roots so that rationals
raised by rationals can actually work & retain the rational (in cases
where the root can actually be taken).

* Ensure rational terms reduce to ints.

This ensures cases like 8/1 become just '8' coefficients rather than
staying as an irrational (for simplification).

* Add partial equivalence checking for new rules.

This ensures irrational constants don't fail to compare when computed
due to rounding inconsistencies in FPUs.

* Add partial constant checking for new rules.

* Add partial equivalence checking for new rules.

* Post-merge fix.

* Add regex check, docs, and resolve TODOs.

This also changes regex handling in the check to be more generic for
better flexibility when matching files.

* Lint fix.

* Fix failing static checks.

* Fix broken CI checks.

Adds missing KDocs, test file exemptions, and fixes the Gradle build.

* Lint fixes.

* Add docs & exempted tests.

* Remove blank line.

* Add docs + tests.

* Add parameterized test runner.

This commit introduces a new parameterized test runner that allows
proper combinations of parameterized & non-parameterized tests in the
same suite, and in a way that should work on both Robolectric & Espresso
(though the latter isn't currently verified).

Further, this commit also introduces a TokenSubject that will be used
more explicitly by the follow-up commit for verifying MathTokenizer.

* Add & update tests.

This introduces tests for PeekableIterator, and reimplements all of
MathTokenizer's tests to be more structured, thorough, and a bit more
maintainable (i.e. by leveraging parameterized tests).

* Lint fixes.

This includes a fix for 'fun interface' not working with ktlint (see #4122).

* Remove internals that broke things.

* Add regex exemptions.

* Post-merge fix.

* Add error tests.

These tests are more or less comprehensive based on existing tests and
some new ideas. All errors are now covered by MathExpressionParserTest.

Error ordering is not tested.

A new Truth subject was added for easier testing, as well (for
MathParsingError).

* Finish algebraic equation tests.

* Reimplement numeric expression tests.

This is almost a full replacement. The new tests are more structured and
intentional to cover key high-level concepts. More tests may be added in
the future, but this is a sensible initial test offering.

This also updates MathExpressionSubject to support checking specifically
for implicit multiplication (and it's now required for such cases since
explicit is otherwise assumed).

* Finish algebraic expression tests.

These largely rely on numeric expression tests (since they focus on
verifying specific variable scenarios).

* Add missing tests for better coverage.

* Add KDocs & test exemptions.

* Lint fixes.

* Remove temporary TODOs.

* Add tests.

* Split StringToFractionParser.

This is a temporary change that will be finished upstream (since there's
an earlier PR that's a better fit for this change).

* Address reviewer comments + other stuff.

This also fixes a typo and incorrectly ordered exemptions list I noticed
during development of downstream PRs.

* Move StringExtensions & fraction parsing.

This splits fraction parsing between UI & utility components.

* Address reviewer comments.

* Alphabetize test exemptions.

* Fix typo & add regex check.

The new regex check makes it so that all parameterized testing can be
more easily tracked by the Android TL.

* Add missing KDocs.

* Post-merge cleanups.

Also, fix text file exemption ordering.

* Add new test for negation with math symbol.

* Post-merge fixes.

* Add KDocs.

Also, add new regex exemption for new parameterized tests in this
branch.

* Refactor & simplify real ext impl.

Also, fix/clarify some KDocs.

* Lint fixes.

* Simplify operation list converter a lot.

This inlines three recursive operations to be done during the actual
computation to simplify the overall converter complexity (and to make
determining the test matrix easier).

* Prepare for new tests.

* Remove the ComparableOperationList wrapper.

* Change parameterized method delimiter.

* Use utility directly in test.

* Post-merge fixes.

This adjusts for the removal of ComparableOperationList (i.e. no wrapper
proto).

* Add first round of tests.

This includes fixes to the converter itself as it wasn't distributing
both product inversions and negation correctly in several cases. Tests
should now be covering these cases.

* Finish initial test suite.

Still needs to be cleaned up, but after converter refactoring attempts.

* Simplify operation sorting comparators.

* Remove old tests.

* Add remaining missing tests.

* KDocs & test exemption.

* Renames & lint fixes.

* Post-merge fixes.

* Add tests.

* KDocs + exemptions.

Also, clean up polynomial sorting.

* Lint fixes.

* Post-merge fixes.

Also, mark methods/classes that need tests.

* Add extension tests.

* Add classifier tests.

* Use more intentional epsilons for float comparing.

* Treat en-dash as a subtraction symbol.

* Add explicit platform selection for paramerized.

This adds explicit platform selection support rather than it being
automatic based on deps. While less flexible for shared tests, this
offers better control for tests that don't want to to use Robolectric
for local tests.

This also adds a JUnit-only test runner, and updates MathTokenizerTest
to use it (which led to an almost 40x decrease in runtime).

* Exemption fixes.

Also, fix name for the AndroidJUnit4 runner.

* Remove failing test.

* Fix unary expression precedence.

Also, use ParameterizedJunitTestRunner for MathExpressionParserTest.

* Fixes & add more test cases.

* Post-merge fixes & test changes.

Also, update RealExtensionsTest to use the faster JUnit runner.

* Use utility directly in LaTeX tests.

* Post-merge fixes.

Also, update ExpressionToComparableOperationConverterTest to use the
fast JUnit-only runner.

* Post-merge fixes.

Also, update PolynomialExtensionsTest to use fast JUnit-only runner.

* Post-merge fixes.

Also, update float interval per new tests.

* Lint & other check fixes.

* Replace deprecated term.

* Post-merge fixes.

* Add full test suites for alg exp classifiers.

* Lint & static check fixes.

* Fix test on Gradle.

* Fix test for Gradle.

* Add tests for math equations.

And, post-merge fixes.

* Static check & lint fixes.

* Post-merge fixes.

Verified CI checks & all unit tests are passing.

* Add missing test in Bazel, and fix it.

* Correct order for genrule.

* Address reviewer comment.

Clarifies the documentation in the test runner around parameter
injection.

* Fix broken build.

* Fix broken build post-merge.

* Fix broken post-merge classifier.

* Address reviewer comment.

* Post-merge build fixes.

* Post-merge build fixes for new classifiers.

* Post-merge build fixes.

* Add and fix missing test (was broken on Gradle).

* Post-merge fix.

* More post-merge fixes.

* Fix TODO comment.
BenHenning added a commit that referenced this issue Mar 27, 2022
…th expressions (#4063)

## Explanation
Fix part of #4044
Originally copied from #2173 when it was in proof-of-concept form

This PR introduces a utility for generation human-readable accessibility strings from math expressions and equations, per [this document](https://docs.google.com/document/d/1SkzAD4k7SWLp5_3L5WNxsnR79ATlOk8pz4irfE2ls-4/edit?usp=sharing). This is needed since the LaTeX and raw representations of expressions aren't guaranteed to be read correctly by screenreaders.

Broadly, this composes an sentence from fragments corresponding to each part of the expression. Currently, only English is supported since a custom routine is needed for each language. See the tests for ideas on the supported scenarios.

MathExpressionAccessibilityUtilTest is being exempted for parameterization since there's an advantage to covering a lot of different conditions for the same base behaviors. This is slightly abusing parameterization since the tests including expected outcomes, but it seems sensible given how the tests are being organized.

Further, TestActivity was updated so that MathExpressionAccessibilityUtil can be tested with a display locale. The resource handler & display locale were updated to include long/double format methods since we can't rely on regular toString() methods since they'll introduce scientific notation (for doubles) or potentially be treated as phone numbers (for longs). This formatting is localized base on the user's locale despite the generated strings always being in English.

Note that overall this is somewhat an accessibility stop-gap since some of the strings are quite long and likely difficult to fully contextualize for a screenreader user. Ideally, we'd introduce some sort of navigation flow longer term so that the learner can read out specific parts of the expression in a more pedagogically friendly way. This should definitely be possible to build on top of the existing infrastructure being introduced in this & past PRs.

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
This isn't hooked up to any UIs yet, and a later PR will demonstrate the end-to-end flow (including accessibility). See MathExpressionAccessibilityUtilTest for example test cases to get an idea on possible accessibility strings.

Commit history:

* Lint fix.

* Fix failing static checks.

* Fix broken CI checks.

Adds missing KDocs, test file exemptions, and fixes the Gradle build.

* Lint fixes.

* Add docs & exempted tests.

* Remove blank line.

* Add docs + tests.

* Add parameterized test runner.

This commit introduces a new parameterized test runner that allows
proper combinations of parameterized & non-parameterized tests in the
same suite, and in a way that should work on both Robolectric & Espresso
(though the latter isn't currently verified).

Further, this commit also introduces a TokenSubject that will be used
more explicitly by the follow-up commit for verifying MathTokenizer.

* Add & update tests.

This introduces tests for PeekableIterator, and reimplements all of
MathTokenizer's tests to be more structured, thorough, and a bit more
maintainable (i.e. by leveraging parameterized tests).

* Lint fixes.

This includes a fix for 'fun interface' not working with ktlint (see #4122).

* Remove internals that broke things.

* Add regex exemptions.

* Post-merge fix.

* Add error tests.

These tests are more or less comprehensive based on existing tests and
some new ideas. All errors are now covered by MathExpressionParserTest.

Error ordering is not tested.

A new Truth subject was added for easier testing, as well (for
MathParsingError).

* Finish algebraic equation tests.

* Reimplement numeric expression tests.

This is almost a full replacement. The new tests are more structured and
intentional to cover key high-level concepts. More tests may be added in
the future, but this is a sensible initial test offering.

This also updates MathExpressionSubject to support checking specifically
for implicit multiplication (and it's now required for such cases since
explicit is otherwise assumed).

* Finish algebraic expression tests.

These largely rely on numeric expression tests (since they focus on
verifying specific variable scenarios).

* Add missing tests for better coverage.

* Add KDocs & test exemptions.

* Lint fixes.

* Remove temporary TODOs.

* Add tests.

* Split StringToFractionParser.

This is a temporary change that will be finished upstream (since there's
an earlier PR that's a better fit for this change).

* Address reviewer comments + other stuff.

This also fixes a typo and incorrectly ordered exemptions list I noticed
during development of downstream PRs.

* Move StringExtensions & fraction parsing.

This splits fraction parsing between UI & utility components.

* Address reviewer comments.

* Alphabetize test exemptions.

* Fix typo & add regex check.

The new regex check makes it so that all parameterized testing can be
more easily tracked by the Android TL.

* Add missing KDocs.

* Post-merge cleanups.

Also, fix text file exemption ordering.

* Add new test for negation with math symbol.

* Post-merge fixes.

* Add KDocs.

Also, add new regex exemption for new parameterized tests in this
branch.

* Refactor & simplify real ext impl.

Also, fix/clarify some KDocs.

* Lint fixes.

* Simplify operation list converter a lot.

This inlines three recursive operations to be done during the actual
computation to simplify the overall converter complexity (and to make
determining the test matrix easier).

* Prepare for new tests.

* Remove the ComparableOperationList wrapper.

* Change parameterized method delimiter.

* Use utility directly in test.

* Post-merge fixes.

This adjusts for the removal of ComparableOperationList (i.e. no wrapper
proto).

* Add first round of tests.

This includes fixes to the converter itself as it wasn't distributing
both product inversions and negation correctly in several cases. Tests
should now be covering these cases.

* Finish initial test suite.

Still needs to be cleaned up, but after converter refactoring attempts.

* Simplify operation sorting comparators.

* Remove old tests.

* Add remaining missing tests.

* KDocs & test exemption.

* Renames & lint fixes.

* Post-merge fixes.

* Add tests.

* KDocs + exemptions.

Also, clean up polynomial sorting.

* Lint fixes.

* Post-merge fixes.

Also, mark methods/classes that need tests.

* Add extension tests.

* Add classifier tests.

* Use more intentional epsilons for float comparing.

* Treat en-dash as a subtraction symbol.

* Add explicit platform selection for paramerized.

This adds explicit platform selection support rather than it being
automatic based on deps. While less flexible for shared tests, this
offers better control for tests that don't want to to use Robolectric
for local tests.

This also adds a JUnit-only test runner, and updates MathTokenizerTest
to use it (which led to an almost 40x decrease in runtime).

* Exemption fixes.

Also, fix name for the AndroidJUnit4 runner.

* Remove failing test.

* Fix unary expression precedence.

Also, use ParameterizedJunitTestRunner for MathExpressionParserTest.

* Fixes & add more test cases.

* Post-merge fixes & test changes.

Also, update RealExtensionsTest to use the faster JUnit runner.

* Use utility directly in LaTeX tests.

* Post-merge fixes.

Also, update ExpressionToComparableOperationConverterTest to use the
fast JUnit-only runner.

* Post-merge fixes.

Also, update PolynomialExtensionsTest to use fast JUnit-only runner.

* Post-merge fixes.

Also, update float interval per new tests.

* Lint & other check fixes.

* Replace deprecated term.

* Post-merge fixes.

* Add full test suites for alg exp classifiers.

* Lint & static check fixes.

* Fix test on Gradle.

* Fix test for Gradle.

* Add tests for math equations.

And, post-merge fixes.

* Static check & lint fixes.

* Post-merge fixes.

Verified CI checks & all unit tests are passing.

* Split up tests.

Also, adds dedicated BUILD.bazel file for new test.

* Add missing test in Bazel, and fix it.

* Correct order for genrule.

* Add full test suite.

* Clean up + KDocs + exemption.

* Lint fixes.

* Address reviewer comment.

Clarifies the documentation in the test runner around parameter
injection.

* Fix broken build.

* Fix broken build post-merge.

* Fix broken post-merge classifier.

* Address reviewer comment.

* Post-merge build fixes.

* Post-merge build fixes for new classifiers.

* Post-merge build fixes.

* Correct reference document link.

* Add and fix missing test (was broken on Gradle).

* Post-merge fix.

* More post-merge fixes.

* Fix TODO comment.
BenHenning added a commit that referenced this issue Mar 27, 2022
…4068)

## Explanation
Fix part of #4044

This PR introduces support for rendering raw LaTeX in the app using the KotliTeX (https://github.com/karino2/kotlitex) library which is necessary since users may input arbitrary math expressions that need to be pretty-rendered (using the math expression -> LaTeX generation support added in #4054).

This implementation is heavily based on #3194 and leverages a new fork (https://github.com/oppia/kotlitex) of KotliTeX based on @anandwana001's fork.

In order to keep the implementation simpler, this hangs the functionality onto the existing custom Oppia noninteractive math tag except it only renders LaTeX if there's no SVG filename to load (which will be the case for dynamically generated LaTeX). This approach has the advantage of the app always being able to fall back to LaTeX rendering in the unlikely scenario where content has a math tag without an SVG URL being present (or where the SVG fails to load which could allow potential fault tolerance when downloads support is fully implemented). These changes required several changes to KotliTeX itself which are outlined in more detail below.

Furthermore, it was observed that the rendering was noticeably slow on a Nexus 5X for repeated renders (think navigation back and forth between views with rendered LaTeX, or expanding/collapsing multiple answers). This didn't seem acceptable, so this PR also introduces a platform parameter for utilizing Glide to pre-render the LaTeX into a PNG bitmap so that it can cache it. This is not only more performant for repeated exposure and faster to render, but it also offloads the expensive rendering step to a background thread rather than blocking the main thread (see the videos in the UI section below for a before-and-after comparison).

### Details on changes to KotliTeX
A custom fork of KotliTeX (https://github.com/oppia/kotlitex) was needed to include the following changes:
- Support to be built on Bazel
- Lower the min sdk required by the library to match the app's
- A slight hack to ensure KotliTeX doesn't auto-fail for larger square roots (the library actually doesn't support them, so this workaround just stretches the smaller square root rather than leading to error text)
- A hack to expose the outer bounds of drawing requires by KotliTeX for its span (see caching details below for why)

Note that KotliTeX only supports a subset of LaTeX, but it seems to support everything the app needs for now (and it should be extensible by porting other portions of KaTeX as needed).

### Details on the rendering caching & positioning challenges
At a high-level, the approach to caching is to render the KotliTeX drawable to a bitmap that can be saved on-disk in a format that Glide can easily load on its own (PNG). While it's not ideal to perform the extra PNG conversion step, it's much more disk-friendly (and in the future, we could look into offloading just this part to another thread if needed). Each individual load is keyed on the exact raw LaTeX, the line height (up to 2 digits), and whether it's inline or block rendered (see later in this section for the distinction). If all three of these properties match, there should be a Glide cache hit and no need to re-render the LaTeX.

A couple of issues arose when trying to implement this:
- Despite us being able to expose the internal KotliTeX drawable (which wasn't by default), I was having a lot of difficulty getting the text alignment correct. Instead, it was much easier to rely on Android's internal text rendering by just rendering the span in a similar way to what TextView does.
- While the above worked, it led to another problem: figuring out how large the bitmap should be. The actual drawable bounds that KotliTeX computes is an underestimate (its ascent and descent go outside of these bounds), so an additional estimation is needed.
- Because we're now drawing the LaTeX as a bitmap in an ImageSpan, the text aligns the image either with its bottom line or its baseline. This introduces a vertical center alignment issue as shown below (the image with the red text shows one attempt at fixing this by eliminating unnecessary upper space by using a combination of vertical translation and negative bounds). Due to this quirk, we can't actually have whitespace below the text without modifying the text's font metrics (specifically the bottom/top/ascent/descent properties). #4170 is tracking this work. Note also that this is really only an issue for inline rendering which we're unlikely to use in the medium-term since all user-submitted answers will be rendered in block style which seems to work well.

Caching is demonstrably much more performant for large amounts of repetition, and it doesn't result in UI lag like direct rendering does since all rendering is offloaded to a background thread (see the videos below for the comparison).

### Details on testing
The tests are mainly focused around changes to the tag handler since the Glide portions can't be tested, and neither can the rendering routines (hence the exemptions). The fake image loader was updated to include support for the math-based rendering load requests.

An aside change was made to make the Robolectric library test-only (it's something that was noticed in passing during development, and isn't directly needed by this PR; it's just a nice-to-have).

The new interfaces are exempted from tests since they have no logic to verify (their implementations are generated by Dagger and verified at compile time). Furthermore, I didn't add any tests for the changes to the compute affected tests script since it's a bit difficult to test, and already quite an edge case. Please let me know if you have any concerns with this.

### Details on the race condition fix
KotliTeX seems to initialized shared state when its parsing the LaTeX, and this with multiple Glide threads can cause a race condition where the LaTeX fails to render. This has been fixed by leveraging coroutines (where we still try to maximize efficiency and parallelization), but it required exposing new injector pathways for the coroutine dispatchers and ``ConsoleLogger``.

### Details on the ComputeAffectedTests fix
This PR exposes an issue with the ComputeAffectedTests script wherein it can sometimes exceed the system's maximum argument length.  This is being worked around by partitioning values for longer Bazel queries into multiple calls and concatenating the results. I'm fairly certain this is functionally equivalent to the current implementation, and for the vast majority of future PRs only one partition will ever be used. There doesn't seem to be an alternative possible since this is a platform-specific Kernel restriction.

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
For the most part, this PR doesn't actually affect users yet since this functionality isn't exposed (all LaTeX currently rendered in the app will utilize the existing SVG pipeline). The user-facing UI changes will be thoroughly documented in the PR that's planned for after this one since it'll be integrating all prior PRs along with the changes introduced here.

That being said, the "What is a Fraction?" test revision card has been updated to include rendered LaTeX text in order to demonstrate the functionality more closely. The following is how rendering looks (with bitmap caching):

![cached_latex_rendering](https://user-images.githubusercontent.com/12983742/153363770-ad1ad20a-c06e-4dae-a9ce-4253285f1adb.png)

Note that the above, when compared with non-cached rendering, demonstrates where some of the spacing issues mentioned above come in:

![direct_render_latex](https://user-images.githubusercontent.com/12983742/153363841-8318f520-0788-449f-9ea8-276c8898981e.png)

Here's the image showing the bounds when trying to force the drawable downward:

![difficult_alignment_inline_math](https://user-images.githubusercontent.com/12983742/153363908-568dca88-6c67-4b11-98f6-fed0e17d56b8.png)

This video shows how long a bunch of LaTeX expressions takes to render with direct rendering:

https://user-images.githubusercontent.com/12983742/153363983-4ff99db5-52da-4360-9630-cc98397dc07b.mp4

Versus the performance of caching for the same experience (note this occurred with a completely clean local app cache):

https://user-images.githubusercontent.com/12983742/153364178-3066d6ce-2a85-45c8-83fe-c0378a786023.mp4

### Notes on rendering approach

Note that rendering during cache time involves a three step process:
1. Approximate the outer bounding volume by tracking all draw calls needed for a given LaTeX expression (these bounds are used to create the bitmap that will store the cacheable render)
2. Render the LaTeX to a canvas with dimensions larger than the approximated bounding (since it's possible for the rendering to exceed these estimates--see notes below)
3. Crop the bitmap to the smallest bounds needed to encapsulate all of the pixels

(1) is needed because KotliTeX actually incorrect computes bounds (it sometimes over and underestimates). This isn't as apparent when directly rendering because Android will handle text expanding past the space KotliTeX expects since it's rendered in-line. I spent some time trying to debug this within KotliTeX but I couldn't make much headway. It may be worth looking into this in the future to see if we can reliably calculate the bounds within KotliTeX (since it will likely be much more performant than the solution used here). Seeing KotliTeX's debug lines makes this clearer (where it thinks the bounds are relative to glyphs; note that this screenshot is using rendering without caching):

![kotlitex_wrong_bounds](https://user-images.githubusercontent.com/12983742/155447759-5e21ba68-8cec-496a-843b-db0109278625.png)

(2) is needed because the methodology used for (1) is inexact. It's actually really difficult to estimate exactly where the final glyph pixels will be rendered for a given glyph since KotliTeX is directly handling positioning (rather than allowing Android to compute x/y coordinates based on nearby characters to account for tracking, kerning, etc.). The dimension approximation for (1) is much closer to correct than KotliTeX's internal bounds calculations, however it tends to slightly underapproximate the bounds. To counteract this, the app uses a bitmap that's 2x what's actually estimated to be needed and offsets rendering into that bitmap such that 50% of the estimated size is a margin around the drawing area. This provides a large buffer to account for incorrect bounds calculations. The app then crops this to the edges of filled pixels so that the smallest bitmap is used to represent the final rendered equation. While this uses a lot more memory than should actually be required for rendering, it guarantees perfect results. See:

Before (notice the over-sized LaTeX, and that some expressions are partially cut-off from the right and above, or have extra space below):

| ![latex_rendering_cutoff_problem1](https://user-images.githubusercontent.com/12983742/155447426-0662f705-7ec3-4422-a45f-f36e42948968.png) | ![latex_rendering_cutoff_problem2](https://user-images.githubusercontent.com/12983742/155447432-35ba68d6-b05b-4a91-82ce-7af03a47851b.png) |
|------|------|

After (with size estimation & croppingl this also includes a fix in UrlImageParser to ensure that LaTeX images are never auto-resized when rendering in block mode since it distorts them, and that they are properly centered):

| ![latex_rendering_cutoff_fix1](https://user-images.githubusercontent.com/12983742/155447617-86eb68bc-9c8b-4483-b630-bba14b2c0cb7.png) | ![latex_rendering_cutoff_fix2](https://user-images.githubusercontent.com/12983742/155447622-fea0e52f-0dc1-4cac-b673-6a102fe5f430.png) |
|------|------|


Commit history:

* Add KDocs & test exemptions.

* Lint fixes.

* Remove temporary TODOs.

* Add tests.

* Split StringToFractionParser.

This is a temporary change that will be finished upstream (since there's
an earlier PR that's a better fit for this change).

* Address reviewer comments + other stuff.

This also fixes a typo and incorrectly ordered exemptions list I noticed
during development of downstream PRs.

* Move StringExtensions & fraction parsing.

This splits fraction parsing between UI & utility components.

* Address reviewer comments.

* Alphabetize test exemptions.

* Fix typo & add regex check.

The new regex check makes it so that all parameterized testing can be
more easily tracked by the Android TL.

* Add missing KDocs.

* Post-merge cleanups.

Also, fix text file exemption ordering.

* Add new test for negation with math symbol.

* Post-merge fixes.

* Add KDocs.

Also, add new regex exemption for new parameterized tests in this
branch.

* Refactor & simplify real ext impl.

Also, fix/clarify some KDocs.

* Lint fixes.

* Simplify operation list converter a lot.

This inlines three recursive operations to be done during the actual
computation to simplify the overall converter complexity (and to make
determining the test matrix easier).

* Prepare for new tests.

* Remove the ComparableOperationList wrapper.

* Change parameterized method delimiter.

* Use utility directly in test.

* Post-merge fixes.

This adjusts for the removal of ComparableOperationList (i.e. no wrapper
proto).

* Add first round of tests.

This includes fixes to the converter itself as it wasn't distributing
both product inversions and negation correctly in several cases. Tests
should now be covering these cases.

* Finish initial test suite.

Still needs to be cleaned up, but after converter refactoring attempts.

* Simplify operation sorting comparators.

* Remove old tests.

* Add remaining missing tests.

* KDocs & test exemption.

* Renames & lint fixes.

* Post-merge fixes.

* Add tests.

* KDocs + exemptions.

Also, clean up polynomial sorting.

* Lint fixes.

* Post-merge fixes.

Also, mark methods/classes that need tests.

* Add extension tests.

* Add classifier tests.

* Use more intentional epsilons for float comparing.

* Treat en-dash as a subtraction symbol.

* Add explicit platform selection for paramerized.

This adds explicit platform selection support rather than it being
automatic based on deps. While less flexible for shared tests, this
offers better control for tests that don't want to to use Robolectric
for local tests.

This also adds a JUnit-only test runner, and updates MathTokenizerTest
to use it (which led to an almost 40x decrease in runtime).

* Exemption fixes.

Also, fix name for the AndroidJUnit4 runner.

* Remove failing test.

* Fix unary expression precedence.

Also, use ParameterizedJunitTestRunner for MathExpressionParserTest.

* Fixes & add more test cases.

* Post-merge fixes & test changes.

Also, update RealExtensionsTest to use the faster JUnit runner.

* Use utility directly in LaTeX tests.

* Post-merge fixes.

Also, update ExpressionToComparableOperationConverterTest to use the
fast JUnit-only runner.

* Post-merge fixes.

Also, update PolynomialExtensionsTest to use fast JUnit-only runner.

* Post-merge fixes.

Also, update float interval per new tests.

* Lint & other check fixes.

* Replace deprecated term.

* Post-merge fixes.

* Add full test suites for alg exp classifiers.

* Lint & static check fixes.

* Fix test on Gradle.

* Fix test for Gradle.

* Add tests for math equations.

And, post-merge fixes.

* Static check & lint fixes.

* Post-merge fixes.

Verified CI checks & all unit tests are passing.

* Split up tests.

Also, adds dedicated BUILD.bazel file for new test.

* Add missing test in Bazel, and fix it.

* Correct order for genrule.

* Add full test suite.

* Clean up + KDocs + exemption.

* Lint fixes.

* Post-merge fix.

* Cache KotliTeX renders.

Directly rendering LaTeX through KotliTeX is way too slow, so this
introduces a custom flow through Glide that computes a PNG for the LaTeX
on a background thread and then caches it as part of Glide's cache to
speed up re-renders of the LaTeX. We may need to manage/prune the cache
over time, but for now we'll just rely on Glide's internal behaviors.

This also documents some new tests that should be added, but it's not
comprehensive.

* Add tests, docs, and exemptions.

* Update to fixed version of KotliTeX.

The newer version correctly computes the bounds for rendered LaTeX.

* Lint fixes.

* Add new dependency licenses.

This isn't done yet (some of the licenses still need to be fixed).

* Fix license links.

Note that the kxml one was tricky since its Maven entry says it's
licensed under BSD and CC0 1.0, and its SourceForge link says the same
plus LGPL 2.0. However, the source code and GitHub version of the
project license it under MIT, and this seems to predate the others so
it seems like the most correct license to use in this case and the one
that we're using to represent the dependency.

* Fix Gradle build.

This uses a version of KotliTeX that builds correctly on Jitpack for Gradle,
and fixes the StaticLayout creation to use an alignment constant that
builds on Gradle (I'm not sure why there's a difference here between
Gradle & Bazel, but the previous constant isn't part of the enum per
official Android docs).

* Create the math drawable synchronously.

This requires exposing new injectors broadly in the app since the math
model loader doesn't have access to the dependency injection graph
directly.

* Remove new deps from Maven list.

They were incorrectly pulled in by KotliTeX.

* Add argument partitioning.

This fixes cases where argument calls may be very large and fail to
execute due to exceeding system limitations.

* Make allowance for empty cases to fix tests.

These tests correspond to real scenarios.

* Lint fixes.

* Address reviewer comment.

Clarifies the documentation in the test runner around parameter
injection.

* Fix broken build.

* Fix broken build post-merge.

* Fix broken post-merge classifier.

* Address reviewer comment.

* Post-merge build fixes.

* Post-merge build fixes for new classifiers.

* Post-merge build fixes.

* Correct reference document link.

* Ensure LaTeX isn't stretched or cut-off.

The comments in-code have much more specifics on the approach.

* Add and fix missing test (was broken on Gradle).

* Post-merge fix.

* More post-merge fixes.

* Fix TODO comment.

* Post-merge fix.

* Update KotliTeX version.

This version doesn't have debug drawing enabled.
BenHenning added a commit that referenced this issue Mar 27, 2022
…ss (#4237)

## Explanation
Fix #3813
Fix #92
Fix part of #4044 (see below for how this relates to the broader math expressions project)

This PR refactors ``AsyncResult`` to be a sealed class rather than using enums which has a distinct advantage: the strong typing guarantees from a sealed class ensure the presence of certain data is guaranteed (such as the ``Throwable`` from the failing ``AsyncResult``). However, this refactor is very far-reaching since essentially every usage of ``AsyncResult`` needed to be updated (though not necessarily just references).

### High-level ``AsyncResult`` pattern changes
Here are comparisons in how the common operations have changed:

Before:
```kotlin
val result: AsyncResult<String> = fetchOrReceiveSomeResult()

// Construction.
val pending = AsyncResult.pending<String>()
val failure = AsyncResult.failure<String>(IllegalStateException("Failure reason"))
val success = AsyncResult.success("success value")

// Check type.
val isPending = result.isPending()
val isFailure = result.isFailure()
val isSuccess = result.isSuccess()
val isCompleted = result.isCompleted()

// Retrieve success values.
val successValue1 = result.getOrDefault("default string")
val successValue2 = result.getOrThrow()

// Retrieve failure reason.
val failureReason = result.getErrorOrNull()

// Generic type check.
when (result) {
  // We typically ignore the pending case.
  result.isFailure() -> { /* Do something with result.getErrorOrNull() */ }
  result.isSuccess() -> { /* Do something with result.getOrDefault() or result.getOrThrow() */ }
  // The 'else' case is possible, and often not implemented.
}
```

After:
```kotlin
val result: AsyncResult<String> = fetchOrReceiveSomeResult()

// Construction.
val pending = AsyncResult.Pending<String>()
val failure = AsyncResult.Failure<String>(IllegalStateException("Failure reason"))
val success = AsyncResult.Success("success value")

// Check type.
val isPending = result is AsyncResult.Pending
val isFailure = result is AsyncResult.Failure
val isSuccess = result is AsyncResult.Success
val isCompleted1 = result !is AsyncResult.Pending
val isCompleted2 = result is AsyncResult.Failure || result is AsyncResult.Success

// Retrieve success values.
val success1 = success.value // Type is 'String'
val success2 = (result as? AsyncResult.Success)?.value // Type is 'String?' conditioned on if the result is a success.

// Retrieve failure reason.
val error1 = failure.error // Type is 'Throwable'
val error2 = (result as? AsyncResult.Failure)?.error // Type is 'Throwable?' conditioned on if the result is a failure.

// Generic type check.
when (result) {
  is AsyncResult.Pending { /* Do something in the pending case, such as showing a loading indicator. */ }
  is AsyncResult.Failure { /* Do something with result.error which is never null. */ }
  is AsyncResult.Success { /* Do something with result.value which is always defined. */ }
  // 'else' can't exist since all cases are exhausted, and Kotlin will eventually produce an error if sealed class 'when's aren't exhaustive.
}

// Note 'getOrThrow' is no longer needed since 'value' can only be retrieved if the result is confirmed to be a success.
// Similarly, 'getErrorOrNull' isn't needed since the error can similarly only be extracted from a Failure result.
// 'getOrDefault' is still possible using patterns as so (where the second is preferred to ensure all cases are considered):
val defaultExample1 = if (result is AsyncResult.Success) result.value else default
val defaultExample2 = when (result) {
  // Or, split out 'failure' to log an error message.
  is AsyncResult.Pending, is AsyncResult.Failure -> default
  is AsyncResult.Success -> result.value
}

// Note that result transformations haven't changed at all.
```

The main advantages of the new approach:
- All type cases must always be considered when using 'when' with the result (forcing consideration of both error and pending cases--this is useful since we often ignore pending cases today despite them existing and occurring)
- There's no longer any question about the presence of success values or failure error throwables (they are always present if the corresponding result is the correct type) which results in safer, more readable, and shorter code
- The implementation properly allows for null success values (see 'rationale' section below)

The only disadvantage is that some generic cases will conflate ``AsyncResult``'s sub-types, such as this case:

```kotlin
val results1 = mutableListOf(AsyncResult.Pending())
// results1 is of type MutableList<AsyncResult.Pending>, but this can be fixed by being explicit:
val results2 = mutableListOf<AsyncResult<String>>(AsyncResult.Pending())
// Or:
val results3: MutableList<AsyncResult<String>> = mutableListOf(AsyncResult.Pending())
```

### Rationale for why this PR is needed & connection to algebraic expressions
#4239 introduces changes to ``QuestionAssessmentProgressController`` and ``ExplorationProgressController`` such that data providers with null payloads are transformed into others. However, that caused an issue when transforming results since 'null' can mean either the absence of 'success' (i.e. a failure) or a successful null value, and ``AsyncResult`` couldn't tell the difference between these without leveraging a sealed class.

See #4239 for why it's necessary for the broader algebraic expressions work (which is the same justification as why this PR is). This PR exists to make #4239 smaller since it brought in some additional refactoring & cleanup beyond the minimal migration to a sealed class. Furthermore, this included in the algebraic project chain for the same reason as #4239: to simplify merging and reviews.

### Tests migration and ``DataProviderMonitorFactory``
Rather than updating all tests which verified ``DataProvider`` results to use the new ``AsyncResultSubject``, it seemed nicer to migrate them over to ``DataProviderMonitorFactory`` (which effectively fully fixed #3813). This resulted in a nice simplification in a lot of test suites by eliminating Mockito usage entirely (except in the monitor's implementation). I also replaced all ``toLiveData``-esque calls to use ``DataProvider``s, instead (which required changing a bunch of controllers over to using ``DataProvider``s instead of ``LiveData`` (which is the preferred approach since ``LiveData`` should never be used in the domain layer to ensure good threading cohesion).

### Test exemptions & changes
Note that there's a new ``AsyncResultSubject`` being introduced here to simplify testing (which consequently fixes #92), but tests aren't added for this subject for similar reasons to past algebraic expression PRs. #4236 was filed to track adding tests for this subject in the future. All existing cases that weren't migrated as part of the monitor factory refactor were moved over to using this new subject (except those explicitly testing ``AsyncResult``).

A bunch of tests have been removed since they are no longer possible due to compile-time guarantees about the value of ``AsyncResult`` in different scenarios.

``StateFragmentTest`` was updated to account for the content description for incorrect answers to not be computed correctly (see below section for details).

### Changes to ``StateRecyclerViewAssembler`` & content descriptions
There was a bug in ``StateRecyclerViewAssembler`` where an answer wouldn't be indicated as incorrect correctly in explorations. I fixed this by introducing a more explicit 'correct_answer' signal in the answer response proto. This fix was needed since one of ``StateFragmentTest``'s tests started failing when another issue was fixed in ``ExplorationProgressControllerTest``: in an old PR, one of the notifications for the current state ``DataProvider`` was dropped mid-answer submission. Restoring this notification triggered the test to correctly fail (due to it actually hitting the bug in the assembler; before, the content description wasn't being correctly defined in that test and it was falsely passing). The fix to the assmbler caused the test to start passing (but another to fail due to it not being supported on Robolectric; it's been subsequently ignored like others of its kind).

The new fix is intentionally designed to be robust against cases when an answer doesn't have a correct label (since that was assumed to be present before).

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
N/A -- This should be a non-side effect change. Despite the UI changes, it shouldn't actually change user behaviors (possibly with the exception of answer notification being a bit faster which led to the UI test fix that was explained above) with one primary exception: this fixes the content description computation for incorrect answers. **Reviewers:** please let me know if you'd like this demonstrated, and how. I also didn't explicitly verify that affected shared tests pass on Espresso, but I highly doubt they wouldn't (or at least, would change their current pass/fail status) given that their Robolectric counterparts pass and this is a non-functional change.

I did not verify StateFragmentTest on Espresso since it's currently broken (see #4238). This is fixed in the next PR where the test suite is verified to pass.

Commit history:

* Alphabetize test exemptions.

* Fix typo & add regex check.

The new regex check makes it so that all parameterized testing can be
more easily tracked by the Android TL.

* Add missing KDocs.

* Post-merge cleanups.

Also, fix text file exemption ordering.

* Add new test for negation with math symbol.

* Post-merge fixes.

* Add KDocs.

Also, add new regex exemption for new parameterized tests in this
branch.

* Refactor & simplify real ext impl.

Also, fix/clarify some KDocs.

* Lint fixes.

* Simplify operation list converter a lot.

This inlines three recursive operations to be done during the actual
computation to simplify the overall converter complexity (and to make
determining the test matrix easier).

* Prepare for new tests.

* Remove the ComparableOperationList wrapper.

* Change parameterized method delimiter.

* Use utility directly in test.

* Post-merge fixes.

This adjusts for the removal of ComparableOperationList (i.e. no wrapper
proto).

* Add first round of tests.

This includes fixes to the converter itself as it wasn't distributing
both product inversions and negation correctly in several cases. Tests
should now be covering these cases.

* Finish initial test suite.

Still needs to be cleaned up, but after converter refactoring attempts.

* Simplify operation sorting comparators.

* Remove old tests.

* Add remaining missing tests.

* KDocs & test exemption.

* Renames & lint fixes.

* Post-merge fixes.

* Add tests.

* KDocs + exemptions.

Also, clean up polynomial sorting.

* Lint fixes.

* Post-merge fixes.

Also, mark methods/classes that need tests.

* Add extension tests.

* Add classifier tests.

* Use more intentional epsilons for float comparing.

* Treat en-dash as a subtraction symbol.

* Add explicit platform selection for paramerized.

This adds explicit platform selection support rather than it being
automatic based on deps. While less flexible for shared tests, this
offers better control for tests that don't want to to use Robolectric
for local tests.

This also adds a JUnit-only test runner, and updates MathTokenizerTest
to use it (which led to an almost 40x decrease in runtime).

* Exemption fixes.

Also, fix name for the AndroidJUnit4 runner.

* Remove failing test.

* Fix unary expression precedence.

Also, use ParameterizedJunitTestRunner for MathExpressionParserTest.

* Fixes & add more test cases.

* Post-merge fixes & test changes.

Also, update RealExtensionsTest to use the faster JUnit runner.

* Use utility directly in LaTeX tests.

* Post-merge fixes.

Also, update ExpressionToComparableOperationConverterTest to use the
fast JUnit-only runner.

* Post-merge fixes.

Also, update PolynomialExtensionsTest to use fast JUnit-only runner.

* Post-merge fixes.

Also, update float interval per new tests.

* Lint & other check fixes.

* Replace deprecated term.

* Post-merge fixes.

* Add full test suites for alg exp classifiers.

* Lint & static check fixes.

* Fix test on Gradle.

* Fix test for Gradle.

* Add tests for math equations.

And, post-merge fixes.

* Static check & lint fixes.

* Post-merge fixes.

Verified CI checks & all unit tests are passing.

* Split up tests.

Also, adds dedicated BUILD.bazel file for new test.

* Add missing test in Bazel, and fix it.

* Correct order for genrule.

* Add full test suite.

* Clean up + KDocs + exemption.

* Lint fixes.

* Post-merge fix.

* Cache KotliTeX renders.

Directly rendering LaTeX through KotliTeX is way too slow, so this
introduces a custom flow through Glide that computes a PNG for the LaTeX
on a background thread and then caches it as part of Glide's cache to
speed up re-renders of the LaTeX. We may need to manage/prune the cache
over time, but for now we'll just rely on Glide's internal behaviors.

This also documents some new tests that should be added, but it's not
comprehensive.

* Add tests, docs, and exemptions.

* Update to fixed version of KotliTeX.

The newer version correctly computes the bounds for rendered LaTeX.

* Lint fixes.

* Add new dependency licenses.

This isn't done yet (some of the licenses still need to be fixed).

* Fix license links.

Note that the kxml one was tricky since its Maven entry says it's
licensed under BSD and CC0 1.0, and its SourceForge link says the same
plus LGPL 2.0. However, the source code and GitHub version of the
project license it under MIT, and this seems to predate the others so
it seems like the most correct license to use in this case and the one
that we're using to represent the dependency.

* Fix Gradle build.

This uses a version of KotliTeX that builds correctly on Jitpack for Gradle,
and fixes the StaticLayout creation to use an alignment constant that
builds on Gradle (I'm not sure why there's a difference here between
Gradle & Bazel, but the previous constant isn't part of the enum per
official Android docs).

* Create the math drawable synchronously.

This requires exposing new injectors broadly in the app since the math
model loader doesn't have access to the dependency injection graph
directly.

* Remove new deps from Maven list.

They were incorrectly pulled in by KotliTeX.

* Add argument partitioning.

This fixes cases where argument calls may be very large and fail to
execute due to exceeding system limitations.

* Make allowance for empty cases to fix tests.

These tests correspond to real scenarios.

* Lint fixes.

* Address reviewer comment.

Clarifies the documentation in the test runner around parameter
injection.

* Fix broken build.

* Fix broken build post-merge.

* Fix broken post-merge classifier.

* Address reviewer comment.

* Post-merge build fixes.

* Post-merge build fixes for new classifiers.

* Post-merge build fixes.

* Correct reference document link.

* Ensure LaTeX isn't stretched or cut-off.

The comments in-code have much more specifics on the approach.

* Add and fix missing test (was broken on Gradle).

* Refactor AsyncResult into a sealed class.

This also introduces an AsyncResultSubject, and more or less fully fixes
issue #3813 in all tests.

This is a cherry-pick from the fix-progress-controller-deadlock branch
since it ended up being quite large (it made more sense to split it into
a pre-requisite PR).

Conflicts:
	app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivityPresenter.kt
	domain/src/main/java/org/oppia/android/domain/exploration/ExplorationProgressController.kt
	domain/src/main/java/org/oppia/android/domain/question/QuestionAssessmentProgressController.kt
	domain/src/test/java/org/oppia/android/domain/exploration/lightweightcheckpointing/BUILD.bazel
	testing/src/main/java/org/oppia/android/testing/data/DataProviderTestMonitor.kt
	utility/src/main/java/org/oppia/android/util/data/DataProviders.kt

* Post-merge fixes and updates for consistency.

* Post-merge fixes.

* TODO has been addressed.

* Fix documentation & add tests.

* Lint fixes.

* Fix gradle tests.

I've verified in this commit that all Gradle tests build & run locally
(at least on Robolectric).

* Post-merge fix.

* More post-merge fixes.

* Fix TODO comment.

* Post-merge lint fixes.

* Post-merge fix.

* Fix exploration routing issue.

The underlying problem was that the PR inadvertently changed the
behavior of comparing two results wherein results of different times
would be considered different and be re-delivered (which happened to
break exploration routing, and likely a variety of other places).

This introduces a new method for checking equivelance rather than
confusingly assuming that users of AsyncResult don't care about the
result's time during comparison checking.

New tests have been added to verify the functionality works as expected
(and fails when expected), and I manually verified that the exploration
routing issue was fixed. More details on the specific user-facing issue
will be added to the PR as a comment.

* Update KotliTeX version.

This version doesn't have debug drawing enabled.
BenHenning added a commit that referenced this issue Mar 27, 2022
…migrating progress controllers over to a command-queue structure (#4239)

## Explanation

Fix #3622
Fix #4238
Fix #3861
Fix part of #4044 (see below for how this relates to the broader math expressions project)

This PR fixes a deadlock that's possible in both ``QuestionAssessmentProgressController`` and ``ExplorationProgressController`` wherein the internal locks used to synchronize session state can conflate with the locks of the controllers' ``HintHandler``s (which calls back into the controller within the lock). While this seems really difficult to hit when playing a training session or exploration, ``StateFragmentTest`` seems to very consistently hit the deadlock after only 10-15 tests (so it essentially can't be run without the fixes introduced here).

While I could've tried rearranging the locks to be more cooperative, it's actually difficult to guarantee that deadlocks won't happen without rebuilding the internal locking mechanism for controller data. I chose the latter by introducing a command queue-esque pattern using a Kotlin actor with ``StateFlow``s for pushing state back to callers (see https://kotlinlang.org/docs/shared-mutable-state-and-concurrency.html; actors are a powerful way to leverage the lightweight nature of coroutines to manage cross-thread state machines). To simplify the implementation, this PR also introduces conversion methods from ``StateFlow``s to ``DataProvider``s (I could see us leveraging these flows more in the future, maybe as a long-term replacement to ``DataProvider``s with some refinement).

One side effect of using a command queue to manage what is effectively a session state is that state can now fairly easily leak between sessions. Efforts have been taken to minimize the chance of this happening (mainly ignoring events that arrive from a previously ended session), but a better long-term solution would be to create new session controllers for each session (which should be something possible with custom scopes once we integrate Dagger Hilt in #1720).

Due to a general push toward using ``DataProvider``s much more heavily in these controllers rather than ``LiveData``, ``AsyncResult`` needed to be refactored in order for some of the internal transformed chains in the controllers to work correctly (which is where #4237 comes in).

This also permanently fixes #3861 and other potential errors that could happen by ensuring that the provider state can continue even if internal errors are encountered rather than entering a permanently broken state (#4230 mitigates the immediate repro steps from the bug by fixing the underlying issue that caused the exception, whereas this PR focuses on preventing those scenarios from causing the bug in question).

### Some explanations for the threading approach
I was originally going to make hint handler also a command queue, but it became a bit complicated. To keep things simpler, I removed the threading restricting on it so that it can only be accessed on a single thread (similarly to the internal progress objects used by both controllers) which helps avoid needing a lock without worrying about data races. Note that this command queue style implementation of ``HintHandler`` is available to view in the past commits in this PR if it's interesting for future development (since it's a simpler command queue than the ones used by the controllers due to having an inherently simpler internal state machine).

This PR demonstrates a general tendency to move the app toward a 'notify-and-pull' model (e.g. ``DataProvider``s) vs. a push-subscribe pattern. The former is easier to synchronize, parallelize, ensure lifecycle safety, and is often more performant since operations only need to execute when their results are needed.

Note that both controllers' internal computation behaviors are completely different with this new approach. Previously, the internal progress state machine would only be fully processed when the current ephemeral state/question was retrieved from the UI layer (i.e. lazily), but now it's computed shortly after the change (i.e. eagerly) since that works a bit better with the ``Flow``s being used for internal cross-thread communication. This technically means the controllers are slightly less efficient since execution may happen in cases when the state/question is no longer being viewed by the user, but it should realistically make a small difference since we have to thread hop for the command queue, anyway, and computing the ephemeral state/question should be cheap. This has the benefit of a potentially snappier load experience in the frontend, though, since the ephemeral state/question will be available sooner.

All operations in the controllers now execute their operations regardless of whether they're observed (which should better match callers' expectations).

Note that the internal state machine complexity can probably be drastically simplified now that the command queue can also act as a state machine, but I didn't want to risk regressions by removing it (I was trying to keep the logical flow as similar as possible to the existing behavior).

### Updates to dependencies

The coroutines core dependency needed to be updated since the older version didn't include ``StateFlow`` which was necessary for providing a sync-back mechanism for the controller command queues.

### Stability checks

I verified that both ``QuestionPlayerActivity`` and ``StateFragmentTest`` pass on Espresso (where the latter deadlocked prior to this PR); see the screenshots below. Further, I tested ``ExplorationProgressControllerTest`` and ``QuestionAssessmentProgressControllerTest`` 100x times to verify both are not flaky (since some flakes were discovered during development). Both test suites passed all 100 runs.

### Bazel app module tests
This PR introduces support for dedicated app module tests in Bazel (rather than bundling them as part of the broader list of app module tests). This required a few changes in .bzl and .bazel files, but the change is otherwise straightforward and extremely beneficial for team members that rely on the Android Studio with Bazel plugin for regular development.

### Miscellaneous
I tried introducing a Bazel emulator test while developing this (as part of #59) but it ran into a desugar issue with Mockito and Robolectric (due to them both being pulled into the test binary that's being built to send to the device) which AGP apparently works around. See bazelbuild/bazel#13684 for a Bazel issue tracking the same issue.

This fixes #3622 since it eliminates the lock entirely in favor of using actors for safe cross-thread communication. The ``MediatorLiveData`` specific part of this issue's TODO comment isn't relevant anymore, either, since we replaced ``MediatorLiveData`` with a custom notifying ``LiveData`` within ``DataProviders`` a while back.

This fixes #4238 fully by ensuring any exceptions caused within the state flow of an exploration or questions session are captured and logged, but don't actually break the internal state machine of the session. This issue was discovered during the development of #2173 due to an exception being thrown in the classifier. While that issue was fixed, a test couldn't actually be added for the #4238 fix since it's difficult to intentionally trigger exceptions during answer classification.

### A note on checkpoints in ``ExplorationProgressController``
The checkpointing behavior in ``ExplorationProgressController`` became more complex as a result of these changes. In particular, checkpointing behaves as follows:
- Something changes in the play session
- A checkpoint recomputation is requested (which results in a new checkpoint being computed and then saved to disk)
- The checkpoint is 'processed' by updating the play state of the exploration (i.e. in ``StoryProgressController``)
- The ephemeral state is recomputed since it contains the checkpoint and the checkpoint has changed

Each of these more or less require a command queue "hop" which means other operations can occur between them. As a result, #3467 can still occur since an exploration can be finished before the process happens (though the behavior has changed: the progress simply won't be processed which means an exploration might not be indicated as played, but the checkpointing should almost always be saved before the exploration session end is processed). Fixing this isn't straightforward since the exploration's play state can't be changed until the checkpoint is confirmed as saved which either requires updating ``StoryProgressController`` to also account for the presence of checkpoints, or to use more flows internally to combine the save-and-process operation into one in a way that doesn't lock up ``ExplorationProgressController`` (I'm currently not exactly sure how one would do this, but I haven't thought about it in depth yet).

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
This PR does not intentionally change and user-perceivable behaviors in the questions or state players except potentially fixing deadlocks/ANRs that could occur while playing. There could be regressions in the players, and they may potentially be more performant after this change, but neither can easily be demonstrated via videos or screenshots. There are no accessibility behavior changes to demonstrate, either.

However, two key tests have been run and verified as passing on Espresso to demonstrate that the deadlock has been fixed. Specifically:

| QuestionPlayerActivityTest | StateFragmentTest |
|------|------|
| ![deadlock_fix_pr_question_player_activity_test_passing](https://user-images.githubusercontent.com/12983742/158529405-6e19a602-19b8-48ab-814b-980831c57cb4.png) | ![deadlock_fix_pr_state_fragment_test_passing](https://user-images.githubusercontent.com/12983742/158529650-cc6ac45f-dbee-46b6-a732-d2e4a1a1fbcb.png) |

Commit history:

* Change parameterized method delimiter.

* Use utility directly in test.

* Post-merge fixes.

This adjusts for the removal of ComparableOperationList (i.e. no wrapper
proto).

* Add first round of tests.

This includes fixes to the converter itself as it wasn't distributing
both product inversions and negation correctly in several cases. Tests
should now be covering these cases.

* Finish initial test suite.

Still needs to be cleaned up, but after converter refactoring attempts.

* Simplify operation sorting comparators.

* Remove old tests.

* Add remaining missing tests.

* KDocs & test exemption.

* Renames & lint fixes.

* Post-merge fixes.

* Add tests.

* KDocs + exemptions.

Also, clean up polynomial sorting.

* Lint fixes.

* Post-merge fixes.

Also, mark methods/classes that need tests.

* Add extension tests.

* Add classifier tests.

* Use more intentional epsilons for float comparing.

* Treat en-dash as a subtraction symbol.

* Add explicit platform selection for paramerized.

This adds explicit platform selection support rather than it being
automatic based on deps. While less flexible for shared tests, this
offers better control for tests that don't want to to use Robolectric
for local tests.

This also adds a JUnit-only test runner, and updates MathTokenizerTest
to use it (which led to an almost 40x decrease in runtime).

* Exemption fixes.

Also, fix name for the AndroidJUnit4 runner.

* Remove failing test.

* Fix unary expression precedence.

Also, use ParameterizedJunitTestRunner for MathExpressionParserTest.

* Fixes & add more test cases.

* Post-merge fixes & test changes.

Also, update RealExtensionsTest to use the faster JUnit runner.

* Use utility directly in LaTeX tests.

* Post-merge fixes.

Also, update ExpressionToComparableOperationConverterTest to use the
fast JUnit-only runner.

* Post-merge fixes.

Also, update PolynomialExtensionsTest to use fast JUnit-only runner.

* Post-merge fixes.

Also, update float interval per new tests.

* Lint & other check fixes.

* Replace deprecated term.

* Post-merge fixes.

* Add full test suites for alg exp classifiers.

* Lint & static check fixes.

* Fix test on Gradle.

* Fix test for Gradle.

* Add tests for math equations.

And, post-merge fixes.

* Static check & lint fixes.

* Post-merge fixes.

Verified CI checks & all unit tests are passing.

* Split up tests.

Also, adds dedicated BUILD.bazel file for new test.

* Add missing test in Bazel, and fix it.

* Correct order for genrule.

* Add full test suite.

* Clean up + KDocs + exemption.

* Lint fixes.

* Post-merge fix.

* Cache KotliTeX renders.

Directly rendering LaTeX through KotliTeX is way too slow, so this
introduces a custom flow through Glide that computes a PNG for the LaTeX
on a background thread and then caches it as part of Glide's cache to
speed up re-renders of the LaTeX. We may need to manage/prune the cache
over time, but for now we'll just rely on Glide's internal behaviors.

This also documents some new tests that should be added, but it's not
comprehensive.

* Add tests, docs, and exemptions.

* Update to fixed version of KotliTeX.

The newer version correctly computes the bounds for rendered LaTeX.

* Lint fixes.

* Add new dependency licenses.

This isn't done yet (some of the licenses still need to be fixed).

* Fix license links.

Note that the kxml one was tricky since its Maven entry says it's
licensed under BSD and CC0 1.0, and its SourceForge link says the same
plus LGPL 2.0. However, the source code and GitHub version of the
project license it under MIT, and this seems to predate the others so
it seems like the most correct license to use in this case and the one
that we're using to represent the dependency.

* Fix Gradle build.

This uses a version of KotliTeX that builds correctly on Jitpack for Gradle,
and fixes the StaticLayout creation to use an alignment constant that
builds on Gradle (I'm not sure why there's a difference here between
Gradle & Bazel, but the previous constant isn't part of the enum per
official Android docs).

* Create the math drawable synchronously.

This requires exposing new injectors broadly in the app since the math
model loader doesn't have access to the dependency injection graph
directly.

* Remove new deps from Maven list.

They were incorrectly pulled in by KotliTeX.

* Add argument partitioning.

This fixes cases where argument calls may be very large and fail to
execute due to exceeding system limitations.

* Make allowance for empty cases to fix tests.

These tests correspond to real scenarios.

* Lint fixes.

* Address reviewer comment.

Clarifies the documentation in the test runner around parameter
injection.

* Fix broken build.

* Fix broken build post-merge.

* Fix broken post-merge classifier.

* Address reviewer comment.

* Post-merge build fixes.

* Post-merge build fixes for new classifiers.

* Post-merge build fixes.

* Correct reference document link.

* Ensure LaTeX isn't stretched or cut-off.

The comments in-code have much more specifics on the approach.

* Add and fix missing test (was broken on Gradle).

* Update to newer version of Kotlin coroutines.

This version includes StateFlow which will be a really useful mechanism
for helping to avoid using critical sections.

* First attempt to fix deadlock.

This uses StateFlows and an actor to avoid the deadlock.

This is missing necessary hint changes that are coming in a later
commit. Tests currently fail, and questions haven't yet been migrated
(and won't until the fixes are verified).

* Attempt to make hint handler multi-threadable.

This is a midway solution that's being committed for posterity, but will
be reverted in favor of a solution that forces hint handler to be unsafe
across multiple threads (since it's much simpler, and works given that
all users of it now synchronize state management using an actor).

* Finish fixing state player.

This includes a fix for accessibility string handling (since the new
flow triggers one of these to fail). It also adds a Bazel file for
StateFragmentTest (I spent some time trying to get Espresso tests
working with Bazel but ran into a compatibility issue).

StateFragmentTest has been verified to pass on Robolectric and Espresso
with this change.

This sets up the project for fixing questions in the next commit.

* First pass on migrating question controller.

This also includes a migration for exploration & question domain tests
to use the test monitor utility.

The question tests currently fail since there's a bug in AsyncResult
where it won't support null values during transformations.

* Refactor AsyncResult into a sealed class.

This also introduces an AsyncResultSubject, and more or less fully fixes
issue #3813 in all tests.

* Refactor AsyncResult into a sealed class.

This also introduces an AsyncResultSubject, and more or less fully fixes
issue #3813 in all tests.

This is a cherry-pick from the fix-progress-controller-deadlock branch
since it ended up being quite large (it made more sense to split it into
a pre-requisite PR).

Conflicts:
	app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivityPresenter.kt
	domain/src/main/java/org/oppia/android/domain/exploration/ExplorationProgressController.kt
	domain/src/main/java/org/oppia/android/domain/question/QuestionAssessmentProgressController.kt
	domain/src/test/java/org/oppia/android/domain/exploration/lightweightcheckpointing/BUILD.bazel
	testing/src/main/java/org/oppia/android/testing/data/DataProviderTestMonitor.kt
	utility/src/main/java/org/oppia/android/util/data/DataProviders.kt

* Post-merge fixes and updates for consistency.

* Post-merge fixes.

* TODO has been addressed.

* Fix documentation & add tests.

* Lint fixes.

* Lint & post-merge fixes.

Questions-related tests still fail and need to be fixed now that
AsyncResult has been refactored to support null values.

* Post-merge test fixes.

The core affected UI/domain tests have now been verified as working on
Robolectric. The full test suite is next.

* Add documentation & tests.

Also, fix a bug in the automatic StateFlow DataProviders wherein they
wouldn't notify correctly on changes. This led to some simplifications
in the exploration & question progress controllers.

* Lint fixes.

* Fix gradle tests.

I've verified in this commit that all Gradle tests build & run locally
(at least on Robolectric).

* Fix Proguard build.

This required bringing kotlinx-coroutines-android up-to-date with
kotlinx-coroutines-core (since that was updated on this branch).

New but reasonable Proguard warning exemptions also needed to be added.

* Post-merge fix.

* More post-merge fixes.

* Fix TODO comment.

* Post-merge lint fixes.

* Post-merge fix.

* Fix exploration routing issue.

The underlying problem was that the PR inadvertently changed the
behavior of comparing two results wherein results of different times
would be considered different and be re-delivered (which happened to
break exploration routing, and likely a variety of other places).

This introduces a new method for checking equivelance rather than
confusingly assuming that users of AsyncResult don't care about the
result's time during comparison checking.

New tests have been added to verify the functionality works as expected
(and fails when expected), and I manually verified that the exploration
routing issue was fixed. More details on the specific user-facing issue
will be added to the PR as a comment.

* Post-merge fixes.

* Update KotliTeX version.

This version doesn't have debug drawing enabled.

* Fix lifecycle breakage.

I noticed downstream that quickly navigating back and forth to enter &
exit an exploration can cause the progress controller to get into a bad
state that either leads to a broken experience (stacked explorations or a
blank state), or a crash.

The issue was coming from DataProviders being shared across sessions
even though the underlying state was different. This change ensures that
providers stay completely independent, similar to the core session
state.

* Update play session controllers.

This ensures that the command queue itself is fully isolated to avoid
delayed events potentially leaking across sessions (now that the session
ID barrier has been removed).
BenHenning added a commit that referenced this issue Mar 27, 2022
…ns (#2173)

## Explanation
Fixes #4044

This PR introduces the UI interaction views for numeric expressions, algebraic expressions, and algebraic equations, finishing the initial implementation of the broader math expressions project. This work unblocks adding support for 4 additional topics in the app: Multiplication (support was lost after Alpha MR2 due to a change that added numeric expression questions), Addition & Subtraction, Division, and Expressions & Equations.

This PR finishes a long chain of PRs that were needed to provide the domain functionality to support these new interactions, as a significant amount of mathematics functionality needed to be added including:
- Support for representing generic math expressions/equations, polynomials, and reals
- Support for parsing math expressions
- Support for converting between math expressions and:
  - LaTeX (for rendering)
  - Human-readable sentences (for accessibility)
  - Polynomials and real-value evaluation (for equivalence checking)
  - Comparable structures to verify that two expressions/equations match irrespective of associativity or commutativity order
- Robust error detection to support nearly 2 dozen special cased errors with potential for countless more if needed to ensure users receive excellent feedback when inputting expressions

All of the above also required thorough testing to ensure correctness. See the previous PRs corresponding to #4044 for the full context, and thorough PR descriptions explaining past changes.

From a UI perspective, this PR is introducing:
- The new interaction views and relevant view models, including the logic for constructing and displaying errors for invalid answers
- Support for playing a custom content description for math expression answers so that screenreader users can better understand the expression they've entered
- Support for rendering the LaTeX representation of entered expressions
- A new developer options menu to provide support to input arbitrary expressions and equations and convert them to different outputs to test the underlying math engine that's been built (though it can't yet test the classifiers, but it provides enough information to verify the more complex bits behind the classifiers)

From a functional perspective, this PR is introducing:
- Support for loading JSON explorations that include math expressions
- A new test exploration that's been added to test topic 0 to demonstrate all three interactions and each of those interaction's classifiers (9 total states have been added)

The above, and more, are explained in more detail below.

### Background on UI implementation for new interactions

The UI implementation leverages a single new interaction view & model for all three new interaction types since they are very similar to one another, including handling accessibility and LaTeX rendering cases. The customization arguments, however, don't exactly match so there are some inconsistencies there.

### Background on parsing approach
The new interactions rely on custom math infrastructure for parsing math expressions since no suitable libraries were found that were both license compatible and didn't leverage native code. While this significantly increases the scope of code that needs to be maintained in the project, it has the added benefit of leveraging extremely specific functionality to reduce inconsistencies with Oppia web and ensure a very high quality learner experience when it comes to answer classification and error detection.

Expressions are constructed using an LL(1) recursive-descent parser with early-exit error detection (see the [technical specification](https://docs.google.com/document/d/1JMpbjqRqdEpye67HvDoqBo_rtScY9oEaB7SwKBBspss/edit)). Parsing first goes through an LL(1) lazy tokenization process before being parsed lazily into proto-defined math expression/equation structures. Classifier needs are satisified through custom infrastructure that can evaluate numeric expressions to a single real value, compare math expressions/equations with floating point error correction, compare math expressions/equations irrespective of operation associativity and commutativity by transforming it to an intermediary representation, and can compare expressions/equations for equivalence by first converting it to a polynomial using a custom nearly fully feature complete polynomial system. Finally, UI-specific needs are satisified through generators for both LaTeX and human-readable accessibility strings.

The preceding PRs in this project contain the implementations for parsing, conversion, and comparsion and include PR descriptions that explain these systems & other peripheral changes in great detail.

### PR history
This PR contains **all** of the PRs corresponding to both the previous attempt at implementing math expressions, and the latest (since all commits end up being duplicated in PR chains). There's also some early work that preceded the original work in this PR that's included in its history (it goes back quite a bit).

For reference, here's the entire chain of PRs in order that precede this one for implementing math expressions support:
1. #4045
2. #4046
3. #4047
4. #4049
5. #4050
6. #4051
7. #4052
8. #4054
9. #4055
10. #4056
11. #4057
12. #4058
13. #4059
14. #4061
15. #4063
16. #4068
17. #4237
18. #4239

### Refactors, miscellaneous changes, and future work
All interaction view models were refactored to use a factory pattern rather than a function to improve readability, and to make that view model construction consistent with other similar situations (i.e. cases where new instances need to be created with Dagger dependencies).

Split screen supported interaction IDs have been refactored to be a Dagger set to avoid a direct dependency on InteractionViewModelModule to access the IDs (the new solution is more Dagger 2 idiomatic).

This PR doesn't finish all aspects of the project as there are a number of improvements that have been proposed toward the end of development. These have been cataloged in #4254 and will be implemented in a follow-up PR (but will only be started after this PR & its predecessors have been merged). None of those work items block the overall project, and are intentionally being delayed to a future work item to ensure that math expressions can land more quickly.

### Test specifics & exemptions
Changes were needed in ``EditTextInputAction`` to support inputting Unicode text (which is necessary for some UI tests that verify using math symbols such as for times and divide). Espresso itself doesn't support inputting these characters (presumably since it can't easily find them on the keyboard), so the action now exposes an option to directly set the text.

Exemption explanations:
- ``MathExpressionInteractionsViewTest``: this has been enabled to allow parameterized tests to ensure it can verify a broad set of questions and answers. This might actually be a nice pattern to follow for other interaction tests in the future.
- All test exemptions are either can't be obviously tested (listeners & annotations), are tested through other suites (view models & presenters), is especially difficult to test (``ParameterizedAutoAndroidTestRunner``), or is deemed not important enough to add tests for due to a trivial implementation and in the effort to finish this project sooner (``SplitScreenInteractionModule``).

Note that the defaulting cases for accessibility strings & LaTeX (i.e. the scenarios where generation to either fails) can't easily be tested since the scenarios in which generation fails arise from invalid answers that can't be submitted since they trigger submit-time errors. However, the failure cases for both of these utilities have been thoroughly checked in their respective test suites.

This PR also introduces a new addition to ``OppiaParameterizedTestRunner``: ``ParameterizedAutoAndroidTestRunner``. This runner acts like ``AndroidJUnit4`` wherein it will automatically select a Robolectric or Espresso runner depending on the current runtime environment. This ought to only be used by shared tests that are supposed to run on both platforms, otherwise Robolectric or Espresso specific runners should be used (or JUnit for non-Robolectric tests).

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
I've verified the interaction with the accessibility scanner locally and it didn't mention any new suggestions (there was one suggestion on the label for the input box when inputting something, and another for the congratulations banner contrast--both of which are existing issues that affect other interactions).

### Screenshots of new UIs
| Device type | Language | Orientation | Input box | Error | Inputted answer |
|-------------|----------|-------------|-----------|-----------------|-------|
| Handset     | English  | Portrait    | ![image](https://user-images.githubusercontent.com/12983742/158960886-b66b5f59-48e9-4005-a05d-60cdc9917cee.png)      | ![image](https://user-images.githubusercontent.com/12983742/158966282-108719be-44cb-42ab-8570-fe2f4dd49cfe.png) | ![image](https://user-images.githubusercontent.com/12983742/158966035-c1db8ee1-01a8-4e2f-bab7-c9622965c5b5.png)        |
| Handset     | English  | Landscape   | ![image](https://user-images.githubusercontent.com/12983742/158966436-ef289ea9-2b69-4bd8-b07a-8e0ac917f30a.png)      | ![image](https://user-images.githubusercontent.com/12983742/158966626-8a19d38c-2cbe-4544-aab1-57da419ca405.png)            | ![image](https://user-images.githubusercontent.com/12983742/158966701-768af358-912c-44c4-8c20-e82db23b39a7.png)  |
| Handset     | Arabic   | Portrait    |  ![image](https://user-images.githubusercontent.com/12983742/158967405-29461acf-d160-46e2-bbaa-233a97b99f91.png)     | ![image](https://user-images.githubusercontent.com/12983742/158967611-d3494512-ff49-4c3e-988d-3c2a0eb48943.png)            | ![image](https://user-images.githubusercontent.com/12983742/158969001-a5d516bb-7db2-49f3-b614-baf298c0ce95.png)  |
| Handset     | Arabic   | Landscape   | ![image](https://user-images.githubusercontent.com/12983742/158969671-f2f92523-6045-4908-9b3a-c0efec525e55.png)      | ![image](https://user-images.githubusercontent.com/12983742/158970091-ba858e7e-deab-4189-b06a-91208049ab79.png)            | ![image](https://user-images.githubusercontent.com/12983742/158971054-c64adfbd-06a1-4cc7-a3df-983793e8bd30.png)  |
| Tablet      | English  | Portrait    | ![image](https://user-images.githubusercontent.com/12983742/158975533-046a1d31-145a-4671-b212-6770f0b09b4a.png)      | ![image](https://user-images.githubusercontent.com/12983742/158975620-e404da29-630b-4a19-be8b-13ff5b853601.png)            | ![image](https://user-images.githubusercontent.com/12983742/158975673-a20f6ddb-1669-4035-a819-c23eccee666b.png)  |
| Tablet      | English  | Landscape   | ![image](https://user-images.githubusercontent.com/12983742/158975794-c36f7bee-4f47-4683-b3a2-7e012ea7039c.png)      | ![image](https://user-images.githubusercontent.com/12983742/158975835-a43957f8-c831-4fe3-a69e-3df7b6be3013.png)            | ![image](https://user-images.githubusercontent.com/12983742/158975869-59a7b989-83c1-4162-9df6-6d07f79c5005.png)  |
| Tablet      | Arabic   | Portrait    |  ![image](https://user-images.githubusercontent.com/12983742/158976398-55014bf8-0146-4507-95a3-733fab00f36f.png)     | ![image](https://user-images.githubusercontent.com/12983742/158976477-0736e4c3-81c0-42fd-8160-6ba0cb528e44.png)            | ![image](https://user-images.githubusercontent.com/12983742/158976579-b0a834fc-793e-4205-a2ad-c4029665430d.png)  |
| Tablet      | Arabic   | Landscape   | ![image](https://user-images.githubusercontent.com/12983742/158976744-7c2fb78b-7338-4688-94a0-afa65126b972.png)      | ![image](https://user-images.githubusercontent.com/12983742/158976832-43aee849-38fe-4fb7-8ebf-af13e0cb5cb0.png)            | ![image](https://user-images.githubusercontent.com/12983742/158976882-4e309290-63ab-4ea3-a3a6-39f64615afe4.png)  |

Note that the interaction's errors haven't yet been translated. Also, some work may need to be done in the future to ensure directionality makes sense both for input and rendering (asking folks yielded that input is typically right-to-left but math expressions are still represented left-to-right, so it's not clear if math input should also be left-to-right). In the case above, the text view actually switches the '+2' to '2+1' once it has another number since the context is more complete. It's possible RTL users are already used to quirks like this. It's also likely the custom math keyboard would be a good means of solving this issue long-term.

Screenshot of the new developer options menu for testing math expressions/equations:
![math_expressions_debug_menu](https://user-images.githubusercontent.com/12983742/158933786-9654c85e-b7b9-49fa-8e5f-8d37e598139f.png)

Note that I didn't bother to screenshot other configurations for the developer options menu since it's only visible to developers, so it's less important to make sure it meets the accessibility, language, device, and orientation requirements that the other screens of the UI are held to.

### Videos
| Flow being demonstrated                                    | Video recording |
|------------------------------------------------------------|-----------------|
| Submitting numeric expressions answers                     | https://user-images.githubusercontent.com/12983742/158933258-74992080-a9ec-46da-b752-c00b406ab3ec.mp4            |
| Submitting algebraic expressions answers                   | https://user-images.githubusercontent.com/12983742/158933296-0ea70704-9e3b-4e0d-9150-680a47f9ece2.mp4            |
| Submitting answers that cause errors                           | https://user-images.githubusercontent.com/12983742/158933391-602356c3-ccad-4199-8c0b-47d42abd7896.mp4            |
| Rendering fractions vs. divisions                          | https://user-images.githubusercontent.com/12983742/158933425-9abc640b-289e-484f-8f30-dab38789c99e.mp4            |
| Submitting numeric expressions with a screenreader enabled | https://user-images.githubusercontent.com/12983742/158933446-21afbcd6-0ea8-4558-8c68-9fa0157a7e3d.mp4            |
| Submitted math equations with a screenreader enabled       | https://user-images.githubusercontent.com/12983742/158933460-ddf39ba3-1fd8-4bfc-82e9-cc1845846646.mp4            |

### Espresso test results
| ![final_math_pr_developeroptionsactivitytest_passing](https://user-images.githubusercontent.com/12983742/158932650-4c55fd70-0c58-4972-b1c9-dda727190f9a.png) | ![final_math_pr_developeroptionsfragmenttest_passing](https://user-images.githubusercontent.com/12983742/158932667-9ab52468-6c7d-4088-8e80-3e71afe3cc29.png) |
|------|------|
| ![final_math_pr_htmlparsertest_passing](https://user-images.githubusercontent.com/12983742/158932678-aadd0ba5-f7c4-49e0-b7a8-0cf0734a269d.png) | ![final_math_pr_mathexpressioninteractionsviewtest_passing](https://user-images.githubusercontent.com/12983742/158932687-e1b5df4a-9a5e-4f5e-a0bb-0b1e60ce562e.png) |
| ![final_math_pr_mathexpressionparseractivitytest_passing](https://user-images.githubusercontent.com/12983742/158932699-e71b0b5a-f4c6-475c-9cc1-db626de9ed51.png) | ![final_math_pr_mathexpressionparserfragmenttest_passing](https://user-images.githubusercontent.com/12983742/158932712-a68cee27-4a2f-49ab-895b-f2d021cbc3d8.png) |
| ![final_math_pr_questionplayeractivitytest_passing](https://user-images.githubusercontent.com/12983742/158932734-e9aefddf-13e9-4cf6-9a22-37588ca527e7.png) | ![final_math_pr_statefragmenttest_passing](https://user-images.githubusercontent.com/12983742/158932752-3bc3d821-b703-46a9-a5ef-29fd568d896f.png) |

Note that in the case of ``MathExpressionInteractionsViewTest`` this is the first PR that demonstrates using parameterized tests with Espresso.

Commit history:

* Treat en-dash as a subtraction symbol.

* Add explicit platform selection for paramerized.

This adds explicit platform selection support rather than it being
automatic based on deps. While less flexible for shared tests, this
offers better control for tests that don't want to to use Robolectric
for local tests.

This also adds a JUnit-only test runner, and updates MathTokenizerTest
to use it (which led to an almost 40x decrease in runtime).

* Exemption fixes.

Also, fix name for the AndroidJUnit4 runner.

* Remove failing test.

* Fix unary expression precedence.

Also, use ParameterizedJunitTestRunner for MathExpressionParserTest.

* Fixes & add more test cases.

* Post-merge fixes & test changes.

Also, update RealExtensionsTest to use the faster JUnit runner.

* Use utility directly in LaTeX tests.

* Post-merge fixes.

Also, update ExpressionToComparableOperationConverterTest to use the
fast JUnit-only runner.

* Post-merge fixes.

Also, update PolynomialExtensionsTest to use fast JUnit-only runner.

* Post-merge fixes.

Also, update float interval per new tests.

* Lint & other check fixes.

* Replace deprecated term.

* Post-merge fixes.

* Add full test suites for alg exp classifiers.

* Lint & static check fixes.

* Fix test on Gradle.

* Fix test for Gradle.

* Add tests for math equations.

And, post-merge fixes.

* Static check & lint fixes.

* Post-merge fixes.

Verified CI checks & all unit tests are passing.

* Split up tests.

Also, adds dedicated BUILD.bazel file for new test.

* Add missing test in Bazel, and fix it.

* Correct order for genrule.

* Add full test suite.

* Clean up + KDocs + exemption.

* Lint fixes.

* Post-merge fix.

* Cache KotliTeX renders.

Directly rendering LaTeX through KotliTeX is way too slow, so this
introduces a custom flow through Glide that computes a PNG for the LaTeX
on a background thread and then caches it as part of Glide's cache to
speed up re-renders of the LaTeX. We may need to manage/prune the cache
over time, but for now we'll just rely on Glide's internal behaviors.

This also documents some new tests that should be added, but it's not
comprehensive.

* Add tests, docs, and exemptions.

* Update to fixed version of KotliTeX.

The newer version correctly computes the bounds for rendered LaTeX.

* Lint fixes.

* Add new dependency licenses.

This isn't done yet (some of the licenses still need to be fixed).

* Fix license links.

Note that the kxml one was tricky since its Maven entry says it's
licensed under BSD and CC0 1.0, and its SourceForge link says the same
plus LGPL 2.0. However, the source code and GitHub version of the
project license it under MIT, and this seems to predate the others so
it seems like the most correct license to use in this case and the one
that we're using to represent the dependency.

* Fix Gradle build.

This uses a version of KotliTeX that builds correctly on Jitpack for Gradle,
and fixes the StaticLayout creation to use an alignment constant that
builds on Gradle (I'm not sure why there's a difference here between
Gradle & Bazel, but the previous constant isn't part of the enum per
official Android docs).

* Create the math drawable synchronously.

This requires exposing new injectors broadly in the app since the math
model loader doesn't have access to the dependency injection graph
directly.

* Remove new deps from Maven list.

They were incorrectly pulled in by KotliTeX.

* Add argument partitioning.

This fixes cases where argument calls may be very large and fail to
execute due to exceeding system limitations.

* Post-merge fixes.

Note that only the following tests are failing (some of which may be due
to the previous branch):
- MarkChaptersCompletedFragmentTest
- MarkStoriesCompletedFragmentTest
- StoryProgressTestHelperTest
- ModifyLessonProgressControllerTest
- TopicListControllerTest
- ComputeAffectedTestsTest
- MarkTopicsCompletedFragmentTest
- HomeActivityTest

* Make allowance for empty cases to fix tests.

These tests correspond to real scenarios.

* Lint fixes.

* Fix broken tests and add reasonable exemptions.

* First pass at adding tests.

Some debugging left, and more tests to add.

This also adds auto-switching support for parameterized tests.

* Fix DeveloperOptionsActivityTest.

Also, fix all other test builds (for Gradle). This will probably require
some reformatting.

* Add & fix remaining tests.

This also fixes a bug where the correct answer a11y label was being
incorrectly assumed to be the correct answer even for incorrect cases.

* Add docs & clean up remaining non-lint parts.

* Lint & small breakage fixes.

* Fix broken tests.

* Address reviewer comment.

Clarifies the documentation in the test runner around parameter
injection.

* Fix broken build.

* Fix broken build post-merge.

* Fix broken post-merge classifier.

* Address reviewer comment.

* Post-merge build fixes.

* Post-merge build fixes for new classifiers.

* Post-merge build fixes.

* Correct reference document link.

* Ensure LaTeX isn't stretched or cut-off.

The comments in-code have much more specifics on the approach.

* Add and fix missing test (was broken on Gradle).

* Fix Gradle tests.

This commit has verified the following tests now pass on Espresso:
- MathExpressionInteractionsViewTest
- HtmlParserTest
- MathExpressionParserActivityTest
- MathExpressionParserFragmentTest
- DeveloperOptionsActivityTest
- DeveloperOptionsFragmentTest

StateFragmentTest is having issues (it ends up hanging), so further
investigation is needed.

* Update to newer version of Kotlin coroutines.

This version includes StateFlow which will be a really useful mechanism
for helping to avoid using critical sections.

* First attempt to fix deadlock.

This uses StateFlows and an actor to avoid the deadlock.

This is missing necessary hint changes that are coming in a later
commit. Tests currently fail, and questions haven't yet been migrated
(and won't until the fixes are verified).

* Attempt to make hint handler multi-threadable.

This is a midway solution that's being committed for posterity, but will
be reverted in favor of a solution that forces hint handler to be unsafe
across multiple threads (since it's much simpler, and works given that
all users of it now synchronize state management using an actor).

* Finish fixing state player.

This includes a fix for accessibility string handling (since the new
flow triggers one of these to fail). It also adds a Bazel file for
StateFragmentTest (I spent some time trying to get Espresso tests
working with Bazel but ran into a compatibility issue).

StateFragmentTest has been verified to pass on Robolectric and Espresso
with this change.

This sets up the project for fixing questions in the next commit.

* First pass on migrating question controller.

This also includes a migration for exploration & question domain tests
to use the test monitor utility.

The question tests currently fail since there's a bug in AsyncResult
where it won't support null values during transformations.

* Refactor AsyncResult into a sealed class.

This also introduces an AsyncResultSubject, and more or less fully fixes
issue #3813 in all tests.

* Refactor AsyncResult into a sealed class.

This also introduces an AsyncResultSubject, and more or less fully fixes
issue #3813 in all tests.

This is a cherry-pick from the fix-progress-controller-deadlock branch
since it ended up being quite large (it made more sense to split it into
a pre-requisite PR).

Conflicts:
	app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivityPresenter.kt
	domain/src/main/java/org/oppia/android/domain/exploration/ExplorationProgressController.kt
	domain/src/main/java/org/oppia/android/domain/question/QuestionAssessmentProgressController.kt
	domain/src/test/java/org/oppia/android/domain/exploration/lightweightcheckpointing/BUILD.bazel
	testing/src/main/java/org/oppia/android/testing/data/DataProviderTestMonitor.kt
	utility/src/main/java/org/oppia/android/util/data/DataProviders.kt

* Post-merge fixes and updates for consistency.

* Post-merge fixes.

* TODO has been addressed.

* Fix documentation & add tests.

* Lint fixes.

* Lint & post-merge fixes.

Questions-related tests still fail and need to be fixed now that
AsyncResult has been refactored to support null values.

* Post-merge test fixes.

The core affected UI/domain tests have now been verified as working on
Robolectric. The full test suite is next.

* Add documentation & tests.

Also, fix a bug in the automatic StateFlow DataProviders wherein they
wouldn't notify correctly on changes. This led to some simplifications
in the exploration & question progress controllers.

* Lint fixes.

* Fix gradle tests.

I've verified in this commit that all Gradle tests build & run locally
(at least on Robolectric).

* Fix Proguard build.

This required bringing kotlinx-coroutines-android up-to-date with
kotlinx-coroutines-core (since that was updated on this branch).

New but reasonable Proguard warning exemptions also needed to be added.

* Post-merge fixes.

* Gradle fixes.

This also fixes an issue wherein answers couldn't be resubmitted for
math expression interactions after an error occurred.

* Fix parameterized runners for Espresso.

This fixes a typo when loading the AndroidJUnit4 runner during
parameterization runer selection, and ensures that test names don't
contain illegal characters that would break the runner.

* Post-merge fix.

* More post-merge fixes.

* Fix TODO comment.

* Post-merge lint fixes.

* Post-merge fix.

* Fix exploration routing issue.

The underlying problem was that the PR inadvertently changed the
behavior of comparing two results wherein results of different times
would be considered different and be re-delivered (which happened to
break exploration routing, and likely a variety of other places).

This introduces a new method for checking equivelance rather than
confusingly assuming that users of AsyncResult don't care about the
result's time during comparison checking.

New tests have been added to verify the functionality works as expected
(and fails when expected), and I manually verified that the exploration
routing issue was fixed. More details on the specific user-facing issue
will be added to the PR as a comment.

* Post-merge fixes.

* Post-merge fixes.

The local repo has been verified to pass nearly all static analysis and
lint tests, as well as all Robolectric tests. The developer and alpha
versions of the app build.

* Update KotliTeX version.

This version doesn't have debug drawing enabled.

* Fix lifecycle breakage.

I noticed downstream that quickly navigating back and forth to enter &
exit an exploration can cause the progress controller to get into a bad
state that either leads to a broken experience (stacked explorations or a
blank state), or a crash.

The issue was coming from DataProviders being shared across sessions
even though the underlying state was different. This change ensures that
providers stay completely independent, similar to the core session
state.

* Address reviewer comment.

* Update play session controllers.

This ensures that the command queue itself is fully isolated to avoid
delayed events potentially leaking across sessions (now that the session
ID barrier has been removed).
@BenHenning BenHenning added the Z-ibt Temporary label for Ben to keep track of issues he's triaged. label Sep 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Z-ibt Temporary label for Ben to keep track of issues he's triaged.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants