Ticket #16010: v2-0017-testutils-add-PluginServer.patch

File v2-0017-testutils-add-PluginServer.patch, 9.9 KB (added by ris, 15 months ago)
  • new file test/unit/org/openstreetmap/josm/testutils/PluginServer.java

    From 00ce25b75f9c8a000ffdc82e61507dfc68da1448 Mon Sep 17 00:00:00 2001
    From: Robert Scott <code@humanleg.org.uk>
    Date: Sun, 15 Apr 2018 11:48:19 +0100
    Subject: [PATCH v2 17/28] testutils: add PluginServer
    
    ---
     .../openstreetmap/josm/testutils/PluginServer.java | 249 +++++++++++++++++++++
     1 file changed, 249 insertions(+)
     create mode 100644 test/unit/org/openstreetmap/josm/testutils/PluginServer.java
    
    diff --git a/test/unit/org/openstreetmap/josm/testutils/PluginServer.java b/test/unit/org/openstreetmap/josm/testutils/PluginServer.java
    new file mode 100644
    index 000000000..4e0586704
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.testutils;
     3
     4import java.io.File;
     5import java.io.IOException;
     6import java.nio.file.Path;
     7import java.util.HashMap;
     8import java.util.List;
     9import java.util.Map;
     10import java.util.Objects;
     11import java.util.jar.JarFile;
     12import java.util.stream.Collectors;
     13
     14import org.openstreetmap.josm.TestUtils;
     15import org.openstreetmap.josm.tools.Logging;
     16
     17import org.junit.runner.Description;
     18import org.junit.runners.model.Statement;
     19
     20import com.google.common.collect.ImmutableList;
     21
     22import com.github.tomakehurst.wiremock.client.MappingBuilder;
     23import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
     24import com.github.tomakehurst.wiremock.client.WireMock;
     25import com.github.tomakehurst.wiremock.core.Options;
     26import com.github.tomakehurst.wiremock.junit.WireMockRule;
     27import com.github.tomakehurst.wiremock.WireMockServer;
     28
     29import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
     30
     31public class PluginServer {
     32    public static class RemotePlugin {
     33        private final File srcJar;
     34        private final Map<String, String> attrOverrides;
     35        private final String pluginName;
     36        private final String pluginURL;
     37
     38        public RemotePlugin(
     39            final File srcJar
     40        ) {
     41            this(srcJar, null, null, null);
     42        }
     43
     44        public RemotePlugin(
     45            final File srcJar,
     46            final Map<String, String> attrOverrides
     47        ) {
     48            this(srcJar, attrOverrides, null, null);
     49        }
     50
     51        public RemotePlugin(
     52            final File srcJar,
     53            final Map<String, String> attrOverrides,
     54            final String pluginName
     55        ) {
     56            this(srcJar, attrOverrides, pluginName, null);
     57        }
     58
     59        public RemotePlugin(
     60            final File srcJar,
     61            final Map<String, String> attrOverrides,
     62            final String pluginName,
     63            final String pluginURL
     64        ) {
     65            this.srcJar = srcJar;
     66            this.attrOverrides = attrOverrides;
     67            this.pluginName = pluginName;
     68            this.pluginURL = pluginURL;
     69        }
     70
     71        @Override
     72        public int hashCode() {
     73            return Objects.hash(this.srcJar, this.attrOverrides, this.pluginName, this.pluginURL, this.getClass());
     74        }
     75
     76        public String getRemotePluginsListManifestSection() {
     77            final Map<String, String> attrs = new HashMap<String, String>();
     78            JarFile jarFile = null;
     79
     80            if (srcJar != null) {
     81                try {
     82                    jarFile = new JarFile(srcJar, false);
     83                    jarFile.getManifest().getMainAttributes().entrySet().stream().forEach(
     84                        entry -> attrs.put(entry.getKey().toString(), entry.getValue().toString())
     85                    );
     86                } catch (IOException e) {
     87                    Logging.warn(
     88                        "Failed to open {0} as a jar file. Using empty initial manifest. Error was: {1}",
     89                        srcJar,
     90                        e
     91                    );
     92                } finally {
     93                    if (jarFile != null) {
     94                        try {
     95                            jarFile.close();
     96                        } catch (IOException e) {
     97                            Logging.warn(
     98                                "Somehow failed to close jar file {0}. Error was: {1}",
     99                                srcJar,
     100                                e
     101                            );
     102                        }
     103                    }
     104                }
     105            }
     106
     107            if (this.attrOverrides != null) {
     108                attrs.putAll(this.attrOverrides);
     109            }
     110
     111            return attrs.entrySet().stream().filter(entry -> entry.getValue() != null).map(
     112                entry -> String.format("\t%s: %s\n", entry.getKey(), entry.getValue())
     113            ).collect(Collectors.joining());
     114        }
     115
     116        private String getJarPathBeneathFilesDir() {
     117            if (this.srcJar != null) {
     118                final Path jarPath = this.srcJar.toPath().toAbsolutePath().normalize();
     119                final Path filesRootPath = new File(TestUtils.getTestDataRoot()).toPath().toAbsolutePath().resolve("__files").normalize();
     120
     121                if (jarPath.startsWith(filesRootPath)) {
     122                    return filesRootPath.relativize(jarPath).toString();
     123                }
     124            }
     125            return null;
     126        }
     127
     128        protected String getPluginURLPath() {
     129            final String jarPathBeneathFilesDir = this.getJarPathBeneathFilesDir();
     130
     131            if (jarPathBeneathFilesDir != null) {
     132                return "/" + jarPathBeneathFilesDir;
     133            }
     134
     135            return String.format("/%h/%s.jar", this.hashCode(), pluginName != null ? pluginName : Integer.toHexString(this.hashCode()));
     136        }
     137
     138        public String getPluginURL(Integer port) {
     139            if (this.pluginURL != null) {
     140                return this.pluginURL;
     141            } else if (port != null && this.getJarPathBeneathFilesDir() != null) {
     142                return String.format("http://localhost:%s%s", port, this.getPluginURLPath());
     143            }
     144            return "http://example.com" + this.getPluginURLPath();
     145        }
     146
     147        public String getName() {
     148            if (this.pluginName != null) {
     149                return this.pluginName;
     150            } else if (this.srcJar != null) {
     151                return this.srcJar.getName().split("\\.", 2)[0];
     152            }
     153            return Integer.toHexString(this.hashCode());
     154        }
     155
     156        public String getRemotePluginsListSection(Integer port) {
     157            return String.format(
     158                "%s.jar;%s\n%s",
     159                this.getName(),
     160                this.getPluginURL(port),
     161                this.getRemotePluginsListManifestSection()
     162            );
     163        }
     164
     165        public MappingBuilder getMappingBuilder() {
     166            final String jarPathBeneathFilesDir = this.getJarPathBeneathFilesDir();
     167
     168            if (jarPathBeneathFilesDir != null) {
     169                return WireMock.get(WireMock.urlMatching(this.getPluginURLPath()));
     170            }
     171            return null;
     172        }
     173
     174        public ResponseDefinitionBuilder getResponseDefinitionBuilder() {
     175            final String jarPathBeneathFilesDir = this.getJarPathBeneathFilesDir();
     176
     177            if (jarPathBeneathFilesDir != null) {
     178                return WireMock.aResponse().withStatus(200).withHeader("Content-Type", "application/java-archive").withBodyFile(
     179                    jarPathBeneathFilesDir
     180                );
     181            }
     182            return null;
     183        }
     184    }
     185
     186    protected final List<RemotePlugin> pluginList;
     187
     188    public PluginServer(RemotePlugin... remotePlugins) {
     189        this.pluginList = ImmutableList.copyOf(remotePlugins);
     190    }
     191
     192    public void applyToWireMockServer(WireMockServer wireMockServer) {
     193        // first add the plugins list
     194        wireMockServer.stubFor(
     195            WireMock.get(WireMock.urlEqualTo("/plugins")).willReturn(
     196                WireMock.aResponse().withStatus(200).withHeader("Content-Type", "text/plain").withBody(
     197                    this.pluginList.stream().map(
     198                        remotePlugin -> remotePlugin.getRemotePluginsListSection(wireMockServer.port())
     199                    ).collect(Collectors.joining())
     200                )
     201            )
     202        );
     203
     204        // now add each file that we're able to serve
     205        for (final RemotePlugin remotePlugin : this.pluginList) {
     206            final MappingBuilder mappingBuilder = remotePlugin.getMappingBuilder();
     207            final ResponseDefinitionBuilder responseDefinitionBuilder = remotePlugin.getResponseDefinitionBuilder();
     208
     209            if (mappingBuilder != null && responseDefinitionBuilder != null) {
     210                wireMockServer.stubFor(
     211                    remotePlugin.getMappingBuilder().willReturn(remotePlugin.getResponseDefinitionBuilder())
     212                );
     213            }
     214        }
     215    }
     216
     217    public PluginServerRule asWireMockRule() {
     218        return this.asWireMockRule(
     219            options().dynamicPort().usingFilesUnderDirectory(TestUtils.getTestDataRoot()),
     220            true
     221        );
     222    }
     223
     224    public PluginServerRule asWireMockRule(Options ruleOptions, boolean failOnUnmatchedRequests) {
     225        return new PluginServerRule(ruleOptions, failOnUnmatchedRequests);
     226    }
     227
     228    public class PluginServerRule extends WireMockRule {
     229        public PluginServerRule(Options ruleOptions, boolean failOnUnmatchedRequests) {
     230            super(ruleOptions, failOnUnmatchedRequests);
     231        }
     232
     233        public PluginServer getPluginServer() {
     234            return PluginServer.this;
     235        }
     236
     237        @Override
     238        public Statement apply(Statement base, Description description) {
     239            return super.apply(new Statement() {
     240                @Override
     241                @SuppressWarnings("unchecked")
     242                public void evaluate() throws Throwable {
     243                    PluginServer.this.applyToWireMockServer(PluginServerRule.this);
     244                    base.evaluate();
     245                }
     246            }, description);
     247        }
     248    }
     249}