package hudson.util;

import static org.junit.Assert.*;
import static org.junit.Assume.*;
import java.io.File;
import hudson.Functions;
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.tasks.Maven;
import hudson.tasks.Shell;
import hudson.util.ProcessTreeRemoting.IOSProcess;
import org.junit.After;
import org.junit.Assume;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.ExtractResourceSCM;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.TestExtension;
import com.google.common.collect.ImmutableMap;

public class ProcessTreeKillerTest {

    @Rule
    public JenkinsRule j = new JenkinsRule();
    private Process process;
    
    @After
    public void tearDown() throws Exception {
        if (null != process)
            process.destroy();
    }    
    
    @Test
	public void manualAbortProcess() throws Exception {
		ProcessTree.enabled = true;
		FreeStyleProject project = j.createFreeStyleProject();

		// this contains a maven project with a single test that sleeps 5s.
		project.setScm(new ExtractResourceSCM(getClass().getResource(
				"ProcessTreeKiller-test-project.jar")));
		project.getBuildersList().add(new Maven("install", "maven"));

		// build the project, wait until tests are running, then cancel.
		project.scheduleBuild2(0).waitForStart();

        FreeStyleBuild b = project.getLastBuild();
        b.doStop();

		Thread.sleep(1000);

		// will fail (at least on windows) if test process is still running
		b.getWorkspace().deleteRecursive();
	}

    @Test
    @Issue("JENKINS-22641")
    public void processProperlyKilledUnix() throws Exception {
        ProcessTree.enabled = true;
        Assume.assumeFalse("This test does not involve windows", Functions.isWindows());

        FreeStyleProject sleepProject = j.createFreeStyleProject();
        FreeStyleProject processJob = j.createFreeStyleProject();

        sleepProject.getBuildersList().add(new Shell("nohup sleep 100000 &"));

        j.assertBuildStatusSuccess(sleepProject.scheduleBuild2(0).get());

        processJob.getBuildersList().add(new Shell("ps -ef | grep sleep"));

        j.assertLogNotContains("sleep 100000", processJob.scheduleBuild2(0).get());
    }
    
    @Test
    @Issue("JENKINS-9104")
    public void considersKillingVetos() throws Exception {
        // on some platforms where we fail to list any processes, this test will
        // just not work
        assumeTrue(ProcessTree.get() != ProcessTree.DEFAULT);

        // kick off a process we (shouldn't) kill
        ProcessBuilder pb = new ProcessBuilder();
        pb.environment().put("cookie", "testKeepDaemonsAlive");

        if (File.pathSeparatorChar == ';') {
            pb.command("cmd");
        } else {
            pb.command("sleep", "5m");
        }

        process = pb.start();

        ProcessTree processTree = ProcessTree.get();
        processTree.killAll(ImmutableMap.of("cookie", "testKeepDaemonsAlive"));
        try {
            process.exitValue();
            fail("Process should have been excluded from the killing");
        } catch (IllegalThreadStateException e) {
            // Means the process is still running
        }
    }

    @TestExtension("considersKillingVetos")
    public static class VetoAllKilling extends ProcessKillingVeto {
        @Override
        public VetoCause vetoProcessKilling(IOSProcess p) {
            return new VetoCause("Peace on earth");
        }
    }
}

@@ -115,24 +115,190 @@
 ort 
-hudson.Functions
+java.io.IOException;%0A%0Aimport hudson.EnvVars;%0Aimport hudson.Functions;%0Aimport hudson.Launcher;%0Aimport hudson.Proc;%0Aimport hudson.model.AbstractBuild;%0Aimport hudson.model.BuildListener
 ;%0Aim
@@ -692,24 +692,66 @@
 enkinsRule;%0A
+import org.jvnet.hudson.test.TestBuilder;%0A
 import org.j
@@ -2394,24 +2394,24 @@
  sleep%22));%0A%0A
-
         j.as
@@ -2483,24 +2483,1154 @@
 et());%0A    %7D
+%0A%0A    @Test%0A    public void doNotKillProcessWithCookie() throws Exception %7B%0A        Assume.assumeFalse(%22This test does not involve windows%22, Functions.isWindows());%0A        ProcessTree.enabled = true;%0A        SpawnBuilder spawner = new SpawnBuilder();%0A%0A        FreeStyleProject p = j.createFreeStyleProject();%0A        p.getBuildersList().add(spawner);%0A        try %7B%0A            j.buildAndAssertSuccess(p);%0A%0A            assertTrue(%22Process should be running%22, spawner.proc.isAlive());%0A        %7D finally %7B%0A            spawner.proc.kill();%0A            assertTrue(%22Process should be dead%22, !spawner.proc.isAlive());%0A        %7D%0A    %7D%0A    public static final class SpawnBuilder extends TestBuilder %7B%0A        private Proc proc;%0A%0A        @Override%0A        public boolean perform(AbstractBuild%3C?, ?%3E build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException %7B%0A            EnvVars envvars = build.getEnvironment(listener);%0A            envvars.addLine(%22BUILD_ID=dontKillMe%22);%0A            proc = launcher.launch().envs(envvars).cmds(%22nohup%22, %22sleep%22, %22100000%22).start();%0A            return true;%0A        %7D%0A    %7D
 %0A    %0A    @T

Test process cleanup can be suppressed changing BUILD_ID variable (#2351)