changeset 151:c55999f78af9

Merge
author Roman Kennke <rkennke@redhat.com>
date Wed, 28 Mar 2012 17:44:33 +0200
parents 64ccdd315b31 (current diff) 95349a1edfdd (diff)
children c1fed4f2c975 955bd3f31075
files
diffstat 10 files changed, 599 insertions(+), 122 deletions(-) [+]
line wrap: on
line diff
--- a/agent/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java	Wed Mar 28 17:43:13 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java	Wed Mar 28 17:44:33 2012 +0200
@@ -120,7 +120,8 @@
                 long stopTime = Long.MIN_VALUE;
                 JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
                 Map<String, String> properties = new HashMap<String, String>();
-                Map<String, String> environment = ProcessEnvironmentBuilder.build(vmId);
+                ProcDataSource dataSource = new ProcDataSource();
+                Map<String, String> environment = new ProcessEnvironmentBuilder(dataSource).build(vmId);
                 // TODO actually figure out the loaded libraries.
                 List<String> loadedNativeLibraries = new ArrayList<String>();
                 info = new VmInfo(vmId, startTime, stopTime,
--- a/agent/src/main/java/com/redhat/thermostat/backend/system/ProcDataSource.java	Wed Mar 28 17:43:13 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/backend/system/ProcDataSource.java	Wed Mar 28 17:44:33 2012 +0200
@@ -46,6 +46,9 @@
     private static final String MEMINFO_FILE = "/proc/meminfo";
     private static final String CPUINFO_FILE = "/proc/cpuinfo";
 
+    private static final String PID_STAT_FILE = "/proc/${pid}/stat";
+    private static final String PID_ENVIRON_FILE = "/proc/${pid}/environ";
+
     /**
      * Returns a reader for /proc/cpuinfo
      */
@@ -67,4 +70,22 @@
         return new FileReader(MEMINFO_FILE);
     }
 
+    /**
+     * Returns a reader for /proc/$PID/stat
+     */
+    public Reader getStatReader(int pid) throws IOException {
+        return new FileReader(getPidFile(PID_STAT_FILE, pid));
+    }
+
+    /**
+     * Returns a reader for /proc/$PID/environ
+     */
+    public Reader getEnvironReader(int pid) throws IOException {
+        return new FileReader(getPidFile(PID_ENVIRON_FILE, pid));
+    }
+
+    private String getPidFile(String fileName, int pid) {
+        return fileName.replace("${pid}", Integer.toString(pid));
+    }
+
 }
--- a/agent/src/main/java/com/redhat/thermostat/backend/system/ProcessEnvironmentBuilder.java	Wed Mar 28 17:43:13 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/backend/system/ProcessEnvironmentBuilder.java	Wed Mar 28 17:44:33 2012 +0200
@@ -36,11 +36,10 @@
 
 package com.redhat.thermostat.backend.system;
 
-import java.io.FileNotFoundException;
-import java.io.FileReader;
 import java.io.IOException;
 import java.io.Reader;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -53,56 +52,51 @@
 
     private static final Logger logger = LoggingUtils.getLogger(ProcessEnvironmentBuilder.class);
 
