8066156: JEP-JDK-8046155: Test task: stress by dcmd
authorppunegov
Tue, 24 Nov 2015 20:52:15 +0300
changeset 34215 ae6ba67d6420
parent 34214 41286f248795
child 34216 2818af1ce748
8066156: JEP-JDK-8046155: Test task: stress by dcmd Summary: Stress dcmd tests that add directives Reviewed-by: iignatyev, neliasso
hotspot/test/compiler/compilercontrol/jcmd/StressAddJcmdBase.java
hotspot/test/compiler/compilercontrol/jcmd/StressAddMultiThreadedTest.java
hotspot/test/compiler/compilercontrol/jcmd/StressAddSequentiallyTest.java
hotspot/test/compiler/compilercontrol/share/actions/BaseAction.java
hotspot/test/compiler/compilercontrol/share/scenario/Executor.java
hotspot/test/compiler/compilercontrol/share/scenario/Scenario.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/compilercontrol/jcmd/StressAddJcmdBase.java	Tue Nov 24 20:52:15 2015 +0300
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+package compiler.compilercontrol.jcmd;
+
+import compiler.compilercontrol.parser.HugeDirectiveUtil;
+import compiler.compilercontrol.share.AbstractTestBase;
+import compiler.compilercontrol.share.method.MethodDescriptor;
+import compiler.compilercontrol.share.scenario.Executor;
+import jdk.test.lib.OutputAnalyzer;
+import jdk.test.lib.TimeLimitedRunner;
+import jdk.test.lib.Utils;
+import pool.PoolHelper;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public abstract class StressAddJcmdBase {
+    private static final int DIRECTIVES_AMOUNT = Integer.getInteger(
+            "compiler.compilercontrol.jcmd.StressAddJcmdBase.directivesAmount",
+            1000);
+    private static final int DIRECTIVE_FILES = Integer.getInteger(
+            "compiler.compilercontrol.jcmd.StressAddJcmdBase.directiveFiles",
+            5);
+    private static final List<MethodDescriptor> DESCRIPTORS = new PoolHelper()
+            .getAllMethods().stream()
+                    .map(pair -> AbstractTestBase
+                            .getValidMethodDescriptor(pair.first))
+                    .collect(Collectors.toList());
+
+    /**
+     * Performs test
+     */
+    public void test() {
+        List<String> commands = prepareCommands();
+        Executor executor = new TimeLimitedExecutor(commands);
+        OutputAnalyzer outputAnalyzer = executor.execute();
+        outputAnalyzer.shouldHaveExitValue(0);
+    }
+
+    /**
+     * Makes connection to the test VM
+     *
+     * @param pid      a pid of the VM under test
+     * @param commands a list of jcmd commands to be executed
+     * @return true if the test should continue invocation of this method
+     */
+    protected abstract boolean makeConnection(int pid, List<String> commands);
+
+    /**
+     * Finish test executions
+     */
+    protected void finish() { }
+
+    private List<String> prepareCommands() {
+        String[] files = new String[DIRECTIVE_FILES];
+        for (int i = 0; i < DIRECTIVE_FILES; i++) {
+            files[i] = "directives" + i + ".json";
+            HugeDirectiveUtil.createHugeFile(DESCRIPTORS, files[i],
+                    DIRECTIVES_AMOUNT);
+        }
+        return Stream.of(files)
+                .map(file -> "Compiler.directives_add " + file)
+                .collect(Collectors.toList());
+    }
+
+    private class TimeLimitedExecutor extends Executor {
+        private final List<String> jcmdCommands;
+
+        public TimeLimitedExecutor(List<String> jcmdCommands) {
+            /* There are no need to check the state */
+            super(true, null, null, jcmdCommands);
+            this.jcmdCommands = jcmdCommands;
+        }
+
+        @Override
+        protected void executeJCMD(int pid) {
+            TimeLimitedRunner runner = new TimeLimitedRunner(
+                    Utils.DEFAULT_TEST_TIMEOUT,
+                    Utils.TIMEOUT_FACTOR,
+                    () -> makeConnection(pid, jcmdCommands));
+            try {
+                runner.call();
+            } catch (Exception e) {
+                throw new Error("Exception during the execution: " + e, e);
+            }
+            finish();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/compilercontrol/jcmd/StressAddMultiThreadedTest.java	Tue Nov 24 20:52:15 2015 +0300
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2015, 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 8137167
+ * @summary Tests jcmd to be able to add a lot of huge directive files with
+ *          parallel executed jcmds until timeout has reached
+ * @library /testlibrary /test/lib /compiler/testlibrary ../share /
+ * @build StressAddMultiThreadedTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox
+ *        compiler.testlibrary.CompilerUtils
+ *        compiler.compilercontrol.share.actions.*
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ *                              sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main/othervm/timeout=360 compiler.compilercontrol.jcmd.StressAddMultiThreadedTest
+ */
+
+package compiler.compilercontrol.jcmd;
+
+import jdk.test.lib.dcmd.PidJcmdExecutor;
+
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+public class StressAddMultiThreadedTest extends StressAddJcmdBase {
+    private static final int THREADS;
+    private final BlockingQueue<Runnable> queue;
+    private final ExecutorService executor;
+
+    static {
+        THREADS = Runtime.getRuntime().availableProcessors()
+                * Integer.getInteger("compiler.compilercontrol.jcmd" +
+                        ".StressAddMultiThreadedTest.threadFactor", 10);
+    }
+
+    public StressAddMultiThreadedTest() {
+        queue = new ArrayBlockingQueue<>(THREADS);
+        executor = new ThreadPoolExecutor(THREADS, THREADS, 100,
+                TimeUnit.MILLISECONDS, queue,
+                new ThreadPoolExecutor.CallerRunsPolicy());
+    }
+
+    public static void main(String[] args) {
+        new StressAddMultiThreadedTest().test();
+    }
+
+    @Override
+    protected boolean makeConnection(int pid, List<String> commands) {
+        commands.forEach(command -> {
+            if (!executor.isShutdown()) {
+                executor.submit(() -> new PidJcmdExecutor(String.valueOf(pid))
+                        .execute(command));
+            }
+        });
+        return !executor.isShutdown();
+    }
+
+    @Override
+    protected void finish() {
+        executor.shutdown();
+        try {
+            executor.awaitTermination(10, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            throw new Error("Interrupted while awaiting for termination: " + e,
+                    e);
+        }
+        executor.shutdownNow();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/compilercontrol/jcmd/StressAddSequentiallyTest.java	Tue Nov 24 20:52:15 2015 +0300
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015, 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 8137167
+ * @summary Tests jcmd to be able to add a lot of huge directives
+ * @library /testlibrary /test/lib /compiler/testlibrary ../share /
+ * @build StressAddSequentiallyTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox
+ *        compiler.testlibrary.CompilerUtils
+ *        compiler.compilercontrol.share.actions.*
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ *                              sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main/othervm/timeout=300 compiler.compilercontrol.jcmd.StressAddSequentiallyTest
+ */
+
+package compiler.compilercontrol.jcmd;
+
+import jdk.test.lib.dcmd.PidJcmdExecutor;
+
+import java.util.List;
+
+public class StressAddSequentiallyTest extends StressAddJcmdBase {
+    public static void main(String[] args) {
+        new StressAddSequentiallyTest().test();
+    }
+
+    @Override
+    protected boolean makeConnection(int pid, List<String> commands) {
+        commands.forEach(command -> new PidJcmdExecutor(String.valueOf(pid))
+                .execute(command));
+        return true;
+    }
+}
--- a/hotspot/test/compiler/compilercontrol/share/actions/BaseAction.java	Tue Nov 24 16:58:20 2015 +0300
+++ b/hotspot/test/compiler/compilercontrol/share/actions/BaseAction.java	Tue Nov 24 20:52:15 2015 +0300
@@ -55,11 +55,24 @@
                 pair -> pair.first));
     }
 
+    /*
+     * args[0] is a port to connect
+     * args[1] is an optional parameter that shows that the state map should be
+     *         passed
+     */
     public static void main(String[] args) {
         if (args.length < 1) {
             throw new Error("TESTBUG: requires port as parameter: "
                     + Arrays.toString(args));
         }
+        boolean getStates = false;
+        if (args.length == 2) {
+            if ("states".equals(args[1])) {
+                getStates = true;
+            } else {
+                throw new Error("TESTBUG: incorrect argument: "+ args[1]);
+            }
+        }
         int pid;
         try {
             pid = ProcessTools.getProcessId();
@@ -78,11 +91,15 @@
             // send own pid to execute jcmd if needed
             out.println(String.valueOf(pid));
             out.flush();
-            lines = in.lines().collect(Collectors.toList());
+            if (getStates) {
+                lines = in.lines().collect(Collectors.toList());
+                check(decodeMap(lines));
+            } else {
+                in.readLine();
+            }
         } catch (IOException e) {
             throw new Error("Error on performing network operation", e);
         }
-        check(decodeMap(lines));
     }
 
     private static Map<Executable, State> decodeMap(List<String> lines) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/compilercontrol/share/scenario/Executor.java	Tue Nov 24 20:52:15 2015 +0300
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+package compiler.compilercontrol.share.scenario;
+
+import compiler.compilercontrol.share.actions.BaseAction;
+import jdk.test.lib.Asserts;
+import jdk.test.lib.OutputAnalyzer;
+import jdk.test.lib.ProcessTools;
+import jdk.test.lib.dcmd.CommandExecutor;
+import jdk.test.lib.dcmd.PidJcmdExecutor;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.lang.reflect.Executable;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class Executor {
+    private final boolean isValid;
+    private final List<String> vmOptions;
+    private final Map<Executable, State> states;
+    private final List<String> jcmdCommands;
+
+    /**
+     * Constructor
+     *
+     * @param isValid      shows that the input given to the VM is valid and
+     *                     VM shouldn't fail
+     * @param vmOptions    a list of VM input options
+     * @param states       a state map, or null for the non-checking execution
+     * @param jcmdCommands a list of diagnostic commands to be preformed
+     *                     on test VM
+     */
+    public Executor(boolean isValid, List<String> vmOptions,
+            Map<Executable, State> states, List<String> jcmdCommands) {
+        this.isValid = isValid;
+        if (vmOptions == null) {
+            this.vmOptions = new ArrayList<>();
+        } else {
+            this.vmOptions = vmOptions;
+        }
+        this.states = states;
+        this.jcmdCommands = jcmdCommands;
+    }
+
+    /**
+     * Executes separate VM a gets an OutputAnalyzer instance with the results
+     * of execution
+     */
+    public OutputAnalyzer execute() {
+        // Add class name that would be executed in a separate VM
+        String classCmd = BaseAction.class.getName();
+        vmOptions.add(classCmd);
+        OutputAnalyzer output;
+        try (ServerSocket serverSocket = new ServerSocket(0)) {
+            if (isValid) {
+                // Get port test VM will connect to
+                int port = serverSocket.getLocalPort();
+                if (port == -1) {
+                    throw new Error("Socket is not bound: " + port);
+                }
+                vmOptions.add(String.valueOf(port));
+                if (states != null) {
+                    // add flag to show that there should be state map passed
+                    vmOptions.add("states");
+                }
+                // Start separate thread to connect with test VM
+                new Thread(() -> connectTestVM(serverSocket)).start();
+            }
+            // Start test VM
+            output = ProcessTools.executeTestJvmAllArgs(
+                    vmOptions.toArray(new String[vmOptions.size()]));
+        } catch (Throwable thr) {
+            throw new Error("Execution failed: " + thr.getMessage(), thr);
+        }
+        return output;
+    }
+
+    /*
+     * Performs connection with a test VM, sends method states and performs
+     * JCMD operations on a test VM.
+     */
+    private void connectTestVM(ServerSocket serverSocket) {
+        /*
+         * There are no way to prove that accept was invoked before we started
+         * test VM that connects to this serverSocket. Connection timeout is
+         * enough
+         */
+        try (
+                Socket socket = serverSocket.accept();
+                PrintWriter pw = new PrintWriter(socket.getOutputStream(),
+                        true);
+                BufferedReader in = new BufferedReader(new InputStreamReader(
+                        socket.getInputStream()))) {
+            // Get pid of the executed process
+            int pid = Integer.parseInt(in.readLine());
+            Asserts.assertNE(pid, 0, "Got incorrect pid");
+            executeJCMD(pid);
+            if (states != null) {
+                // serialize and send state map
+                states.forEach((executable, state) -> {
+                    pw.println("{");
+                    pw.println(executable.toGenericString());
+                    pw.println(state.toString());
+                    pw.println("}");
+                });
+            } else {
+                pw.println();
+            }
+        } catch (IOException e) {
+            throw new Error("Failed to write data: " + e.getMessage(), e);
+        }
+    }
+
+    // Executes all diagnostic commands
+    protected void executeJCMD(int pid) {
+        CommandExecutor jcmdExecutor = new PidJcmdExecutor(String.valueOf(pid));
+        for (String command : jcmdCommands) {
+            jcmdExecutor.execute(command);
+        }
+    }
+}
--- a/hotspot/test/compiler/compilercontrol/share/scenario/Scenario.java	Tue Nov 24 16:58:20 2015 +0300
+++ b/hotspot/test/compiler/compilercontrol/share/scenario/Scenario.java	Tue Nov 24 20:52:15 2015 +0300
@@ -23,7 +23,6 @@
 
 package compiler.compilercontrol.share.scenario;
 
-import compiler.compilercontrol.share.actions.BaseAction;
 import compiler.compilercontrol.share.method.MethodDescriptor;
 import compiler.compilercontrol.share.processors.CommandProcessor;
 import compiler.compilercontrol.share.processors.LogProcessor;
@@ -32,20 +31,10 @@
 import jdk.test.lib.Asserts;
 import jdk.test.lib.OutputAnalyzer;
 import jdk.test.lib.Pair;
-import jdk.test.lib.ProcessTools;
-import jdk.test.lib.dcmd.CommandExecutorException;
-import jdk.test.lib.dcmd.JcmdExecutor;
 import pool.PoolHelper;
 
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
 import java.lang.reflect.Executable;
-import java.net.ServerSocket;
-import java.net.Socket;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
@@ -60,10 +49,9 @@
  */
 public final class Scenario {
     private final boolean isValid;
-    private final List<String> vmopts;
     private final Map<Executable, State> states;
     private final List<Consumer<OutputAnalyzer>> processors;
-    private final List<String> jcmdExecCommands;
+    private final Executor executor;
 
     private Scenario(boolean isValid,
                      List<String> vmopts,
@@ -71,7 +59,6 @@
                      List<CompileCommand> compileCommands,
                      List<JcmdCommand> jcmdCommands) {
         this.isValid = isValid;
-        this.vmopts = vmopts;
         this.states = states;
         processors = new ArrayList<>();
         processors.add(new LogProcessor(states));
@@ -89,7 +76,7 @@
         }
         processors.add(new CommandProcessor(nonQuieted));
         processors.add(new QuietProcessor(quieted));
-        jcmdExecCommands = new ArrayList<>();
+        List<String> jcmdExecCommands = new ArrayList<>();
         boolean addCommandMet = false;
         for (JcmdCommand cmd : jcmdCommands) {
             switch (cmd.jcmdType) {
@@ -104,42 +91,17 @@
                     break;
             }
         }
+        executor = new Executor(isValid, vmopts, states, jcmdExecCommands);
     }
 
     /**
      * Executes scenario
      */
     public void execute() {
-        // Construct execution command with CompileCommand and class
-        List<String> argsList = new ArrayList<>();
-        // Add VM options
-        argsList.addAll(vmopts);
-        // Add class name that would be executed in a separate VM
-        String classCmd = BaseAction.class.getName();
-        argsList.add(classCmd);
-        OutputAnalyzer output;
-        try (ServerSocket serverSocket = new ServerSocket(0)) {
-            if (isValid) {
-                // Get port test VM will connect to
-                int port = serverSocket.getLocalPort();
-                if (port == -1) {
-                    throw new Error("Socket is not bound: " + port);
-                }
-                argsList.add(String.valueOf(port));
-                // Start separate thread to connect with test VM
-                new Thread(() -> connectTestVM(serverSocket)).start();
-            }
-            // Start test VM
-            output = ProcessTools.executeTestJvmAllArgs(
-                    argsList.toArray(new String[argsList.size()]));
-        } catch (Throwable thr) {
-            throw new Error("Execution failed", thr);
-        }
+        OutputAnalyzer output = executor.execute();
         if (isValid) {
             output.shouldHaveExitValue(0);
-            for (Consumer<OutputAnalyzer> processor : processors) {
-                processor.accept(output);
-            }
+            processors.forEach(processor -> processor.accept(output));
         } else {
             Asserts.assertNE(output.getExitValue(), 0, "VM should exit with "
                     + "error for incorrect directives");
@@ -147,52 +109,6 @@
         }
     }
 
-    /*
-     * Performs connection with a test VM, sends method states and performs
-     * JCMD operations on a test VM.
-     */
-    private void connectTestVM(ServerSocket serverSocket) {
-        /*
-         * There are no way to prove that accept was invoked before we started
-         * test VM that connects to this serverSocket. Connection timeout is
-         * enough
-         */
-        try (
-                Socket socket = serverSocket.accept();
-                PrintWriter pw = new PrintWriter(socket.getOutputStream(),
-                        true);
-                BufferedReader in = new BufferedReader(new InputStreamReader(
-                        socket.getInputStream()))) {
-            // Get pid of the executed process
-            int pid = Integer.parseInt(in.readLine());
-            Asserts.assertNE(pid, 0, "Got incorrect pid");
-            executeJCMD(pid);
-            // serialize and send state map
-            for (Executable x : states.keySet()) {
-                pw.println("{");
-                pw.println(x.toGenericString());
-                pw.println(states.get(x).toString());
-                pw.println("}");
-            }
-        } catch (IOException e) {
-            throw new Error("Failed to write data", e);
-        }
-    }
-
-    // Executes all diagnostic commands
-    private void executeJCMD(int pid) {
-        for (String command : jcmdExecCommands) {
-            new JcmdExecutor() {
-                @Override
-                protected List<String> createCommandLine(String cmd)
-                        throws CommandExecutorException {
-                    return Arrays.asList(jcmdBinary, Integer.toString(pid),
-                            cmd);
-                }
-            }.execute(command);
-        }
-    }
-
     /**
      * Gets states of methods for this scenario
      *