// Copyright 2015 The Bazel Authors. All rights reserved.
//
// 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.google.devtools.build.lib.actions;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;

import com.google.devtools.build.lib.util.StringUtilities;
import com.google.devtools.build.lib.vfs.util.FsApparatus;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public class LocalHostCapacityTest {

  private FsApparatus scratch = FsApparatus.newNative();

  @Test
  public void testNonHyperthreadedMachine() throws Exception {
    String cpuinfoContent = StringUtilities.joinLines(
        "processor\t: 0",
        "vendor_id\t: GenuineIntel",
        "cpu family\t: 15",
        "model\t\t: 4",
        "model name\t:               Intel(R) Pentium(R) 4 CPU 3.40GHz",
        "stepping\t: 10",
        "cpu MHz\t\t: 3400.000",
        "cache size\t: 2048 KB",
        "fpu\t\t: yes",
        "fpu_exception\t: yes",
        "cpuid level\t: 5",
        "wp\t\t: yes",
        "flags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca "
            + "cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm "
            + "syscall nx lm constant_tsc up pni monitor ds_cpl est cid cx16 "
            + "xtpr lahf_lm",
        "bogomips\t: 6803.83",
        "clflush size\t: 64",
        "cache_alignment\t: 128",
        "address sizes\t: 36 bits physical, 48 bits virtual",
        "power management:"
        );
    String cpuinfoFile =
        scratch.file("test_cpuinfo_nonht", cpuinfoContent).getPathString();
    String meminfoContent = StringUtilities.joinLines(
        "MemTotal:      3091732 kB",
        "MemFree:       2167344 kB",
        "Buffers:         60644 kB",
        "Cached:         509940 kB",
        "SwapCached:          0 kB",
        "Active:         636892 kB",
        "Inactive:       212760 kB",
        "HighTotal:           0 kB",
        "HighFree:            0 kB",
        "LowTotal:      3091732 kB",
        "LowFree:       2167344 kB",
        "SwapTotal:     9124880 kB",
        "SwapFree:      9124880 kB",
        "Dirty:               0 kB",
        "Writeback:           0 kB",
        "AnonPages:      279028 kB",
        "Mapped:          54404 kB",
        "Slab:            42820 kB",
        "PageTables:       5184 kB",
        "NFS_Unstable:        0 kB",
        "Bounce:              0 kB",
        "CommitLimit:  10670744 kB",
        "Committed_AS:   665840 kB",
        "VmallocTotal: 34359738367 kB",
        "VmallocUsed:    300484 kB",
        "VmallocChunk: 34359437307 kB",
        "HugePages_Total:     0",
        "HugePages_Free:      0",
        "HugePages_Rsvd:      0",
        "Hugepagesize:     2048 kB"
        );
    String stat1Content = StringUtilities.joinLines(
        "cpu 29793342 260290 3479274 636259369 6683218 656426 714057 0",
        "cpu0 29793342 260290 3479274 636259369 6683218 656426 714057 0",
        "intr 2870488853 2486517107 3 0 0 2 0 5 0 0 0 0 0 3 0 74363716 " +
        "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 " +
        "0 0 52483586 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 973315 0 0 0 0 0 0 0 46 " +
        "0 0 0 0 0 0 0 98792358 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 " +
        "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 " +
        "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 114339590 " +
        "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 " +
        "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 43019122 0 0 0 0 0",
        "ctxt 15053799843",
        "btime 1199289688",
        "processes 25799993",
        "procs_running 1",
        "procs_blocked 0"
        );
    String stat2Content = StringUtilities.joinLines(
        "cpu 29794509 260290 3479474 636287862 6683283 656450 714087 0 0",
        "cpu0 29794509 260290 3479474 636287862 6683283 656450 714087 0 0",
        "intr 2870488853 2486517107 3 0 0 2 0 5 0 0 0 0 0 3 0 74363716 " +
        "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 " +
        "0 0 52483586 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 973315 0 0 0 0 0 0 0 46 " +
        "0 0 0 0 0 0 0 98792358 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 " +
        "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 " +
        "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 114339590 " +
        "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 " +
        "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 43019122 0 0 0 0 0",
        "ctxt 15053799843",
        "btime 1199289688",
        "processes 25799993",
        "procs_running 1",
        "procs_blocked 0"
        );
    String meminfoFile =
        scratch.file("test_meminfo_nonht", meminfoContent).getPathString();
    assertEquals(1, LocalHostCapacity.getLogicalCpuCount(cpuinfoContent));
    assertEquals(1, LocalHostCapacity.getPhysicalCpuCount(cpuinfoContent, 1));
    assertEquals(1, LocalHostCapacity.getCoresPerCpu(cpuinfoContent));
    ResourceSet capacity =
        LocalHostCapacity.getLocalHostCapacity(cpuinfoFile, meminfoFile);
    assertEquals(1.0, capacity.getCpuUsage(), 0.01);
    assertEquals(3091.732, capacity.getMemoryMb(), 0.1); // +/- 0.1MB
    LocalHostCapacity.setLocalHostCapacity(capacity);
    assertSame(capacity, LocalHostCapacity.getLocalHostCapacity());
  }

  @Test
  public void testHyperthreadedMachine() throws Exception {
    String cpuinfoContent = StringUtilities.joinLines(
        "processor\t: 0",
        "vendor_id\t: GenuineIntel",
        "cpu family\t: 15",
        "model\t\t: 4",
        "model name\t:               Intel(R) Pentium(R) 4 CPU 3.40GHz",
        "stepping\t: 1",
        "cpu MHz\t\t: 3400.245",
        "cache size\t: 1024 KB",
        "physical id\t: 0",
        "siblings\t: 2",
        "core id\t\t: 0",
        "cpu cores\t: 1",
        "fpu\t\t: yes",
        "fpu_exception\t: yes",
        "cpuid level\t: 5",
        "wp\t\t: yes",
        "flags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge "
            + "mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm "
            + "syscall lm constant_tsc pni monitor ds_cpl cid cx16 xtpr",
        "bogomips\t: 6806.31",
        "clflush size\t: 64",
        "cache_alignment\t: 128",
        "address sizes\t: 36 bits physical, 48 bits virtual",
        "power management:",
        "",
        "processor\t: 1",
        "vendor_id\t: GenuineIntel",
        "cpu family\t: 15",
        "model\t\t: 4",
        "model name\t:               Intel(R) Pentium(R) 4 CPU 3.40GHz",
        "stepping\t: 1",
        "cpu MHz\t\t: 3400.245",
        "cache size\t: 1024 KB",
        "physical id\t: 0",
        "siblings\t: 2",
        "core id\t\t: 0",
        "cpu cores\t: 1",
        "fpu\t\t: yes",
        "fpu_exception\t: yes",
        "cpuid level\t: 5",
        "wp\t\t: yes",
        "flags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge "
            + "mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm "
            + "syscall lm constant_tsc pni monitor ds_cpl cid cx16 xtpr",
        "bogomips\t: 6800.76",
        "clflush size\t: 64",
        "cache_alignment\t: 128",
        "address sizes\t: 36 bits physical, 48 bits virtual",
        "power management:",
        ""
        );
    String cpuinfoFile =
        scratch.file("test_cpuinfo_ht", cpuinfoContent).getPathString();
    String meminfoContent = StringUtilities.joinLines(
        "MemTotal:      3092004 kB",
        "MemFree:         26124 kB",
        "Buffers:          3836 kB",
        "Cached:          52400 kB",
        "SwapCached:      68204 kB",
        "Active:        2281464 kB",
        "Inactive:       260908 kB",
        "HighTotal:           0 kB",
        "HighFree:            0 kB",
        "LowTotal:      3092004 kB",
        "LowFree:         26124 kB",
        "SwapTotal:     9124880 kB",
        "SwapFree:      8264920 kB",
        "Dirty:             616 kB",
        "Writeback:           0 kB",
        "AnonPages:     2466336 kB",
        "Mapped:          37576 kB",
        "Slab:           483004 kB",
        "PageTables:      11912 kB",
        "NFS_Unstable:        0 kB",
        "Bounce:              0 kB",
        "CommitLimit:  10670880 kB",
        "Committed_AS:  3627984 kB",
        "VmallocTotal: 34359738367 kB",
        "VmallocUsed:    300460 kB",
        "VmallocChunk: 34359437307 kB",
        "HugePages_Total:     0",
        "HugePages_Free:      0",
        "HugePages_Rsvd:      0",
        "Hugepagesize:     2048 kB"
        );
    String meminfoFile =
        scratch.file("test_meminfo_ht", meminfoContent).getPathString();
    assertEquals(2, LocalHostCapacity.getLogicalCpuCount(cpuinfoContent));
    assertEquals(1, LocalHostCapacity.getPhysicalCpuCount(cpuinfoContent, 2));
    assertEquals(1, LocalHostCapacity.getCoresPerCpu(cpuinfoContent));
    ResourceSet capacity =
        LocalHostCapacity.getLocalHostCapacity(cpuinfoFile, meminfoFile);
    assertEquals(1.2, capacity.getCpuUsage(), .0001);
    assertEquals(3092.004, capacity.getMemoryMb(), 0.1); // +/- 0.1MB
  }

  @Test
  public void testAMDMachine() throws Exception {
    String cpuinfoContent = StringUtilities.joinLines(
        "processor\t: 0",
        "vendor_id\t: AuthenticAMD",
        "cpu family\t: 15",
        "model\t\t: 65",
        "model name\t: Dual-Core AMD Opteron(tm) Processor 8214 HE",
        "stepping\t: 2",
        "cpu MHz\t\t: 2200.000",
        "cache size\t: 1024 KB",
        "physical id\t: 0",
        "siblings\t: 2",
        "core id\t\t: 0",
        "cpu cores\t: 2",
        "fpu\t\t: yes",
        "fpu_exception\t: yes",
        "cpuid level\t: 1",
        "wp\t\t: yes",
        "flags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr "
            + "pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall "
            + "nx mmxext fxsr_opt rdtscp lm 3dnowext 3dnow pni cx16 lahf_lm "
            + "cmp_legacy svm cr8_legacy",
        "bogomips\t: 4425.84",
        "TLB size\t: 1024 4K pages",
        "clflush size\t: 64",
        "cache_alignment\t: 64",
        "address sizes\t: 40 bits physical, 48 bits virtual",
        "power management: ts fid vid ttp tm stc",
        "",
        "processor\t: 1",
        "vendor_id\t: AuthenticAMD",
        "cpu family\t: 15",
        "model\t\t: 65",
        "model name\t: Dual-Core AMD Opteron(tm) Processor 8214 HE",
        "stepping\t: 2",
        "cpu MHz\t\t: 2200.000",
        "cache size\t: 1024 KB",
        "physical id\t: 0",
        "siblings\t: 2",
        "core id\t\t: 1",
        "cpu cores\t: 2",
        "fpu\t\t: yes",
        "fpu_exception\t: yes",
        "cpuid level\t: 1",
        "wp\t\t: yes",
        "flags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr "
            + "pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall "
            + "nx mmxext fxsr_opt rdtscp lm 3dnowext 3dnow pni cx16 lahf_lm "
            + "cmp_legacy svm cr8_legacy",
        "bogomips\t: 4460.61",
        "TLB size\t: 1024 4K pages",
        "clflush size\t: 64",
        "cache_alignment\t: 64",
        "address sizes\t: 40 bits physical, 48 bits virtual",
        "power management: ts fid vid ttp tm stc",
        "",
        "processor\t: 2",
        "vendor_id\t: AuthenticAMD",
        "cpu family\t: 15",
        "model\t\t: 65",
        "model name\t: Dual-Core AMD Opteron(tm) Processor 8214 HE",
        "stepping\t: 2",
        "cpu MHz\t\t: 2200.000",
        "cache size\t: 1024 KB",
        "physical id\t: 1",
        "siblings\t: 2",
        "core id\t\t: 0",
        "cpu cores\t: 2",
        "fpu\t\t: yes",
        "fpu_exception\t: yes",
        "cpuid level\t: 1",
        "wp\t\t: yes",
        "flags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr "
            + "pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall "
            + "nx mmxext fxsr_opt rdtscp lm 3dnowext 3dnow pni cx16 lahf_lm "
            + "cmp_legacy svm cr8_legacy",
        "bogomips\t: 4420.45",
        "TLB size\t: 1024 4K pages",
        "clflush size\t: 64",
        "cache_alignment\t: 64",
        "address sizes\t: 40 bits physical, 48 bits virtual",
        "power management: ts fid vid ttp tm stc",
        "",
        "processor\t: 3",
        "vendor_id\t: AuthenticAMD",
        "cpu family\t: 15",
        "model\t\t: 65",
        "model name\t: Dual-Core AMD Opteron(tm) Processor 8214 HE",
        "stepping\t: 2",
        "cpu MHz\t\t: 2200.000",
        "cache size\t: 1024 KB",
        "physical id\t: 1",
        "siblings\t: 2",
        "core id\t\t: 1",
        "cpu cores\t: 2",
        "fpu\t\t: yes",
        "fpu_exception\t: yes",
        "cpuid level\t: 1",
        "wp\t\t: yes",
        "flags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr "
            + "pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall "
            + "nx mmxext fxsr_opt rdtscp lm 3dnowext 3dnow pni cx16 lahf_lm "
            + "cmp_legacy svm cr8_legacy",
        "bogomips\t: 4460.39",
        "TLB size\t: 1024 4K pages",
        "clflush size\t: 64",
        "cache_alignment\t: 64",
        "address sizes\t: 40 bits physical, 48 bits virtual",
        "power management: ts fid vid ttp tm stc",
        ""
        );
    String cpuinfoFile =
        scratch.file("test_cpuinfo_amd", cpuinfoContent).getPathString();
    String meminfoContent = StringUtilities.joinLines(
        "MemTotal:      8223956 kB",
        "MemFree:       3670396 kB",
        "Buffers:        374068 kB",
        "Cached:        3366980 kB",
        "SwapCached:          0 kB",
        "Active:        3275860 kB",
        "Inactive:       737816 kB",
        "HighTotal:           0 kB",
        "HighFree:            0 kB",
        "LowTotal:      8223956 kB",
        "LowFree:       3670396 kB",
        "SwapTotal:     6024332 kB",
        "SwapFree:      6024332 kB",
        "Dirty:              84 kB",
        "Writeback:           0 kB",
        "AnonPages:      272308 kB",
        "Mapped:          62604 kB",
        "Slab:           506140 kB",
        "PageTables:       4608 kB",
        "NFS_Unstable:        0 kB",
        "Bounce:              0 kB",
        "CommitLimit:  10136308 kB",
        "Committed_AS:   600672 kB",
        "VmallocTotal: 34359738367 kB",
        "VmallocUsed:    299068 kB",
        "VmallocChunk: 34359438843 kB",
        "HugePages_Total:     0",
        "HugePages_Free:      0",
        "HugePages_Rsvd:      0",
        "Hugepagesize:     2048 kB");
    String meminfoFile =
        scratch.file("test_meminfo_amd", meminfoContent).getPathString();
    assertEquals(4, LocalHostCapacity.getLogicalCpuCount(cpuinfoContent));
    assertEquals(2, LocalHostCapacity.getPhysicalCpuCount(cpuinfoContent, 4));
    assertEquals(2, LocalHostCapacity.getCoresPerCpu(cpuinfoContent));
    ResourceSet capacity =
        LocalHostCapacity.getLocalHostCapacity(cpuinfoFile, meminfoFile);
    assertEquals(capacity.getCpuUsage(), 4.0, 0.01);
    assertEquals(8223.956, capacity.getMemoryMb(), 0.1); // +/- 0.1MB
  }
}
