uk.ac.soton.simulation.jsit.core.TestModelVersioningAssistantSVN.java Source code

Java tutorial

Introduction

Here is the source code for uk.ac.soton.simulation.jsit.core.TestModelVersioningAssistantSVN.java

Source

/*  
Copyright 2015 University of Southampton
    
This file is part of JSIT.
    
JSIT is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
JSIT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.
    
You should have received a copy of the GNU Lesser General Public License
along with JSIT.  If not, see <http://www.gnu.org/licenses/>.
 */
package uk.ac.soton.simulation.jsit.core;

import org.junit.*;
import org.junit.rules.TemporaryFolder;
import org.slf4j.*;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.configuration.PropertiesConfiguration;

// Imports for non-legacy Apache JavaHL package. Test code for the legacy Tigris API
// uses explicit package names referring to org.tigris.subversion.javahl

import org.apache.commons.io.FileUtils;
import org.apache.subversion.javahl.*;
import org.apache.subversion.javahl.callback.CommitCallback;
import org.apache.subversion.javahl.callback.CommitMessageCallback;
import org.apache.subversion.javahl.types.Depth;

/**
 * Unit tests for ModelVersioningAssistantSVN. Everything assumes that tests are being run from
 * the root JSIT directory (which is what happens under Eclipse). If running under Eclipse, make
 * sure that (non-JUnit) assertions (-ea JVM option) has been specified to trigger assertions in
 * the code under test: see http://stackoverflow.com/questions/1798016.
 * 
 * @author Stuart Rossiter
 * @since 0.1
 */
public class TestModelVersioningAssistantSVN {

    private static Logger logger = LoggerFactory.getLogger(TestModelVersioningAssistantSVN.class);

    @Rule
    public TemporaryFolder tempFolder = new TemporaryFolder();

    private static final String RESOURCE_PATH = "src/test/resources/";
    private static final String DUMMY_MODEL_DIR = "DummyModelSVN";
    private static final String DUMMY_MODEL_DIR_LEGACY = "DummyModelSVN_Legacy";
    private static final String LOCAL_SVN_REPO = "TestSVN";
    private static final String WORKING_COPY_DIR = "WC";

    /**
     * Before all tests, give user heads-up on working dir.
     */
    @BeforeClass
    public static void preTests() {

        String workingDirPath = System.getProperty("user.dir");
        logger.warn(TestModelVersioningAssistantSVN.class.getSimpleName() + " running from working dir "
                + workingDirPath + "; set-up will fail if this is not the JSIT root dir");

        System.out.println("Test classpath: " + System.getProperty("java.class.path"));

    }

    /**
     * Test that version details returned appropriately for never-committed
     * model code.
     */
    @Test
    public void neverCommittedVersionDetails() throws IOException {

        logger.info("Testing version details for never-committed model code");

        setUpTempAreaWithModel(false);
        neverCommittedVersionDetailsShared(false);

    }

    /**
     * Test that version details returned appropriately for never-committed
     * model code.
     */
    @Test
    public void neverCommittedVersionDetailsLegacy() throws IOException {

        logger.info("Testing version details for never-committed model code (legacy API)");

        setUpTempAreaWithModel(true);
        neverCommittedVersionDetailsShared(true);

    }

    private void neverCommittedVersionDetailsShared(boolean isLegacy) throws IOException {

        // Assistant needs model *code* directory path
        ModelVersioningAssistant assistant = ModelVersioningAssistantSVN
                .createAssistantInternal(tempFolder.getRoot().getAbsolutePath() + File.separator + DUMMY_MODEL_DIR
                        + File.separator + "Code");

        Assert.assertNull(assistant.getLastCommitID());
        Assert.assertNull(assistant.getModelCommittedURL());
        if (isLegacy) {
            Assert.assertEquals("SVN Legacy", assistant.getModelVCS());
        } else {
            Assert.assertEquals("SVN", assistant.getModelVCS());
        }
        Assert.assertEquals("DummyModel", assistant.getUserModelName());
        Assert.assertEquals("0.1", assistant.getUserModelVersion());

    }

