You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by dp...@apache.org on 2018/10/12 17:10:20 UTC

[ignite-teamcity-bot] branch master updated: IGNITE-9869: Bug fix for Running builds not uploaded into prefetched builds collection: fixed JAXB 2 roots problem - Fixes #37.

This is an automated email from the ASF dual-hosted git repository.

dpavlov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite-teamcity-bot.git


The following commit(s) were added to refs/heads/master by this push:
     new 84bbae6  IGNITE-9869: Bug fix for Running builds not uploaded into prefetched builds collection: fixed JAXB 2 roots problem - Fixes #37.
84bbae6 is described below

commit 84bbae684d55b191e7ee66ee19a2832e81408dac
Author: Dmitriy Pavlov <dp...@apache.org>
AuthorDate: Fri Oct 12 20:10:13 2018 +0300

    IGNITE-9869: Bug fix for Running builds not uploaded into prefetched builds collection: fixed JAXB 2 roots problem - Fixes #37.
    
    Signed-off-by: Dmitriy Pavlov <dp...@apache.org>
---
 .../Builds.java => di/scheduler/NoOpSheduler.java} |  34 ++--
 .../apache/ignite/ci/tcmodel/hist/BuildRef.java    |   3 +-
 .../org/apache/ignite/ci/tcmodel/hist/Builds.java  |  15 ++
 .../ci/teamcity/ignited/BuildRefCompacted.java     |   7 +
 .../ignite/ci/teamcity/ignited/BuildRefDao.java    |  42 +++--
 .../ci/teamcity/ignited/TeamcityIgnitedImpl.java   |  45 +++---
 .../java/org/apache/ignite/ci/util/XmlUtil.java    |  31 ++--
 .../org/apache/ignite/ci/web/model/Version.java    |   2 +-
 .../ignited/IgnitedTcInMemoryIntegrationTest.java  | 175 +++++++++++++++++----
 .../ci/teamcity/pure/BuildHistoryEmulator.java     | 108 +++++++++++++
 10 files changed, 368 insertions(+), 94 deletions(-)

diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/hist/Builds.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/scheduler/NoOpSheduler.java
similarity index 52%
copy from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/hist/Builds.java
copy to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/scheduler/NoOpSheduler.java
index 82a76a5..c4cae20 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/hist/Builds.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/scheduler/NoOpSheduler.java
@@ -14,32 +14,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.ignite.ci.di.scheduler;
 
-package org.apache.ignite.ci.tcmodel.hist;
+import java.util.concurrent.TimeUnit;
 
