/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.plugin.typescript.dto; import org.eclipse.che.api.core.util.SystemInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Stream; import static java.util.stream.Collectors.toList; /** * Integration test of TypeScriptDTOGeneratorMojo * It uses docker to launch TypeScript compiler and then launch JavaScript tests to ensure generator has worked correctly * @author Florent Benoit */ public class TypeScriptDTOGeneratorMojoITest { /** * Logger. */ private static final Logger LOG = LoggerFactory.getLogger(TypeScriptDTOGeneratorMojoITest.class); /** * DTO Generated file */ private static final String GENERATED_DTO_NAME = "my-typescript-test-module.ts"; /** * DTO new name */ private static final String DTO_FILENAME = "dto.ts"; /** * DTO test name */ private static final String DTO_SPEC_FILENAME = "dto.spec.ts"; /** * Target folder of maven. */ private Path buildDirectory; /** * Path to the package.json file used to setup typescript compiler */ private Path dtoSpecJsonPath; /** * Path to the package.json file used to setup typescript compiler */ private Path packageJsonPath; /** * Root directory for our tests */ private Path rootPath; /** * Linux uid. */ private String linuxUID; /** * Linux gid. */ private String linuxGID; /** * Init folders */ @BeforeClass public void init() throws URISyntaxException, IOException, InterruptedException { // setup packages this.packageJsonPath = new File(TypeScriptDTOGeneratorMojoITest.class.getClassLoader().getResource("package.json").toURI()).toPath(); this.rootPath = this.packageJsonPath.getParent(); // target folder String buildDirectoryProperty = System.getProperty("buildDirectory"); if (buildDirectoryProperty != null) { buildDirectory = new File(buildDirectoryProperty).toPath(); } else { buildDirectory = packageJsonPath.getParent().getParent(); } LOG.info("Using building directory {0}", buildDirectory); } /** * Generates a docker exec command used to launch node commands * @return list of command parameters */ protected List<String> getDockerExec() { // setup command line List<String> command = new ArrayList(); command.add("docker"); command.add("run"); command.add("--rm"); command.add("-v"); command.add(rootPath.toString() + ":/usr/src/app"); command.add("-w"); command.add("/usr/src/app"); command.add("node:6"); command.add("/bin/sh"); command.add("-c"); return command; } /** * Get UID of current user (used on Linux) */ protected String getUid() throws IOException, InterruptedException { if (this.linuxUID == null) { // grab user id ProcessBuilder uidProcessBuilder = new ProcessBuilder("id", "-u"); Process processId = uidProcessBuilder.start(); int resultId = processId.waitFor(); String uid = ""; try (BufferedReader outReader = new BufferedReader(new InputStreamReader(processId.getInputStream()))) { uid = String.join(System.lineSeparator(), outReader.lines().collect(toList())); } catch (Exception error) { throw new IllegalStateException("Unable to get uid" + uid); } if (resultId != 0) { throw new IllegalStateException("Unable to get uid" + uid); } try { Integer.valueOf(uid); } catch (NumberFormatException e) { throw new IllegalStateException("The uid is not a number" + uid); } this.linuxUID = uid; } return this.linuxUID; } /** * Get GID of current user (used on Linux) */ protected String getGid() throws IOException, InterruptedException { if (this.linuxGID == null) { ProcessBuilder gidProcessBuilder = new ProcessBuilder("id", "-g"); Process processGid = gidProcessBuilder.start(); int resultGid = processGid.waitFor(); String gid = ""; try (BufferedReader outReader = new BufferedReader(new InputStreamReader(processGid.getInputStream()))) { gid = String.join(System.lineSeparator(), outReader.lines().collect(toList())); } catch (Exception error) { throw new IllegalStateException("Unable to get gid" + gid); } if (resultGid != 0) { throw new IllegalStateException("Unable to get gid" + gid); } try { Integer.valueOf(gid); } catch (NumberFormatException e) { throw new IllegalStateException("The uid is not a number" + gid); } this.linuxGID = gid; } return this.linuxGID; } /** * Setup typescript compiler by downloading the dependencies * @throws IOException if unable to start process * @throws InterruptedException if unable to wait the end of the process */ @Test(groups = {"tools"}) protected void installTypeScriptCompiler() throws IOException, InterruptedException { // setup command line List<String> command = getDockerExec(); // avoid root permissions in generated files if (SystemInfo.isLinux()) { command.add(wrapLinuxCommand("npm install")); } else { command.add("npm install"); } // setup typescript compiler ProcessBuilder processBuilder = new ProcessBuilder().command(command).directory(rootPath.toFile()).redirectErrorStream(true).inheritIO(); Process process = processBuilder.start(); LOG.info("Installing TypeScript compiler in {0}", rootPath); int resultProcess = process.waitFor(); if (resultProcess != 0) { throw new IllegalStateException("Install of TypeScript has failed"); } LOG.info("TypeScript compiler installed."); } /** * Wrap the given command into a command with chown. Also add group/user that match host environment if not exists * @param command the command to wrap * @return an updated command with chown applied on it */ protected String wrapLinuxCommand(String command) throws IOException, InterruptedException { String setGroup = "export GROUP_NAME=`(getent group " + getGid() + " || (groupadd -g " + getGid() + " user && echo user:x:" + getGid() +")) | cut -d: -f1`"; String setUser = "export USER_NAME=`(getent passwd " + getUid() + " || (useradd -u " + getUid() + " -g ${GROUP_NAME} user && echo user:x:" + getGid() +")) | cut -d: -f1`"; String chownCommand= "chown --silent -R ${USER_NAME}.${GROUP_NAME} /usr/src/app || true"; return setGroup + " && " + setUser + " && " + chownCommand + " && " + command + " && " + chownCommand; } /** * Starts tests by compiling first generated DTO from maven plugin * @throws IOException if unable to start process * @throws InterruptedException if unable to wait the end of the process */ @Test(dependsOnGroups = "tools") public void compileDTOAndLaunchTests() throws IOException, InterruptedException { // search DTO Path p = this.buildDirectory; final int maxDepth = 10; Stream<Path> matches = java.nio.file.Files.find(p, maxDepth, (path, basicFileAttributes) -> path.getFileName().toString().equals(GENERATED_DTO_NAME)); // take first Optional<Path> optionalPath = matches.findFirst(); if (!optionalPath.isPresent()) { throw new IllegalStateException("Unable to find generated DTO file named '" + GENERATED_DTO_NAME + "'. Check it has been generated first"); } Path generatedDtoPath = optionalPath.get(); //copy it in test resources folder where package.json is java.nio.file.Files.copy(generatedDtoPath, this.rootPath.resolve(DTO_FILENAME), StandardCopyOption.REPLACE_EXISTING); // setup command line List<String> command = getDockerExec(); // avoid root permissions in generated files if (SystemInfo.isLinux()) { command.add(wrapLinuxCommand("npm test")); } else { command.add("npm test"); } // setup typescript compiler ProcessBuilder processBuilder = new ProcessBuilder().command(command).directory(rootPath.toFile()).redirectErrorStream(true).inheritIO(); Process process = processBuilder.start(); LOG.info("Starting TypeScript tests..."); int resultProcess = process.waitFor(); if (resultProcess != 0) { throw new IllegalStateException("DTO has failed to compile"); } LOG.info("TypeScript tests OK"); } }