/*
 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * @test
 * @bug 4227137
 * @summary Basic functional test for virtual-machine shutdown hooks
 * @modules java.desktop
 * @library /test/lib
 * @build jdk.test.lib.process.*
 * @run testng Basic
 */

import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.io.PrintStream;
import java.io.FileOutputStream;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;

import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import jdk.test.lib.process.ProcessTools;

public class Basic {

    static Runtime rt = Runtime.getRuntime();
    static PrintStream out = System.out;

    // Expect that no finalizer is invoked at exit
    @DataProvider(name = "testcase")
    public Object[][] getTestCase() {
        return new Object[][] {
            { "fallThrough", 0, "h1", "" },
            { "exit0",       0, "h1", "" },
            { "exit0NoHook", 0, "",   "" },
            { "exit1",       1, "h1", "" },
            { "exit1NoHook", 1, "",   "" },
            { "halt",        0, "",   "" },
            { "haltNoHook",  0, "",   "" },
            { "haltInHook",  0, "h1", "" },
            { "addLate",     0, "h1",
                "Caught as expected: java.lang.IllegalStateException: Shutdown in progress" },
            { "removeLate",  0, "h2",
                "Caught as expected: java.lang.IllegalStateException: Shutdown in progress" }
        };
    }

    @Test(dataProvider = "testcase")
    public void test(String testcase, int exitValue, String hook, String finalizer)
            throws Exception {
        System.out.println("Test " + testcase);
        ProcessTools.executeTestJava("Basic", testcase)
                .shouldHaveExitValue(exitValue)
                .stdoutShouldMatch(
                    hook + (hook.isEmpty() ? "" : System.lineSeparator()) + finalizer);
        System.out.println("Passed");
    }

    public static class Fin {
        String name;

        public Fin(String name) {
            this.name = name;
        }

        public void go() { }

        protected void finalize() {
            out.println(name);
            go();
        }
    }


    public static class Hook extends Thread {
        String name;

        public Hook(String name) {
            this.name = name;
        }

        public void go() { }

        public void run() {
            out.println(name);
            go();
        }
    }

    public static void fallThrough() throws Exception {
        rt.addShutdownHook(new Hook("h1"));
        Fin f = new Fin("f1");
    }

    public static void exit0() throws Exception {
        rt.addShutdownHook(new Hook("h1"));
        Fin f = new Fin("f1");
        rt.exit(0);
    }

    public static void exit0NoHook() throws Exception {
        Fin f = new Fin("f1");
        rt.exit(0);
    }

    /* Finalizer should not run */
    public static void exit1() throws Exception {
        rt.addShutdownHook(new Hook("h1"));
        Fin f = new Fin("f1");
        rt.exit(1);
    }

    public static void exit1NoHook() throws Exception {
        Fin f = new Fin("f1");
        rt.exit(1);
    }

    public static void halt() throws Exception {
        rt.addShutdownHook(new Hook("h1") {
            public void go() { rt.halt(1); }});
        Fin f = new Fin("f1") { public void go() { rt.halt(1); }};
        rt.halt(0);
    }

    public static void haltNoHook() throws Exception {
        Fin f = new Fin("f1") { public void go() { rt.halt(1); }};
        rt.halt(0);
    }

    public static void haltInHook() throws Exception {
        rt.addShutdownHook(new Hook("h1") {
            public void go() { rt.halt(0); }});
        Fin f = new Fin("f1");
        rt.exit(1);
    }

    public static void addLate() throws Exception {
        rt.addShutdownHook(new Hook("h1") {
            public void go() {
                try {
                    rt.addShutdownHook(new Hook("h2"));
                } catch (IllegalStateException x) {
                    out.println("Caught as expected: " + x);
                    rt.halt(0);
                }
            }});
        rt.exit(1);
    }

    public static void removeLate() throws Exception {
        final Hook h1 = new Hook("h1");
        rt.addShutdownHook(new Hook("h2") {
            public void go() {
                try {
                    rt.removeShutdownHook(h1);
                } catch (IllegalStateException x) {
                    out.println("Caught as expected: " + x);
                    rt.halt(0);
                }
            }});
        rt.exit(1);
    }

    /* For INT, HUP, TERM */
    public static void stall() throws Exception {
        Fin f = new Fin("f1");
        rt.addShutdownHook(new Hook("h1"));
        out.print("Type ^C now: ");
        out.flush();
        Thread.sleep(100000);
    }


    public static void main(String[] args) throws Throwable {
        Method m = Basic.class.getMethod(args[0], new Class[] { });
        String log = null;
        try {
            log = System.getProperty("log");
        } catch (SecurityException x) { }
        if (log != null) {
            out = new PrintStream(new FileOutputStream(log), true);
        }
        try {
            m.invoke(null, new Object[] { });
        } catch (InvocationTargetException x) {
            throw x.getTargetException();
        }
    }

}
