From 974e87fe6ed7b2c0fb021f19d4d596af4475c55e Mon Sep 17 00:00:00 2001 From: Alekseeva Yana Date: Fri, 12 Jan 2024 12:03:34 +0300 Subject: [PATCH 1/2] fix(#2764): caching logic changed, added StHash, StHashTest, fix tests --- .../eolang/maven/optimization/OptCached.java | 22 +---- .../org/eolang/maven/OptimizeMojoTest.java | 25 +++-- .../java/org/eolang/maven/ShakeMojoTest.java | 21 ++++- .../maven/optimization/OptCachedTest.java | 69 +++++++++++--- .../main/java/org/eolang/parser/EoSyntax.java | 13 ++- .../java/org/eolang/parser/PhiSyntax.java | 11 ++- .../main/java/org/eolang/parser/StHash.java | 79 ++++++++++++++++ eo-parser/src/main/resources/XMIR.xsd | 1 + .../java/org/eolang/parser/EoSyntaxTest.java | 12 +++ .../java/org/eolang/parser/PhiSyntaxTest.java | 11 +++ .../java/org/eolang/parser/StHashTest.java | 93 +++++++++++++++++++ 11 files changed, 302 insertions(+), 55 deletions(-) create mode 100644 eo-parser/src/main/java/org/eolang/parser/StHash.java create mode 100644 eo-parser/src/test/java/org/eolang/parser/StHashTest.java diff --git a/eo-maven-plugin/src/main/java/org/eolang/maven/optimization/OptCached.java b/eo-maven-plugin/src/main/java/org/eolang/maven/optimization/OptCached.java index f808fbfc94..4a47f35260 100644 --- a/eo-maven-plugin/src/main/java/org/eolang/maven/optimization/OptCached.java +++ b/eo-maven-plugin/src/main/java/org/eolang/maven/optimization/OptCached.java @@ -28,9 +28,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.attribute.BasicFileAttributes; -import java.time.ZonedDateTime; -import java.time.temporal.ChronoUnit; import java.util.Optional; import org.eolang.maven.AssembleMojo; import org.eolang.maven.Place; @@ -41,12 +38,6 @@ * Returns already optimized XML if it's found in the cache. * * @since 0.28.11 - * @todo #2746:30min Use checksum, not time. - * The following tests show that fetching from the cache doesn't work correctly: - * - {@link OptCachedTest#returnsFromCacheCorrectProgram(Path path)}, - * - {@link OptCachedTest#returnsFromCacheButTimesSaveAndExecuteDifferent(Path path)}. - * Need to fix the file validation from cache: using checksum, but not time. - * Don't forget to enable the tests. */ public final class OptCached implements Optimization { @@ -113,16 +104,11 @@ private Path cached(final XML xml) { */ private boolean contains(final XML xml) throws IOException { final Path path = this.cached(xml); - final Optional time = xml.xpath("/program/@time").stream().findFirst(); + final Optional hash = xml.xpath("/program/@hash").stream().findFirst(); final boolean res; - if (Files.exists(path) && time.isPresent()) { - res = Files.readAttributes(path, BasicFileAttributes.class) - .creationTime() - .toInstant() - .truncatedTo(ChronoUnit.MINUTES) - .equals( - ZonedDateTime.parse(time.get()).toInstant().truncatedTo(ChronoUnit.MINUTES) - ); + if (Files.exists(path) && hash.isPresent()) { + res = new XMLDocument(path).xpath("/program/@hash").stream().findFirst() + .equals(hash); } else { res = false; } diff --git a/eo-maven-plugin/src/test/java/org/eolang/maven/OptimizeMojoTest.java b/eo-maven-plugin/src/test/java/org/eolang/maven/OptimizeMojoTest.java index 36d1231348..b70d00389a 100644 --- a/eo-maven-plugin/src/test/java/org/eolang/maven/OptimizeMojoTest.java +++ b/eo-maven-plugin/src/test/java/org/eolang/maven/OptimizeMojoTest.java @@ -33,17 +33,18 @@ import java.util.stream.IntStream; import javax.xml.transform.TransformerFactory; import net.sf.saxon.TransformerFactoryImpl; -import org.cactoos.io.ResourceOf; +import org.cactoos.Text; +import org.cactoos.io.InputOf; import org.cactoos.text.TextOf; import org.eolang.jucs.ClasspathSource; import org.eolang.maven.util.HmBase; import org.eolang.parser.CheckPack; +import org.eolang.parser.EoSyntax; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.hamcrest.io.FileMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; @@ -113,16 +114,22 @@ void optimizesIfExpired(@TempDir final Path temp) throws Exception { * * @param temp Temporary test directory. * @throws Exception if unexpected error happened. - * @todo #2422:60min This test is unstable for now. - * We should resolve issues with unstable failures and only - * then enable the test. - * Also, see this issue. */ - @Disabled @Test void getsAlreadyOptimizedResultsFromCache(@TempDir final Path temp) throws Exception { - final TextOf cached = new TextOf( - new ResourceOf("org/eolang/maven/optimize/main.xml") + final Text cached = new TextOf( + new EoSyntax( + "test-it-4", + new InputOf( + String.join( + "\n", + "+alias stdout org.eolang.io.stdout", + "+package f\n", + "[x] > main", + " (stdout \"Hello!\" x).print > @" + ) + ) + ).parsed().toString() ); final Path cache = temp.resolve("cache"); final String hash = "abcdef1"; diff --git a/eo-maven-plugin/src/test/java/org/eolang/maven/ShakeMojoTest.java b/eo-maven-plugin/src/test/java/org/eolang/maven/ShakeMojoTest.java index 93f3393571..0a7f8ce645 100644 --- a/eo-maven-plugin/src/test/java/org/eolang/maven/ShakeMojoTest.java +++ b/eo-maven-plugin/src/test/java/org/eolang/maven/ShakeMojoTest.java @@ -29,15 +29,16 @@ import java.nio.file.Paths; import java.util.Map; import java.util.concurrent.TimeUnit; -import org.cactoos.io.ResourceOf; +import org.cactoos.Text; +import org.cactoos.io.InputOf; import org.cactoos.text.TextOf; import org.eolang.maven.util.HmBase; +import org.eolang.parser.EoSyntax; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.hamcrest.io.FileMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -82,11 +83,21 @@ void shakesSuccessfully(@TempDir final Path temp) throws IOException { ); } - @Disabled @Test void getsAlreadyShakenResultsFromCache(@TempDir final Path temp) throws Exception { - final TextOf cached = new TextOf( - new ResourceOf("org/eolang/maven/optimize/main.xml") + final Text cached = new TextOf( + new EoSyntax( + "test-it-4", + new InputOf( + String.join( + "\n", + "+alias stdout org.eolang.io.stdout", + "+package f\n", + "[x] > main", + " (stdout \"Hello!\" x).print > @" + ) + ) + ).parsed().toString() ); final Path cache = temp.resolve("cache"); final String hash = "abcdef1"; diff --git a/eo-maven-plugin/src/test/java/org/eolang/maven/optimization/OptCachedTest.java b/eo-maven-plugin/src/test/java/org/eolang/maven/optimization/OptCachedTest.java index 7098317237..733ad2ddef 100644 --- a/eo-maven-plugin/src/test/java/org/eolang/maven/optimization/OptCachedTest.java +++ b/eo-maven-plugin/src/test/java/org/eolang/maven/optimization/OptCachedTest.java @@ -26,25 +26,29 @@ import com.jcabi.xml.XML; import com.jcabi.xml.XMLDocument; import java.io.IOException; +import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import org.eolang.maven.util.HmBase; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.hamcrest.io.FileMatchers; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.xembly.Directives; +import org.xembly.ImpossibleModificationException; import org.xembly.Xembler; /** * Test case for {@link org.eolang.maven.optimization.OptCached}. * @since 0.28.12 */ +@SuppressWarnings("PMD.TooManyMethods") final class OptCachedTest { /** @@ -52,15 +56,14 @@ final class OptCachedTest { * * @param tmp Temp dir * @throws IOException if I/O fails - * @todo #2422:60min returnsFromCacheIfXmlAlreadyInCache: this test is unstable. - * We should resolve issues with unstable failures and only - * then enable the test. * Also, see this issue. */ - @Disabled @Test - void returnsFromCacheIfXmlAlreadyInCache(@TempDir final Path tmp) throws IOException { - final XML program = OptCachedTest.program(ZonedDateTime.now()); + void returnsFromCacheIfXmlAlreadyInCache(@TempDir final Path tmp) + throws IOException, ImpossibleModificationException, NoSuchAlgorithmException { + final XML program = OptCachedTest.updatedProgram( + OptCachedTest.program(ZonedDateTime.now()) + ); OptCachedTest.save(tmp, program); MatcherAssert.assertThat( "We expected that the program will be returned from the cache.", @@ -74,11 +77,12 @@ void returnsFromCacheIfXmlAlreadyInCache(@TempDir final Path tmp) throws IOExcep ); } - @Disabled @Test void returnsFromCacheButTimesSaveAndExecuteDifferent(@TempDir final Path tmp) - throws IOException { - final XML program = OptCachedTest.program(ZonedDateTime.now().minusMinutes(2)); + throws IOException, ImpossibleModificationException, NoSuchAlgorithmException { + final XML program = OptCachedTest.updatedProgram( + OptCachedTest.program(ZonedDateTime.now()) + ); OptCachedTest.save(tmp, program); MatcherAssert.assertThat( "We expected that the not immediately saved program will be returned from the cache.", @@ -92,13 +96,16 @@ void returnsFromCacheButTimesSaveAndExecuteDifferent(@TempDir final Path tmp) ); } - @Disabled @Test void returnsFromCacheCorrectProgram(@TempDir final Path tmp) - throws IOException { - final XML prev = OptCachedTest.program(ZonedDateTime.now(), "first program"); + throws IOException, ImpossibleModificationException, NoSuchAlgorithmException { + final XML prev = OptCachedTest.updatedProgram( + OptCachedTest.program(ZonedDateTime.now(), "first program") + ); OptCachedTest.save(tmp, prev); - final XML current = OptCachedTest.program(ZonedDateTime.now(), "second program"); + final XML current = OptCachedTest.updatedProgram( + OptCachedTest.program(ZonedDateTime.now(), "second program") + ); MatcherAssert.assertThat( "Expecting current program to be compiled, but prev program was returned from cache.", new OptCached( @@ -199,9 +206,43 @@ private static XML program(final ZonedDateTime time, final String something) { .add("program") .attr("name", "main") .attr("time", time.format(DateTimeFormatter.ISO_INSTANT)) + .add("objects") .attr("something", something) .up() ).xmlQuietly() ); } + + /** + * Adds attribute "hash" in EO program for tests. + * @param xml XML. + * @return XML representation of program. + */ + private static XML updatedProgram(final XML xml) + throws NoSuchAlgorithmException, ImpossibleModificationException { + final String hash = OptCachedTest.getHash(xml); + return new XMLDocument( + new Xembler( + new Directives() + .xpath("//program").attr("hash", hash) + ).apply(xml.node()) + ); + } + + /** + * Return hash code in EO program for tests. + * @param xml XML. + * @return String hash code representation of program. + */ + private static String getHash(final XML xml) throws NoSuchAlgorithmException { + final MessageDigest algorithm = MessageDigest.getInstance("MD5"); + final String program = xml.nodes("/program/objects").toString(); + final byte[] code = algorithm.digest(program.getBytes()); + final BigInteger number = new BigInteger(1, code); + final StringBuilder hash = new StringBuilder(number.toString(16)); + while (hash.length() < 32) { + hash.insert(0, "0"); + } + return hash.toString(); + } } diff --git a/eo-parser/src/main/java/org/eolang/parser/EoSyntax.java b/eo-parser/src/main/java/org/eolang/parser/EoSyntax.java index 6200d5f8d1..825354b85f 100644 --- a/eo-parser/src/main/java/org/eolang/parser/EoSyntax.java +++ b/eo-parser/src/main/java/org/eolang/parser/EoSyntax.java @@ -26,6 +26,7 @@ import com.jcabi.log.Logger; import com.jcabi.xml.XML; import com.jcabi.xml.XMLDocument; +import com.yegor256.xsline.Xsline; import java.io.IOException; import java.util.List; import org.antlr.v4.runtime.CommonTokenStream; @@ -70,7 +71,7 @@ public EoSyntax(final String nme, final Input ipt) { } /** - * Compile it to XML and save. + * Compile it to XML. * *

No exception will be thrown if the syntax is invalid. In any case, XMIR will * be generated and saved. Read it in order to find the errors, @@ -92,10 +93,12 @@ public XML parsed() throws IOException { parser.addErrorListener(spy); final XeEoListener xel = new XeEoListener(this.name); new ParseTreeWalker().walk(xel, parser.program()); - final XML dom = new XMLDocument( - new Xembler( - new Directives(xel).append(spy) - ).domQuietly() + final XML dom = new Xsline(new StHash()).pass( + new XMLDocument( + new Xembler( + new Directives(xel).append(spy) + ).domQuietly() + ) ); new Schema(dom).check(); if (spy.size() == 0) { diff --git a/eo-parser/src/main/java/org/eolang/parser/PhiSyntax.java b/eo-parser/src/main/java/org/eolang/parser/PhiSyntax.java index c06438cdd0..79a7265632 100644 --- a/eo-parser/src/main/java/org/eolang/parser/PhiSyntax.java +++ b/eo-parser/src/main/java/org/eolang/parser/PhiSyntax.java @@ -26,6 +26,7 @@ import com.jcabi.log.Logger; import com.jcabi.xml.XML; import com.jcabi.xml.XMLDocument; +import com.yegor256.xsline.Xsline; import java.io.IOException; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; @@ -84,10 +85,12 @@ public XML parsed() throws IOException { parser.removeErrorListeners(); parser.addErrorListener(spy); new ParseTreeWalker().walk(xel, parser.program()); - final XML dom = new XMLDocument( - new Xembler( - new Directives(xel).append(spy) - ).domQuietly() + final XML dom = new Xsline(new StHash()).pass( + new XMLDocument( + new Xembler( + new Directives(xel).append(spy) + ).domQuietly() + ) ); new Schema(dom).check(); if (spy.size() == 0) { diff --git a/eo-parser/src/main/java/org/eolang/parser/StHash.java b/eo-parser/src/main/java/org/eolang/parser/StHash.java new file mode 100644 index 0000000000..21372edb9d --- /dev/null +++ b/eo-parser/src/main/java/org/eolang/parser/StHash.java @@ -0,0 +1,79 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2023 Objectionary.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.eolang.parser; + +import com.jcabi.xml.XML; +import com.jcabi.xml.XMLDocument; +import com.yegor256.xsline.StEnvelope; +import com.yegor256.xsline.StLambda; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import org.xembly.Directives; +import org.xembly.Xembler; + +/** + * Add attribute 'hash' in node 'program' in XML. + * Hash is generated by MD5 using node '/program/objects'. + * Returns already XML with hash. + * + * @since 0.35.0 + */ +public final class StHash extends StEnvelope { + + /** + * Returns XML with attribute 'hash'. + */ + public StHash() { + super( + new StLambda(( + position, + xml) -> + new XMLDocument( + new Xembler( + new Directives().xpath("//program").attr("hash", getHash(xml)) + ).apply(xml.node()) + ) + ) + ); + } + + /** + * Return program hash using node "/program/objects". + * @param xml XML. + * @return String hash of this XML. + * @throws NoSuchAlgorithmException If fails. + */ + private static String getHash(final XML xml) throws NoSuchAlgorithmException { + final MessageDigest algorithm = MessageDigest.getInstance("MD5"); + final String program = xml.nodes("/program/objects").toString(); + final byte[] code = algorithm.digest(program.getBytes()); + final BigInteger number = new BigInteger(1, code); + final StringBuilder hash = new StringBuilder(number.toString(16)); + while (hash.length() < 32) { + hash.insert(0, "0"); + } + return hash.toString(); + } +} diff --git a/eo-parser/src/main/resources/XMIR.xsd b/eo-parser/src/main/resources/XMIR.xsd index 2032196dc3..6a7e95ff67 100644 --- a/eo-parser/src/main/resources/XMIR.xsd +++ b/eo-parser/src/main/resources/XMIR.xsd @@ -113,6 +113,7 @@ SOFTWARE. + diff --git a/eo-parser/src/test/java/org/eolang/parser/EoSyntaxTest.java b/eo-parser/src/test/java/org/eolang/parser/EoSyntaxTest.java index 6757a7fe3d..24f03d8094 100644 --- a/eo-parser/src/test/java/org/eolang/parser/EoSyntaxTest.java +++ b/eo-parser/src/test/java/org/eolang/parser/EoSyntaxTest.java @@ -47,6 +47,7 @@ * * @since 0.1 */ +@SuppressWarnings("PMD.TooManyMethods") final class EoSyntaxTest { @Test void parsesSimpleCode() throws Exception { @@ -175,6 +176,17 @@ void parsesDefinition() throws IOException { ); } + @Test + void containsHash() throws IOException { + MatcherAssert.assertThat( + new EoSyntax( + "test-it-5", + new InputOf("[v] > p\n f.write > @\n") + ).parsed(), + XhtmlMatchers.hasXPath("/program/@hash") + ); + } + @Test void parsesMethodCalls() throws IOException { MatcherAssert.assertThat( diff --git a/eo-parser/src/test/java/org/eolang/parser/PhiSyntaxTest.java b/eo-parser/src/test/java/org/eolang/parser/PhiSyntaxTest.java index 835c490614..4da109a7dd 100644 --- a/eo-parser/src/test/java/org/eolang/parser/PhiSyntaxTest.java +++ b/eo-parser/src/test/java/org/eolang/parser/PhiSyntaxTest.java @@ -46,4 +46,15 @@ void addsError() throws IOException { ) ); } + + @Test + void containsHash() throws IOException { + MatcherAssert.assertThat( + "Result XML must contain hash", + new PhiSyntax( + "empty ↦ Φ.org.eolang.bytes" + ).parsed(), + XhtmlMatchers.hasXPath("/program/@hash") + ); + } } diff --git a/eo-parser/src/test/java/org/eolang/parser/StHashTest.java b/eo-parser/src/test/java/org/eolang/parser/StHashTest.java new file mode 100644 index 0000000000..8ce17a3b6e --- /dev/null +++ b/eo-parser/src/test/java/org/eolang/parser/StHashTest.java @@ -0,0 +1,93 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2023 Objectionary.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.eolang.parser; + +import com.jcabi.matchers.XhtmlMatchers; +import com.jcabi.xml.XML; +import com.jcabi.xml.XMLDocument; +import com.yegor256.xsline.Xsline; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import org.hamcrest.MatcherAssert; +import org.junit.jupiter.api.Test; +import org.xembly.Directives; +import org.xembly.Xembler; + +/** + * Test case for {@link StHash}. + * + * @since 0.35.0 + */ +final class StHashTest { + + @Test + void isHashInXml() { + MatcherAssert.assertThat( + "We should get XML, which has new attribute 'hash', but didn't", + new Xsline(new StHash()).pass(program()), + XhtmlMatchers.hasXPath("/program/@hash") + ); + } + + @Test + void checksHash() + throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { + MatcherAssert.assertThat( + "We should get the same hash code, but didn't", + new Xsline(new StHash()).pass(program()), + XhtmlMatchers.hasXPath("/program/@hash", hash(program())) + ); + } + + /** + * Generates EO program for tests with specified time and context. + * @return XML representation of program. + */ + private static XML program() { + return new XMLDocument( + new Xembler( + new Directives() + .add("program") + .attr("name", "main") + .add("objects") + .attr("object", "10") + .up() + ).xmlQuietly() + ); + } + + /** + * Returns hash code of EO program for tests. + * @param xml XML. + * @return String hash code of program. + */ + private static String hash(final XML xml) + throws NoSuchMethodException, InvocationTargetException, + IllegalAccessException { + final Class clazz = StHash.class; + final Method method = clazz.getDeclaredMethod("getHash", XML.class); + method.setAccessible(true); + return method.invoke(null, xml).toString(); + } +} From 71ed70058d208b557b48f915bf359cf5101329c6 Mon Sep 17 00:00:00 2001 From: Alekseeva Yana Date: Fri, 12 Jan 2024 13:47:27 +0300 Subject: [PATCH 2/2] fix(#2764): deleted reflection, created class Hash --- .../maven/optimization/OptCachedTest.java | 22 +------- .../main/java/org/eolang/parser/StHash.java | 53 +++++++++++++------ .../java/org/eolang/parser/StHashTest.java | 22 ++------ 3 files changed, 43 insertions(+), 54 deletions(-) diff --git a/eo-maven-plugin/src/test/java/org/eolang/maven/optimization/OptCachedTest.java b/eo-maven-plugin/src/test/java/org/eolang/maven/optimization/OptCachedTest.java index 733ad2ddef..a3b1383d69 100644 --- a/eo-maven-plugin/src/test/java/org/eolang/maven/optimization/OptCachedTest.java +++ b/eo-maven-plugin/src/test/java/org/eolang/maven/optimization/OptCachedTest.java @@ -26,15 +26,14 @@ import com.jcabi.xml.XML; import com.jcabi.xml.XMLDocument; import java.io.IOException; -import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; -import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import org.eolang.maven.util.HmBase; +import org.eolang.parser.StHash; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.hamcrest.io.FileMatchers; @@ -220,7 +219,7 @@ private static XML program(final ZonedDateTime time, final String something) { */ private static XML updatedProgram(final XML xml) throws NoSuchAlgorithmException, ImpossibleModificationException { - final String hash = OptCachedTest.getHash(xml); + final String hash = new StHash.Hash(xml).getHash(); return new XMLDocument( new Xembler( new Directives() @@ -228,21 +227,4 @@ private static XML updatedProgram(final XML xml) ).apply(xml.node()) ); } - - /** - * Return hash code in EO program for tests. - * @param xml XML. - * @return String hash code representation of program. - */ - private static String getHash(final XML xml) throws NoSuchAlgorithmException { - final MessageDigest algorithm = MessageDigest.getInstance("MD5"); - final String program = xml.nodes("/program/objects").toString(); - final byte[] code = algorithm.digest(program.getBytes()); - final BigInteger number = new BigInteger(1, code); - final StringBuilder hash = new StringBuilder(number.toString(16)); - while (hash.length() < 32) { - hash.insert(0, "0"); - } - return hash.toString(); - } } diff --git a/eo-parser/src/main/java/org/eolang/parser/StHash.java b/eo-parser/src/main/java/org/eolang/parser/StHash.java index 21372edb9d..b75c2ff418 100644 --- a/eo-parser/src/main/java/org/eolang/parser/StHash.java +++ b/eo-parser/src/main/java/org/eolang/parser/StHash.java @@ -35,7 +35,6 @@ /** * Add attribute 'hash' in node 'program' in XML. - * Hash is generated by MD5 using node '/program/objects'. * Returns already XML with hash. * * @since 0.35.0 @@ -52,7 +51,7 @@ public StHash() { xml) -> new XMLDocument( new Xembler( - new Directives().xpath("//program").attr("hash", getHash(xml)) + new Directives().xpath("//program").attr("hash", new Hash(xml).getHash()) ).apply(xml.node()) ) ) @@ -60,20 +59,44 @@ public StHash() { } /** - * Return program hash using node "/program/objects". - * @param xml XML. - * @return String hash of this XML. - * @throws NoSuchAlgorithmException If fails. + * Hash is generated by MD5 using node '/program/objects'. + * Returns program hash. + * + * @since 0.35.0 */ - private static String getHash(final XML xml) throws NoSuchAlgorithmException { - final MessageDigest algorithm = MessageDigest.getInstance("MD5"); - final String program = xml.nodes("/program/objects").toString(); - final byte[] code = algorithm.digest(program.getBytes()); - final BigInteger number = new BigInteger(1, code); - final StringBuilder hash = new StringBuilder(number.toString(16)); - while (hash.length() < 32) { - hash.insert(0, "0"); + public static final class Hash { + + /** + * XML for which the hash code will be made. + */ + private final XML xml; + + /** + * The constructor. + * + * @param xml XML for which the hash code will be made. + */ + public Hash(final XML xml) { + this.xml = xml; + } + + /** + * Return program hash using node "/program/objects". + * + * @return String hash of this XML. + * @throws NoSuchAlgorithmException If fails. + */ + public String getHash() throws NoSuchAlgorithmException { + final MessageDigest algorithm = MessageDigest.getInstance("MD5"); + final String program = this.xml.nodes("/program/objects").toString(); + final byte[] code = algorithm.digest(program.getBytes()); + final BigInteger number = new BigInteger(1, code); + final StringBuilder hash = new StringBuilder(number.toString(16)); + while (hash.length() < 32) { + hash.insert(0, "0"); + } + return hash.toString(); } - return hash.toString(); } } + diff --git a/eo-parser/src/test/java/org/eolang/parser/StHashTest.java b/eo-parser/src/test/java/org/eolang/parser/StHashTest.java index 8ce17a3b6e..20cd3b7b2f 100644 --- a/eo-parser/src/test/java/org/eolang/parser/StHashTest.java +++ b/eo-parser/src/test/java/org/eolang/parser/StHashTest.java @@ -27,8 +27,7 @@ import com.jcabi.xml.XML; import com.jcabi.xml.XMLDocument; import com.yegor256.xsline.Xsline; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; +import java.security.NoSuchAlgorithmException; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Test; import org.xembly.Directives; @@ -51,12 +50,11 @@ void isHashInXml() { } @Test - void checksHash() - throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { + void checksHash() throws NoSuchAlgorithmException { MatcherAssert.assertThat( "We should get the same hash code, but didn't", new Xsline(new StHash()).pass(program()), - XhtmlMatchers.hasXPath("/program/@hash", hash(program())) + XhtmlMatchers.hasXPath("/program/@hash", new StHash.Hash(program()).getHash()) ); } @@ -76,18 +74,4 @@ private static XML program() { ).xmlQuietly() ); } - - /** - * Returns hash code of EO program for tests. - * @param xml XML. - * @return String hash code of program. - */ - private static String hash(final XML xml) - throws NoSuchMethodException, InvocationTargetException, - IllegalAccessException { - final Class clazz = StHash.class; - final Method method = clazz.getDeclaredMethod("getHash", XML.class); - method.setAccessible(true); - return method.invoke(null, xml).toString(); - } }