/*
* Copyright 2016 NAVER Corp.
*
* 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 com.navercorp.pinpoint.profiler.receiver.service;
import com.navercorp.pinpoint.bootstrap.util.jdk.ThreadLocalRandom;
import com.navercorp.pinpoint.common.util.PinpointThreadFactory;
import com.navercorp.pinpoint.profiler.context.active.ActiveTraceInfo;
import com.navercorp.pinpoint.profiler.context.active.ActiveTraceRepository;
import com.navercorp.pinpoint.thrift.dto.command.TActiveThreadLightDump;
import com.navercorp.pinpoint.thrift.dto.command.TCmdActiveThreadLightDump;
import com.navercorp.pinpoint.thrift.dto.command.TCmdActiveThreadLightDumpRes;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author Taejin Koo
*/
public class ActiveThreadLightDumpServiceTest {
private static final int CREATE_SIZE = 10;
private static final long DEFAULT_TIME_MILLIS = System.currentTimeMillis() - 1000000;
private static final long TIME_DIFF_INTERVAL = 100;
private final AtomicInteger idGenerator = new AtomicInteger();
private final PinpointThreadFactory pinpointThreadFactory = new PinpointThreadFactory(this.getClass().getSimpleName());
@Before
public void setup() {
idGenerator.set(0);
}
@Test
public void basicFunctionTest1() throws Exception {
List<WaitingJob> waitingJobList = createWaitingJobList(CREATE_SIZE);
try {
List<ActiveTraceInfo> activeTraceInfoList = createMockActiveTraceInfoList(CREATE_SIZE, DEFAULT_TIME_MILLIS, TIME_DIFF_INTERVAL, waitingJobList);
ActiveThreadLightDumpService service = createService(activeTraceInfoList);
TCmdActiveThreadLightDumpRes response = (TCmdActiveThreadLightDumpRes) service.requestCommandService(createRequest(0, null, null));
Assert.assertEquals(CREATE_SIZE, response.getThreadDumpsSize());
} finally {
clearResource(waitingJobList);
}
}
@Test
public void basicFunctionTest2() throws Exception {
List<WaitingJob> waitingJobList = createWaitingJobList(CREATE_SIZE);
try {
List<ActiveTraceInfo> activeTraceInfoList = createMockActiveTraceInfoList(CREATE_SIZE, DEFAULT_TIME_MILLIS, TIME_DIFF_INTERVAL, waitingJobList);
TCmdActiveThreadLightDump tCmdActiveThreadDump = createRequest(0, null, Arrays.asList(1L));
ActiveThreadLightDumpService service = createService(activeTraceInfoList);
TCmdActiveThreadLightDumpRes response = (TCmdActiveThreadLightDumpRes) service.requestCommandService(tCmdActiveThreadDump);
Assert.assertEquals(1, response.getThreadDumpsSize());
} finally {
clearResource(waitingJobList);
}
}
@Test
public void basicFunctionTest3() throws Exception {
List<WaitingJob> waitingJobList = createWaitingJobList(CREATE_SIZE);
try {
int targetThreadNameSize = 3;
List<ActiveTraceInfo> activeTraceInfoList = createMockActiveTraceInfoList(CREATE_SIZE, DEFAULT_TIME_MILLIS, TIME_DIFF_INTERVAL, waitingJobList);
List<String> threadNameList = extractThreadNameList(activeTraceInfoList, targetThreadNameSize);
TCmdActiveThreadLightDump tCmdActiveThreadDump = createRequest(0, threadNameList, null);
ActiveThreadLightDumpService service = createService(activeTraceInfoList);
TCmdActiveThreadLightDumpRes response = (TCmdActiveThreadLightDumpRes) service.requestCommandService(tCmdActiveThreadDump);
Assert.assertEquals(3, response.getThreadDumpsSize());
} finally {
clearResource(waitingJobList);
}
}
@Test
public void basicFunctionTest4() throws Exception {
List<WaitingJob> waitingJobList = createWaitingJobList(CREATE_SIZE);
try {
List<ActiveTraceInfo> activeTraceInfoList = createMockActiveTraceInfoList(CREATE_SIZE, DEFAULT_TIME_MILLIS, TIME_DIFF_INTERVAL, waitingJobList);
List<ActiveTraceInfo> copied = shuffle(activeTraceInfoList);
int targetThreadNameSize = 3;
List<String> threadNameList = extractThreadNameList(copied.subList(0, targetThreadNameSize), targetThreadNameSize);
int targetTraceIdSize = 3;
List<Long> localTraceIdList = extractLocalTraceIdList(copied.subList(targetThreadNameSize, CREATE_SIZE), targetTraceIdSize);
TCmdActiveThreadLightDump tCmdActiveThreadDump = createRequest(0, threadNameList, localTraceIdList);
ActiveThreadLightDumpService service = createService(activeTraceInfoList);
TCmdActiveThreadLightDumpRes response = (TCmdActiveThreadLightDumpRes) service.requestCommandService(tCmdActiveThreadDump);
Assert.assertEquals(targetThreadNameSize + targetTraceIdSize, response.getThreadDumpsSize());
} finally {
clearResource(waitingJobList);
}
}
@Test
public void basicFunctionTest5() throws Exception {
List<WaitingJob> waitingJobList = createWaitingJobList(CREATE_SIZE);
try {
List<ActiveTraceInfo> activeTraceInfoList = createMockActiveTraceInfoList(CREATE_SIZE, DEFAULT_TIME_MILLIS, TIME_DIFF_INTERVAL, waitingJobList);
int limit = 3;
List<Long> oldTimeList = getOldTimeList(limit);
TCmdActiveThreadLightDump tCmdActiveThreadDump = createRequest(limit, null, null);
ActiveThreadLightDumpService service = createService(activeTraceInfoList);
TCmdActiveThreadLightDumpRes response = (TCmdActiveThreadLightDumpRes) service.requestCommandService(tCmdActiveThreadDump);
Assert.assertEquals(limit, response.getThreadDumpsSize());
for (TActiveThreadLightDump dump : response.getThreadDumps()) {
Assert.assertTrue(oldTimeList.contains(dump.getStartTime()));
}
} finally {
clearResource(waitingJobList);
}
}
private List<WaitingJob> createWaitingJobList(int createActiveTraceRepositorySize) {
List<WaitingJob> waitingJobList = new ArrayList<WaitingJob>();
for (int i = 0; i < createActiveTraceRepositorySize; i++) {
waitingJobList.add(new WaitingJob(100));
}
return waitingJobList;
}
private List<ActiveTraceInfo> createMockActiveTraceInfoList(int createActiveTraceRepositorySize, long currentTimeMillis, long diff, List<WaitingJob> waitingJobList) {
List<ActiveTraceInfo> activeTraceInfoList = new ArrayList<ActiveTraceInfo>(createActiveTraceRepositorySize);
for (int i = 0; i < createActiveTraceRepositorySize; i++) {
ActiveTraceInfo activeTraceInfo = createActiveTraceInfo(currentTimeMillis + (diff * i), waitingJobList.get(i));
activeTraceInfoList.add(activeTraceInfo);
}
return activeTraceInfoList;
}
private ActiveTraceInfo createActiveTraceInfo(long startTime, Runnable runnable) {
Thread thread = pinpointThreadFactory.newThread(runnable);
thread.start();
return new ActiveTraceInfo(idGenerator.incrementAndGet(), startTime, thread);
}
private List<Long> getOldTimeList(int maxCount) {
List<Long> startTimeMillisList = new ArrayList<Long>(maxCount);
for (int i = 0; i < maxCount; i++) {
startTimeMillisList.add(DEFAULT_TIME_MILLIS + (TIME_DIFF_INTERVAL * i));
}
return startTimeMillisList;
}
private List<String> extractThreadNameList(List<ActiveTraceInfo> activeTraceInfoList, int size) {
List<ActiveTraceInfo> copied = shuffle(activeTraceInfoList);
List<String> threadNameList = new ArrayList<String>(size);
for (int i = 0; i < size; i++) {
threadNameList.add(copied.get(i).getThread().getName());
}
return threadNameList;
}
private List<Long> extractLocalTraceIdList(List<ActiveTraceInfo> activeTraceInfoList, int size) {
List<ActiveTraceInfo> copied = shuffle(activeTraceInfoList);
List<Long> localTraceIdList = new ArrayList<Long>(size);
for (int i = 0; i < size; i++) {
localTraceIdList.add(copied.get(i).getLocalTraceId());
}
return localTraceIdList;
}
private <E> List<E> shuffle(List<E> list) {
ArrayList<E> copied = new ArrayList<E>(list);
Collections.shuffle(copied, ThreadLocalRandom.current());
return copied;
}
private ActiveThreadLightDumpService createService(List<ActiveTraceInfo> activeTraceInfoList) {
ActiveTraceRepository activeTraceRepository = mock(ActiveTraceRepository.class);
when(activeTraceRepository.collect()).thenReturn(activeTraceInfoList);
return new ActiveThreadLightDumpService(activeTraceRepository);
}
private TCmdActiveThreadLightDump createRequest(int limit, List<String> threadNameList, List<Long> localTraceIdList) {
TCmdActiveThreadLightDump request = new TCmdActiveThreadLightDump();
if (limit > 0) {
request.setLimit(limit);
}
if (threadNameList != null) {
request.setThreadNameList(threadNameList);
}
if (localTraceIdList != null) {
request.setLocalTraceIdList(localTraceIdList);
}
return request;
}
private void clearResource(List<WaitingJob> waitingJobList) {
if (waitingJobList == null) {
return;
}
for (WaitingJob waitingJob : waitingJobList) {
waitingJob.close();
}
}
private static class WaitingJob implements Runnable {
private final long timeIntervalMillis;
private boolean close = false;
public WaitingJob(long timeIntervalMillis) {
this.timeIntervalMillis = timeIntervalMillis;
}
@Override
public void run() {
while (!close) {
try {
Thread.sleep(timeIntervalMillis);
} catch (InterruptedException e) {
close = true;
}
}
}
public void close() {
this.close = true;
}
}
}