/*
 * Copyright (c) 2025, 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 8350582
 * @library /test/lib /javax/net/ssl/templates
 * @summary Correct the parsing of the ssl value in javax.net.debug
 * @run junit DebugPropertyValuesTest
 */

// A test to verify debug output for different javax.net.debug scenarios

import jdk.test.lib.process.ProcessTools;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Stream;

import jdk.test.lib.process.OutputAnalyzer;

public class DebugPropertyValuesTest extends SSLSocketTemplate {

    private static final Path LOG_FILE = Path.of("logging.conf");
    private static final HashMap<String, List<String>> debugMessages = new HashMap<>();
    private static final String DATE_REGEX = "\\d{4}-\\d{2}-\\d{2}";

    static {


        debugMessages.put("handshake",
                List.of("Produced ClientHello handshake message",
                        "supported_versions"));
        debugMessages.put("keymanager", List.of("choosing key:"));
        debugMessages.put("packet", List.of("Raw write"));
        debugMessages.put("plaintext", List.of("Plaintext before ENCRYPTION"));
        debugMessages.put("record", List.of("handshake, length =", "WRITE:"));
        debugMessages.put("session", List.of("Session initialized:"));
        debugMessages.put("sslctx", List.of("trigger seeding of SecureRandom"));
        debugMessages.put("ssl", List.of("jdk.tls.keyLimits:"));
        debugMessages.put("trustmanager", List.of("adding as trusted certificates"));
        debugMessages.put("verbose", List.of("Ignore unsupported cipher suite:"));
        debugMessages.put("handshake-expand",
                List.of("\"logger\".*: \"javax.net.ssl\",",
                        "\"message\".*: \"Produced ClientHello handshake message"));
        debugMessages.put("record-expand",
                List.of("\"logger\".*: \"javax.net.ssl\",",
                        "\"message\".*: \"READ: TLSv1.2 application_data"));
        debugMessages.put("help",
                List.of("print the help messages",
                        "debugging can be widened with:"));
        debugMessages.put("java.security.debug",
                List.of("properties\\[.*\\|main\\|.*" + DATE_REGEX + ".*\\]:",
                        "certpath\\[.*\\|main\\|.*" + DATE_REGEX + ".*\\]:"));
        debugMessages.put("javax.net.debug.logger",
                List.of("FINE: adding as trusted certificates",
                        "FINE: WRITE: TLSv1.3 application_data"));
    }

    @BeforeAll
    static void setup() throws Exception {
        Files.writeString(LOG_FILE, ".level = ALL\n" +
                "handlers= java.util.logging.ConsoleHandler\n" +
                "java.util.logging.ConsoleHandler.level = ALL\n");
    }

    private static Stream<Arguments> patternMatches() {
        return Stream.of(
                // all should print everything
                Arguments.of(List.of("-Djavax.net.debug=all"),
                        List.of("handshake", "keymanager", "packet",
                                "plaintext", "record", "session", "ssl",
                                "sslctx", "trustmanager", "verbose")),
                // ssl should print most details except verbose details
                Arguments.of(List.of("-Djavax.net.debug=ssl"),
                        List.of("handshake", "keymanager",
                                "record", "session", "ssl",
                                "sslctx", "trustmanager", "verbose")),
                // allow expand option for more verbose output
                Arguments.of(List.of("-Djavax.net.debug=ssl,handshake,expand"),
                        List.of("handshake", "handshake-expand", "keymanager",
                                "record", "session", "record-expand", "ssl",
                                "sslctx", "trustmanager", "verbose")),
                // filtering on record option, with expand
                Arguments.of(List.of("-Djavax.net.debug=ssl:record,expand"),
                        List.of("handshake", "handshake-expand", "keymanager",
                                "record", "record-expand", "session", "ssl",
                                "sslctx", "trustmanager", "verbose")),
                // this test is equivalent to ssl:record mode
                Arguments.of(List.of("-Djavax.net.debug=ssl,record"),
                        List.of("handshake", "keymanager", "record",
                                "session", "ssl", "sslctx",
                                "trustmanager", "verbose")),
                // example of test where no "ssl" value is passed
                // handshake debugging with verbose mode
                // only verbose gets printed. Needs fixing (JDK-8044609)
                Arguments.of(List.of("-Djavax.net.debug=handshake:verbose"),
                        List.of("verbose")),
                // another example of test where no "ssl" value is passed
                Arguments.of(List.of("-Djavax.net.debug=record"),
                        List.of("record")),
                // ignore bad sub-option. treat like "ssl"
                Arguments.of(List.of("-Djavax.net.debug=ssl,typo"),
                        List.of("handshake", "keymanager",
                                "record", "session", "ssl",
                                "sslctx", "trustmanager", "verbose")),
                // ssltypo contains "ssl". Treat like "ssl"
                Arguments.of(List.of("-Djavax.net.debug=ssltypo"),
                        List.of("handshake", "keymanager",
                                "record", "session", "ssl",
                                "sslctx", "trustmanager", "verbose")),
                // plaintext is valid for record option
                Arguments.of(List.of("-Djavax.net.debug=ssl:record:plaintext"),
                        List.of("handshake", "keymanager", "plaintext",
                                "record", "session", "ssl",
                                "sslctx", "trustmanager", "verbose")),
                Arguments.of(List.of("-Djavax.net.debug=ssl:trustmanager"),
                        List.of("handshake", "keymanager", "record", "session",
                                "ssl", "sslctx", "trustmanager", "verbose")),
                Arguments.of(List.of("-Djavax.net.debug=ssl:sslctx"),
                        List.of("handshake", "keymanager", "record", "session",
                                "ssl", "sslctx", "trustmanager", "verbose")),
                // help message test. Should exit without running test
                Arguments.of(List.of("-Djavax.net.debug=help"),
                        List.of("help")),
                // add in javax.net.debug sanity test
                Arguments.of(List.of("-Djavax.net.debug=ssl:trustmanager",
                                "-Djava.security.debug=all"),
                        List.of("handshake", "java.security.debug", "keymanager",
                                "record", "session", "ssl", "sslctx",
                                "trustmanager", "verbose")),
                // empty invokes System.Logger use
                Arguments.of(List.of("-Djavax.net.debug",
                        "-Djava.util.logging.config.file=" + LOG_FILE),
                        List.of("handshake", "javax.net.debug.logger",
                                "keymanager", "packet",  "plaintext",
                                "record", "session", "ssl",
                                "sslctx", "trustmanager", "verbose"))
        );
    }

    @ParameterizedTest
    @MethodSource("patternMatches")
    public void checkDebugOutput(List<String> params,
                                 List<String> expected) throws Exception {

        List<String> args = new ArrayList<>(params);
        args.add("DebugPropertyValuesTest");
        OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJava(args);
        outputAnalyzer.shouldHaveExitValue(0);
        for (String s : debugMessages.keySet()) {
            for (String output : debugMessages.get(s)) {
                if (expected.contains(s)) {
                    outputAnalyzer.shouldMatch(output);
                } else {
                    outputAnalyzer.shouldNotMatch(output);
                }
            }
        }
    }
}
