/* * RHQ Management Platform * Copyright (C) 2013 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.enterprise.server.util; import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import java.util.Collections; import java.util.List; import javax.persistence.Query; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.rhq.core.domain.util.PageControl; import org.rhq.core.domain.util.PageList; /** * @author Lukas Krejci * @since 4.9 */ @Test public class QueryUtilityTest { private int[] attemptCounters; private Query dataQuery; private Query countQuery; private int numberOfInconsistentResults; @BeforeClass public void setupMocks() { attemptCounters = new int[2]; dataQuery = Mockito.mock(Query.class); when(dataQuery.getResultList()).then(new Answer<List<?>>() { @Override public List<?> answer(InvocationOnMock invocation) throws Throwable { int attempt = attemptCounters[0]++; if (attempt < numberOfInconsistentResults) { return Collections.emptyList(); } else if (attempt < QueryUtility.PHANTOM_READ_MAX_ATTEMPTS) { return Collections.singletonList(this); } else { throw new AssertionError( "Shouldn't have been called more than " + QueryUtility.PHANTOM_READ_MAX_ATTEMPTS + " times"); } } }); countQuery = Mockito.mock(Query.class); when(countQuery.getSingleResult()).then(new Answer<Long>() { @Override public Long answer(InvocationOnMock invocation) throws Throwable { if (attemptCounters[1]++ < QueryUtility.PHANTOM_READ_MAX_ATTEMPTS) { return 1l; } else { throw new AssertionError( "Shouldn't have been called more than " + QueryUtility.PHANTOM_READ_MAX_ATTEMPTS + " times"); } } }); } @BeforeMethod public void resetAttemptCounters() { for(int i = 0; i < attemptCounters.length; ++i) { attemptCounters[i] = 0; } } public void defaultPagedDataFetchSettings() { QueryUtility.PagedDataFetchSettings settings = new QueryUtility.PagedDataFetchSettings(); assertEquals(settings.getMaxAttempts(), QueryUtility.PHANTOM_READ_MAX_ATTEMPTS, "Wrong default max attempts"); assertEquals(settings.getMinWaitTime(), QueryUtility.PHANTOM_READ_MIN_WAIT_TIME, "Wrong default min wait time"); assertEquals(settings.getMaxWaitTime(), QueryUtility.PHANTOM_READ_MAX_WAIT_TIME, "Wrong default max wait time"); assertFalse(settings.isThrowOnMaxAttempts(), "Wrong default throw on max attempts"); } public void lagCoefficientForPagedDataFetch() { QueryUtility.PagedDataFetchSettings settings = new QueryUtility.PagedDataFetchSettings(); settings.setMaxAttempts(10); settings.setMinWaitTime(1); settings.setMaxWaitTime(10); float lag = settings.getLagIncreaseCoefficient(); assertEquals(lag, (float) Math.pow(10, 1d / 8), "Unexpected lag computed"); } public void dataFetchPerformsMaxAttemptsOnInconsistentResults() { numberOfInconsistentResults = QueryUtility.DEFAULT_PHANTOM_READ_MAX_ATTEMPTS; PageControl pc = PageControl.getUnlimitedInstance(); PageList<Object> result = QueryUtility.fetchPagedDataAndCount(dataQuery, countQuery, pc, null); assertEquals(result, Collections.emptyList(), "The result should be empty"); assertEquals(result.getTotalSize(), 1, "Unexpected total size"); assertFalse(result.isConsistent(), "The result should be inconsistent"); assertEquals(attemptCounters[0], QueryUtility.PHANTOM_READ_MAX_ATTEMPTS); assertEquals(attemptCounters[1], QueryUtility.PHANTOM_READ_MAX_ATTEMPTS); } public void dataFetchReturnsConsistentResultsWhenDetected() { numberOfInconsistentResults = 2; PageControl pc = PageControl.getUnlimitedInstance(); PageList<Object> result = QueryUtility.fetchPagedDataAndCount(dataQuery, countQuery, pc, null); assertEquals(result.size(), 1, "The result should have 1 element"); assertEquals(result.getTotalSize(), 1, "Unexpected total size"); assertTrue(result.isConsistent(), "The result should be consistent"); assertEquals(attemptCounters[0], numberOfInconsistentResults + 1); assertEquals(attemptCounters[1], numberOfInconsistentResults + 1); } public void dataFetchThrowsAfterMaxAttemptsWhenSetUpSo() { numberOfInconsistentResults = QueryUtility.DEFAULT_PHANTOM_READ_MAX_ATTEMPTS; PageControl pc = PageControl.getUnlimitedInstance(); QueryUtility.PagedDataFetchSettings settings = new QueryUtility.PagedDataFetchSettings(); settings.setThrowOnMaxAttempts(true); try { QueryUtility.fetchPagedDataAndCount(dataQuery, countQuery, pc, settings); fail("Fetch should have thrown an exception after max attempts"); } catch (PhantomReadMaxAttemptsExceededException e) { //expected PageList<?> result = e.getList(); assertEquals(result, Collections.emptyList(), "The result should be empty"); assertEquals(result.getTotalSize(), 1, "Unexpected total size"); assertFalse(result.isConsistent(), "The result should be inconsistent"); } assertEquals(attemptCounters[0], QueryUtility.PHANTOM_READ_MAX_ATTEMPTS); assertEquals(attemptCounters[1], QueryUtility.PHANTOM_READ_MAX_ATTEMPTS); } public void repeatedFetchesWaitLongEnough() { numberOfInconsistentResults = QueryUtility.DEFAULT_PHANTOM_READ_MAX_ATTEMPTS; PageControl pc = PageControl.getUnlimitedInstance(); long time = System.currentTimeMillis(); PageList<?> result = QueryUtility.fetchPagedDataAndCount(dataQuery, countQuery, pc, null); time = System.currentTimeMillis() - time; QueryUtility.PagedDataFetchSettings unusedSettings = new QueryUtility.PagedDataFetchSettings(); assertTrue(time >= unusedSettings.getMinimumTotalWaitTime(), "The fetch should have spent more time trying. Was " + time + "ms, but should have been at least " + unusedSettings.getMinimumTotalWaitTime() + "ms"); assertEquals(result, Collections.emptyList(), "The result should be empty"); assertEquals(result.getTotalSize(), 1, "Unexpected total size"); assertFalse(result.isConsistent(), "The result should be inconsistent"); assertEquals(attemptCounters[0], QueryUtility.PHANTOM_READ_MAX_ATTEMPTS); assertEquals(attemptCounters[1], QueryUtility.PHANTOM_READ_MAX_ATTEMPTS); } }