001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.util.tester;
018
019import static org.junit.jupiter.api.Assertions.assertEquals;
020
021import java.io.File;
022import java.io.FileOutputStream;
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.PrintWriter;
026import java.net.URISyntaxException;
027import java.net.URL;
028
029import org.apache.wicket.markup.MarkupParser;
030import org.apache.wicket.markup.MarkupStream;
031import org.apache.wicket.util.diff.Diff;
032import org.apache.wicket.util.diff.DifferentiationFailedException;
033import org.apache.wicket.util.io.Streams;
034import org.apache.wicket.util.lang.Args;
035import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
036import org.apache.wicket.util.string.StringList;
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
039
040/**
041 * This is a utility class. It serves two purposes.
042 * <p>
043 * First: compare a string output generated by wicket with a file's content (expected result).
044 * <p>
045 * Second: Create/replace the expected result file with the new content, if a system property has be
046 * made available like -Dwicket.replace.expected.results=true
047 *
048 * @author Juergen Donnerstag
049 */
050public final class DiffUtil
051{
052        private static final Logger log = LoggerFactory.getLogger(DiffUtil.class);
053        private static final String ENCODING = "UTF-8";
054
055        /**
056         * Replace the expected result file with the current output.
057         *
058         * @param document
059         *            How the expected result should look like
060         * @param clazz
061         *            Used to load the file (relative to clazz package)
062         * @param file
063         *            The name of the expected result file to be created
064         * @throws IOException
065         */
066        public static void replaceExpectedResultFile(final String document, final Class<?> clazz,
067                final String file) throws IOException
068        {
069                String filename = clazz.getPackage().getName();
070                filename = filename.replace('.', '/');
071                filename += "/" + file;
072
073                try
074                {
075                        final URL url = clazz.getClassLoader().getResource(filename);
076                        filename = new File(url.toURI()).getAbsolutePath();
077                }
078                catch (URISyntaxException ex)
079                {
080                        throw new IOException(ex);
081                }
082
083                filename = filename.replaceAll("([/\\\\])target\\1test-classes\\1", "$1src$1test$1java$1");
084                PrintWriter out = new PrintWriter(new FileOutputStream(filename));
085                out.print(document);
086                out.close();
087        }
088
089        /**
090         * Compare the output generated by Wicket ("document") with the a previously generated file
091         * which contains the expected result.
092         *
093         * @param document
094         *            Current output
095         * @param file
096         *            Expected output
097         * @param clazz
098         *            Used to load the file (relative to clazz package)
099         * @param failWithAssert
100         * @return true, if equal
101         * @throws IOException
102         */
103        public static boolean validatePage(String document, final Class<?> clazz,
104                final String file, boolean failWithAssert) throws IOException
105        {
106                Args.notNull(document, "document");
107
108                String filename = clazz.getPackage().getName();
109                filename = filename.replace('.', '/');
110                filename += "/" + file;
111
112                InputStream in = clazz.getClassLoader().getResourceAsStream(filename);
113                if (in == null)
114                {
115                        throw new IOException("File not found: " + filename);
116                }
117
118                String reference = Streams.readString(in, ENCODING);
119
120                // replace all line endings with unix style line ending
121                reference = reference.replaceAll("\n\r", "\n");
122                reference = reference.replaceAll("\r\n", "\n");
123
124                // replace all line endings with unix style line ending
125                document = document.replaceAll("\n\r", "\n");
126                document = document.replaceAll("\r\n", "\n");
127
128                boolean equals = compareMarkup(document, reference);
129                if (equals == false)
130                {
131                        // Change the condition to true, if you want to make the new output
132                        // the reference output for future tests. That is, it is regarded as
133                        // correct. It'll replace the current reference files. Thus change
134                        // it only for one test-run.
135                        // -Dwicket.replace.expected.results=true
136                        if (Boolean.getBoolean("wicket.replace.expected.results"))
137                        {
138                                in.close();
139                                in = null;
140
141                                replaceExpectedResultFile(document, clazz, file);
142                                return true;
143                        }
144
145                        log.error("File name: " + file);
146                        /*  */
147                        log.error("===================");
148                        log.error(reference);
149                        log.error("===================");
150
151                        log.error(document);
152                        log.error("===================");
153                        /* */
154
155                        String[] test1 = StringList.tokenize(reference, "\n").toArray();
156                        String[] test2 = StringList.tokenize(document, "\n").toArray();
157                        Diff df = new Diff(test1);
158                        try
159                        {
160                                df.diff(test2);
161                        }
162                        catch (DifferentiationFailedException e)
163                        {
164                                throw new RuntimeException(e);
165                        }
166
167                        // System.out.println(r.toString());
168
169                        if (failWithAssert)
170                        {
171                                assertEquals(reference, document, filename);
172                        }
173                }
174
175                return equals;
176        }
177
178        /**
179         * @param a
180         *            String a
181         * @param b
182         *            String b
183         * @return True if the two strings have the same markup tags
184         */
185        private static boolean compareMarkup(final String a, final String b)
186        {
187                try
188                {
189                        // Parse a and b into markup and compare
190                        final MarkupStream amarkup = new MarkupStream(new MarkupParser(a).parse());
191                        final MarkupStream bmarkup = new MarkupStream(new MarkupParser(b).parse());
192                        return amarkup.equalTo(bmarkup);
193                }
194                catch (IOException e)
195                {
196                        log.error(e.getMessage(), e);
197                }
198                catch (ResourceStreamNotFoundException e)
199                {
200                        log.error(e.getMessage(), e);
201                }
202                return false;
203        }
204
205}