Skip to content

Commit

Permalink
test: add ThreadCPUTests (#54)
Browse files Browse the repository at this point in the history
* wip commit

Signed-off-by: Kartavya Vashishtha <[email protected]>

* add setup code

Signed-off-by: Kartavya Vashishtha <[email protected]>

* wip commit

Signed-off-by: Kartavya Vashishtha <[email protected]>

* add ThreadCPUTests

Signed-off-by: Kartavya Vashishtha <[email protected]>

* threadCPU test passes, threadsched uses OSTests

Signed-off-by: Kartavya Vashishtha <[email protected]>

* revert threadDiskIO to previous state

Signed-off-by: Kartavya Vashishtha <[email protected]>

---------

Signed-off-by: Kartavya Vashishtha <[email protected]>
  • Loading branch information
kartva authored Nov 14, 2023
1 parent 12bfa9b commit e3dfb15
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.performanceanalyzer.commons.os;


import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PowerMockIgnore("javax.management.*")
@SuppressStaticInitializationFor({"org.opensearch.performanceanalyzer.commons.os.OSGlobals"})
// whenNew requires the class calling the constructor to be PreparedForTest
@PrepareForTest({SchemaFileParser.class, OSGlobals.class})
public abstract class OSTests {
Map<String, Map<String, Object>> tidKVMap;
Map<String, Map<String, Object>> nextTidKVMap;

public OSTests(
SortedMap<String, Map<String, Object>> tidKVMap,
SortedMap<String, Map<String, Object>> nextTidKVMap,
String pid,
List<String> tids,
long scClkTlk,
long millisStart,
long millisEnd)
throws Exception {
// mock OSGlobals
PowerMockito.mockStatic(OSGlobals.class);
PowerMockito.when(OSGlobals.getPid()).thenReturn(pid);
PowerMockito.when(OSGlobals.getTids()).thenReturn(tids);
PowerMockito.when(OSGlobals.getScClkTck()).thenReturn(scClkTlk);

// mock System.currentTimeMillis()
// used by ThreadSched to compute SchedMetric
PowerMockito.mockStatic(System.class);
// having the time difference = 1000ms
// means that contextSwitchRate = difference in totctxsws
PowerMockito.when(System.currentTimeMillis()).thenReturn(millisStart, millisEnd);

this.tidKVMap = tidKVMap;
this.nextTidKVMap = nextTidKVMap;
}

void mockSchemaFileParser() throws Exception {
// mock SchemaFileParser (used by ThreadSched and ThreadCPU to read procfiles)
SchemaFileParser schemaFileParser = Mockito.mock(SchemaFileParser.class);

// create a list that contains all the values of tidKVMap
List<Map<String, Object>> tidArr = new ArrayList<Map<String, Object>>(tidKVMap.values());
List<Map<String, Object>> nextTidArr =
new ArrayList<Map<String, Object>>(nextTidKVMap.values());

var thenReturn = PowerMockito.when(schemaFileParser.parse()).thenReturn(tidArr.get(0));
for (int i = 1; i < tidArr.size(); i++) {
thenReturn = thenReturn.thenReturn(tidArr.get(i));
}
for (int i = 0; i < nextTidArr.size(); i++) {
thenReturn = thenReturn.thenReturn(nextTidArr.get(i));
}

PowerMockito.whenNew(SchemaFileParser.class)
.withAnyArguments()
.thenReturn(schemaFileParser);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,86 @@

package org.opensearch.performanceanalyzer.commons.os;

import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.verify;

import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.opensearch.performanceanalyzer.commons.metrics_generator.linux.LinuxCPUPagingActivityGenerator;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

public class ThreadCPUTests {
public static void main(String[] args) throws Exception {
runOnce();
}
@RunWith(PowerMockRunner.class)
// whenNew requires the class calling the constructor to be PreparedForTest
@PrepareForTest({SchemaFileParser.class, OSGlobals.class, ThreadCPU.class})
public class ThreadCPUTests extends OSTests {
public ThreadCPUTests() throws Exception {
super(
new TreeMap<String, Map<String, Object>>(
Map.of(
"1",
Map.of(
"pid", 1, "minflt", 1L, "majflt", 1L, "utime", 1L,
"stime", 1L, "rss", 1L),
"2",
Map.of(
"pid", 2, "minflt", 5L, "majflt", 10L, "utime", 3L,
"stime", 13L, "rss", 1L))),
new TreeMap<String, Map<String, Object>>(
Map.of(
"1",
Map.of(
"pid", 1, "minflt", 10L, "majflt", 100L, "utime",
6L, "stime", 6L, "rss", 2L),
"2",
Map.of(
"pid", 2, "minflt", 10L, "majflt", 15L, "utime",
13L, "stime", 18L, "rss", 4L))),
"0",
List.of("1", "2", "3"),
100,
10,
1010);

private static void runOnce() {
ThreadCPU.INSTANCE.addSample();
System.out.println(
"cpumap and pagemap:" + ThreadCPU.INSTANCE.getCPUPagingActivity().toString());
mockSchemaFileParser();
}

// - to enhance
@Test
public void testMetrics() {}
public void testMetrics() throws Exception {
LinuxCPUPagingActivityGenerator linuxCPUPagingActivityGenerator =
Mockito.mock(LinuxCPUPagingActivityGenerator.class);
PowerMockito.whenNew(LinuxCPUPagingActivityGenerator.class)
.withNoArguments()
.thenReturn(linuxCPUPagingActivityGenerator);

ThreadCPU.INSTANCE.addSample();

PowerMockito.verifyNew(SchemaFileParser.class)
.withArguments(
eq("/proc/0/task/1/stat"),
isA(String[].class),
isA(SchemaFileParser.FieldTypes[].class),
eq(true));
PowerMockito.verifyNew(SchemaFileParser.class)
.withArguments(
eq("/proc/0/task/2/stat"),
isA(String[].class),
isA(SchemaFileParser.FieldTypes[].class),
eq(true));

ThreadCPU.INSTANCE.addSample();

verify(linuxCPUPagingActivityGenerator)
.setPagingActivities("1", new Double[] {14.0, 9.0, 4.0});
verify(linuxCPUPagingActivityGenerator).setCPUUtilization("1", 0.29);
verify(linuxCPUPagingActivityGenerator)
.setPagingActivities("2", new Double[] {5.0, 5.0, 4.0});
verify(linuxCPUPagingActivityGenerator).setCPUUtilization("2", 0.15);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,85 +12,95 @@

import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.opensearch.performanceanalyzer.commons.metrics_generator.linux.LinuxSchedMetricsGenerator;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PowerMockIgnore("javax.management.*")
@SuppressStaticInitializationFor({"org.opensearch.performanceanalyzer.commons.os.OSGlobals"})
// whenNew requires the class calling the constructor to be PreparedForTest
@PrepareForTest({SchemaFileParser.class, OSGlobals.class, ThreadSched.class})
public class ThreadSchedTests {

private Map<String, Map<String, Object>> tidKVMap =
Map.of(
"1", Map.of("runticks", 100000000L, "waitticks", 100000000L, "totctxsws", 100L),
"2", Map.of("runticks", 100000000L, "waitticks", 100000000L, "totctxsws", 10L),
"3",
Map.of(
"runticks",
500000000L,
"waitticks",
500000000L,
"totctxsws",
120L));

private Map<String, Map<String, Object>> nextTidKVMap =
Map.of(
"1", Map.of("runticks", 200000000L, "waitticks", 200000000L, "totctxsws", 200L),
"2", Map.of("runticks", 500000000L, "waitticks", 500000000L, "totctxsws", 20L),
"3",
Map.of(
"runticks",
700000000L,
"waitticks",
700000000L,
"totctxsws",
220L));
public class ThreadSchedTests extends OSTests {
public ThreadSchedTests() throws Exception {
super(
new TreeMap<String, Map<String, Object>>(
Map.of(
"1",
Map.of(
"runticks",
100000000L,
"waitticks",
100000000L,
"totctxsws",
100L),
"2",
Map.of(
"runticks",
100000000L,
"waitticks",
100000000L,
"totctxsws",
10L),
"3",
Map.of(
"runticks",
500000000L,
"waitticks",
500000000L,
"totctxsws",
120L))),
new TreeMap<String, Map<String, Object>>(
Map.of(
"1",
Map.of(
"runticks",
200000000L,
"waitticks",
200000000L,
"totctxsws",
200L),
"2",
Map.of(
"runticks",
500000000L,
"waitticks",
500000000L,
"totctxsws",
20L),
"3",
Map.of(
"runticks",
700000000L,
"waitticks",
700000000L,
"totctxsws",
220L))),
"0",
List.of("1", "2", "3"),
100,
10,
1010);

mockSchemaFileParser();
}

@Test
public void testMetrics() throws Exception {
// this test checks that
// 1. ThreadSched calls the SchemaFileParser constructor with the correct path
// 2. ThreadSched calculates the correct metrics from procfile data

// mock OSGlobals
PowerMockito.mockStatic(OSGlobals.class);
PowerMockito.when(OSGlobals.getPid()).thenReturn("0");
PowerMockito.when(OSGlobals.getTids()).thenReturn(List.of("1", "2", "3"));

// mock System.currentTimeMillis()
// used by ThreadSched to compute SchedMetric
PowerMockito.mockStatic(System.class);
// having the time difference = 1000ms
// means that contextSwitchRate = difference in totctxsws
PowerMockito.when(System.currentTimeMillis()).thenReturn(10L, 1010L);

// mock the metrics generator used by ThreadSched
LinuxSchedMetricsGenerator linuxSchedMetricsGenerator =
Mockito.mock(LinuxSchedMetricsGenerator.class);
PowerMockito.whenNew(LinuxSchedMetricsGenerator.class)
.withNoArguments()
.thenReturn(linuxSchedMetricsGenerator);

// mock SchemaFileParser (used by ThreadSched to read procfiles)
SchemaFileParser schemaFileParser = Mockito.mock(SchemaFileParser.class);

PowerMockito.when(schemaFileParser.parse())
.thenReturn(tidKVMap.get("1"), tidKVMap.get("2"), tidKVMap.get("3"))
.thenReturn(nextTidKVMap.get("1"), nextTidKVMap.get("2"), nextTidKVMap.get("3"));

PowerMockito.whenNew(SchemaFileParser.class)
.withAnyArguments()
.thenReturn(schemaFileParser);

ThreadSched.INSTANCE.addSample();

// assert that ThreadSched calls the SchemaFileParser constructor with the
Expand Down

0 comments on commit e3dfb15

Please sign in to comment.