1 /*
   2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package jdk.incubator.jpackage.internal;
  26 
  27 import java.io.*;
  28 import java.nio.charset.StandardCharsets;
  29 import java.nio.file.Files;
  30 import java.nio.file.Path;
  31 import java.util.HashMap;
  32 import java.util.Iterator;
  33 import java.util.List;
  34 import java.util.Map;
  35 import jdk.incubator.jpackage.internal.resources.ResourceLocator;
  36 import static org.hamcrest.CoreMatchers.is;
  37 import static org.hamcrest.CoreMatchers.not;
  38 import static org.junit.Assert.*;
  39 import org.junit.Rule;
  40 import org.junit.Test;
  41 import org.junit.rules.TemporaryFolder;
  42 
  43 public class OverridableResourceTest {
  44 
  45     @Rule
  46     public final TemporaryFolder tempFolder = new TemporaryFolder();
  47 
  48     @Test
  49     public void testDefault() throws IOException {
  50         byte[] actualBytes = saveToFile(new OverridableResource(DEFAULT_NAME));
  51 
  52         try (InputStream is = ResourceLocator.class.getResourceAsStream(
  53                 DEFAULT_NAME)) {
  54             assertArrayEquals(is.readAllBytes(), actualBytes);
  55         }
  56     }
  57 
  58     @Test
  59     public void testDefaultWithSubstitution() throws IOException {
  60         OverridableResource resource = new OverridableResource(DEFAULT_NAME);
  61 
  62         List<String> linesBeforeSubstitution = convertToStringList(saveToFile(
  63                 resource));
  64 
  65         if (SUBSTITUTION_DATA.size() != 1) {
  66             // Test setup issue
  67             throw new IllegalArgumentException(
  68                     "Substitution map should contain only a single entry");
  69         }
  70 
  71         resource.setSubstitutionData(SUBSTITUTION_DATA);
  72         List<String> linesAfterSubstitution = convertToStringList(saveToFile(
  73                 resource));
  74 
  75         assertEquals(linesBeforeSubstitution.size(), linesAfterSubstitution.size());
  76 
  77         Iterator<String> beforeIt = linesBeforeSubstitution.iterator();
  78         Iterator<String> afterIt = linesAfterSubstitution.iterator();
  79 
  80         var substitutionEntry = SUBSTITUTION_DATA.entrySet().iterator().next();
  81 
  82         boolean linesMismatch = false;
  83         while (beforeIt.hasNext()) {
  84             String beforeStr = beforeIt.next();
  85             String afterStr = afterIt.next();
  86 
  87             if (beforeStr.equals(afterStr)) {
  88                 assertFalse(beforeStr.contains(substitutionEntry.getKey()));
  89             } else {
  90                 linesMismatch = true;
  91                 assertTrue(beforeStr.contains(substitutionEntry.getKey()));
  92                 assertTrue(afterStr.contains(substitutionEntry.getValue()));
  93                 assertFalse(afterStr.contains(substitutionEntry.getKey()));
  94             }
  95         }
  96 
  97         assertTrue(linesMismatch);
  98     }
  99 
 100     @Test
 101     public void testCustom() throws IOException {
 102         testCustom(DEFAULT_NAME);
 103     }
 104 
 105     @Test
 106     public void testCustomNoDefault() throws IOException {
 107         testCustom(null);
 108     }
 109 
 110     private void testCustom(String defaultName) throws IOException {
 111         List<String> expectedResourceData = List.of("A", "B", "C");
 112 
 113         Path customFile = createCustomFile("foo", expectedResourceData);
 114 
 115         List<String> actualResourceData = convertToStringList(saveToFile(
 116                 new OverridableResource(defaultName)
 117                         .setPublicName(customFile.getFileName())
 118                         .setResourceDir(customFile.getParent())));
 119 
 120         assertArrayEquals(expectedResourceData.toArray(String[]::new),
 121                 actualResourceData.toArray(String[]::new));
 122     }
 123 
 124     @Test
 125     public void testCustomtWithSubstitution() throws IOException {
 126         testCustomtWithSubstitution(DEFAULT_NAME);
 127     }
 128 
 129     @Test
 130     public void testCustomtWithSubstitutionNoDefault() throws IOException {
 131         testCustomtWithSubstitution(null);
 132     }
 133 
 134     private void testCustomtWithSubstitution(String defaultName) throws IOException {
 135         final List<String> resourceData = List.of("A", "[BB]", "C", "Foo",
 136                 "GoodbyeHello");
 137         final Path customFile = createCustomFile("foo", resourceData);
 138 
 139         final Map<String, String> substitutionData = new HashMap(Map.of("B",
 140                 "Bar", "Foo", "B"));
 141         substitutionData.put("Hello", null);
 142 
 143         final List<String> expectedResourceData = List.of("A", "[BarBar]", "C",
 144                 "B", "Goodbye");
 145 
 146         final List<String> actualResourceData = convertToStringList(saveToFile(
 147                 new OverridableResource(defaultName)
 148                         .setPublicName(customFile.getFileName())
 149                         .setSubstitutionData(substitutionData)
 150                         .setResourceDir(customFile.getParent())));
 151         assertArrayEquals(expectedResourceData.toArray(String[]::new),
 152                 actualResourceData.toArray(String[]::new));
 153 
 154         // Don't call setPublicName()
 155         final Path dstFile = tempFolder.newFolder().toPath().resolve(customFile.getFileName());
 156         new OverridableResource(defaultName)
 157                 .setSubstitutionData(substitutionData)
 158                 .setResourceDir(customFile.getParent())
 159                 .saveToFile(dstFile);
 160         assertArrayEquals(expectedResourceData.toArray(String[]::new),
 161                 convertToStringList(Files.readAllBytes(dstFile)).toArray(
 162                         String[]::new));
 163 
 164         // Verify setSubstitutionData() stores a copy of passed in data
 165         Map<String, String> substitutionData2 = new HashMap(substitutionData);
 166         var resource = new OverridableResource(defaultName)
 167                 .setResourceDir(customFile.getParent());
 168 
 169         resource.setSubstitutionData(substitutionData2);
 170         substitutionData2.clear();
 171         Files.delete(dstFile);
 172         resource.saveToFile(dstFile);
 173         assertArrayEquals(expectedResourceData.toArray(String[]::new),
 174                 convertToStringList(Files.readAllBytes(dstFile)).toArray(
 175                         String[]::new));
 176     }
 177 
 178     @Test
 179     public void testNoDefault() throws IOException {
 180         Path dstFolder = tempFolder.newFolder().toPath();
 181         Path dstFile = dstFolder.resolve(Path.of("foo", "bar"));
 182 
 183         new OverridableResource(null).saveToFile(dstFile);
 184 
 185         assertFalse(dstFile.toFile().exists());
 186     }
 187 
 188     private final static String DEFAULT_NAME;
 189     private final static Map<String, String> SUBSTITUTION_DATA;
 190     static {
 191         if (Platform.isWindows()) {
 192             DEFAULT_NAME = "WinLauncher.template";
 193             SUBSTITUTION_DATA = Map.of("COMPANY_NAME", "Foo9090345");
 194         } else if (Platform.isLinux()) {
 195             DEFAULT_NAME = "template.control";
 196             SUBSTITUTION_DATA = Map.of("APPLICATION_PACKAGE", "Package1967");
 197         } else if (Platform.isMac()) {
 198             DEFAULT_NAME = "Info-lite.plist.template";
 199             SUBSTITUTION_DATA = Map.of("DEPLOY_BUNDLE_IDENTIFIER", "12345");
 200         } else {
 201             throw Platform.throwUnknownPlatformError();
 202         }
 203     }
 204 
 205     private byte[] saveToFile(OverridableResource resource) throws IOException {
 206         Path dstFile = tempFolder.newFile().toPath();
 207         resource.saveToFile(dstFile);
 208         assertThat(0, is(not(dstFile.toFile().length())));
 209 
 210         return Files.readAllBytes(dstFile);
 211     }
 212 
 213     private Path createCustomFile(String publicName, List<String> data) throws
 214             IOException {
 215         Path resourceFolder = tempFolder.newFolder().toPath();
 216         Path customFile = resourceFolder.resolve(publicName);
 217 
 218         Files.write(customFile, data);
 219 
 220         return customFile;
 221     }
 222 
 223     private static List<String> convertToStringList(byte[] data) {
 224         return List.of(new String(data, StandardCharsets.UTF_8).split("\\R"));
 225     }
 226 }