-    private ProcessEnvironmentBuilder() {
-        /* should not be instantiated */
+    private final ProcDataSource dataSource;
+
+    public ProcessEnvironmentBuilder(ProcDataSource dataSource) {
+        this.dataSource = dataSource;
     }
 
-    public static Map<String, String> build(int pid) {
+    public Map<String, String> build(int pid) {
+        try (Reader reader = dataSource.getEnvironReader(pid)) {
+            return build(reader);
+        } catch (IOException ioe) {
+            logger.log(Level.WARNING, "error reading env", ioe);
+        }
+
+        return Collections.emptyMap();
+    }
+
+    private Map<String,String> build(Reader reader) throws IOException {
+
         Map<String, String> env = new HashMap<String, String>();
 
-        String filename = "/proc/" + pid + "/environ";
-        try {
-            Reader reader = new FileReader(filename);
-            try {
-                char[] fileBuffer = new char[1024];
-                int fileBufferIndex = 0;
-                char[] buffer = new char[1024];
-                int read = 0;
-                while (true) {
-                    read = reader.read(buffer);
-                    if (read == -1) {
-                        break;
-                    }
-
-                    if (read + fileBufferIndex > fileBuffer.length) {
-                        char[] newFileBuffer = new char[fileBuffer.length * 2];
-                        System.arraycopy(fileBuffer, 0, newFileBuffer, 0, fileBufferIndex);
-                        fileBuffer = newFileBuffer;
-                    }
-                    System.arraycopy(buffer, 0, fileBuffer, fileBufferIndex, read);
-                    fileBufferIndex = fileBufferIndex + read;
+        char[] fileBuffer = new char[1024];
+        int fileBufferIndex = 0;
+        char[] buffer = new char[1024];
+        int read = 0;
+        while (true) {
+            read = reader.read(buffer);
+            if (read == -1) {
+                break;
+            }
 
-                }
-                List<String> parts = getParts(fileBuffer, fileBufferIndex);
-                for (String part : parts) {
-                    int splitterPos = part.indexOf("=");
-                    String key = part.substring(0, splitterPos);
-                    String value = part.substring(splitterPos + 1);
-                    env.put(key, value);
-                }
-            } catch (IOException e) {
-                logger.log(Level.WARNING, "error reading " + filename, e);
-            } finally {
-                try {
-                    reader.close();
-                } catch (IOException e) {
-                    e.printStackTrace();
-                    logger.log(Level.WARNING, "error closing " + filename);
-                }
+            if (read + fileBufferIndex > fileBuffer.length) {
+                char[] newFileBuffer = new char[fileBuffer.length * 2];
+                System.arraycopy(fileBuffer, 0, newFileBuffer, 0, fileBufferIndex);
+                fileBuffer = newFileBuffer;
             }
-        } catch (FileNotFoundException e) {
-            e.printStackTrace();
-            logger.log(Level.WARNING, "file " + filename + " not found");
+            System.arraycopy(buffer, 0, fileBuffer, fileBufferIndex, read);
+            fileBufferIndex = fileBufferIndex + read;
+
+        }
+        List<String> parts = getParts(fileBuffer, fileBufferIndex);
+        for (String part : parts) {
+            int splitterPos = part.indexOf("=");
+            String key = part.substring(0, splitterPos);
+            String value = part.substring(splitterPos + 1);
+            env.put(key, value);
         }
 
         return env;
@@ -112,7 +106,7 @@
      * Split a char array, where items are separated by a null into into a list
      * of strings
      */
-    private static List<String> getParts(char[] nullSeparatedBuffer, int bufferLength) {
+    private List<String> getParts(char[] nullSeparatedBuffer, int bufferLength) {
         int maxLength = Math.min(nullSeparatedBuffer.length, bufferLength);
         List<String> parts = new ArrayList<String>();
 
--- a/agent/src/main/java/com/redhat/thermostat/backend/system/ProcessStatusInfo.java	Wed Mar 28 17:43:13 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/backend/system/ProcessStatusInfo.java	Wed Mar 28 17:44:33 2012 +0200
@@ -36,82 +36,18 @@
 
 package com.redhat.thermostat.backend.system;
 
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.Scanner;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-/**
- * Extract status information about the process from /proc/. This is what tools
- * like {@code ps} and {@code top} use.
- *
- * @see {@code proc(5)}
- */
 public class ProcessStatusInfo {
 
-    private static final Logger logger = LoggingUtils.getLogger(ProcessStatusInfo.class);
-
     /* All times are measured in clock ticks */
 
-    /* TODO map these (effectively c) data types to java types more sanely */
-
-    private int pid;
-    private long utime;
-    private long stime;
-
-    public static ProcessStatusInfo getFor(int pid) {
-        return new ProcessStatusInfo(pid);
-    }
-
-    private ProcessStatusInfo(int pid) {
-        Scanner scanner = null;
-        String fileName = "/proc/" + pid + "/stat";
-        BufferedReader reader = null;
-        try {
-            reader = new BufferedReader(new FileReader(fileName));
-            String statusLine = reader.readLine();
-
-            /* be prepared for process names like '1 ) 2 3 4 foo 5' */
-
-            scanner = new Scanner(statusLine);
-            this.pid = scanner.nextInt();
-            scanner.close();
-
-            int execEndNamePos = statusLine.lastIndexOf(')');
-
-            String cleanStatusLine = statusLine.substring(execEndNamePos + 1);
+    private final int pid;
+    private final long userTime;
+    private final long kernelTime;
 
-            scanner = new Scanner(cleanStatusLine);
-            /* state = */scanner.next();
-            /* ppid = */scanner.nextInt();
-            /* pgrp = */scanner.nextInt();
-            /* session = */scanner.nextInt();
-            /* tty_nr = */scanner.nextInt();
-            /* tpgid = */scanner.nextInt();
-            /* flags = */scanner.nextInt();
-            /* minflt = */scanner.nextLong();
-            /* cminflt = */scanner.nextLong();
-            /* majflt = */scanner.nextLong();
-            /* cmajflt = */scanner.nextLong();
-            utime = scanner.nextLong();
-            stime = scanner.nextLong();
-            scanner.close();
-
-        } catch (IOException e) {
-            logger.log(Level.WARNING, "unable to read " + fileName);
-        } finally {
-            if (reader != null) {
-                try {
-                    reader.close();
-                } catch (IOException e) {
-                    logger.log(Level.WARNING, "unable to close " + fileName);
-                }
-            }
-        }
+    public ProcessStatusInfo(int pid, long userTime, long kernelTime) {
+        this.pid = pid;
+        this.userTime = userTime;
+        this.kernelTime = kernelTime;
     }
 
     public int getPid() {
@@ -123,7 +59,7 @@
      * kernel ticks
      */
     public long getUserTime() {
-        return utime;
+        return userTime;
     }
 
     /**
@@ -131,7 +67,7 @@
      * ticks
      */
     public long getKernelTime() {
-        return stime;
+        return kernelTime;
     }
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/main/java/com/redhat/thermostat/backend/system/ProcessStatusInfoBuilder.java	Wed Mar 28 17:44:33 2012 +0200
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat 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; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.backend.system;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Scanner;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+/**
+ * Extract status information about the process from /proc/. This is what tools
+ * like {@code ps} and {@code top} use.
+ *
+ * @see {@code proc(5)}
+ */
+public class ProcessStatusInfoBuilder {
+
+    private static final Logger logger = LoggingUtils.getLogger(ProcessStatusInfoBuilder.class);
+
+    private final ProcDataSource dataSource;
+
+    public ProcessStatusInfoBuilder(ProcDataSource dataSource) {
+        this.dataSource = dataSource;
+    }
+
+    public ProcessStatusInfo build(int pid) {
+        try (BufferedReader reader = new BufferedReader(dataSource.getStatReader(pid))) {
+            return build(reader);
+        } catch (IOException e) {
+            logger.log(Level.WARNING, "unable to read stat info for " + pid);
+        }
+
+        return new ProcessStatusInfo(-1, -1, -1);
+    }
+
+    private ProcessStatusInfo build(Reader r) throws IOException {
+
+        int pid = -1;
+        long utime = -1;
+        long stime = -1;
+
+        Scanner scanner = null;
+
+        /* TODO map these (effectively c) data types to java types more sanely */
+
+        try (BufferedReader reader = new BufferedReader(r)) {
+            String statusLine = reader.readLine();
+
+            /* be prepared for process names like '1 ) 2 3 4 foo 5' */
+
+            scanner = new Scanner(statusLine);
+            pid = scanner.nextInt();
+            scanner.close();
+
+            int execEndNamePos = statusLine.lastIndexOf(')');
+
+            String cleanStatusLine = statusLine.substring(execEndNamePos + 1);
+
+            scanner = new Scanner(cleanStatusLine);
+            /* state = */scanner.next();
+            /* ppid = */scanner.nextInt();
+            /* pgrp = */scanner.nextInt();
+            /* session = */scanner.nextInt();
+            /* tty_nr = */scanner.nextInt();
+            /* tpgid = */scanner.nextInt();
+            /* flags = */scanner.nextInt();
+            /* minflt = */scanner.nextLong();
+            /* cminflt = */scanner.nextLong();
+            /* majflt = */scanner.nextLong();
+            /* cmajflt = */scanner.nextLong();
+            utime = scanner.nextLong();
+            stime = scanner.nextLong();
+            scanner.close();
+        }
+
+        return new ProcessStatusInfo(pid, utime, stime);
+
+    }
+
+}
--- a/agent/src/main/java/com/redhat/thermostat/backend/system/VmCpuStatBuilder.java	Wed Mar 28 17:43:13 2012 +0200
+++ b/agent/src/main/java/com/redhat/thermostat/backend/system/VmCpuStatBuilder.java	Wed Mar 28 17:44:33 2012 +0200
@@ -60,7 +60,8 @@
      */
     public static synchronized VmCpuStat build(Integer pid) {
 
-        ProcessStatusInfo info = ProcessStatusInfo.getFor(pid);
+        ProcDataSource dataSource = new ProcDataSource();
+        ProcessStatusInfo info = new ProcessStatusInfoBuilder(dataSource).build(pid);
         long miliTime = System.currentTimeMillis();
         long time = System.nanoTime();
         long programTicks = (info.getKernelTime() + info.getUserTime());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/test/java/com/redhat/thermostat/backend/system/ProcDataSourceTest.java	Wed Mar 28 17:44:33 2012 +0200
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat 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; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.backend.system;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.TestUtils;
+
+public class ProcDataSourceTest {
+
+    @Test
+    public void testGetCpuInfoReader() throws IOException {
+        Reader r = new ProcDataSource().getCpuInfoReader();
+        assertNotNull(r);
+    }
+
+    @Test
+    public void testGetCpuLoadReader() throws IOException {
+        Reader r = new ProcDataSource().getCpuLoadReader();
+        assertNotNull(r);
+    }
+
+    @Test
+    public void testGetMemInfoReader() throws IOException {
+        Reader r = new ProcDataSource().getMemInfoReader();
+        assertNotNull(r);
+    }
+
+    @Test
+    public void testGetStatReader() throws IOException {
+        int pid = TestUtils.getProcessId();
+        Reader r = new ProcDataSource().getStatReader(pid);
+        assertNotNull(r);
+    }
+
+
+    @Test
+    public void testGetEnvironReader() throws IOException {
+        int pid = TestUtils.getProcessId();
+        Reader r = new ProcDataSource().getEnvironReader(pid);
+        assertNotNull(r);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/test/java/com/redhat/thermostat/backend/system/ProcessEnvironmentBuilderTest.java	Wed Mar 28 17:44:33 2012 +0200
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat 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; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.backend.system;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.Map;
+import java.util.Random;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.TestUtils;
+
+public class ProcessEnvironmentBuilderTest {
+
+    private final Random r = new Random();
+
+    @Test
+    public void test() {
+        ProcDataSource dataSource = new ProcDataSource();
+        Map<String, String> result = new ProcessEnvironmentBuilder(dataSource).build(TestUtils.getProcessId());
+        assertNotNull(result);
+        assertFalse(result.isEmpty());
+        assertTrue(result.containsKey("USER"));
+    }
+
+    @Test
+    public void testCustomEnvironment() throws IOException {
+        byte[] data = ("USER=test\000HOME=house\000").getBytes();
+
+        Reader r = new InputStreamReader(new ByteArrayInputStream(data));
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        when(dataSource.getEnvironReader(any(Integer.class))).thenReturn(r);
+
+        Map<String, String> result = new ProcessEnvironmentBuilder(dataSource).build(0);
+
+        verify(dataSource).getEnvironReader(eq(0));
+        assertEquals("test", result.get("USER"));
+        assertEquals("house", result.get("HOME"));
+    }
+
+    @Test
+    public void testLargeRandomEnvironment() throws IOException {
+        int TEST_ENV_SIZE = 1024 * 1024;
+        byte[] data = new byte[TEST_ENV_SIZE];
+        int currentPosition = 0;
+        do {
+            byte[] key = generateRandomBytes();
+            byte[] value = generateRandomBytes();
+            if (currentPosition + key.length + value.length + 2 >= data.length) {
+                break;
+            }
+            System.arraycopy(key, 0, data, currentPosition, key.length);
+            currentPosition += key.length;
+            data[currentPosition] = (byte) '=';
+            currentPosition++;
+            System.arraycopy(value, 0, data, currentPosition, value.length);
+            currentPosition += value.length;
+            data[currentPosition] = 0x00;
+            currentPosition++;
+        } while (true);
+        Reader r = new InputStreamReader(new ByteArrayInputStream(data, 0, currentPosition));
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        when(dataSource.getEnvironReader(any(Integer.class))).thenReturn(r);
+
+        Map<String, String> result = new ProcessEnvironmentBuilder(dataSource).build(0);
+
+        verify(dataSource).getEnvironReader(eq(0));
+        assertNotNull(result);
+    }
+
+    private byte[] generateRandomBytes() {
+        byte start = (byte) 'a';
+        byte end = (byte) 'z' + 1;
+
+        byte[] alphabet = new byte[end - start];
+        for (int i = 0; i < (end-start); i++) {
+            alphabet[i] = (byte) (i + start);
+        }
+        int size = r.nextInt(15) + 10;
+        byte[] result = new byte[size];
+        for (int i = 0; i < result.length; i++) {
+            result[i] = alphabet[r.nextInt(alphabet.length)];
+        }
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/test/java/com/redhat/thermostat/backend/system/ProcessStatusInfoBuilderTest.java	Wed Mar 28 17:44:33 2012 +0200
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat 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; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.backend.system;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+import org.junit.Test;
+
+public class ProcessStatusInfoBuilderTest {
+
+    @Test
+    public void testSimpleProcessStatus() {
+        ProcDataSource dataSource = new ProcDataSource();
+        ProcessStatusInfo stat = new ProcessStatusInfoBuilder(dataSource).build(1);
+        assertNotNull(stat);
+    }
+
+    @Test
+    public void testKnownProcessStatus() throws IOException {
+        final int PID = 10363;
+        String PROCESS_NAME = "(bash)";
+        String STATE = "S";
+        String PPID = "1737";
+        String PROCESS_GROUP_ID = "10363";
+        String SESSION_ID = "10363";
+        String TTY_NUMBER = "34817";
+        String TTY_PROCESS_GROUP_ID = "11404";
+        String FLAGS_WORD = "4202496";
+        String MINOR_FAULTS = "8093";
+        String MINOR_FAULTS_CHILDREN = "607263";
+        String MAJOR_FAULTS = "1";
+        String MAJOR_FAULTS_CHILDREN = "251";
+        final long USER_TIME_TICKS = 21;
+        final long KERNEL_TIME_TICKS = 7;
+        final long USER_TIME_CHILDREN = 10;
+        String KERNEL_TIME_CHILDREN = "1000";
+        String PRIORITY = "20";
+        String statString = "" +
+                PID + " " + PROCESS_NAME + " " + STATE + " " + PPID + " "
+                + PROCESS_GROUP_ID + " " + SESSION_ID + " " + TTY_NUMBER + " "
+                + TTY_PROCESS_GROUP_ID + " " + FLAGS_WORD + " " + MINOR_FAULTS + " "
+                + MINOR_FAULTS_CHILDREN + " " + MAJOR_FAULTS + " " + MAJOR_FAULTS_CHILDREN + " " +
+                USER_TIME_TICKS + " " + KERNEL_TIME_TICKS + " " + USER_TIME_CHILDREN + " " +
+                KERNEL_TIME_CHILDREN + " " + PRIORITY;
+
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        when(dataSource.getStatReader(any(Integer.class))).thenReturn(new StringReader(statString));
+        ProcessStatusInfoBuilder builder = new ProcessStatusInfoBuilder(dataSource);
+        ProcessStatusInfo stat = builder.build(PID);
+
+        verify(dataSource).getStatReader(PID);
+        assertNotNull(stat);
+        assertEquals(PID, stat.getPid());
+        assertEquals(USER_TIME_TICKS, stat.getUserTime());
+        assertEquals(KERNEL_TIME_TICKS, stat.getKernelTime());
+    }
+
+    @Test
+    public void testBadProcessName() throws IOException {
+        final int PID = 10363;
+        String PROCESS_NAME = "(secretly-bad process sleep 10 20 ) 6)";
+        String STATE = "S";
+        String PPID = "1737";
+        String PROCESS_GROUP_ID = "10363";
+        String SESSION_ID = "10363";
+        String TTY_NUMBER = "34817";
+        String TTY_PROCESS_GROUP_ID = "11404";
+        String FLAGS_WORD = "4202496";
+        String MINOR_FAULTS = "8093";
+        String MINOR_FAULTS_CHILDREN = "607263";
+        String MAJOR_FAULTS = "1";
+        String MAJOR_FAULTS_CHILDREN = "251";
+        final long USER_TIME_TICKS = 21;
+        final long KERNEL_TIME_TICKS = 7;
+        final long USER_TIME_CHILDREN = 10;
+        String KERNEL_TIME_CHILDREN = "1000";
+        String PRIORITY = "20";
+        String statString = "" +
+                PID + " " + PROCESS_NAME + " " + STATE + " " + PPID + " "
+                + PROCESS_GROUP_ID + " " + SESSION_ID + " " + TTY_NUMBER + " "
+                + TTY_PROCESS_GROUP_ID + " " + FLAGS_WORD + " " + MINOR_FAULTS + " "
+                + MINOR_FAULTS_CHILDREN + " " + MAJOR_FAULTS + " " + MAJOR_FAULTS_CHILDREN + " " +
+                USER_TIME_TICKS + " " + KERNEL_TIME_TICKS + " " + USER_TIME_CHILDREN + " " +
+                KERNEL_TIME_CHILDREN + " " + PRIORITY;
+
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        when(dataSource.getStatReader(any(Integer.class))).thenReturn(new StringReader(statString));
+        ProcessStatusInfoBuilder builder = new ProcessStatusInfoBuilder(dataSource);
+        ProcessStatusInfo stat = builder.build(PID);
+
+        verify(dataSource).getStatReader(PID);
+        assertNotNull(stat);
+        assertEquals(PID, stat.getPid());
+        assertEquals(USER_TIME_TICKS, stat.getUserTime());
+        assertEquals(KERNEL_TIME_TICKS, stat.getKernelTime());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/test/java/com/redhat/thermostat/backend/system/SysConfTest.java	Wed Mar 28 17:44:33 2012 +0200
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat 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; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.backend.system;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class SysConfTest {
+
+    @Test
+    public void test() {
+        long ticksPerSecond = SysConf.getClockTicksPerSecond();
+        assertTrue(ticksPerSecond >= 1);
+    }
+}