1 /*
   2  * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.util.concurrent.ExecutorService;
  25 import java.util.concurrent.Executors;
  26 import java.util.function.Consumer;
  27 
  28 import org.testng.annotations.Test;
  29 
  30 import static org.testng.Assert.assertEquals;
  31 import static org.testng.Assert.assertTrue;
  32 
  33 public abstract class EditorTestBase extends ReplToolTesting {
  34 
  35     private static ExecutorService executor;
  36 
  37     public abstract void writeSource(String s);
  38     public abstract String getSource();
  39     public abstract void accept();
  40     public abstract void exit();
  41     public abstract void cancel();
  42     public abstract void shutdownEditor();
  43 
  44     public void testEditor(ReplTest... tests) {
  45         testEditor(false, new String[]{"-nostartup"}, tests);
  46     }
  47 
  48     public void testEditor(boolean defaultStartup, String[] args, ReplTest... tests) {
  49         test(defaultStartup, args, tests);
  50     }
  51 
  52     public abstract void assertEdit(boolean after, String cmd,
  53                                     Consumer<String> checkInput, Consumer<String> checkOutput, Action action);
  54 
  55     public void assertEditInput(boolean after, String cmd, Consumer<String> checkInput, Action action) {
  56         assertEdit(after, cmd, checkInput, s -> {}, action);
  57     }
  58 
  59     public void assertEditOutput(boolean after, String cmd, Consumer<String> checkOutput, Action action) {
  60         assertEdit(after, cmd, s -> {}, checkOutput, action);
  61     }
  62 
  63     public void assertEditInput(boolean after, String cmd, String input, Action action) {
  64         assertEditInput(after, cmd, s -> assertEquals(s, input, "Input"), action);
  65     }
  66 
  67     public void assertEditOutput(boolean after, String cmd, String output, Action action) {
  68         assertEditOutput(after, cmd, s -> assertEquals(s.trim(), output.trim(), "command"), action);
  69     }
  70 
  71     @Test
  72     public void testEditNegative() {
  73         for (String edit : new String[] {"/ed", "/edit"}) {
  74             test(new String[]{"-nostartup"},
  75                     a -> assertCommandOutputStartsWith(a, edit + " 1",
  76                             "|  No such snippet: 1"),
  77                     a -> assertCommandOutputStartsWith(a, edit + " unknown",
  78                             "|  No such snippet: unknown")
  79             );
  80         }
  81     }
  82 
  83     @Test
  84     public void testDoNothing() {
  85         testEditor(
  86                 a -> assertVariable(a, "int", "a", "0", "0"),
  87                 a -> assertEditOutput(a, "/ed 1", "", this::exit),
  88                 a -> assertCommandCheckOutput(a, "/v", assertVariables())
  89         );
  90     }
  91 
  92     @Test
  93     public void testEditVariable1() {
  94         testEditor(
  95                 a -> assertVariable(a, "int", "a", "0", "0"),
  96                 a -> assertEditOutput(a, "/ed 1", "a ==> 10", () -> {
  97                     writeSource("\n\n\nint a = 10;\n\n\n");
  98                     exit();
  99                     loadVariable(true, "int", "a", "10", "10");
 100                 }),
 101                 a -> assertEditOutput(a, "/ed 1", "a ==> 15", () -> {
 102                     writeSource("int a = 15;");
 103                     exit();
 104                     loadVariable(true, "int", "a", "15", "15");
 105                 }),
 106                 a -> assertCommandCheckOutput(a, "/v", assertVariables())
 107         );
 108     }
 109 
 110     @Test
 111     public void testEditVariable2() {
 112         testEditor(
 113                 a -> assertVariable(a, "int", "a", "0", "0"),
 114                 a -> assertEditOutput(a, "/ed 1", "b ==> 10", () -> {
 115                     writeSource("int b = 10;");
 116                     exit();
 117                     loadVariable(true, "int", "b", "10", "10");
 118                 }),
 119                 a -> assertEditOutput(a, "/ed 1", "a ==> 15", () -> {
 120                     writeSource("int a = 15;");
 121                     exit();
 122                     loadVariable(true, "int", "a", "15", "15");
 123                 }),
 124                 a -> assertCommandCheckOutput(a, "/v", assertVariables())
 125         );
 126     }
 127 
 128     @Test
 129     public void testEditClass1() {
 130         testEditor(
 131                 a -> assertClass(a, "class A {}", "class", "A"),
 132                 a -> assertEditOutput(a, "/ed 1", "", () -> {
 133                     writeSource("\n\n\nclass A {}\n\n\n");
 134                     exit();
 135                     loadClass(true, "class A {}", "class", "A");
 136                 }),
 137                 a -> assertEditOutput(a, "/ed 1",
 138                         "|  replaced enum A", () -> {
 139                     writeSource("enum A {}");
 140                     exit();
 141                     loadClass(true, "enum A {}", "enum", "A");
 142                 }),
 143                 a -> assertCommandCheckOutput(a, "/types", assertClasses())
 144         );
 145     }
 146 
 147     @Test
 148     public void testEditClass2() {
 149         testEditor(
 150                 a -> assertClass(a, "class A {}", "class", "A"),
 151                 a -> assertEditOutput(a, "/ed 1", "|  created class B", () -> {
 152                     writeSource("class B { }");
 153                     exit();
 154                     loadClass(true, "class B {}", "class", "B");
 155                 }),
 156                 a -> assertEditOutput(a, "/ed 1",
 157                         "|  replaced enum A", () -> {
 158                     writeSource("enum A {}");
 159                     exit();
 160                     loadClass(true, "enum A {}", "enum", "A");
 161                 }),
 162                 a -> assertCommandCheckOutput(a, "/types", assertClasses())
 163         );
 164     }
 165 
 166     @Test
 167     public void testEditMethod1() {
 168         testEditor(
 169                 a -> assertMethod(a, "void f() {}", "()void", "f"),
 170                 a -> assertEditOutput(a, "/ed 1", "", () -> {
 171                     writeSource("\n\n\nvoid f() {}\n\n\n");
 172                     exit();
 173                     loadMethod(true, "void f() {}", "()void", "f");
 174                 }),
 175                 a -> assertEditOutput(a, "/ed 1",
 176                         "|  replaced method f()", () -> {
 177                     writeSource("double f() { return 0; }");
 178                     exit();
 179                     loadMethod(true, "double f() { return 0; }", "()double", "f");
 180                 }),
 181                 a -> assertCommandCheckOutput(a, "/m", assertMethods())
 182         );
 183     }
 184 
 185     @Test
 186     public void testEditMethod2() {
 187         testEditor(
 188                 a -> assertMethod(a, "void f() {}", "()void", "f"),
 189                 a -> assertEditOutput(a, "/ed 1", "|  created method g()", () -> {
 190                     writeSource("void g() {}");
 191                     exit();
 192                     loadMethod(true, "void g() {}", "()void", "g");
 193                 }),
 194                 a -> assertEditOutput(a, "/ed 1",
 195                         "|  replaced method f()", () -> {
 196                     writeSource("double f() { return 0; }");
 197                     exit();
 198                     loadMethod(true, "double f() { return 0; }", "()double", "f");
 199                 }),
 200                 a -> assertCommandCheckOutput(a, "/m", assertMethods())
 201         );
 202     }
 203 
 204     @Test
 205     public void testNoArguments() {
 206         testEditor(
 207                 a -> assertVariable(a, "int", "a"),
 208                 a -> assertMethod(a, "void f() {}", "()void", "f"),
 209                 a -> assertClass(a, "class A {}", "class", "A"),
 210                 a -> assertEditInput(a, "/ed", s -> {
 211                     String[] ss = s.split("\n");
 212                     assertEquals(ss.length, 3, "Expected 3 lines: " + s);
 213                     assertEquals(ss[0], "int a;");
 214                     assertEquals(ss[1], "void f() {}");
 215                     assertEquals(ss[2], "class A {}");
 216                 }, this::exit)
 217         );
 218     }
 219 
 220     @Test
 221     public void testStartup() {
 222         testEditor(true, new String[0],
 223                 a -> assertEditInput(a, "/ed", s -> assertTrue(s.isEmpty(), "Checking of startup: " + s), this::cancel),
 224                 a -> assertEditInput(a, "/ed printf", assertStartsWith("void printf"), this::cancel));
 225     }
 226 
 227     @Test
 228     public void testCancel() {
 229         testEditor(
 230                 a -> assertVariable(a, "int", "a"),
 231                 a -> assertEditOutput(a, "/ed a", "", () -> {
 232                     writeSource("int b = 10");
 233                     cancel();
 234                 })
 235         );
 236     }
 237 
 238     @Test
 239     public void testAccept() {
 240         testEditor(
 241                 a -> assertVariable(a, "int", "a"),
 242                 a -> assertEditOutput(a, "/ed a", "b ==> 10", () -> {
 243                     writeSource("int b = 10");
 244                     accept();
 245                     exit();
 246                 })
 247         );
 248     }
 249 
 250     public static ExecutorService getExecutor() {
 251         if (executor == null) {
 252             executor = Executors.newSingleThreadExecutor();
 253         }
 254         return executor;
 255     }
 256 
 257     public static void executorShutdown() {
 258         if (executor != null) {
 259             executor.shutdown();
 260             executor = null;
 261         }
 262     }
 263 
 264     interface Action {
 265         void accept();
 266     }
 267 }