    /**
     * Test that version details returned appropriately for non-JSIT-committed working
     * copy code.
     */
    @Test
    public void nonJSIT_Commit() throws IOException, ClientException {

        logger.info("Testing version details for non-JSIT-committed model code");
        setUpTempAreaWithModel(false);
        SVNClient svnClient = new SVNClient();
        String repoModelURI = setUpModelWorkingCopy(svnClient);
        nonJSIT_CommitModel(svnClient);
        nonJSIT_CommitShared(repoModelURI, false);

    }

    /**
     * Test that version details returned appropriately for non-JSIT-committed working
     * copy code.
     */
    @Test
    public void nonJSIT_CommitLegacy() throws IOException, org.tigris.subversion.javahl.ClientException {

        logger.info("Testing version details for non-JSIT-committed model code (legacy API)");
        setUpTempAreaWithModel(true);
        org.tigris.subversion.javahl.SVNClient svnClient = new org.tigris.subversion.javahl.SVNClient();
        String repoModelURI = setUpModelWorkingCopyLegacy(svnClient);
        nonJSIT_CommitModelLegacy(svnClient);
        nonJSIT_CommitShared(repoModelURI, true);

    }

    private void nonJSIT_CommitShared(String repoModelURI, boolean isLegacy) {

        // Assistant needs model *code* directory path
        ModelVersioningAssistant assistant = ModelVersioningAssistantSVN
                .createAssistantInternal(tempFolder.getRoot().getAbsolutePath() + File.separator + WORKING_COPY_DIR
                        + File.separator + "Code");

        Assert.assertEquals("r2", assistant.getLastCommitID());
        Assert.assertEquals(repoModelURI + "/Code", assistant.getModelCommittedURL());
        if (isLegacy) {
            Assert.assertEquals("SVN Legacy", assistant.getModelVCS());
        } else {
            Assert.assertEquals("SVN", assistant.getModelVCS());
        }
        Assert.assertEquals("DummyModel", assistant.getUserModelName());
        Assert.assertEquals("0.1", assistant.getUserModelVersion());

    }

    /**
     * Test the JSIT commit process via a number of sequential actions.
     * Does not use the command-line interface. (Could test this by launching it
     * via ProcessBuilder, but the extra main logic is very basic.)
     */
    @Test
    public void validJSIT_Commit() throws IOException, ClientException {

        logger.info("Testing version details for JSIT-committed model code");
        setUpTempAreaWithModel(false);
        SVNClient svnClient = new SVNClient();
        String repoModelURI = setUpModelWorkingCopy(svnClient); // WC is now for rev 1
        validJSIT_CommitShared(repoModelURI, false);

    }

    /**
     * Test the JSIT commit process via a number of sequential actions.
     * Does not use the command-line interface. (Could test this by launching it
     * via ProcessBuilder, but the extra main logic is very basic.)
     */
    @Test
    public void validJSIT_CommitLegacy() throws IOException, org.tigris.subversion.javahl.ClientException {

        logger.info("Testing version details for JSIT-committed model code (legacy API)");
        setUpTempAreaWithModel(true);
        org.tigris.subversion.javahl.SVNClient svnClient = new org.tigris.subversion.javahl.SVNClient();
        String repoModelURI = setUpModelWorkingCopyLegacy(svnClient); // WC is now for rev 1
        validJSIT_CommitShared(repoModelURI, true);

    }

