package org.datadog.jmxfetch; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.mockito.Spy; import static org.mockito.Mockito.*; import java.lang.management.ManagementFactory; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import javax.management.InstanceAlreadyExistsException; import javax.management.InstanceNotFoundException; import javax.management.MBeanRegistrationException; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; import org.apache.log4j.Level; import org.datadog.jmxfetch.reporter.ConsoleReporter; import org.datadog.jmxfetch.reporter.Reporter; import org.datadog.jmxfetch.util.CustomLogger; import org.junit.After; import org.junit.BeforeClass; import com.beust.jcommander.JCommander; public class TestCommon { AppConfig appConfig = spy(new AppConfig()); App app; MBeanServer mbs; ArrayList<ObjectName> objectNames = new ArrayList<ObjectName>(); LinkedList<HashMap<String, Object>> metrics; LinkedList<HashMap<String, Object>> serviceChecks; /** * Setup logger. */ @BeforeClass public static void init() throws Exception { CustomLogger.setup(Level.toLevel("ALL"), "/tmp/jmxfetch_test.log"); } /** * Register a MBean with the given name, and application attributes. * @throws NotCompliantMBeanException * @throws MBeanRegistrationException * @throws InstanceAlreadyExistsException * @throws MalformedObjectNameException */ protected void registerMBean(Object application, String objectStringName) throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException, MalformedObjectNameException { mbs = (mbs == null)? ManagementFactory.getPlatformMBeanServer() : mbs; ObjectName objectName = new ObjectName(objectStringName); objectNames.add(objectName); mbs.registerMBean(application, objectName); } /** * Unregister MBeans. * Note: executed after execution of every test. * @throws InstanceNotFoundException * @throws MBeanRegistrationException */ @After public void unregisterMBean() throws MBeanRegistrationException, InstanceNotFoundException{ if (mbs != null) { for (ObjectName objectName: objectNames ) { mbs.unregisterMBean(objectName); } } } /** * Init JMXFetch with the given YAML configuration file. */ protected void initApplication(String yamlFileName, String serviceDiscoveryPipeFile) throws FileNotFoundException, IOException { // We do a first collection // We initialize the main app that will collect these metrics using JMX String confdDirectory = Thread.currentThread().getContextClassLoader().getResource(yamlFileName).getPath(); confdDirectory = new String(confdDirectory.substring(0, confdDirectory.length() - yamlFileName.length())); List<String> params = new ArrayList<String>(); boolean sdEnabled = (serviceDiscoveryPipeFile.length() > 0); params.add("--reporter"); params.add("console"); params.add("-c"); params.add(yamlFileName); params.add("--conf_directory"); params.add(confdDirectory); params.add("collect"); if (sdEnabled) { params.add(4, "--tmp_directory"); params.add(5, "/foo"); //could be anything we're stubbing it out params.add(6, "--sd_enabled"); } new JCommander(appConfig, params.toArray(new String[params.size()])); if (sdEnabled) { String SDPipe = Thread.currentThread().getContextClassLoader().getResource( serviceDiscoveryPipeFile).getPath(); when(appConfig.getServiceDiscoveryPipe()).thenReturn(SDPipe); //mocking with fixture file. } app = new App(appConfig); if (sdEnabled) { FileInputStream sdPipe = new FileInputStream(appConfig.getServiceDiscoveryPipe()); int len = sdPipe.available(); byte[] buffer = new byte[len]; sdPipe.read(buffer); app.setReinit(app.processServiceDiscovery(buffer)); } app.init(false); } protected void initApplication(String yamlFileName) throws FileNotFoundException, IOException { initApplication(yamlFileName, ""); } /** * Run a JMXFetch iteration. */ protected void run(){ if (app != null) { app.doIteration(); metrics = ((ConsoleReporter) appConfig.getReporter()).getMetrics(); } } /** * Return JMXFetch reporter. */ protected Reporter getReporter(){ return appConfig.getReporter(); } /** * Return the metrics collected by JMXFetch. */ protected LinkedList<HashMap<String, Object>> getMetrics(){ return metrics; } /** * Return the service checks collected by JMXFetch. */ protected LinkedList<HashMap<String, Object>> getServiceChecks(){ return ((ConsoleReporter) appConfig.getReporter()).getServiceChecks(); } /** * Assert that a specific metric was collected. * Brand the metric so we can easily know which metric have/have not been tested * * @param name metric name * * @param value metric value * * @param lowerBound lower bound metric value * * @param upperBound upper bound metric value * * @param commonTags metric tags inherited from the instance configuration * * @param additionalTags metric tags inherited from the bean properties * * @param countTags number of metric tags * * @param metricType type of the metric (gauge, histogram, ...) * * @return fail if the metric was not found */ public void assertMetric(String name, Number value, Number lowerBound, Number upperBound, List<String> commonTags, List<String> additionalTags, int countTags, String metricType){ List<String> tags = new ArrayList<String>(commonTags); tags.addAll(additionalTags); for (HashMap<String, Object> m: metrics) { String mName = (String) (m.get("name")); Double mValue = (Double) (m.get("value")); Set<String> mTags = new HashSet<String>(Arrays.asList((String[]) (m.get("tags")))); if (mName.equals(name)) { if (!value.equals(-1)){ assertEquals((Double)value.doubleValue(), mValue); } else if (!lowerBound.equals(-1) || !upperBound.equals(-1)){ assertTrue(mValue > (Double)lowerBound.doubleValue()); assertTrue(mValue < (Double)upperBound.doubleValue()); } if (countTags != -1) { assertEquals(countTags, mTags.size()); } for (String t: tags) { assertTrue(mTags.contains(t)); } if (metricType != null) { assertEquals(metricType, m.get("type")); } // Brand the metric m.put("tested", true); return; } } fail("Metric assertion failed (name: "+name+", value: "+value+", tags: "+tags+", #tags: "+countTags+")."); } public void assertMetric(String name, Number value, Number lowerBound, Number upperBound, List<String> commonTags, List<String> additionalTags, int countTags) { assertMetric(name, value, lowerBound, upperBound, commonTags, additionalTags, countTags, null); } public void assertMetric(String name, Number value, List<String> commonTags, List<String> additionalTags, int countTags){ assertMetric(name, value, -1, -1, commonTags, additionalTags, countTags, null); } public void assertMetric(String name, Number value, List<String> commonTags, List<String> additionalTags, int countTags, String metricType){ assertMetric(name, value, -1, -1, commonTags, additionalTags, countTags, metricType); } public void assertMetric(String name, Number lowerBound, Number upperBound, List<String> commonTags, List<String> additionalTags, int countTags){ assertMetric(name, -1, lowerBound, upperBound, commonTags, additionalTags, countTags, null); } public void assertMetric(String name, Number lowerBound, Number upperBound, List<String> commonTags, List<String> additionalTags, int countTags, String metricType){ assertMetric(name, -1, lowerBound, upperBound, commonTags, additionalTags, countTags, metricType); } public void assertMetric(String name, Number value, List<String> tags, int countTags){ assertMetric(name, value, tags, new ArrayList<String>(), countTags); } public void assertMetric(String name, Number value, List<String> tags, int countTags, String metricType){ assertMetric(name, value, tags, new ArrayList<String>(), countTags, metricType); } public void assertMetric(String name, Number lowerBound, Number upperBound, List<String> tags, int countTags){ assertMetric(name, lowerBound, upperBound, tags, new ArrayList<String>(), countTags); } public void assertMetric(String name, Number lowerBound, Number upperBound, List<String> tags, int countTags, String metricType){ assertMetric(name, lowerBound, upperBound, tags, new ArrayList<String>(), countTags, metricType); } public void assertMetric(String name, List<String> tags, int countTags){ assertMetric(name, -1, tags, new ArrayList<String>(), countTags); } public void assertMetric(String name, List<String> tags, int countTags, String metricType){ assertMetric(name, -1, tags, new ArrayList<String>(), countTags, metricType); } /** * Assert that all -excluding JVM related- metrics were tested. * * @return fail if a metric was not tested */ public void assertCoverage(){ int totalMetrics = 0; LinkedList<HashMap<String, Object>> untestedMetrics = new LinkedList<HashMap<String, Object>>(); for (HashMap<String, Object> m: metrics) { String mName = (String) (m.get("name")); // Exclusion logic if (mName.startsWith("jvm.")) { continue; } // End of exclusion logic totalMetrics += 1; if (!m.containsKey("tested")) { untestedMetrics.add(m); } } if (untestedMetrics.size() > 0) { String message = generateReport(untestedMetrics, totalMetrics); fail(message); } return; } /** * Generate a report with untested metrics. * * @return String report */ private static String generateReport(LinkedList<HashMap<String, Object>> untestedMetrics, int totalMetricsCount){ StringBuilder sb = new StringBuilder(); // Compute indicators int testedMetricsCount = totalMetricsCount - untestedMetrics.size(); int coverageMetrics = (int)((testedMetricsCount * 100.0f) / totalMetricsCount); sb.append("Coverage\n"); sb.append("========================================\n"); sb.append("\tMETRICS\n"); sb.append("\t\tTested "); sb.append(testedMetricsCount); sb.append("/"); sb.append(totalMetricsCount); sb.append(" ("); sb.append(coverageMetrics); sb.append("%)\n"); sb.append("\t\tUNTESTED: \n"); for (HashMap<String, Object> m: untestedMetrics) { sb.append(m); sb.append("\n"); } sb.append("========================================\n"); return sb.toString(); } }