diff options
author | Andy Magee <andymagee@google.com> | 2023-09-12 11:11:07 -0700 |
---|---|---|
committer | Andy Magee <andymagee@google.com> | 2023-09-13 10:49:09 -0700 |
commit | 2ea4682beb071d905074a88ba476ad10af8db8d0 (patch) | |
tree | aa9653397c562ff3faa0f7b6e06a044573f24ec5 | |
parent | 78ce30c67f45d150f17de4af91dd0c6f01bcc11c (diff) | |
download | data-binding-2ea4682beb071d905074a88ba476ad10af8db8d0.tar.gz |
Remove DataBindingCompilationTestCase base class
All tests that use DataBindingCompilationTestCase are currently disabled
for Windows due to timeouts. Those timeouts were caused by deadlocks
happening during initialization in the base AndroidGradleTestCase.
That base class is unnecessary, since all the derived classes specify a
runner and the logic that's used can be provided by
AndroidGradleProjectRule instead. Switching to the rule removes any
timeouts on Windows.
This test failed when run from HEAD on Windows ~4-5% of the time. With
this change, it's passed for 500 runs:
https://fusion2.corp.google.com/invocations/0fd0691d-3658-45c3-bb2a-fb91865af04c
Test: n/a (test code)
Fixes: 177936851
Change-Id: I46422a074733480e381e9abd36d0cf41898235fc
6 files changed, 136 insertions, 90 deletions
diff --git a/compilationTests/BUILD.bazel b/compilationTests/BUILD.bazel index cbe36699..aa5a97b4 100644 --- a/compilationTests/BUILD.bazel +++ b/compilationTests/BUILD.bazel @@ -8,7 +8,6 @@ iml_module( jvm_target = "8", tags = [ "no_test_mac", # b/178123368 - "no_test_windows", # b/177936851 ], test_class = "androidx.databinding.compilationTest.bazel.DataBindingCompilationTestSuite", test_data = [ diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/DataBindingCompilationTestCase.kt b/compilationTests/src/test/java/androidx/databinding/compilationTest/DataBindingCompilationTestCase.kt index 8258ef9c..75c74a17 100644 --- a/compilationTests/src/test/java/androidx/databinding/compilationTest/DataBindingCompilationTestCase.kt +++ b/compilationTests/src/test/java/androidx/databinding/compilationTest/DataBindingCompilationTestCase.kt @@ -21,22 +21,28 @@ import com.android.testutils.TestUtils import com.android.tools.analytics.Environment import com.android.tools.analytics.EnvironmentFakes import com.android.tools.idea.gradle.project.build.invoker.GradleBuildInvoker +import com.android.tools.idea.gradle.project.build.invoker.GradleBuildResult +import com.android.tools.idea.testing.AndroidGradleProjectRule import com.android.tools.idea.testing.AndroidGradleTestCase import com.android.tools.idea.testing.TestProjectPaths +import com.google.common.util.concurrent.ListenableFuture import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListenerAdapter +import com.intellij.openapi.project.Project import com.intellij.openapi.util.io.FileUtil.toSystemDependentName import com.intellij.util.io.createDirectories import com.intellij.util.io.readText import org.junit.After import org.junit.Assert import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.rules.TemporaryFolder import java.io.File import java.io.IOException import java.nio.charset.StandardCharsets import java.nio.file.Files +import java.util.function.Function import java.util.regex.Pattern private const val TEST_DEPENDENCIES = "implementation 'androidx.fragment:fragment:+'" @@ -51,10 +57,13 @@ const val KEY_DEPENDENCIES = "DEPENDENCIES" const val KEY_SETTINGS_INCLUDES = "SETTINGS_INCLUDES" const val DEFAULT_APP_PACKAGE = "com.android.databinding.compilationTest.test" -abstract class DataBindingCompilationTestCase : AndroidGradleTestCase() { +abstract class DataBindingCompilationTestCase { @get:Rule val temporaryFolder = TemporaryFolder() + @get:Rule + val androidGradleProjectRule = AndroidGradleProjectRule() + @Before fun setup() { EnvironmentFakes.setSingleProperty( @@ -73,7 +82,7 @@ abstract class DataBindingCompilationTestCase : AndroidGradleTestCase() { } protected fun loadApp(appReplacements: Map<String, String>) { - loadProject(TestProjectPaths.DATA_BINDING_COMPILATION) + androidGradleProjectRule.loadProject(TestProjectPaths.DATA_BINDING_COMPILATION) projectRoot.toPath().resolve("app/src/main").createDirectories() val replacements = appendTestReplacements(appReplacements) copyTestDataWithReplacement( @@ -126,14 +135,15 @@ abstract class DataBindingCompilationTestCase : AndroidGradleTestCase() { } val request = GradleBuildInvoker.Request.builder( - project, - File(toSystemDependentName(project.basePath!!)), + androidGradleProjectRule.project, + File(toSystemDependentName(androidGradleProjectRule.project.basePath!!)), tasks ) .setCommandLineArguments(listOf("--offline") + args) .setListener(taskListener) .build() - val result = invokeGradle(project) { gradleInvoker -> + + val result = DelegateGradleTestCase.doInvokeGradle(androidGradleProjectRule.project) { gradleInvoker -> gradleInvoker.executeTasks(request) } return CompilationResult( @@ -143,8 +153,26 @@ abstract class DataBindingCompilationTestCase : AndroidGradleTestCase() { ) } - protected val projectRoot: File - get() = File(toSystemDependentName(project.basePath!!)) + /** + * Inheritance is used here to allow access to this protected static method; + * [AndroidGradleProjectRule] does effectively the same thing to use this, but the data binding + * tests need to be able to invoke Gradle directly so that they can get the error output. + */ + @Ignore + private class DelegateGradleTestCase : AndroidGradleTestCase() { + companion object { + fun <T : GradleBuildResult> doInvokeGradle( + project: Project, + gradleInvocationTask: Function<GradleBuildInvoker, ListenableFuture<T>?> + ): T { + return invokeGradle(project, gradleInvocationTask) + } + } + } + + protected val projectRoot by lazy { + File(toSystemDependentName(androidGradleProjectRule.project.basePath!!)) + } /** * Copies the file in the testData directory to the target directory. diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/InverseMethodTest.java b/compilationTests/src/test/java/androidx/databinding/compilationTest/InverseMethodTest.java index 0d99c44c..e76b9f00 100644 --- a/compilationTests/src/test/java/androidx/databinding/compilationTest/InverseMethodTest.java +++ b/compilationTests/src/test/java/androidx/databinding/compilationTest/InverseMethodTest.java @@ -23,6 +23,8 @@ import org.junit.runners.JUnit4; import java.io.File; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertNotEquals; @SuppressWarnings("ThrowableResultOfMethodCallIgnored") @@ -71,14 +73,19 @@ public class InverseMethodTest extends DataBindingCompilationTestCase { Lists.newArrayList("--stacktrace")); assertNotEquals(0, result.resultCode); String error = getErrorLine(result.error); - assertNotNull("Couldn't find error in \n" + result.error, error); + assertWithMessage("Couldn't find error in \n" + result.error).that(error).isNotNull(); File errorFile = new File(getProjectRoot(), "app/src/main/java/androidx/databinding/compilationTest/badJava/" + className + ".java"); - assertEquals(errorFile.getCanonicalPath() + ":" + lineNumber + ": error: " + expectedError, - error); + String expected = errorFile.getCanonicalPath() + + ":" + + lineNumber + + ": error: " + + expectedError; + assertThat(error) + .isEqualTo(expected); } private static String getErrorLine(String err) { diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/MultiLayoutVerificationTest.java b/compilationTests/src/test/java/androidx/databinding/compilationTest/MultiLayoutVerificationTest.java index d07d366b..d535a69e 100644 --- a/compilationTests/src/test/java/androidx/databinding/compilationTest/MultiLayoutVerificationTest.java +++ b/compilationTests/src/test/java/androidx/databinding/compilationTest/MultiLayoutVerificationTest.java @@ -30,7 +30,10 @@ import java.util.Collections; import java.util.List; import static androidx.databinding.compilationTest.DataBindingCompilationTestCaseKt.DEFAULT_APP_PACKAGE; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertNotEquals; +import static junit.framework.TestCase.fail; @RunWith(JUnit4.class) @@ -54,46 +57,49 @@ public class MultiLayoutVerificationTest extends DataBindingCompilationTestCase CompilationResult result = assembleDebug(); assertNotEquals(result.output, 0, result.resultCode); List<ScopedException> exceptions = result.getBindingExceptions(); - assertEquals(result.error, 2, exceptions.size()); + assertWithMessage(result.error).that(exceptions).hasSize(2); boolean foundNormal = false; boolean foundLandscape = false; for (ScopedException exception : exceptions) { ScopedErrorReport report = exception.getScopedErrorReport(); - assertNotNull(report); + assertThat(report).isNotNull(); File file = requireErrorFile(report); - assertEquals(1, report.getLocations().size()); + assertThat(report.getLocations()).hasSize(1); Location location = report.getLocations().get(0); String name = file.getParentFile().getName(); if ("layout".equals(name)) { - assertEquals(new File(getProjectRoot(), - "/app/src/main/res/layout/with_class_name.xml") - .getCanonicalFile(), file.getCanonicalFile()); + File expected = getExpectedCanonicalFile( + "/app/src/main/res/layout/with_class_name.xml"); + assertThat(file.getCanonicalFile()).isEqualTo( + expected); String extract = extract("/app/src/main/res/layout/with_class_name.xml", - location); - assertEquals(extract, "AClassName"); - assertEquals(String.format( + location); + assertThat(extract).isEqualTo("AClassName"); + assertThat(exception.getBareMessage()).isEqualTo(String.format( ErrorMessages.MULTI_CONFIG_LAYOUT_CLASS_NAME_MISMATCH, DEFAULT_APP_PACKAGE + ".databinding.AClassName", - "layout/with_class_name"), exception.getBareMessage()); + "layout/with_class_name")); foundNormal = true; } else if ("layout-land".equals(name)) { - assertEquals(new File(getProjectRoot(), - "/app/src/main/res/layout-land/with_class_name.xml") - .getCanonicalFile(), file.getCanonicalFile()); - String extract = extract("/app/src/main/res/layout-land/with_class_name.xml", - location); - assertEquals("SomeOtherClassName", extract); - assertEquals(String.format( - ErrorMessages.MULTI_CONFIG_LAYOUT_CLASS_NAME_MISMATCH, - DEFAULT_APP_PACKAGE + ".databinding.SomeOtherClassName", - "layout-land/with_class_name"), exception.getBareMessage()); - foundLandscape = true; + File expected = getExpectedCanonicalFile( + "/app/src/main/res/layout-land/with_class_name.xml"); + assertThat(file.getCanonicalFile()).isEqualTo(expected); + String extract = extract("/app/src/main/res/layout-land/with_class_name.xml", + location); + assertThat(extract).isEqualTo("SomeOtherClassName"); + assertThat(exception.getBareMessage()).isEqualTo(String.format( + ErrorMessages.MULTI_CONFIG_LAYOUT_CLASS_NAME_MISMATCH, + DEFAULT_APP_PACKAGE + ".databinding.SomeOtherClassName", + "layout-land/with_class_name")); + foundLandscape = true; } else { fail("unexpected error file"); } } - assertTrue("should find default config error\n" + result.error, foundNormal); - assertTrue("should find landscape error\n" + result.error, foundLandscape); + assertWithMessage("should find default config error\n" + result.error) + .that(foundNormal).isTrue(); + assertWithMessage("should find landscape error\n" + result.error) + .that(foundLandscape).isTrue(); } @Test @@ -109,14 +115,14 @@ public class MultiLayoutVerificationTest extends DataBindingCompilationTestCase CompilationResult result = assembleDebug(); assertNotEquals(result.output, 0, result.resultCode); List<ScopedException> exceptions = result.getBindingExceptions(); - assertEquals(result.error, 2, exceptions.size()); + assertWithMessage(result.error).that(exceptions).hasSize(2); boolean foundNormal = false; boolean foundLandscape = false; for (ScopedException exception : exceptions) { ScopedErrorReport report = exception.getScopedErrorReport(); - assertNotNull(report); + assertThat(report).isNotNull(); File file = requireErrorFile(report); - assertEquals(result.error, 1, report.getLocations().size()); + assertWithMessage(result.error).that(report.getLocations()).hasSize(1); Location location = report.getLocations().get(0); // validated in switch String name = file.getParentFile().getName(); @@ -130,19 +136,21 @@ public class MultiLayoutVerificationTest extends DataBindingCompilationTestCase } else { fail("unexpected error file"); } - assertEquals(new File(getProjectRoot(), - "/app/src/main/res/" + name + "/layout_with_variable_type.xml") - .getCanonicalFile(), file.getCanonicalFile()); + assertThat(file.getCanonicalFile()).isEqualTo( + getExpectedCanonicalFile( + "/app/src/main/res/" + + name + + "/layout_with_variable_type.xml")); String extract = extract("/app/src/main/res/" + name + "/layout_with_variable_type.xml", location); - assertEquals(extract, "<variable name=\"myVariable\" type=\"" + type + "\"/>"); - assertEquals(String.format( + assertThat(extract).isEqualTo("<variable name=\"myVariable\" type=\"" + type + "\"/>"); + assertThat(exception.getBareMessage()).isEqualTo(String.format( ErrorMessages.MULTI_CONFIG_VARIABLE_TYPE_MISMATCH, "myVariable", type, - name + "/layout_with_variable_type"), exception.getBareMessage()); + name + "/layout_with_variable_type")); } - assertTrue(result.error, foundNormal); - assertTrue(result.error, foundLandscape); + assertWithMessage(result.error).that(foundNormal).isTrue(); + assertWithMessage(result.error).that(foundLandscape).isTrue(); } @Test @@ -161,14 +169,14 @@ public class MultiLayoutVerificationTest extends DataBindingCompilationTestCase CompilationResult result = assembleDebug(); assertNotEquals(result.output, 0, result.resultCode); List<ScopedException> exceptions = result.getBindingExceptions(); - assertEquals(result.error, 2, exceptions.size()); + assertWithMessage(result.error).that(exceptions).hasSize(2); boolean foundNormal = false; boolean foundLandscape = false; for (ScopedException exception : exceptions) { ScopedErrorReport report = exception.getScopedErrorReport(); - assertNotNull(report); + assertThat(report).isNotNull(); File file = requireErrorFile(report); - assertEquals(result.error, 1, report.getLocations().size()); + assertWithMessage(result.error).that(report.getLocations()).hasSize(1); Location location = report.getLocations().get(0); // validated in switch String name = file.getParentFile().getName(); @@ -182,19 +190,19 @@ public class MultiLayoutVerificationTest extends DataBindingCompilationTestCase } else { fail("unexpected error file"); } - assertEquals(new File(getProjectRoot(), - "/app/src/main/res/" + name + "/layout_with_import_type.xml") - .getCanonicalFile(), file.getCanonicalFile()); + assertThat(file.getCanonicalFile()).isEqualTo( + getExpectedCanonicalFile( + "/app/src/main/res/" + name + "/layout_with_import_type.xml")); String extract = extract("/app/src/main/res/" + name + "/layout_with_import_type.xml", location); - assertEquals(extract, "<import alias=\"Blah\" type=\"" + type + "\"/>"); - assertEquals(String.format( + assertThat(extract).isEqualTo("<import alias=\"Blah\" type=\"" + type + "\"/>"); + assertThat(exception.getBareMessage()).isEqualTo(String.format( ErrorMessages.MULTI_CONFIG_IMPORT_TYPE_MISMATCH, "Blah", type, - name + "/layout_with_import_type"), exception.getBareMessage()); + name + "/layout_with_import_type")); } - assertTrue(result.error, foundNormal); - assertTrue(result.error, foundLandscape); + assertWithMessage(result.error).that(foundNormal).isTrue(); + assertWithMessage(result.error).that(foundLandscape).isTrue(); } @Test @@ -216,37 +224,41 @@ public class MultiLayoutVerificationTest extends DataBindingCompilationTestCase boolean foundLandscape = false; for (ScopedException exception : exceptions) { ScopedErrorReport report = exception.getScopedErrorReport(); - assertNotNull(report); + assertThat(report).isNotNull(); if (exception.getBareMessage().startsWith("Cannot find a setter")) { continue; } File file = requireErrorFile(report); - assertEquals(result.error, 1, report.getLocations().size()); + assertWithMessage(result.error).that(report.getLocations()).hasSize(1); Location location = report.getLocations().get(0); // validated in switch String config = file.getParentFile().getName(); if ("layout".equals(config)) { String extract = extract("/app/src/main/res/" + config + "/foo.xml", location); - assertEquals(extract, "<include layout=\"@layout/basic_layout\" " + assertThat(extract).isEqualTo( + "<include layout=\"@layout/basic_layout\" " + "android:id=\"@+id/sharedId\" bind:myVariable=\"@{myVariable}\"/>"); foundNormal = true; } else if ("layout-land".equals(config)) { String extract = extract("/app/src/main/res/" + config + "/foo.xml", location); - assertEquals(extract, "<TextView android:layout_width=\"wrap_content\" " + assertThat(extract).isEqualTo( + "<TextView android:layout_width=\"wrap_content\" " + "android:layout_height=\"wrap_content\" android:id=\"@+id/sharedId\" " + "android:text=\"@{myVariable}\"/>"); foundLandscape = true; } else { fail("unexpected error file"); } - assertEquals(new File(getProjectRoot(), - "/app/src/main/res/" + config + "/foo.xml").getCanonicalFile(), - file.getCanonicalFile()); - assertEquals(String.format( - ErrorMessages.MULTI_CONFIG_ID_USED_AS_IMPORT, "@+id/sharedId"), - exception.getBareMessage()); + assertThat(file.getCanonicalFile()).isEqualTo( + getExpectedCanonicalFile("/app/src/main/res/" + config + "/foo.xml")); + assertThat(exception.getBareMessage()).isEqualTo(String.format( + ErrorMessages.MULTI_CONFIG_ID_USED_AS_IMPORT, "@+id/sharedId")); } - assertTrue(result.error, foundNormal); - assertTrue(result.error, foundLandscape); + assertWithMessage(result.error).that(foundNormal).isTrue(); + assertWithMessage(result.error).that(foundLandscape).isTrue(); + } + + private File getExpectedCanonicalFile(String relativePath) throws IOException { + return new File(getProjectRoot(), relativePath).getCanonicalFile(); } } diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/SimpleCompilationTest.kt b/compilationTests/src/test/java/androidx/databinding/compilationTest/SimpleCompilationTest.kt index 957b8f86..50956bcd 100644 --- a/compilationTests/src/test/java/androidx/databinding/compilationTest/SimpleCompilationTest.kt +++ b/compilationTests/src/test/java/androidx/databinding/compilationTest/SimpleCompilationTest.kt @@ -20,6 +20,7 @@ import android.databinding.tool.processing.ScopedException import android.databinding.tool.util.FileUtil import com.google.common.base.Joiner import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage import org.apache.commons.io.FileUtils import org.apache.commons.io.filefilter.NameFileFilter import org.apache.commons.io.filefilter.PrefixFileFilter @@ -39,15 +40,15 @@ class SimpleCompilationTest : DataBindingCompilationTestCase() { @Test fun listTasks() { loadApp() - val result = invokeGradleTasks(project, "tasks") - assertTrue("Empty project tasks failed", result.isBuildSuccessful) + val result = androidGradleProjectRule.invokeTasks("tasks") + assertWithMessage("Empty project tasks failed").that(result.isBuildSuccessful).isTrue() } @Test fun testEmptyCompilation() { loadApp() - val result = invokeGradleTasks(project, "assembleDebug") - assertTrue("Basic compile failed", result.isBuildSuccessful) + val result = androidGradleProjectRule.invokeTasks("assembleDebug") + assertWithMessage("Basic compile failed").that(result.isBuildSuccessful).isTrue() } @Test @@ -61,8 +62,8 @@ class SimpleCompilationTest : DataBindingCompilationTestCase() { "layout/basic_layout.xml", "app/src/main/res/layout-sw100dp/main.xml" ) - val result = invokeGradleTasks(project, "assembleDebug") - assertTrue(result.isBuildSuccessful) + val result = androidGradleProjectRule.invokeTasks("assembleDebug") + assertThat(result.isBuildSuccessful).isTrue() val debugOut = File( projectRoot, "app/build/intermediates/incremental/debug/mergeDebugResources/stripped.dir" @@ -71,19 +72,17 @@ class SimpleCompilationTest : DataBindingCompilationTestCase() { debugOut, NameFileFilter("main.xml"), PrefixFileFilter("layout") ) - assertTrue("Unexpected generated layout count", layoutFiles.size > 1) + assertWithMessage("Unexpected generated layout count").that(layoutFiles.size > 1).isTrue() for (layout: File in layoutFiles) { val contents = FileUtils.readFileToString(layout, StandardCharsets.UTF_8) if (layout.parent.contains("sw100")) { - assertTrue( - "File has wrong tag:" + layout.path, - contents.indexOf("android:tag=\"layout-sw100dp/main_0\"") > 0 - ) + assertWithMessage("File has wrong tag:" + layout.path) + .that(contents) + .contains("android:tag=\"layout-sw100dp/main_0\"") } else { - assertTrue( - "File has wrong tag:" + layout.path + "\n" + contents, - contents.indexOf("android:tag=\"layout/main_0\"") > 0 - ) + assertWithMessage("File has wrong tag:" + layout.path + "\n" + contents) + .that(contents) + .contains("android:tag=\"layout/main_0\"") } } } @@ -259,7 +258,7 @@ class SimpleCompilationTest : DataBindingCompilationTestCase() { copyTestData("layout/basic_layout.xml", "app/src/main/res/layout/app_layout.xml") val result: CompilationResult = assembleDebug() - assertEquals(result.error, 0, result.resultCode.toLong()) + assertWithMessage(result.error).that(result.resultCode).isEqualTo(0) } @Test @@ -562,13 +561,13 @@ class SimpleCompilationTest : DataBindingCompilationTestCase() { loadApp() copyTestData(resource, targetFile) val result = assembleDebug() - assertFalse(result.isBuildSuccessful) + assertThat(result.isBuildSuccessful).isFalse() val scopedException = result.bindingException ?: throw AssertionError( result.error ) val report = scopedException.scopedErrorReport - assertNotNull(result.error, report) - assertEquals(1, report.locations.size.toLong()) + assertWithMessage(result.error).that(report).isNotNull() + assertThat(report.locations).hasSize(1) val loc = report.locations[0] if (expectedExtract != null) { val extract: String = extract(targetFile, loc) diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/TooManyLayoutsTest.kt b/compilationTests/src/test/java/androidx/databinding/compilationTest/TooManyLayoutsTest.kt index 242b53e4..61f607b7 100644 --- a/compilationTests/src/test/java/androidx/databinding/compilationTest/TooManyLayoutsTest.kt +++ b/compilationTests/src/test/java/androidx/databinding/compilationTest/TooManyLayoutsTest.kt @@ -15,6 +15,7 @@ */ package androidx.databinding.compilationTest +import com.google.common.truth.Truth.assertWithMessage import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 @@ -37,7 +38,7 @@ class TooManyLayoutsTest : DataBindingCompilationTestCase() { ) } val result = assembleDebug() - assertEquals(result.error, 0, result.resultCode.toLong()) + assertWithMessage(result.error).that(result.resultCode).isEqualTo(0) } @Test @@ -69,7 +70,7 @@ class TooManyLayoutsTest : DataBindingCompilationTestCase() { } val result = assembleDebug() - assertEquals(result.error, 0, result.resultCode) + assertWithMessage(result.error).that(result.resultCode).isEqualTo(0) } @Test @@ -110,6 +111,6 @@ class TooManyLayoutsTest : DataBindingCompilationTestCase() { } val result = assembleDebug() - assertEquals(result.error, 0, result.resultCode) + assertWithMessage(result.error).that(result.resultCode).isEqualTo(0) } } |