    private void validJSIT_CommitShared(String repoModelURI, boolean isLegacy) throws IOException {

        final String rootPath = tempFolder.getRoot().getAbsolutePath() + File.separator + WORKING_COPY_DIR;
        final File rootPathFile = new File(rootPath);
        final String codePath = rootPath + File.separator + "Code";

        // Assistant needs in-VCS model code directory path
        ModelVersioningAssistant assistant = ModelVersioningAssistantSVN.createAssistantInternal(codePath);

        /*
         * First try without any working copy changes. Should work because no
         * previous JSIT commits
         */

        logger.info("JSIT commit without WC changes (first JSIT commit)");
        boolean committedStuff = assistant.commitModelMaterial(rootPathFile);
        Assert.assertTrue(committedStuff);
        File prevChangesFile = new File(rootPath + File.separator + ModelVersioningAssistant.PREV_CHANGES_FILE);
        Assert.assertTrue(prevChangesFile.exists()); // We check content later

        // Re-instantiate to read hopefully altered version details
        assistant = ModelVersioningAssistantSVN.createAssistantInternal(codePath);
        Assert.assertEquals("r2", assistant.getLastCommitID());
        Assert.assertEquals(repoModelURI + "/Code", assistant.getModelCommittedURL());
        if (isLegacy) {
            Assert.assertEquals("SVN Legacy", assistant.getModelVCS());
        } else {
            Assert.assertEquals("SVN", assistant.getModelVCS());
        }
        Assert.assertEquals("DummyModel", assistant.getUserModelName());
        Assert.assertEquals("0.1", assistant.getUserModelVersion());
        logger.debug("Hash code after commit: " + assistant.getCommitTimeSourceDirsHash());

        /*
         * Now check further commit doesn't commit anything
         */

        logger.info("JSIT commit without WC changes (already JSIT committed)");
        committedStuff = assistant.commitModelMaterial(rootPathFile);
        Assert.assertFalse(committedStuff);

        /*
         * Now make a change (to the version file, using assistant code to write
         * the props for us) and check the commit fails because of missing
         * working changes file
         */

        logger.info("JSIT commit changed code without working changes file");
        PropertiesConfiguration verProps = assistant.getVersionProperties();
        String revisedVer = "0.1FINAL";
        verProps.setProperty(ModelVersioningAssistant.MODEL_VER_PROPERTY, revisedVer);
        assistant.saveProperties();
        // Re-instantiate to read hopefully altered version details
        assistant = ModelVersioningAssistantSVN.createAssistantInternal(codePath);
        try {
            committedStuff = assistant.commitModelMaterial(rootPathFile);
            Assert.fail("Expected VersionControlException");
        } catch (VersionControlException e) {
            Assert.assertTrue(e.getMessage().startsWith("Code changes need specifying"));
        }

        /* 
         * Create the changes file and check commit works OK, and that prev changes
         * file has appended the details
         */

        logger.info("JSIT commit changed code with working changes file");
        File changesFile = new File(rootPath + File.separator + ModelVersioningAssistant.WORKING_CHANGES_FILE);
        PrintWriter writer = new PrintWriter(new FileWriter(changesFile));
        String changeText = "Changed model version number";
        writer.println(changeText);
        writer.close();

        // Re-instantiate just to ensure correctness of test
        assistant = ModelVersioningAssistantSVN.createAssistantInternal(codePath);
        committedStuff = assistant.commitModelMaterial(rootPathFile);
        Assert.assertTrue(committedStuff);
        // Temp file should have been deleted
        Assert.assertFalse(new File(assistant.getModelVersionFileBackupPath()).exists());
        // Prev changes file should exist and have appended content
        Assert.assertTrue(prevChangesFile.exists());
        BufferedReader prevReader = new BufferedReader(new FileReader(prevChangesFile));
        String prevDesc = "prev chgs file";
        String commitLinePattern = ">>> Commit \\d{4}-\\d{2}-\\d{2} \\d{9},.+";
        Assert.assertTrue(readAndShowLine(prevDesc, prevReader).matches(commitLinePattern));
        Assert.assertTrue(readAndShowLine(prevDesc, prevReader).equals("Initial JSIT commit"));
        Assert.assertTrue(readAndShowLine(prevDesc, prevReader).equals("")); // Blank 3rd line
        Assert.assertTrue(readAndShowLine(prevDesc, prevReader).matches(commitLinePattern));
        Assert.assertTrue(readAndShowLine(prevDesc, prevReader).equals(changeText));
        prevReader.close();

        // Working changes file should be empty
        Assert.assertEquals(0, changesFile.length());

        // Re-instantiate for fresh property values
        assistant = ModelVersioningAssistantSVN.createAssistantInternal(codePath);
        Assert.assertEquals("r3", assistant.getLastCommitID());
        Assert.assertEquals(repoModelURI + "/Code", assistant.getModelCommittedURL());
        if (isLegacy) {
            Assert.assertEquals("SVN Legacy", assistant.getModelVCS());
        } else {
            Assert.assertEquals("SVN", assistant.getModelVCS());
        }
        Assert.assertEquals("DummyModel", assistant.getUserModelName());
        Assert.assertEquals(revisedVer, assistant.getUserModelVersion());

    }

    private String readAndShowLine(String fileDesc, BufferedReader reader) throws IOException {

        String line = reader.readLine();
        logger.info("Read from " + fileDesc + ": " + line);
        return line;

    }

