diff --git a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/InvalidTestRunException.java b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/InvalidTestRunException.kt similarity index 81% rename from play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/InvalidTestRunException.java rename to play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/InvalidTestRunException.kt index 0841c18..8a32135 100644 --- a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/InvalidTestRunException.java +++ b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/InvalidTestRunException.kt @@ -13,17 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.google.wear.watchface.dfx.memory; +package com.google.wear.watchface.dfx.memory /** * Exception thrown when the memory footprint test encounters an unexpected issue. The test fails, * but the watch face should not be rejected nor accepted, but the error should be escalated for * further analysis. */ -class InvalidTestRunException extends RuntimeException { - - InvalidTestRunException(String message) { - super(message); - } -} +internal class InvalidTestRunException(message: String?) : RuntimeException(message) diff --git a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/SizedIterator.java b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/SizedIterator.java deleted file mode 100644 index 2090074..0000000 --- a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/SizedIterator.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.wear.watchface.dfx.memory; - -import java.util.Collection; -import java.util.Iterator; -import java.util.function.BiFunction; -import java.util.function.Function; - -/** - * A lazy iterator, allowing to generate all combinations of a set without getting into stack - * overflows. - */ -interface SizedIterator extends Iterator { - /** The size of the collection of elements that is generated */ - long getSize(); - - /** Constructs a sized iterator from a plain java iterator and a size. */ - static SizedIterator fromIterator(Iterator iterator, long size) { - return new SizedIterator() { - @Override - public long getSize() { - return size; - } - - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - @Override - public T next() { - return iterator.next(); - } - }; - } - - /** - * Lazily combines an existing iterator with a new collection of elements by taking every - * element of the new collection and combining it with every element of the existing iterator. - * The resulting iterator will have a size of iterator.size() * elements.size(). - * - * @param iterator the existing iterator - * @param elements the collection providing the new elements that are combined with the already - * generated ones. - * @param combineFn the function used to combine each element of the elements argument with each - * element of the existing iterator. - * @param mapFn the function used to map a single element of the elements collection to elements - * of the iterator, used when the existing iterator is empty and combineFn cannot be called. - * @return a new iterator with the combined elements. - * @param the elements of the iterator - * @param the elements of the collection - */ - static SizedIterator combine( - SizedIterator iterator, - Collection elements, - BiFunction combineFn, - Function mapFn) { - // if the current key does not have any configuration values, then ignore it - // and return the next iterator. - if (elements.size() == 0) { - return iterator; - } - - // if the rest iterator has no configuration values, then start a new iterator - // from the current keys. - if (!iterator.hasNext()) { - return fromIterator(elements.stream().map(mapFn).iterator(), elements.size()); - } - - // lazily consume each partial config from the rest iterator and append each value for the - // current key, producing new partial configs. - return new SizedIterator() { - private final long size = elements.size() * iterator.getSize(); - private Iterator crtValuesIter = elements.iterator(); - private T currentFromTail = iterator.next(); - - @Override - public long getSize() { - return size; - } - - @Override - public boolean hasNext() { - return crtValuesIter.hasNext() || iterator.hasNext(); - } - - @Override - public T next() { - if (!crtValuesIter.hasNext()) { - crtValuesIter = elements.iterator(); - currentFromTail = iterator.next(); - } - return combineFn.apply(currentFromTail, crtValuesIter.next()); - } - }; - } -} diff --git a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/SizedIterator.kt b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/SizedIterator.kt new file mode 100644 index 0000000..0b37da2 --- /dev/null +++ b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/SizedIterator.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.wear.watchface.dfx.memory + +/** + * A lazy iterator, allowing to generate all combinations of a set without getting into stack + * overflows. + */ +internal interface SizedIterator : Iterator { + /** The size of the collection of elements that is generated */ + fun getSize(): Long + + companion object { + /** Constructs a sized iterator from a plain java iterator and a size. */ + fun fromIterator(iterator: Iterator, size: Long): SizedIterator { + return object : SizedIterator { + override fun getSize(): Long { + return size + } + + override fun hasNext(): Boolean { + return iterator.hasNext() + } + + override fun next(): T { + return iterator.next() + } + } + } + + /** + * Lazily combines an existing iterator with a new collection of elements by taking every + * element of the new collection and combining it with every element of the existing + * iterator. The resulting iterator will have a size of iterator.size() * elements.size(). + * + * @param iterator the existing iterator + * @param elements the collection providing the new elements that are combined with the + * already generated ones. + * @param combineFn the function used to combine each element of the elements argument with + * each element of the existing iterator. + * @param mapFn the function used to map a single element of the elements collection to + * elements of the iterator, used when the existing iterator is empty and combineFn cannot + * be called. + * @return a new iterator with the combined elements. + * @param the elements of the iterator + * @param the elements of the collection + */ + fun combine( + iterator: SizedIterator, + elements: Collection, + combineFn: (T, U) -> T, + mapFn: (U) -> T + ): SizedIterator { + // if the current key does not have any configuration values, then ignore it + // and return the next iterator. + if (elements.isEmpty()) { + return iterator + } + + // if the rest iterator has no configuration values, then start a new iterator + // from the current keys. + if (!iterator.hasNext()) { + return fromIterator(elements.stream().map(mapFn).iterator(), elements.size.toLong()) + } + + // lazily consume each partial config from the rest iterator and append each value for + // the + // current key, producing new partial configs. + return object : SizedIterator { + private val _size = elements.size * iterator.getSize() + private var crtValuesIter = elements.iterator() + private var currentFromTail = iterator.next() + override fun getSize(): Long { + return _size + } + + override fun hasNext(): Boolean { + return crtValuesIter.hasNext() || iterator.hasNext() + } + + override fun next(): T { + if (!crtValuesIter.hasNext()) { + crtValuesIter = elements.iterator() + currentFromTail = iterator.next() + } + return combineFn(currentFromTail, crtValuesIter.next()) + } + } + } + } +} diff --git a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/TestFailedException.java b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/TestFailedException.kt similarity index 79% rename from play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/TestFailedException.java rename to play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/TestFailedException.kt index cfddf38..ec20409 100644 --- a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/TestFailedException.java +++ b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/TestFailedException.kt @@ -13,16 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.google.wear.watchface.dfx.memory; +package com.google.wear.watchface.dfx.memory /** * Exception thrown when the memory footprint test fails for an expected reason and the watch face * must be rejected from the store. */ -public class TestFailedException extends RuntimeException { - - public TestFailedException(String message) { - super(message); - } -} +class TestFailedException(message: String?) : RuntimeException(message) diff --git a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/TestResultFormatter.java b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/TestResultFormatter.kt similarity index 52% rename from play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/TestResultFormatter.java rename to play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/TestResultFormatter.kt index 9f797bc..f9e528f 100644 --- a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/TestResultFormatter.java +++ b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/TestResultFormatter.kt @@ -1,12 +1,12 @@ /* * Copyright 2023 Google LLC - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -14,23 +14,22 @@ * limitations under the License. */ -package com.google.wear.watchface.dfx.memory; +package com.google.wear.watchface.dfx.memory /** Annotates test results to make it easier for humans to read them. */ -class TestResultFormatter { - - static String formatSuccess(String successMessage) { - return String.format("[MEMORY_FOOTPRINT]: ✅PASS✅ %s ✅ ", successMessage); +internal object TestResultFormatter { + @JvmStatic + fun formatSuccess(successMessage: String): String { + return "[MEMORY_FOOTPRINT]: ✅PASS✅ $successMessage ✅ " } - static String formatFailure(String failedMessage) { - return String.format("[MEMORY_FOOTPRINT]: ❌FAIL❌ %s ❌ ", failedMessage); + @JvmStatic + fun formatFailure(failedMessage: String): String { + return "[MEMORY_FOOTPRINT]: ❌FAIL❌ $failedMessage ❌ " } - static String formatException(String exceptionMessage) { - return String.format( - "%s\n%s", - "❗❗❗❗ Something went wrong. Please retry or seek assistance.❗❗❗❗", - exceptionMessage); + @JvmStatic + fun formatException(exceptionMessage: String): String { + return "❗❗❗❗ Something went wrong. Please retry or seek assistance.❗❗❗❗\n$exceptionMessage" } } diff --git a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/UserConfigKey.java b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/UserConfigKey.java index 37cb1cf..8dbd716 100644 --- a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/UserConfigKey.java +++ b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/UserConfigKey.java @@ -148,11 +148,11 @@ static SizedIterator buildConfigSets(Iterable conf private static SizedIterator buildConfigSets(Iterator configs) { if (!configs.hasNext()) { - return SizedIterator.fromIterator(emptyIterator(), 0); + return SizedIterator.Companion.fromIterator(emptyIterator(), 0); } UserConfigKey head = configs.next(); SizedIterator tailExpanded = buildConfigSets(configs); - return SizedIterator.combine( + return SizedIterator.Companion.combine( tailExpanded, head.getConfigurationValues(), (configSet, configValue) -> configSet.plus(head, configValue), diff --git a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/UserConfigValue.java b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/UserConfigValue.java deleted file mode 100644 index bb459c7..0000000 --- a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/UserConfigValue.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.wear.watchface.dfx.memory; - -import static com.google.wear.watchface.dfx.memory.WatchFaceDocuments.getNodeAttribute; - -import java.util.Arrays; -import java.util.Objects; -import java.util.Optional; -import org.w3c.dom.Node; - -/** - * Represents a UserConfiguration value. For example, in the following XML: - * - *
{@code
- * 
- *   
- *   
- *     
- *     
- *   
- * 
- * }
- * - *

The UserConfigValues are: - for dateDisplayed: TRUE, FALSE, - for icon-id-option: 0, and 1. - */ -class UserConfigValue { - - public static UserConfigValue fromNode(Node node) { - return new UserConfigValue( - getNodeAttribute(node, "id") - .orElseThrow( - () -> new IllegalArgumentException("Node does not contain an id"))); - } - - /** - * The UserConfiguration children node names that are treated as configuration values. We ignore - * ColorOptions because they don't enable or disable particular DOM nodes. - */ - enum SupportedConfigs { - ListConfiguration, - BooleanConfiguration; - - static boolean isValidUserConfigNode(Node node) { - return Arrays.stream(SupportedConfigs.values()) - .map(Enum::toString) - .anyMatch(x -> x.equals(node.getNodeName())); - } - } - - private final String configValueId; - - public UserConfigValue(String configValueId) { - this.configValueId = configValueId; - } - - /** - * Selects the child of the node that must be evaluated based on this configuration id. It works - * on either a ListConfiguration with ListOptions or BooleanConfiguration with BooleanOptions. - * If no match with the same id as configValueId is found in the child nodes, then it returns - * None. - * - * @throws IllegalArgumentException If the node is not a ListConfiguration or a - * BooleanConfiguration. - */ - public Optional getNodeToEvaluate(Node node) { - if (!SupportedConfigs.isValidUserConfigNode(node)) { - throw new IllegalArgumentException( - String.format( - "Cannot extract User Configuration child from %s", node.getNodeName())); - } - for (int i = 0; i < node.getChildNodes().getLength(); i++) { - Node listOption = node.getChildNodes().item(i); - try { - if (listOption - .getAttributes() - .getNamedItem("id") - .getNodeValue() - .equals(configValueId)) { - return Optional.of(listOption); - } - } catch (NullPointerException e) { - // ignore child nodes without ID - } - } - // If no node is found to match the setting id - return Optional.empty(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof UserConfigValue)) return false; - UserConfigValue that = (UserConfigValue) o; - return Objects.equals(configValueId, that.configValueId); - } - - @Override - public int hashCode() { - return Objects.hash(configValueId); - } - - @Override - public String toString() { - return "UserConfigValue{" + "configValueId='" + configValueId + '\'' + '}'; - } -} diff --git a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/UserConfigValue.kt b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/UserConfigValue.kt new file mode 100644 index 0000000..46bb113 --- /dev/null +++ b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/UserConfigValue.kt @@ -0,0 +1,97 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.wear.watchface.dfx.memory + +import com.google.wear.watchface.dfx.memory.WatchFaceDocuments.getNodeAttribute +import org.w3c.dom.Node +import java.util.Arrays +import java.util.Optional + +/** + * Represents a UserConfiguration value. For example, in the following XML: + * + *

{@code
+ * 
+ *   
+ *   
+ *     
+ *     
+ *   
+ * 
+ * }
+ * + * The UserConfigValues are: - for dateDisplayed: TRUE, FALSE, - for icon-id-option: 0, and 1. + */ +internal data class UserConfigValue(private val configValueId: String) { + + /** + * The UserConfiguration children node names that are treated as configuration values. We ignore + * ColorOptions because they don't enable or disable particular DOM nodes. + */ + enum class SupportedConfigs { + ListConfiguration, + BooleanConfiguration; + + companion object { + @JvmStatic + fun isValidUserConfigNode(node: Node): Boolean { + return entries.any { it.toString() == node.nodeName } + } + } + } + + /** + * Selects the child of the node that must be evaluated based on this configuration id. It works + * on either a ListConfiguration with ListOptions or BooleanConfiguration with BooleanOptions. + * If no match with the same id as configValueId is found in the child nodes, then it returns + * None. + * + * @throws IllegalArgumentException If the node is not a ListConfiguration or a + * BooleanConfiguration. + */ + fun getNodeToEvaluate(node: Node): Optional { + if (!SupportedConfigs.isValidUserConfigNode(node)) { + throw IllegalArgumentException( + String.format("Cannot extract User Configuration child from %s", node.nodeName) + ) + } + for (i in 0 until node.childNodes.length) { + val listOption = node.childNodes.item(i) + try { + if ( + listOption.attributes.getNamedItem("id").nodeValue == + configValueId + ) { + return Optional.of(listOption) + } + } catch (e: NullPointerException) { + // ignore child nodes without ID + } + } + // If no node is found to match the setting id + return Optional.empty() + } + + companion object { + @JvmStatic + fun fromNode(node: Node): UserConfigValue { + return UserConfigValue( + getNodeAttribute(node, "id") + .orElseThrow { IllegalArgumentException("Node does not contain an id") } + ) + } + } +} \ No newline at end of file