-import java.util.Collections;
-import java.util.List;
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
+/**
+ * Sheduler which never waits
+ */
+public class NoOpSheduler implements IScheduler {
+    /** {@inheritDoc} */
+    @Override public void invokeLater(Runnable cmd, long delay, TimeUnit unit) {
 
-/** List of builds from build history */
-@XmlRootElement(name = "builds")
-@XmlAccessorType(XmlAccessType.FIELD)
-public class Builds {
-    @XmlAttribute
-    private String nextHref;
+    }
 
-    @XmlElement(name = "build")
-    private List<BuildRef> builds;
+    /** {@inheritDoc} */
+    @Override public void sheduleNamed(String fullName, Runnable cmd, long queitPeriod, TimeUnit unit) {
 
-    public List<BuildRef> getBuildsNonNull() {
-        return builds == null ? Collections.emptyList() : builds;
     }
 
-    public String nextHref() {
-        return nextHref;
+    /** {@inheritDoc} */
+    @Override public void stop() {
+
     }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/hist/BuildRef.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/hist/BuildRef.java
index 10d312d..4ce62f7 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/hist/BuildRef.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/hist/BuildRef.java
@@ -18,11 +18,10 @@
 package org.apache.ignite.ci.tcmodel.hist;
 
 import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAttribute;
-
-import com.google.common.base.Objects;
 import org.apache.ignite.ci.tcmodel.result.AbstractRef;
 
 /**
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/hist/Builds.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/hist/Builds.java
index 82a76a5..0de40d5 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/hist/Builds.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/hist/Builds.java
@@ -32,6 +32,9 @@ public class Builds {
     @XmlAttribute
     private String nextHref;
 
+    @XmlAttribute
+    private Integer count;
+
     @XmlElement(name = "build")
     private List<BuildRef> builds;
 
@@ -42,4 +45,16 @@ public class Builds {
     public String nextHref() {
         return nextHref;
     }
+
+    public void count(int count) {
+        this.count = count;
+    }
+
+    public void nextHref(String nextHref) {
+        this.nextHref = nextHref;
+    }
+
+    public void builds(List<BuildRef> list) {
+        this.builds = list;
+    }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java
index 6494e56..a983e77 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java
@@ -77,4 +77,11 @@ public class BuildRefCompacted {
     @Override public int hashCode() {
         return Objects.hashCode(id, buildTypeId, branchName, status, state);
     }
+
+    /**
+     *
+     */
+    public int id() {
+        return id;
+    }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefDao.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefDao.java
index cfcd3bd..2b68806 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefDao.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefDao.java
@@ -33,6 +33,7 @@ import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.ci.db.TcHelperDb;
 import org.apache.ignite.ci.tcmodel.hist.BuildRef;
+import org.apache.ignite.internal.util.GridIntList;
 import org.jetbrains.annotations.NotNull;
 
 public class BuildRefDao {
@@ -53,16 +54,16 @@ public class BuildRefDao {
         buildsCache = ignite.getOrCreateCache(TcHelperDb.getCacheV2Config(TEAMCITY_BUILD_CACHE_NAME));
     }
 
-    @NotNull protected Stream<BuildRefCompacted> compactedBuildsForServer(long srvIdMaskHigh) {
+    @NotNull protected Stream<BuildRefCompacted> compactedBuildsForServer(long srvId) {
         return StreamSupport.stream(buildsCache.spliterator(), false)
-            .filter(entry -> entry.getKey() >> 32 == srvIdMaskHigh)
+            .filter(entry -> entry.getKey() >> 32 == srvId)
             .map(javax.cache.Cache.Entry::getValue);
     }
 
-    public int saveChunk(long srvIdMaskHigh, List<BuildRef> ghData) {
+    public int saveChunk(long srvId, List<BuildRef> ghData) {
         Set<Long> ids = ghData.stream().map(BuildRef::getId)
             .filter(Objects::nonNull)
-            .map(buildId -> buildIdToCacheKey(srvIdMaskHigh, buildId))
+            .map(buildId -> buildIdToCacheKey(srvId, buildId))
             .collect(Collectors.toSet());
 
         Map<Long, BuildRefCompacted> existingEntries = buildsCache.getAll(ids);
@@ -73,7 +74,7 @@ public class BuildRefDao {
             .collect(Collectors.toList());
 
         for (BuildRefCompacted next : collect) {
-            long cacheKey = buildIdToCacheKey(srvIdMaskHigh, next.id);
+            long cacheKey = buildIdToCacheKey(srvId, next.id);
             BuildRefCompacted buildPersisted = existingEntries.get(cacheKey);
 
             if (buildPersisted == null || !buildPersisted.equals(next))
@@ -87,19 +88,19 @@ public class BuildRefDao {
     }
 
     /**
-     * @param srvIdMaskHigh Server id mask high.
+     * @param srvId Server id mask high.
      * @param buildId Build id.
      */
-    private long buildIdToCacheKey(long srvIdMaskHigh, int buildId) {
-        return (long)buildId | srvIdMaskHigh << 32;
+    private long buildIdToCacheKey(long srvId, int buildId) {
+        return (long)buildId | srvId << 32;
     }
 
     /**
-     * @param srvIdMaskHigh Server id mask high.
+     * @param srvId Server id mask high.
      * @param buildTypeId Build type id.
      * @param bracnhNameQry Bracnh name query.
      */
-    @NotNull public List<BuildRef> findBuildsInHistory(long srvIdMaskHigh,
+    @NotNull public List<BuildRef> findBuildsInHistory(long srvId,
         @Nullable String buildTypeId,
         String bracnhNameQry) {
 
@@ -111,10 +112,29 @@ public class BuildRefDao {
         if (bracnhNameQryId == null)
             return Collections.emptyList();
 
-        return compactedBuildsForServer(srvIdMaskHigh)
+        return compactedBuildsForServer(srvId)
             .filter(e -> e.buildTypeId == (int)buildTypeIdId)
             .filter(e -> e.branchName == (int)bracnhNameQryId)
             .map(compacted -> compacted.toBuildRef(compactor))
             .collect(Collectors.toList());
     }
+
+    /**
+     * @param srvId Server id.
+     */
+    public List<BuildRefCompacted> getQueuedAndRunning(long srvId) {
+        GridIntList list = new GridIntList(2);
+        Integer stateQueuedId = compactor.getStringIdIfPresent(BuildRef.STATE_QUEUED);
+        if (stateQueuedId != null)
+            list.add(stateQueuedId);
+
+        Integer stateRunningId = compactor.getStringIdIfPresent(BuildRef.STATE_RUNNING);
+        if (stateRunningId != null)
+            list.add(stateRunningId);
+
+
+        return compactedBuildsForServer(srvId)
+            .filter(e ->  list.contains(e.state) )
+            .collect(Collectors.toList());
+    }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedImpl.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedImpl.java
index 746ef38..5e598ec 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedImpl.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedImpl.java
@@ -16,11 +16,16 @@
  */
 package org.apache.ignite.ci.teamcity.ignited;
 
+
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
 import javax.annotation.Nullable;
 import javax.inject.Inject;
+import com.google.common.collect.Sets;
 import org.apache.ignite.ci.ITeamcity;
 import org.apache.ignite.ci.di.AutoProfiling;
 import org.apache.ignite.ci.di.MonitoredTask;
@@ -81,7 +86,7 @@ public class TeamcityIgnitedImpl implements ITeamcityIgnited {
         Build build = conn.triggerBuild(buildTypeId, branchName, cleanRebuild, queueAtTop);
 
         //todo may add additional parameter: load builds into DB in sync/async fashion
-        runAсtualizeBuilds(srvId, false, build.getId());
+        runActualizeBuilds(srvId, false, Sets.newHashSet(build.getId()));
 
         return build;
     }
@@ -89,8 +94,12 @@ public class TeamcityIgnitedImpl implements ITeamcityIgnited {
     /**
      *
      */
-    private void actualizeRecentBuilds() {
-        runAсtualizeBuilds(srvId, false, null);
+    void actualizeRecentBuilds() {
+        List<BuildRefCompacted> running = buildRefDao.getQueuedAndRunning(srvIdMaskHigh);
+
+        Set<Integer> collect = running.stream().map(BuildRefCompacted::id).collect(Collectors.toSet());
+
+        runActualizeBuilds(srvId, false, collect);
 
         // schedule full resync later
         scheduler.invokeLater(this::sheduleResync, 60, TimeUnit.SECONDS);
@@ -107,27 +116,27 @@ public class TeamcityIgnitedImpl implements ITeamcityIgnited {
     /**
      *
      */
-    private void fullReindex() {
-        runAсtualizeBuilds(srvId, true, null);
+    void fullReindex() {
+        runActualizeBuilds(srvId, true, null);
     }
 
     /**
-     * @param srvId Server id.
-     * @param fullReindex Reindex all builds from TC history. Ignored if particular ID provided.
-     * @param buildIdCanFinish Build ID can be used as end of syncing.
+     * @param srvId Server id. todo to be added as composite name extend
+     * @param fullReindex Reindex all builds from TC history.
+     * @param mandatoryToReload Build ID can be used as end of syncing. Ignored if fullReindex mode.
      */
     @MonitoredTask(name = "Actualize BuildRefs, full resync", nameExtArgIndex = 1)
     @AutoProfiling
-    protected String runAсtualizeBuilds(String srvId, boolean fullReindex,
-        @Nullable Integer buildIdCanFinish) {
+    protected String runActualizeBuilds(String srvId, boolean fullReindex,
+        @Nullable Set<Integer> mandatoryToReload) {
         AtomicReference<String> outLinkNext = new AtomicReference<>();
         List<BuildRef> tcDataFirstPage = conn.getBuildRefs(null, outLinkNext);
 
         int cntSaved = buildRefDao.saveChunk(srvIdMaskHigh, tcDataFirstPage);
         int totalChecked = tcDataFirstPage.size();
 
-        boolean noRequiredBuild = buildIdCanFinish == null;
-        boolean requiredBuildFound = false;
+        final Set<Integer> stillNeedToFind =
+            mandatoryToReload == null ? Collections.emptySet() : Sets.newHashSet(mandatoryToReload);
 
         while (outLinkNext.get() != null) {
             String nextPageUrl = outLinkNext.get();
@@ -138,13 +147,13 @@ public class TeamcityIgnitedImpl implements ITeamcityIgnited {
             cntSaved += savedCurChunk;
             totalChecked += tcDataNextPage.size();
 
-            if (buildIdCanFinish != null) {
-                if (tcDataNextPage.stream().map(BuildRef::getId).anyMatch(buildIdCanFinish::equals))
-                    requiredBuildFound = true; // Syncing till specific build ID.
-            }
+            if (!fullReindex) {
+                if (!stillNeedToFind.isEmpty())
+                    tcDataNextPage.stream().map(BuildRef::getId).forEach(stillNeedToFind::remove);
 
-            if (savedCurChunk == 0 && ((requiredBuildFound) || (noRequiredBuild && !fullReindex)))
-                break; // There are no modification at current page, hopefully no modifications at all
+                if (savedCurChunk == 0 && stillNeedToFind.isEmpty())
+                    break; // There are no modification at current page, hopefully no modifications at all
+            }
         }
 
         return "Entries saved " + cntSaved + " Builds checked " + totalChecked;
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/util/XmlUtil.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/util/XmlUtil.java
index 5eb1510..958edeb 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/util/XmlUtil.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/util/XmlUtil.java
@@ -18,9 +18,11 @@
 package org.apache.ignite.ci.util;
 
 import java.io.Reader;
+import java.io.StringWriter;
 import java.util.concurrent.ConcurrentHashMap;
 import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
 import javax.xml.bind.Unmarshaller;
 
 /**
@@ -31,7 +33,26 @@ public class XmlUtil {
     private static ConcurrentHashMap<Class, JAXBContext> cachedCtx = new ConcurrentHashMap<>();
 
     public static <T> T load(Class<T> tCls, Reader reader) throws JAXBException {
-        final JAXBContext ctx = cachedCtx.computeIfAbsent(tCls, c -> {
+        Unmarshaller unmarshaller = getContext(tCls).createUnmarshaller();
+        T unmarshal = (T)unmarshaller.unmarshal(reader);
+
+        ObjectInterner.internFields(unmarshal);
+
+        return unmarshal;
+    }
+
+    public static String save(Object obj) throws JAXBException {
+        Marshaller marshaller = getContext(obj.getClass()).createMarshaller();
+        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
+        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
+        StringWriter writer = new StringWriter();
+        marshaller.marshal(obj, writer);
+
+        return writer.toString();
+    }
+
+    private static <T> JAXBContext getContext(Class<T> tCls) {
+        return cachedCtx.computeIfAbsent(tCls, c -> {
             try {
                 return JAXBContext.newInstance(tCls);
             }
@@ -39,14 +60,6 @@ public class XmlUtil {
                 throw new RuntimeException(e);
             }
         });
-        Unmarshaller unmarshaller = ctx.createUnmarshaller();
-        T unmarshal = (T)unmarshaller.unmarshal(reader);
-
-        int interned = ObjectInterner.internFields(unmarshal);
-       // if (interned > 0)
-       //     System.out.println("Strings saved: " + interned);
-
-        return unmarshal;
     }
 
     /**
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Version.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Version.java
index 0b3e902..d5b8765 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Version.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Version.java
@@ -23,7 +23,7 @@ package org.apache.ignite.ci.web.model;
     public static final String GITHUB_REF = "https://github.com/apache/ignite-teamcity-bot";
 
     /** TC Bot Version. */
-    public static final String VERSION = "20181011";
+    public static final String VERSION = "20181012";
 
     /** TC Bot Version. */
     public String version = VERSION;
diff --git a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/IgnitedTcInMemoryIntegrationTest.java b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/IgnitedTcInMemoryIntegrationTest.java
index f66abd5..ea7e0a9 100644
--- a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/IgnitedTcInMemoryIntegrationTest.java
+++ b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/IgnitedTcInMemoryIntegrationTest.java
@@ -21,14 +21,22 @@ import com.google.inject.Guice;
 import com.google.inject.Injector;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.Ignition;
 import org.apache.ignite.ci.di.scheduler.DirectExecNoWaitSheduler;
 import org.apache.ignite.ci.di.scheduler.IScheduler;
+import org.apache.ignite.ci.di.scheduler.NoOpSheduler;
 import org.apache.ignite.ci.tcmodel.hist.BuildRef;
+import org.apache.ignite.ci.teamcity.pure.BuildHistoryEmulator;
 import org.apache.ignite.ci.teamcity.pure.ITeamcityHttpConnection;
 import org.apache.ignite.ci.user.ICredentialsProv;
+import org.jetbrains.annotations.NotNull;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.mockito.Mockito;
 
@@ -38,18 +46,37 @@ import static org.apache.ignite.ci.teamcity.ignited.IgniteStringCompactor.STRING
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.when;
 
+/**
+ * Test for ignite persistence
+ */
 public class IgnitedTcInMemoryIntegrationTest {
-
+    /** Server Name for test. */
     public static final String APACHE = "apache";
+    /** Ignite. */
+    private static Ignite ignite;
+
+    /**
+     *
+     */
+    @BeforeClass
+    public static void startIgnite() {
+        ignite = Ignition.start();
+    }
+
+    /**
+     *
+     */
+    @AfterClass
+    public static void stopIgnite() {
+        ignite.close();
+    }
 
     @Test
     public void saveAndLoadBuildReference() throws IOException {
-        Ignite ignite = Ignition.start();
-
         ITeamcityHttpConnection http = Mockito.mock(ITeamcityHttpConnection.class);
 
         when(http.sendGet(anyString(), anyString())).thenAnswer(
-            (invocationOnMock)->{
+            (invocationOnMock) -> {
                 String url = invocationOnMock.getArgument(1);
 
                 if (url.contains("/app/rest/latest/builds?locator=defaultFilter:false,count:1000,start:1000"))
@@ -62,46 +89,128 @@ public class IgnitedTcInMemoryIntegrationTest {
             }
         );
 
-        try {
-            TeamcityIgnitedModule module = new TeamcityIgnitedModule();
+        TeamcityIgnitedModule module = new TeamcityIgnitedModule();
 
-            module.overrideHttp(http);
+        module.overrideHttp(http);
 
-            Injector injector = Guice.createInjector(module, new AbstractModule() {
-                @Override protected void configure() {
-                    bind(Ignite.class).toInstance(ignite);
-                    bind(IScheduler.class).to(DirectExecNoWaitSheduler.class);
-                }
-            });
+        Injector injector = Guice.createInjector(module, new AbstractModule() {
+            @Override protected void configure() {
+                bind(Ignite.class).toInstance(ignite);
+                bind(IScheduler.class).to(DirectExecNoWaitSheduler.class);
+            }
+        });
+
+        ITeamcityIgnited srv = injector.getInstance(ITeamcityIgnitedProvider.class).server(APACHE, creds());
 
-            ICredentialsProv mock = Mockito.mock(ICredentialsProv.class);
-            when(mock.hasAccess(anyString())).thenReturn(true);
-            when(mock.getUser(anyString())).thenReturn("mtcga");
-            when(mock.getPassword(anyString())).thenReturn("123");
-            ITeamcityIgnited srv = injector.getInstance(ITeamcityIgnitedProvider.class).server(APACHE, mock);
+        String buildTypeId = "IgniteTests24Java8_RunAll";
+        String branchName = "<default>";
+        List<BuildRef> hist = srv.getBuildHistory(buildTypeId, branchName);
+        //todo mult branches including pull/4926/head
 
-            String buildTypeId = "IgniteTests24Java8_RunAll";
-            String branchName = "<default>";
-            List<BuildRef> hist = srv.getBuildHistory(buildTypeId, branchName);
+        assertTrue(!hist.isEmpty());
 
-            assertTrue(!hist.isEmpty());
+        for (BuildRef h : hist) {
+            System.out.println(h);
 
-            for (BuildRef h : hist) {
-                System.out.println(h);
+            assertEquals(buildTypeId, h.suiteId());
 
-                assertEquals(buildTypeId, h.suiteId());
+            assertEquals("refs/heads/master", h.branchName());
+        }
 
-                assertEquals("refs/heads/master", h.branchName());
+        ignite.cache(STRINGS_CACHE).forEach(
+            (e) -> {
+                System.out.println(e.getValue());
             }
+        );
+    }
 
-            ignite.cache(STRINGS_CACHE).forEach(
-                (e) -> {
-                    System.out.println(e.getValue());
-                }
-            );
+    @Test
+    public void incrementalActualizationOfBuildsContainsQueued() throws IOException {
+        ITeamcityHttpConnection http = Mockito.mock(ITeamcityHttpConnection.class);
+
+        int queuedBuildIdx = 500;
+        ArrayList<BuildRef> tcBuilds = new ArrayList<>();
+        for (int i = 0; i < 1000; i++) {
+            BuildRef e = new BuildRef();
+            e.state = i >= queuedBuildIdx ?
+                (Math.random() * 2 > 1 ? BuildRef.STATE_QUEUED : BuildRef.STATE_RUNNING)
+                : BuildRef.STATE_FINISHED;
+            e.status = BuildRef.STATUS_SUCCESS;
+            e.buildTypeId = "IgniteTests24Java8_RunAll";
+            e.branchName = "refs/heads/master";
+            e.setId(i + 50000);
+            tcBuilds.add(e);
         }
-        finally {
-            ignite.close();
+
+        BuildHistoryEmulator emulator = new BuildHistoryEmulator(tcBuilds);
+
+        when(http.sendGet(anyString(), anyString())).thenAnswer(
+            (invocationOnMock) -> {
+                String url = invocationOnMock.getArgument(1);
+
+                InputStream stream = emulator.handleUrl(url);
+
+                if (stream != null)
+                    return stream;
+
+                throw new FileNotFoundException(url);
+            }
+        );
+
+        TeamcityIgnitedModule module = new TeamcityIgnitedModule();
+
+        module.overrideHttp(http);
+
+        Injector injector = Guice.createInjector(module, new AbstractModule() {
+            @Override protected void configure() {
+                bind(Ignite.class).toInstance(ignite);
+                bind(IScheduler.class).to(NoOpSheduler.class);
+            }
+        });
+
+        ITeamcityIgnited srv = injector.getInstance(ITeamcityIgnitedProvider.class).server(APACHE, creds());
+
+        TeamcityIgnitedImpl teamcityIgnited = (TeamcityIgnitedImpl)srv;
+        teamcityIgnited.fullReindex();
+        String buildTypeId = "IgniteTests24Java8_RunAll";
+        String branchName = "<default>";
+        List<String> statues = srv.getBuildHistory(buildTypeId, branchName).stream().map(BuildRef::state).distinct().collect(Collectors.toList());
+        System.out.println("Before " + statues);
+
+        for (int i = queuedBuildIdx; i < tcBuilds.size(); i++)
+            tcBuilds.get(i).state = BuildRef.STATE_FINISHED;
+
+        teamcityIgnited.actualizeRecentBuilds();
+
+
+        List<BuildRef> hist = srv.getBuildHistory(buildTypeId, branchName);
+
+        assertTrue(!hist.isEmpty());
+
+        for (BuildRef h : hist) {
+            assertEquals(buildTypeId, h.suiteId());
+
+            assertEquals("refs/heads/master", h.branchName());
+
+            assertTrue("Build " + h + " is expected to be finished" , h.isFinished());
         }
+
+        statues = hist.stream().map(BuildRef::state).distinct().collect(Collectors.toList());
+
+        System.out.println("After " + statues);
     }
+
+    /**
+     *
+     */
+    @NotNull public ICredentialsProv creds() {
+        ICredentialsProv mock = Mockito.mock(ICredentialsProv.class);
+
+        when(mock.hasAccess(anyString())).thenReturn(true);
+        when(mock.getUser(anyString())).thenReturn("mtcga");
+        when(mock.getPassword(anyString())).thenReturn("123");
+
+        return mock;
+    }
+
 }
diff --git a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/pure/BuildHistoryEmulator.java b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/pure/BuildHistoryEmulator.java
new file mode 100644
index 0000000..d9d40be
--- /dev/null
+++ b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/pure/BuildHistoryEmulator.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.ci.teamcity.pure;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.xml.bind.JAXBException;
+import org.apache.ignite.ci.tcmodel.hist.BuildRef;
+import org.apache.ignite.ci.tcmodel.hist.Builds;
+import org.apache.ignite.ci.util.XmlUtil;
+import org.jetbrains.annotations.Nullable;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class BuildHistoryEmulator {
+    private ArrayList<BuildRef> sharedState;
+
+    public BuildHistoryEmulator(ArrayList<BuildRef> sharedState) {
+        this.sharedState = sharedState;
+    }
+
+    /**
+     * @param url Url.
+     */
+    @Nullable public InputStream handleUrl(String url) throws JAXBException {
+        if (!url.contains("/app/rest/latest/builds?locator=defaultFilter:false"))
+            return null;
+
+        int cnt = getIntFromLocator(url, "count:", 100);
+        int start = getIntFromLocator(url, "start:", 100);
+
+        int totalBuilds = sharedState.size();
+        int totalRemained = totalBuilds - start;
+        if (totalRemained < 0)
+            totalRemained = 0;
+
+        int returnNow = Math.min(totalRemained, cnt);
+
+        int nextStart = 0;
+        if (totalBuilds > start + returnNow)
+            nextStart = start + returnNow;
+
+        Builds builds = createBuilds(cnt, returnNow, nextStart);
+        List<BuildRef> buildsList  = new ArrayList<>();
+
+        for (int i = start; i < start + returnNow; i++)
+            buildsList.add(sharedState.get(i));
+
+        builds.builds(buildsList);
+
+        return new ByteArrayInputStream(XmlUtil.save(builds).getBytes(UTF_8));
+    }
+
+    public Builds createBuilds(int cnt, int returnNow, int nextStart) {
+        Builds builds = new Builds();
+        builds.count(returnNow);
+        if (nextStart > 0) {
+            StringBuffer buf = new StringBuffer();
+            buf.append("/app/rest/latest/builds?locator=defaultFilter:false,count:");
+            buf.append(cnt);
+            buf.append(",start:");
+            buf.append(nextStart);
+
+            builds.nextHref(buf.toString());
+        }
+
+        return builds;
+
+    }
+
+    /**
+     * @param url Url.
+     * @param prefix Prefix.
+     * @param def Def.
+     */
+    public int getIntFromLocator(String url, String prefix, int def) {
+        Pattern compile = Pattern.compile(prefix + "[0-9]*");
+        Matcher m = compile.matcher(url);
+        if (!m.find())
+            return def;
+
+        String cntStr = m.group(0);
+
+        if(cntStr == null)
+            return def;
+
+        return Integer.parseInt(cntStr.substring(prefix.length()));
+
+    }
+}