    /*
     * Put the dummy model source into the temp folder created by the per-test rule
     */
    private void setUpTempAreaWithModel(boolean isLegacy) throws IOException {

        File tempArea = tempFolder.getRoot();
        logger.info("Created temporary folder at " + tempArea.getAbsolutePath());

        File dummyModelMaterial;

        if (isLegacy) {
            dummyModelMaterial = new File(RESOURCE_PATH + DUMMY_MODEL_DIR_LEGACY);
        } else {
            dummyModelMaterial = new File(RESOURCE_PATH + DUMMY_MODEL_DIR);
        }
        Assert.assertTrue(
                "Can't find dummy model material which should be at " + dummyModelMaterial.getCanonicalPath(),
                dummyModelMaterial.exists() && dummyModelMaterial.isDirectory());
        FileUtils.copyDirectory(dummyModelMaterial,
                new File(tempArea.getAbsolutePath() + File.separator + DUMMY_MODEL_DIR), false); // Don't preserve file dates

    }

    /*
     * Set up working copy of dummy model by creating a local SVN server, importing
     * into it and then checking-out. Return the dummy model's repo URI
     */
    private String setUpModelWorkingCopy(SVNClient svnClient) throws IOException, ClientException {

        File tempArea = tempFolder.getRoot();
        SVNRepos svnAdmin = new SVNRepos();

        svnAdmin.create(new File(tempArea.getAbsolutePath() + File.separator + LOCAL_SVN_REPO), // Path
                false, // Don't disable fysnc commit
                false, // Don't keep log
                null, // No config path
                SVNRepos.FSFS); // Use FS-based repo

        // Java's File toURI (and related toURL or toURI().toURL()) return file URIs with
        // a single forward slash. JavaHL insists on the double slash form (+ extra slash that
        // starts the path)
        String repoModelURI = "file://" + tempArea.toURI().getRawPath() + LOCAL_SVN_REPO + "/" + DUMMY_MODEL_DIR;
        String dummyMaterialPath = tempArea.getAbsolutePath() + File.separator + DUMMY_MODEL_DIR;
        Assert.assertTrue(new File(dummyMaterialPath).exists());
        svnClient.doImport(dummyMaterialPath, // Path
                repoModelURI, // URL
                Depth.infinity, // Infinite depth
                false, // Don't process ignores
                false, // Ignore unknown types
                null, // No revprops                      
                new CommitMessageCallback() {
                    @Override
                    public String getLogMessage(Set<CommitItem> items) {
                        return "Import dummy model";
                    }
                }, // Commit msg callback
                new CommitCallback() {
                    @Override
                    public void commitInfo(CommitInfo commitInfo) {
                        String errMsg = commitInfo.getPostCommitError();
                        if (errMsg != null) {
                            throw new RuntimeException("SVN import failed with message " + errMsg);
                        }
                        System.out.println("Successfully imported dummy model r" + commitInfo.getRevision());
                    }
                }); // Commit callback

        String wcPath = tempFolder.newFolder(WORKING_COPY_DIR).getAbsolutePath();
        long checkedOutRev = svnClient.checkout(repoModelURI, // 'Module name' (URL)
                wcPath, // Dest path
                null, // No specific rev
                null, // No specific peg rev
                Depth.infinity, // Infinite depth
                true, // Ignore externals
                false); // No unversioned paths
        Assert.assertEquals(1, checkedOutRev); // Should be checking-out rev 1
        System.out.println("Successfully checked-out dummy model r" + checkedOutRev);

        return repoModelURI;

    }

