view mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SubrepoChangesTest.java @ 1006:dc66807c0c28

test commit
author Yuriy Tabolin yuriy.tabolin@jetbrains.com
date Fri, 19 Oct 2018 12:44:08 +0300
parents 7bf4d943d5bb
children 10dc26b32c35
line wrap: on
line source
/*
 * Copyright 2000-2018 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jetbrains.buildServer.buildTriggers.vcs.mercurial;

import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*;
import jetbrains.buildServer.vcs.*;
import org.jetbrains.annotations.NotNull;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import static jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialSupportBuilder.mercurialSupport;
import static jetbrains.buildServer.buildTriggers.vcs.mercurial.ModificationDataMatcher.modificationData;
import static jetbrains.buildServer.buildTriggers.vcs.mercurial.Util.copyRepository;
import static jetbrains.buildServer.buildTriggers.vcs.mercurial.VcsRootBuilder.vcsRoot;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.testng.AssertJUnit.assertEquals;

@RequiredHgVersion(min = "1.7.0") //support subrepos only for hg with revsets
@Test(dataProviderClass = HgVersionConstraint.class, dataProvider = "installedHgVersion")
public class SubrepoChangesTest extends BaseMercurialTestCase {

  private MercurialVcsSupport myVcs;
  private File myRemoteRepo1;
  private File myRemoteRepo2;
  private File myRemoteRepo3;
  private File myRepoNoSubs;
  private HgPathProvider myHgPathProvider;

  @BeforeMethod
  public void setUp() throws Exception {
    super.setUp();
    ServerPluginConfig pluginConfig = new ServerPluginConfigBuilder()
            .cachesDir(myTempFiles.createTempDir())
            .detectSubrepoChanges(true)
            .build();
    File remoteRepoParentDir = myTempFiles.createTempDir();
    myRemoteRepo1 = new File(remoteRepoParentDir, "r1");
    myRemoteRepo2 = new File(remoteRepoParentDir, "r2");
    myRemoteRepo3 = new File(remoteRepoParentDir, "r3");
    myRepoNoSubs = new File(remoteRepoParentDir, "repo.no.subs");
    copyRepository(new File("mercurial-tests/testData/subrepos/r1"), myRemoteRepo1);
    copyRepository(new File("mercurial-tests/testData/subrepos/r2"), myRemoteRepo2);
    copyRepository(new File("mercurial-tests/testData/subrepos/r3"), myRemoteRepo3);
    copyRepository(new File("mercurial-tests/testData/rep1"), myRepoNoSubs);
    MercurialSupportBuilder mercurialBuilder = mercurialSupport().withConfig(pluginConfig);
    myVcs = mercurialBuilder.build();
    myHgPathProvider = mercurialBuilder.getHgPathProvider();
  }


  public void should_report_changes_from_subrepos(@NotNull HgVersion _) throws Exception {
    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).withSubrepoChanges(true).build();
    List<ModificationData> changes = myVcs.getCollectChangesPolicy().collectChanges(root, "d350e7209906", "09c256b6163e", CheckoutRules.DEFAULT);
    assertEquals(4, changes.size());
  }


  public void should_include_from_revisions_into_changes_from_subrepos(@NotNull HgVersion _) throws Exception {
    //otherwise TeamCity won't show changes from subrepos in UI
    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).withSubrepoChanges(true).build();
    List<ModificationData> changes = myVcs.getCollectChangesPolicy().collectChanges(root, "d350e7209906", "09c256b6163e", CheckoutRules.DEFAULT);
    assertThat(changes, hasItem(modificationData().withVersion("9e4a2fef1a1c")));
  }


  public void should_not_report_any_changes_when_subrepo_removed(@NotNull HgVersion _) throws Exception {
    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).withSubrepoChanges(true).build();
    List<ModificationData> changes = myVcs.getCollectChangesPolicy().collectChanges(root, "34017377d9c3", "4d7b3db8779f", CheckoutRules.DEFAULT);
    assertEquals(1, changes.size());
  }


  public void should_not_report_any_changes_when_subrepo_added(@NotNull HgVersion _) throws Exception {
    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).withSubrepoChanges(true).build();
    List<ModificationData> changes = myVcs.getCollectChangesPolicy().collectChanges(root, "4d7b3db8779f", "d350e7209906", CheckoutRules.DEFAULT);
    assertEquals(1, changes.size());
  }


  public void should_report_subrepo_changes_recursevly(@NotNull HgVersion _) throws Exception {
    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).withSubrepoChanges(true).build();
    List<ModificationData> changes = myVcs.getCollectChangesPolicy().collectChanges(root, "09c256b6163e", "d64d9799c143", CheckoutRules.DEFAULT);
    assertEquals(7, changes.size());

    assertThat(changes, hasItem(modificationData().withVersion("d64d9799c143").withVcsRootProperties(root.getProperties())));

    Map<String, String> s1props = new HashMap<String, String>(root.getProperties());
    s1props.put("teamcity.internal.subrepo.path", "r2");
    s1props.put(Constants.REPOSITORY_PROP, myRemoteRepo3.getCanonicalPath());
    s1props.put("teamcity.internal.subrepo", "true");
    assertThat(changes, hasItem(modificationData().withVersion("514c3e09cddf").withVcsRootProperties(s1props)));
    assertThat(changes, hasItem(modificationData().withVersion("1f9eb39a3921").withVcsRootProperties(s1props)));

    Map<String, String> s2props = new HashMap<String, String>(root.getProperties());
    s2props.put("teamcity.internal.subrepo.path", "r2/r2");
    s2props.put(Constants.REPOSITORY_PROP, myRemoteRepo2.getCanonicalPath());
    s2props.put("teamcity.internal.subrepo", "true");
    assertThat(changes, hasItem(modificationData().withVersion("ac0003deae69").withVcsRootProperties(s2props)));
    assertThat(changes, hasItem(modificationData().withVersion("29053b4b29ce").withVcsRootProperties(s2props)));
  }


  public void report_subrepo_changes_without_revsets(@NotNull HgVersion _) throws Exception {
    ServerPluginConfig pluginConfig = new ServerPluginConfigBuilder()
            .cachesDir(myTempFiles.createTempDir())
            .detectSubrepoChanges(true)
            .dontUseRevsets()
            .build();
    myVcs = mercurialSupport().withConfig(pluginConfig).build();
    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).withSubrepoChanges(true).build();
    List<ModificationData> changes = myVcs.getCollectChangesPolicy().collectChanges(root, "d350e7209906", "09c256b6163e", CheckoutRules.DEFAULT);
    assertEquals(3, changes.size());
  }


  public void report_subrepo_changes(@NotNull HgVersion _) throws Exception {
    ServerPluginConfig pluginConfig = new ServerPluginConfigBuilder()
            .cachesDir(myTempFiles.createTempDir())
            .detectSubrepoChanges(true)
            .dontUseRevsets()
            .build();
    myVcs = mercurialSupport().withConfig(pluginConfig).build();
    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).withSubrepoChanges(true).build();
    List<ModificationData> changes = myVcs.getCollectChangesPolicy().collectChanges(root, "09c256b6163e", "d64d9799c143", CheckoutRules.DEFAULT);
    assertEquals(5, changes.size());

    assertThat(changes, hasItem(modificationData().withVersion("d64d9799c143").withVcsRootProperties(root.getProperties())));

    Map<String, String> subrepoRootProperties = new HashMap<String, String>(root.getProperties());
    subrepoRootProperties.put("teamcity.internal.subrepo.path", "r2");
    subrepoRootProperties.put(Constants.REPOSITORY_PROP, myRemoteRepo3.getCanonicalPath());
    subrepoRootProperties.put("teamcity.internal.subrepo", "true");
    assertThat(changes, hasItem(modificationData().withVersion("514c3e09cddf").withVcsRootProperties(subrepoRootProperties)));
    assertThat(changes, hasItem(modificationData().withVersion("1f9eb39a3921").withVcsRootProperties(subrepoRootProperties)));
  }


  public void report_subrepo_revision_in_each_main_root_commit(@NotNull HgVersion _) throws Exception {
    ServerPluginConfig pluginConfig = new ServerPluginConfigBuilder()
            .cachesDir(myTempFiles.createTempDir())
            .detectSubrepoChanges(true)
            .dontUseRevsets()
            .build();
    myVcs = mercurialSupport().withConfig(pluginConfig).build();
    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).withSubrepoChanges(true).build();
    List<ModificationData> changes = myVcs.getCollectChangesPolicy().collectChanges(root, "e4eced2b7381", "d64d9799c143", CheckoutRules.DEFAULT);

    assertThat(changes.size(), is(11));

    SubrepoRevisionAttributesBuilder builder = new SubrepoRevisionAttributesBuilder();
    builder.addSubrepo(new SubrepoConfig(root)
            .setSubrepoRootParamDiff(Constants.REPOSITORY_PROP, myRemoteRepo2.getCanonicalPath())
            .setSubrepoPath("r2")
            .setSubrepoRevision("916933c1dd8e"));
    assertThat(changes, hasItem(modificationData().withVersion("34017377d9c3").withAttributes(builder.buildAttributes())));

    builder = new SubrepoRevisionAttributesBuilder();
    builder.addSubrepo(new SubrepoConfig(root)
            .setSubrepoRootParamDiff(Constants.REPOSITORY_PROP, myRemoteRepo3.getCanonicalPath())
            .setSubrepoPath("r2")
            .setSubrepoRevision("9e4a2fef1a1c"));
    assertThat(changes, hasItem(modificationData().withVersion("d350e7209906").withAttributes(builder.buildAttributes())));

    builder = new SubrepoRevisionAttributesBuilder();
    builder.addSubrepo(new SubrepoConfig(root)
            .setSubrepoRootParamDiff(Constants.REPOSITORY_PROP, myRemoteRepo3.getCanonicalPath())
            .setSubrepoPath("r2")
            .setSubrepoRevision("ebb884b1b691"));
    assertThat(changes, hasItem(modificationData().withVersion("09c256b6163e").withAttributes(builder.buildAttributes())));

    builder = new SubrepoRevisionAttributesBuilder();
    builder.addSubrepo(new SubrepoConfig(root)
            .setSubrepoRootParamDiff(Constants.REPOSITORY_PROP, myRemoteRepo3.getCanonicalPath())
            .setSubrepoPath("r2")
            .setSubrepoRevision("514c3e09cddf"));
    assertThat(changes, hasItem(modificationData().withVersion("d64d9799c143").withAttributes(builder.buildAttributes())));


    builder = new SubrepoRevisionAttributesBuilder();
    builder.addSubrepo(new SubrepoConfig(root)
            .setSubrepoRootParamDiff(Constants.REPOSITORY_PROP, myRemoteRepo2.getCanonicalPath())
            .setSubrepoPath("r2/r2")
            .setSubrepoRevision("916933c1dd8e"));
    assertThat(changes, hasItem(modificationData().withVersion("1f9eb39a3921").withAttributes(builder.buildAttributes())));

    builder = new SubrepoRevisionAttributesBuilder();
    builder.addSubrepo(new SubrepoConfig(root)
            .setSubrepoRootParamDiff(Constants.REPOSITORY_PROP, myRemoteRepo2.getCanonicalPath())
            .setSubrepoPath("r2/r2")
            .setSubrepoRevision("ac0003deae69"));
    assertThat(changes, hasItem(modificationData().withVersion("514c3e09cddf").withAttributes(builder.buildAttributes())));
  }

  public void should_retrieve_subrepo_config_only_when_necessary(@NotNull HgVersion _) throws Exception {
    //Every commit from a main repository has a table of subrepo revisions.
    //To build this table we call hg cat .hgsub .hgsubstate. -r <main root revision>
    //When main repository has a lot of new changes (thousands) calling 'hg cat'
    //for each main root revision becomes a bottleneck. We should call hg cat only
    //if a main root change has .hgsub/.hgsubstate among its changed files,
    //and reuse subrepo table from a parent commit if it doesn't.

    ServerPluginConfig pluginConfig = new ServerPluginConfigBuilder()
            .cachesDir(myTempFiles.createTempDir())
            .detectSubrepoChanges(true)
            .dontUseRevsets()
            .build();

    final AtomicInteger catCallCounter = new AtomicInteger(0);
    RepoFactory repoFactory = new RepoFactory(pluginConfig, new CommandSettingsForRootImpl(new TestCommandSettingsFactory(), new ExtensionsWeaver(), new CommandlineViaFileWrapperWeaver()), myHgPathProvider) {
      @NotNull
      @Override
      protected ServerHgRepo create(@NotNull File workingDir, @NotNull String hgPath, @NotNull AuthSettings authSettings, @NotNull CommandSettingsFactory commandSettingsFactory, @NotNull ServerPluginConfig config) {
        return new CountingServerHgRepo(commandSettingsFactory, config, workingDir, hgPath, authSettings, catCallCounter);
      }
    };

    myVcs = mercurialSupport().withConfig(pluginConfig).withRepoFactory(repoFactory).build();
    VcsRoot root = vcsRoot().withUrl(myRepoNoSubs.getAbsolutePath()).withSubrepoChanges(true).build();
    myVcs.getCollectChangesPolicy().collectChanges(root, "1d446e82d356", "e6935c9c80bf", CheckoutRules.DEFAULT);

    //this root contains no subrepos, we should call getSubrepos just once and then reuse this info
    assertEquals(1, catCallCounter.intValue());
  }


  protected static class CountingServerHgRepo extends ServerHgRepo {
    private final AtomicInteger myCatCallCounter;

    public CountingServerHgRepo(@NotNull CommandSettingsFactory commandSettingsFactory,
                                @NotNull ServerPluginConfig config,
                                @NotNull File workingDir,
                                @NotNull String hgPath,
                                @NotNull AuthSettings authSettings,
                                @NotNull AtomicInteger catCallCounter) {
      super(commandSettingsFactory, config, workingDir, hgPath, authSettings);
      myCatCallCounter = catCallCounter;
    }

    @Override
    public CatCommand cat() {
      myCatCallCounter.incrementAndGet();
      return super.cat();
    }
  }
}