diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/os/OSTests.java b/src/test/java/org/opensearch/performanceanalyzer/commons/os/OSTests.java new file mode 100644 index 0000000..fd89278 --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/os/OSTests.java @@ -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> tidKVMap; + Map> nextTidKVMap; + + public OSTests( + SortedMap> tidKVMap, + SortedMap> nextTidKVMap, + String pid, + List 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> tidArr = new ArrayList>(tidKVMap.values()); + List> nextTidArr = + new ArrayList>(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); + } +} diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadCPUTests.java b/src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadCPUTests.java index 2cbe87b..c08fa97 100644 --- a/src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadCPUTests.java +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadCPUTests.java @@ -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>( + 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>( + 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); + } } diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadSchedTests.java b/src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadSchedTests.java index 254f914..335f645 100644 --- a/src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadSchedTests.java +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadSchedTests.java @@ -12,48 +12,81 @@ 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> 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> 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>( + 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>( + 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 { @@ -61,18 +94,6 @@ public void testMetrics() throws Exception { // 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); @@ -80,17 +101,6 @@ public void testMetrics() throws Exception { .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