    /*
     * Set up working copy of dummy model by creating a local SVN server, importing
     * into it and then checking-out. Return the dummy model's repo URI
     */
    private String setUpModelWorkingCopyLegacy(org.tigris.subversion.javahl.SVNClient svnClient)
            throws IOException, org.tigris.subversion.javahl.ClientException {

        File tempArea = tempFolder.getRoot();
        org.tigris.subversion.javahl.SVNAdmin svnAdmin = new org.tigris.subversion.javahl.SVNAdmin();

        svnAdmin.create(tempArea.getAbsolutePath() + File.separator + LOCAL_SVN_REPO, // Path
                false, // Don't disable fysnc commit
                false, // Don't keep log
                null, // No config path
                org.tigris.subversion.javahl.SVNAdmin.FSFS); // Use FS-based repo

        // Java's File toURI (and related toURL or toURI().toURL()) return file URIs with
        // a single forward slash. JavaHL insists on the double slash form (+ extra slash that
        // starts the path)
        String repoModelURI = "file://" + tempArea.toURI().getRawPath() + LOCAL_SVN_REPO + "/" + DUMMY_MODEL_DIR;
        String dummyMaterialPath = tempArea.getAbsolutePath() + File.separator + DUMMY_MODEL_DIR;
        Assert.assertTrue(new File(dummyMaterialPath).exists());
        svnClient.doImport(dummyMaterialPath, // Path
                repoModelURI, // URL
                "Import dummy model", // Message
                org.tigris.subversion.javahl.Depth.infinity, // Infinite depth
                true, // Don't process ignores
                false, // Ignore unknown types
                null); // No rev props

        String wcPath = tempFolder.newFolder(WORKING_COPY_DIR).getAbsolutePath();
        long checkedOutRev = svnClient.checkout(repoModelURI, // 'Module name' (URL)
                wcPath, // Dest path
                null, // No specific rev
                null, // No specific peg rev
                org.tigris.subversion.javahl.Depth.infinity, // Infinite depth
                true, // Ignore externals
                false); // No unversioned paths
        Assert.assertEquals(1, checkedOutRev); // Should be checking-out rev 1
        return repoModelURI;

    }

    /*
     * Commit the dummy model but not using the JSIT process
     */
    private void nonJSIT_CommitModel(SVNClient svnClient) throws ClientException {

        String wcPath = tempFolder.getRoot().getAbsolutePath() + File.separator + WORKING_COPY_DIR;
        String verPropsPath = wcPath + File.separator + "Code" + File.separator + "modelVersion.properties";

        HashSet<String> pathsSet = new HashSet<String>(1, 1.0f);
        pathsSet.add(verPropsPath);
        svnClient.propertySetLocal(pathsSet, // Paths
                ModelVersioningAssistantSVN.KEYWORDS_PROP, // Prop name
                ModelVersioningAssistantSVN.KEYWORDS_VALUE.getBytes(), // Prop value as byte array
                Depth.unknown, // Depth not relevant for single file
                null, // No filtering changelists
                false); // Don't force

        pathsSet = new HashSet<String>(1, 1.0f);
        pathsSet.add(wcPath);
        svnClient.commit(pathsSet, // Paths set
                Depth.infinity, // Infinite depth
                false, // Don't not unlock!
                false, // Don't keep changelist
                null, // No changelists
                null, // No revprop mapping
                new CommitMessageCallback() {
                    @Override
                    public String getLogMessage(Set<CommitItem> items) {
                        return "Test Commit";
                    }
                }, // Commit msg callback
                new CommitCallback() {
                    @Override
                    public void commitInfo(CommitInfo commitInfo) {
                        String errMsg = commitInfo.getPostCommitError();
                        if (errMsg != null) {
                            throw new RuntimeException("SVN commit failed with message " + errMsg);
                        }
                        Assert.assertEquals(2, commitInfo.getRevision());
                        System.out.println("Successfully committed new revision " + commitInfo.getRevision());
                    }
                }); // Commit callback

    }

    /*
     * Commit the dummy model but not using the JSIT process
     */
    private void nonJSIT_CommitModelLegacy(org.tigris.subversion.javahl.SVNClient svnClient)
            throws org.tigris.subversion.javahl.ClientException {

        String wcPath = tempFolder.getRoot().getAbsolutePath() + File.separator + WORKING_COPY_DIR;
        String verPropsPath = wcPath + File.separator + "Code" + File.separator + "modelVersion.properties";

        svnClient.propertySet(verPropsPath, // Path
                ModelVersioningAssistantSVN.KEYWORDS_PROP, // Prop name
                ModelVersioningAssistantSVN.KEYWORDS_VALUE, // Prop value
                org.tigris.subversion.javahl.Depth.unknown, // Depth not relevant for single file
                null, // No filtering changelists
                false, // Don't force
                null); // No revprop mapping on commit

        long commitResult = svnClient.commit(new String[] { wcPath }, // Working copy dir
                "Test commit", // Commit msg
                org.tigris.subversion.javahl.Depth.infinity, // Infinite depth
                false, // Don't not unlock!
                false, // Don't keep changelist
                null, // No changelists
                null); // No revprop mapping

        Assert.assertEquals(2, commitResult);

    }

}