unittest/shell_prompt_t.cc (1,200 lines of code) (raw):

/* * Copyright (c) 2017, 2024, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2.0, * as published by the Free Software Foundation. * * This program is designed to work with certain software (including * but not limited to OpenSSL) that is licensed under separate terms, * as designated in a particular file or component or in included license * documentation. The authors of MySQL hereby grant you an additional * permission to link the program and your derivative works with the * separately licensed software that they have either included with * the program or referenced in the documentation. * * This program 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.0, for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "unittest/gprod_clean.h" #include "unittest/gtest_clean.h" #include "mysqlsh/prompt_manager.h" #include "mysqlsh/prompt_renderer.h" #include "mysqlshdk/libs/textui/term_vt100.h" #include "mysqlshdk/libs/utils/strformat.h" #include "mysqlshdk/libs/utils/utils_file.h" #include "mysqlshdk/libs/utils/utils_general.h" #include "mysqlshdk/libs/utils/utils_path.h" #include "mysqlshdk/libs/utils/utils_string.h" #include "unittest/test_utils/command_line_test.h" namespace mysqlsh { class Shell_prompt : public ::testing::Test { public: static void TearDownTestCase() { mysqlshdk::textui::set_color_capability(mysqlshdk::textui::No_color); } }; TEST_F(Shell_prompt, prompt) { Prompt_renderer prompt; prompt.set_prompt("> ", "-> ", mysqlshdk::textui::Style()); EXPECT_EQ("> ", prompt.render()); prompt.set_is_continuing(true); EXPECT_EQ("-> ", prompt.render()); } TEST_F(Shell_prompt, prompt_static) { { Prompt_renderer prompt; prompt.set_width(100); prompt.set_prompt("> ", "-> ", mysqlshdk::textui::Style()); prompt.add_segment("mysql"); EXPECT_EQ("mysql> ", prompt.render()); prompt.clear(); prompt.add_segment("mysql"); prompt.add_segment("js"); EXPECT_EQ("mysql js> ", prompt.render()); prompt.clear(); prompt.add_segment("mysql"); prompt.add_segment("py"); EXPECT_EQ("mysql py> ", prompt.render()); prompt.set_is_continuing(true); EXPECT_EQ(" -> ", prompt.render()); } { Prompt_renderer prompt; prompt.set_prompt("> ", "-> ", mysqlshdk::textui::Style()); prompt.add_segment("mysql", mysqlshdk::textui::Style(), 0, 1, 3); EXPECT_EQ(" mysql > ", prompt.render()); } } static mysqlshdk::textui::Style mkstyle(int fg, int bg) { mysqlshdk::textui::Style style1; if (fg >= 0) style1.field_mask |= mysqlshdk::textui::Style::Color_16_fg_set; if (bg >= 0) style1.field_mask |= mysqlshdk::textui::Style::Color_16_bg_set; style1.fg.color_16 = fg; style1.bg.color_16 = bg; return style1; } TEST_F(Shell_prompt, separator_styling) { mysqlshdk::textui::set_color_capability(mysqlshdk::textui::Color_256); { Prompt_renderer prompt; prompt.add_segment("F", mkstyle(4, 5)); prompt.add_segment("B", mkstyle(3, 5)); EXPECT_EQ("\x1B[34;45mF \x1B[0m\x1B[33;45mB\x1B[0m", prompt.render()); } // == As a separator // triangle with same bg color (use hollow) { Prompt_renderer prompt; prompt.set_separator(Prompt_renderer::k_symbol_sep_right_pl, Prompt_renderer::k_symbol_sep_right_hollow_pl); prompt.add_segment("F", mkstyle(4, 5)); prompt.add_segment("B", mkstyle(3, 5)); EXPECT_EQ("\x1B[34;45mF\x1B[34;45m\xEE\x82\xB1\x1B[0m\x1B[33;45mB\x1B[0m", prompt.render()); } // triangle with diff bg color (use previous bg as fg) { Prompt_renderer prompt; prompt.set_separator(Prompt_renderer::k_symbol_sep_right_pl, Prompt_renderer::k_symbol_sep_right_hollow_pl); prompt.add_segment("F", mkstyle(4, 5)); prompt.add_segment("B", mkstyle(3, 6)); EXPECT_EQ("\x1B[34;45mF\x1B[35;46m\xEE\x82\xB0\x1B[0m\x1B[33;46mB\x1B[0m", prompt.render()); } // no-sep { Prompt_renderer prompt; prompt.add_segment("F", mkstyle(4, 5)); prompt.add_segment("B", mkstyle(3, 5)); prompt.set_separator("", ""); EXPECT_EQ("\x1B[34;45mF\x1B[0m\x1B[33;45mB\x1B[0m", prompt.render()); } // space { Prompt_renderer prompt; prompt.add_segment("F", mkstyle(4, 5)); prompt.add_segment("B", mkstyle(3, 5)); prompt.set_separator(" ", " "); EXPECT_EQ("\x1B[34;45mF \x1B[0m\x1B[33;45mB\x1B[0m", prompt.render()); } // some char { Prompt_renderer prompt; prompt.add_segment("F", mkstyle(4, 5)); prompt.add_segment("B", mkstyle(3, 5)); prompt.set_separator("#", "#"); EXPECT_EQ("\x1B[34;45mF\x1B[33;45m#\x1B[0m\x1B[33;45mB\x1B[0m", prompt.render()); } // == As the prompt itself // triangle default color { Prompt_renderer prompt; prompt.set_prompt(Prompt_renderer::k_symbol_sep_right_pl, Prompt_renderer::k_symbol_sep_right_pl, mysqlshdk::textui::Style()); prompt.add_segment("F", mkstyle(4, 5)); EXPECT_EQ("\x1B[34;45mF\x1B[0m\x1B[35m\xEE\x82\xB0\x1B[0m", prompt.render()); } { Prompt_renderer prompt; prompt.set_prompt(Prompt_renderer::k_symbol_sep_right_pl, Prompt_renderer::k_symbol_sep_right_pl, mkstyle(-1, 0)); prompt.add_segment("F", mkstyle(4, 5)); EXPECT_EQ("\x1B[34;45mF\x1B[0m\x1B[35;40m\xEE\x82\xB0\x1B[0m", prompt.render()); } // triangle with specific fg color { Prompt_renderer prompt; prompt.set_prompt(Prompt_renderer::k_symbol_sep_right_pl, Prompt_renderer::k_symbol_sep_right_pl, mkstyle(1, -1)); prompt.add_segment("F", mkstyle(4, 5)); EXPECT_EQ("\x1B[34;45mF\x1B[0m\x1B[31m\xEE\x82\xB0\x1B[0m", prompt.render()); } // some char default color { Prompt_renderer prompt; prompt.set_prompt("> ", "-> ", mysqlshdk::textui::Style()); prompt.add_segment("F", mkstyle(4, 5)); EXPECT_EQ("\x1B[34;45mF\x1B[0m> ", prompt.render()); } // some char specific fg color { Prompt_renderer prompt; prompt.set_prompt("> ", "-> ", mkstyle(1, -1)); prompt.add_segment("F", mkstyle(4, 5)); EXPECT_EQ("\x1B[34;45mF\x1B[0m\x1B[31m> \x1B[0m", prompt.render()); } // some char specific colors { Prompt_renderer prompt; prompt.set_prompt("> ", "-> ", mkstyle(4, 5)); prompt.add_segment("F", mkstyle(4, 5)); EXPECT_EQ("\x1B[34;45mF\x1B[0m\x1B[34;45m> \x1B[0m", prompt.render()); } } TEST_F(Shell_prompt, prompt_line_shrink) { // Truncate_on_dot { Prompt_renderer prompt(5); prompt.set_prompt("> ", "-> ", mysqlshdk::textui::Style()); prompt.set_width(20); prompt.add_segment( "my.host.com", mysqlshdk::textui::Style(), 5, 2, 0, Prompt_renderer::Shrinker_type::Truncate_on_dot_from_right); prompt.set_width(20); EXPECT_EQ("my.host.com> ", prompt.render()); prompt.set_width(19); EXPECT_EQ("my.host.com> ", prompt.render()); prompt.set_width(18); EXPECT_EQ("my.host.com> ", prompt.render()); prompt.set_width(17); EXPECT_EQ("my.host> ", prompt.render()); prompt.set_width(16); EXPECT_EQ("my.host> ", prompt.render()); prompt.set_width(15); EXPECT_EQ("my.host> ", prompt.render()); prompt.set_width(14); EXPECT_EQ("my.host> ", prompt.render()); prompt.set_width(13); EXPECT_EQ("my> ", prompt.render()); prompt.set_width(12); EXPECT_EQ("my> ", prompt.render()); prompt.set_width(11); EXPECT_EQ("my> ", prompt.render()); prompt.set_width(10); EXPECT_EQ("my> ", prompt.render()); prompt.set_width(9); EXPECT_EQ("my> ", prompt.render()); prompt.set_width(8); EXPECT_EQ("> ", prompt.render()); prompt.set_width(7); EXPECT_EQ("> ", prompt.render()); prompt.set_width(6); EXPECT_EQ("> ", prompt.render()); prompt.set_width(5); EXPECT_EQ("> ", prompt.render()); } // Ellipsis { Prompt_renderer prompt(5); prompt.set_prompt("> ", "-> ", mysqlshdk::textui::Style()); prompt.add_segment("my.host.com", mysqlshdk::textui::Style(), 7, 0, 0, Prompt_renderer::Shrinker_type::Ellipsize_on_char); prompt.set_width(20); EXPECT_EQ("my.host.com> ", prompt.render()); prompt.set_width(19); EXPECT_EQ("my.host.com> ", prompt.render()); prompt.set_width(18); EXPECT_EQ("my.host.com> ", prompt.render()); prompt.set_width(17); EXPECT_EQ("my.host.c-> ", prompt.render()); prompt.set_width(16); EXPECT_EQ("my.host.-> ", prompt.render()); prompt.set_width(15); EXPECT_EQ("my.host-> ", prompt.render()); prompt.set_width(14); EXPECT_EQ("my.hos-> ", prompt.render()); prompt.set_width(13); EXPECT_EQ("my.ho-> ", prompt.render()); prompt.set_width(12); EXPECT_EQ("my.h-> ", prompt.render()); prompt.set_width(11); EXPECT_EQ("my.-> ", prompt.render()); prompt.set_width(10); EXPECT_EQ("my-> ", prompt.render()); prompt.set_width(9); EXPECT_EQ("m-> ", prompt.render()); prompt.set_width(8); EXPECT_EQ("-> ", prompt.render()); prompt.set_width(7); EXPECT_EQ("> ", prompt.render()); } // Combined with priority/weight { Prompt_renderer prompt(20); prompt.set_prompt("> ", "-> ", mysqlshdk::textui::Style()); prompt.set_width(100); prompt.add_segment("mysql-js", mysqlshdk::textui::Style(), 100, 7); prompt.add_segment( "my.host.com", mysqlshdk::textui::Style(), 70, 2, 0, Prompt_renderer::Shrinker_type::Truncate_on_dot_from_right); prompt.add_segment("sakila", mysqlshdk::textui::Style(), 50, 4); EXPECT_EQ("mysql-js my.host.com sakila> ", prompt.render()); prompt.set_width(45); EXPECT_EQ("mysql-js my.host sakila> ", prompt.render()); prompt.set_width(40); EXPECT_EQ("mysql-js my sakila> ", prompt.render()); } // Double line { Prompt_renderer prompt(20); prompt.set_prompt("> ", "-> ", mysqlshdk::textui::Style()); prompt.set_width(100); prompt.add_segment("mysql-js", mysqlshdk::textui::Style(), 100, 7); prompt.add_segment( "my.host.com", mysqlshdk::textui::Style(), 70, 2, 0, Prompt_renderer::Shrinker_type::Truncate_on_dot_from_right); prompt.add_segment("sakila", mysqlshdk::textui::Style(), 50, 4); prompt.add_break(); EXPECT_EQ("mysql-js my.host.com sakila\n> ", prompt.render()); prompt.set_width(45); EXPECT_EQ("mysql-js my.host.com sakila\n> ", prompt.render()); prompt.set_width(40); EXPECT_EQ("mysql-js my.host.com sakila\n> ", prompt.render()); prompt.set_width(20); EXPECT_EQ("mysql-js my sakila\n> ", prompt.render()); } { Prompt_renderer prompt1(20); Prompt_renderer prompt2(20); prompt1.set_prompt("> ", "-> ", mysqlshdk::textui::Style()); prompt2.set_prompt("> ", "-> ", mysqlshdk::textui::Style()); prompt1.add_segment("a.a.a.a", mysqlshdk::textui::Style(), 20, 2, 0, Prompt_renderer::Shrinker_type::No_shrink); prompt1.add_segment( "b.b.b.b", mysqlshdk::textui::Style(), 10, 2, 0, Prompt_renderer::Shrinker_type::Truncate_on_dot_from_right); prompt1.add_segment( "c.c.c.c", mysqlshdk::textui::Style(), 30, 2, 0, Prompt_renderer::Shrinker_type::Truncate_on_dot_from_right); prompt2.add_segment("a.a.a.a", mysqlshdk::textui::Style(), 30, 2, 0, Prompt_renderer::Shrinker_type::No_shrink); prompt2.add_segment( "b.b.b.b", mysqlshdk::textui::Style(), 20, 2, 0, Prompt_renderer::Shrinker_type::Truncate_on_dot_from_right); prompt2.add_segment( "c.c.c.c", mysqlshdk::textui::Style(), 10, 2, 0, Prompt_renderer::Shrinker_type::Truncate_on_dot_from_right); prompt1.set_width(45); EXPECT_EQ("a.a.a.a b.b.b.b c.c.c.c> ", prompt1.render()); prompt2.set_width(45); EXPECT_EQ("a.a.a.a b.b.b.b c.c.c.c> ", prompt2.render()); prompt1.set_width(44); EXPECT_EQ("a.a.a.a b.b.b c.c.c.c> ", prompt1.render()); prompt2.set_width(44); EXPECT_EQ("a.a.a.a b.b.b.b c.c.c> ", prompt2.render()); prompt1.set_width(43); EXPECT_EQ("a.a.a.a b.b.b c.c.c.c> ", prompt1.render()); prompt2.set_width(43); EXPECT_EQ("a.a.a.a b.b.b.b c.c.c> ", prompt2.render()); prompt1.set_width(42); EXPECT_EQ("a.a.a.a b.b c.c.c.c> ", prompt1.render()); prompt2.set_width(42); EXPECT_EQ("a.a.a.a b.b.b.b c.c> ", prompt2.render()); prompt1.set_width(41); EXPECT_EQ("a.a.a.a b.b c.c.c.c> ", prompt1.render()); prompt2.set_width(41); EXPECT_EQ("a.a.a.a b.b.b.b c.c> ", prompt2.render()); prompt1.set_width(40); EXPECT_EQ("a.a.a.a b c.c.c.c> ", prompt1.render()); prompt2.set_width(40); EXPECT_EQ("a.a.a.a b.b.b.b c> ", prompt2.render()); prompt1.set_width(39); EXPECT_EQ("a.a.a.a b c.c.c.c> ", prompt1.render()); prompt2.set_width(39); EXPECT_EQ("a.a.a.a b.b.b.b c> ", prompt2.render()); prompt1.set_width(38); EXPECT_EQ("a.a.a.a b c.c.c> ", prompt1.render()); prompt2.set_width(38); EXPECT_EQ("a.a.a.a b.b.b c> ", prompt2.render()); prompt1.set_width(37); EXPECT_EQ("a.a.a.a b c.c.c> ", prompt1.render()); prompt2.set_width(37); EXPECT_EQ("a.a.a.a b.b.b c> ", prompt2.render()); prompt1.set_width(36); EXPECT_EQ("a.a.a.a b c.c> ", prompt1.render()); prompt2.set_width(36); EXPECT_EQ("a.a.a.a b.b c> ", prompt2.render()); prompt1.set_width(35); EXPECT_EQ("a.a.a.a b c.c> ", prompt1.render()); prompt2.set_width(35); EXPECT_EQ("a.a.a.a b.b c> ", prompt2.render()); prompt1.set_width(34); EXPECT_EQ("a.a.a.a b c> ", prompt1.render()); prompt2.set_width(34); EXPECT_EQ("a.a.a.a b c> ", prompt2.render()); prompt1.set_width(33); EXPECT_EQ("a.a.a.a b c> ", prompt1.render()); prompt2.set_width(33); EXPECT_EQ("a.a.a.a b c> ", prompt2.render()); prompt1.set_width(32); EXPECT_EQ("a.a.a.a c> ", prompt1.render()); prompt2.set_width(32); EXPECT_EQ("a.a.a.a b> ", prompt2.render()); prompt1.set_width(31); EXPECT_EQ("a.a.a.a c> ", prompt1.render()); prompt2.set_width(31); EXPECT_EQ("a.a.a.a b> ", prompt2.render()); prompt1.set_width(29); EXPECT_EQ("c.c.c.c> ", prompt1.render()); prompt2.set_width(29); EXPECT_EQ("a.a.a.a> ", prompt2.render()); prompt1.set_width(20); EXPECT_EQ("> ", prompt1.render()); prompt2.set_width(20); EXPECT_EQ("> ", prompt2.render()); } } TEST_F(Shell_prompt, color_16) { mysqlshdk::textui::set_color_capability(mysqlshdk::textui::Color_256); } TEST_F(Shell_prompt, color_24bit) { mysqlshdk::textui::set_color_capability(mysqlshdk::textui::Color_256); } TEST_F(Shell_prompt, color_i256) { mysqlshdk::textui::set_color_capability(mysqlshdk::textui::Color_256); } TEST_F(Shell_prompt, color_i256_unicode) { mysqlshdk::textui::set_color_capability(mysqlshdk::textui::Color_256); Prompt_renderer prompt; uint8_t rgb0[3] = {0, 0, 0}; prompt.set_width(60); prompt.set_prompt("> ", "-> ", mysqlshdk::textui::Style()); mysqlshdk::textui::Style red({0, 196, rgb0}, {0, 15, rgb0}); mysqlshdk::textui::Style blue({0, 21, rgb0}, {0, 15, rgb0}); prompt.add_segment("hello", red); EXPECT_EQ("\x1B[48;5;15m\x1B[38;5;196mhello\x1B[0m> ", prompt.render()); prompt.add_segment("world", blue); EXPECT_EQ( "\x1B[48;5;15m\x1B[38;5;196mhello " "\x1B[0m\x1B[48;5;15m\x1B[38;5;21mworld\x1B[0m> ", prompt.render()); prompt.clear(); prompt.set_prompt("> ", "-> ", mysqlshdk::textui::Style()); mysqlshdk::textui::Style style; style.field_mask = mysqlshdk::textui::Style::Attributes_set; style.bold = true; style.underline = false; prompt.add_segment("aa", style); style.bold = true; style.underline = true; prompt.add_segment("bb", style); style.bold = false; style.underline = true; prompt.add_segment("cc", style); style.bold = false; style.underline = false; prompt.add_segment("dd", style); EXPECT_EQ( "\x1B[1maa \x1B[0m\x1B[1;4mbb \x1B[0m\x1B[4mcc \x1B[0m\x1B[0mdd\x1B[0m> ", prompt.render()); prompt.clear(); prompt.set_prompt(shcore::Value::parse("'\\uF37A'").get_string(), "", mysqlshdk::textui::Style()); EXPECT_EQ(u8"\uF37A", prompt.render()); } TEST(Shell_prompt_manager, load) { mysqlshdk::textui::set_color_capability(mysqlshdk::textui::Color_256); Prompt_manager prompt; shcore::Value theme; std::map<std::string, std::string> vars; auto getvar = [](const std::string &, mysqlsh::Prompt_manager::Dynamic_variable_type) -> std::string { return ""; }; theme = shcore::Value::parse( "{ 'segments' : [{'text':'abab', 'padding':1}, {'text':'cd', " "'padding':0}], " "'symbols': {'separator':'|', 'separator2':'|', 'prompt':'>', " "'prompt2':'-', 'ellipsis':'-'}}"); prompt.set_theme(theme); EXPECT_EQ(" abab |cd> ", prompt.get_prompt(&vars, getvar)); theme = shcore::Value::parse( "{ 'segments' : [{'text':'abab', 'bg':'255', 'fg':'232', 'padding':1}]}"); prompt.set_theme(theme); EXPECT_EQ("\x1B[48;5;255m\x1B[38;5;232m abab \x1B[0m> ", prompt.get_prompt(&vars, getvar)); theme = shcore::Value::parse( "{ 'segments' : [{'text':'abab', 'bg':'1', 'fg':'2', 'padding':1}, " "{'bg':'3','fg':'4','text':'cd', 'padding':0}], " "'symbols': {'separator':'|', 'separator2':'|', 'prompt':'>', " "'prompt2':'-', 'ellipsis':'-'}}"); prompt.set_theme(theme); EXPECT_EQ( "\x1B[48;5;1m\x1B[38;5;2m abab " "\x1B[48;5;3m\x1B[38;5;4m|\x1B[0m\x1B[48;5;3m\x1B[38;5;4mcd\x1B[0m> ", prompt.get_prompt(&vars, getvar)); theme = shcore::Value::parse( "{ 'segments' : [{'text':'%var1%%var2%%', 'padding':1}, " "{'text':'%var3%', 'padding':0}], " "'symbols': {'separator':'|', 'separator2':'|', 'prompt':'>', " "'prompt2':'-', 'ellipsis':'-'}}"); prompt.set_theme(theme); vars["var1"] = "AAA"; vars["var2"] = "BBB"; vars["var3"] = "CCC"; EXPECT_EQ(" AAABBB% |CCC> ", prompt.get_prompt(&vars, getvar)); // check attributes theme = shcore::Value::parse( "{'segments': [{'text': 'aa', 'fg':'red', 'bg':'yellow', 'bold':true}]}"); prompt.set_theme(theme); EXPECT_EQ("\x1B[48;5;3m\x1B[38;5;1m\x1B[1maa\x1B[0m> ", prompt.get_prompt(&vars, getvar)); theme = shcore::Value::parse( "{'segments': [{'text': 'aa', 'fg':'red', 'bg':'yellow', 'bold':true}, " "{'text': 'bb'}]}"); prompt.set_theme(theme); EXPECT_EQ("\x1B[48;5;3m\x1B[38;5;1m\x1B[1maa \x1B[0mbb> ", prompt.get_prompt(&vars, getvar)); theme = shcore::Value::parse( "{'segments': [{'text': 'aa', 'fg':'red', 'bg':'yellow', 'bold':true}, " "{'text': 'bb', 'fg': ';123', 'bg':';65', 'underline':true}]}"); prompt.set_theme(theme); EXPECT_EQ( "\x1B[48;5;3m\x1B[38;5;1m\x1B[1maa " "\x1B[0m\x1B[48;5;65m\x1B[38;5;123m\x1B[4mbb\x1B[0m> ", prompt.get_prompt(&vars, getvar)); // with a prompt theme = shcore::Value::parse("{'prompt': {'text': '$$ ', 'cont_text': '@@ '}}"); prompt.set_theme(theme); EXPECT_EQ("$$ ", prompt.get_prompt(&vars, getvar)); prompt.set_is_continuing(true); EXPECT_EQ("@@ ", prompt.get_prompt(&vars, getvar)); } TEST(Shell_prompt_manager, classes) { mysqlshdk::textui::set_color_capability(mysqlshdk::textui::Color_256); Prompt_manager prompt; shcore::Value theme; std::map<std::string, std::string> vars; auto getvar = [](const std::string &, mysqlsh::Prompt_manager::Dynamic_variable_type) -> std::string { return ""; }; theme = shcore::Value::parse( "{ 'classes': {'c1':{'bg':'4','fg':'5','padding':2}}, 'segments' : " "[{'text':'aaa', 'classes':['c1']}]}"); prompt.set_theme(theme); EXPECT_EQ("\x1B[48;5;4m\x1B[38;5;5m aaa \x1B[0m> ", prompt.get_prompt(&vars, getvar)); theme = shcore::Value::parse( "{ 'classes': {'c1':{'bg':'4'},'c2':{'padding':5,'fg':'5'}}, " "'segments' : " "[{'text':'aaa', 'classes':['c1','c2']}]}"); prompt.set_theme(theme); EXPECT_EQ("\x1B[48;5;4maaa\x1B[0m> ", prompt.get_prompt(&vars, getvar)); theme = shcore::Value::parse( "{ 'classes': {'c1':{'bg':'4', 'match_tags': " "['tag']},'c2':{'padding':5,'fg':'5'}}, 'segments' : [{'text':'aaa', " "'tag':'tag', 'classes':['c1','c2']}]}"); prompt.set_theme(theme); EXPECT_EQ("\x1B[48;5;4maaa\x1B[0m> ", prompt.get_prompt(&vars, getvar)); // Bug#26501553 SHELL PRINTS: ERROR IN PROMPT THEME: BASIC_STRING::APPEND theme = shcore::Value::parse( "{ 'classes': {'c':{'padding':-1,'fg':'5'}}, " "'segments' : [{'text':'aaa', " "'classes':['c']}]}"); prompt.set_theme(theme); EXPECT_EQ( "(Error in prompt theme: Error in class c of prompt theme: padding value " "must be >= 0)>", prompt.get_prompt(&vars, getvar)); theme = shcore::Value::parse( "{ 'classes': {'c':{'min_width':-1,'fg':'5'}}, " "'segments' : [{'text':'aaa', " "'classes':['c']}]}"); prompt.set_theme(theme); EXPECT_EQ( "(Error in prompt theme: Error in class c of prompt theme: min_width " "value must be >= 0)>", prompt.get_prompt(&vars, getvar)); theme = shcore::Value::parse( "{ 'classes': {" "'c':{'min_width':1,'fg':'5'}}, 'segments' : [{'text':'aaa', " "'min_width': -1, 'classes':['c']}]}"); prompt.set_theme(theme); EXPECT_EQ("(Error in prompt theme: min_width value must be >= 0)>", prompt.get_prompt(&vars, getvar)); theme = shcore::Value::parse( "{ 'classes': {" "'c':{'min_width':1,'fg':'5'}}, 'segments' : [{'text':'aaa', " "'padding': -1, 'classes':['c']}]}"); prompt.set_theme(theme); EXPECT_EQ("(Error in prompt theme: padding value must be >= 0)>", prompt.get_prompt(&vars, getvar)); } TEST(Shell_prompt_manager, variables) { Prompt_manager promptm; Prompt_manager::Variables_map vars; EXPECT_NE("", promptm.do_apply_vars("%date%", &vars, [](const std::string &, Prompt_manager::Dynamic_variable_type) -> std::string { return ""; })); EXPECT_NE("", promptm.do_apply_vars("%time%", &vars, [](const std::string &, Prompt_manager::Dynamic_variable_type) -> std::string { return ""; })); EXPECT_EQ("%invalid%", promptm.do_apply_vars( "%invalid%", &vars, [](const std::string &, Prompt_manager::Dynamic_variable_type) -> std::string { return ""; })); EXPECT_EQ(std::string(getenv("PATH")), promptm.do_apply_vars( "%env:PATH%", &vars, [](const std::string &, Prompt_manager::Dynamic_variable_type) -> std::string { return ""; })); EXPECT_EQ("FOO", promptm.do_apply_vars( "%Sysvar:foo%", &vars, [](const std::string &var, Prompt_manager::Dynamic_variable_type type) -> std::string { if (Prompt_manager::Mysql_system_variable != type) throw std::logic_error("mismatch"); return shcore::str_upper(var); })); EXPECT_EQ(0, vars.size()); EXPECT_EQ("FOO", promptm.do_apply_vars( "%sysvar:foo%", &vars, [](const std::string &var, Prompt_manager::Dynamic_variable_type type) -> std::string { if (Prompt_manager::Mysql_system_variable != type) throw std::logic_error("mismatch"); return shcore::str_upper(var); })); EXPECT_EQ(1, vars.size()); EXPECT_EQ("BAR", promptm.do_apply_vars( "%sessvar:bar%", &vars, [](const std::string &var, Prompt_manager::Dynamic_variable_type type) -> std::string { if (Prompt_manager::Mysql_session_variable != type) throw std::logic_error("mismatch"); return shcore::str_upper(var); })); EXPECT_EQ(2, vars.size()); EXPECT_EQ("BLA", promptm.do_apply_vars( "%Status:bla%", &vars, [](const std::string &var, Prompt_manager::Dynamic_variable_type type) -> std::string { if (Prompt_manager::Mysql_status != type) throw std::logic_error("mismatch"); return shcore::str_upper(var); })); EXPECT_EQ(2, vars.size()); EXPECT_EQ("BLA", promptm.do_apply_vars( "%status:bla%", &vars, [](const std::string &var, Prompt_manager::Dynamic_variable_type type) -> std::string { if (Prompt_manager::Mysql_status != type) throw std::logic_error("mismatch"); return shcore::str_upper(var); })); EXPECT_EQ(3, vars.size()); EXPECT_EQ("ZZZ", promptm.do_apply_vars( "%sessstatus:zzz%", &vars, [](const std::string &var, Prompt_manager::Dynamic_variable_type type) -> std::string { if (Prompt_manager::Mysql_session_status != type) throw std::logic_error("mismatch"); return shcore::str_upper(var); })); EXPECT_EQ(4, vars.size()); // lowercase means cache variable, Uppercase means query every time int num_queries = 0; EXPECT_EQ("VARNAME", promptm.do_apply_vars( "%sessstatus:varname%", &vars, [&num_queries]( const std::string &var, Prompt_manager::Dynamic_variable_type type) -> std::string { if (Prompt_manager::Mysql_session_status != type) throw std::logic_error("mismatch"); num_queries++; return shcore::str_upper(var); })); // first time, so not cached yet, so hits a query EXPECT_EQ(1, num_queries); EXPECT_EQ(5, vars.size()); EXPECT_EQ("VARNAME", promptm.do_apply_vars( "%sessstatus:varname%", &vars, [&num_queries]( const std::string &var, Prompt_manager::Dynamic_variable_type type) -> std::string { if (Prompt_manager::Mysql_session_status != type) throw std::logic_error("mismatch"); num_queries++; return shcore::str_upper(var); })); // this time it's cached EXPECT_EQ(1, num_queries); EXPECT_EQ(5, vars.size()); EXPECT_EQ("VARNAME", promptm.do_apply_vars( "%Sessstatus:varname%", &vars, [&num_queries]( const std::string &var, Prompt_manager::Dynamic_variable_type type) -> std::string { if (Prompt_manager::Mysql_session_status != type) throw std::logic_error("mismatch"); num_queries++; return shcore::str_upper(var); })); // Uppercase, so skip cache EXPECT_EQ(2, num_queries); EXPECT_EQ(5, vars.size()); } TEST(Shell_prompt_manager, custom_variable) { auto theme = shcore::Value::parse( "{'variables' : " " {'test' : {'match' : {'value':'%value%', 'pattern':'%pattern%'}, " " 'if_true': '%true%', " " 'if_false':'%false%'}}, " "'segments':[{'text':'%test%'}]}"); Prompt_manager prompt; prompt.set_theme(theme); std::map<std::string, std::string> vars; vars["true"] = "IS_TRUE"; vars["false"] = "IS_FALSE"; vars["value"] = "FOOOOOOOBAR"; vars["pattern"] = "FOO*BAR"; auto getvar = [](const std::string &, mysqlsh::Prompt_manager::Dynamic_variable_type) -> std::string { return ""; }; EXPECT_EQ("IS_TRUE> ", prompt.get_prompt(&vars, getvar)); vars["value"] = "FOOOOOOOBAR"; vars["pattern"] = "FOOBAR"; EXPECT_EQ("IS_FALSE> ", prompt.get_prompt(&vars, getvar)); vars["value"] = "XBAR"; vars["pattern"] = "?BAR"; EXPECT_EQ("IS_TRUE> ", prompt.get_prompt(&vars, getvar)); } TEST(Shell_prompt_manager, custom_variables_bug26502508) { auto theme = shcore::Value::parse(R"##({ 'variables' : {'test' : {'match' : {'value': '1', 'pattern': '1'}, 'if_true': '%test%', 'if_false':'%test%' } }, 'segments':[{'text':'%test%'}] })##"); auto theme2 = shcore::Value::parse(R"##({'variables' : {'test' : {'match' : {'value': '1', 'pattern': '1'}, 'if_true': '%test2%', 'if_false':'%test2%' }, 'test2' : {'match' : {'value': '1', 'pattern': '1'}, 'if_true': '%test%', 'if_false':'%test%' } }, 'segments':[{'text':'%test%'}] })##"); Prompt_manager prompt; std::map<std::string, std::string> vars; vars["value"] = "FOOOOOOOBAR"; vars["pattern"] = "FOO*BAR"; auto getvar = [](const std::string &, mysqlsh::Prompt_manager::Dynamic_variable_type) -> std::string { return ""; }; prompt.set_theme(theme); EXPECT_EQ("<<Recursion detected during variable evaluation>>> ", prompt.get_prompt(&vars, getvar)); prompt.set_theme(theme2); EXPECT_EQ("<<Recursion detected during variable evaluation>>> ", prompt.get_prompt(&vars, getvar)); } TEST(Shell_prompt_manager, misc) { // check default style Prompt_manager promptm; Prompt_manager::Variables_map vmap; vmap["host"] = "localhost"; vmap["schema"] = ""; vmap["mode"] = "js"; EXPECT_EQ( "mysql-js []> ", promptm.get_prompt(&vmap, Prompt_manager::Dynamic_variable_callback())); vmap["schema"] = "sakila"; vmap["mode"] = "js"; EXPECT_EQ( "mysql-js [sakila]> ", promptm.get_prompt(&vmap, Prompt_manager::Dynamic_variable_callback())); // check whether errors during render are properly caught vmap["schema"] = "sakila"; vmap["mode"] = "js"; EXPECT_NO_THROW(promptm.get_prompt( &vmap, [](const std::string &, Prompt_manager::Dynamic_variable_type) -> std::string { throw std::runtime_error("catchme"); })); // bad theme promptm.set_theme(shcore::Value::parse("{'segments': 'bla'}")); EXPECT_EQ("> ", promptm.get_prompt( &vmap, Prompt_manager::Dynamic_variable_callback())); } TEST(Shell_prompt_manager, attributes_other) { mysqlshdk::textui::set_color_capability(mysqlshdk::textui::Color_256); shcore::Value theme; { Prompt_manager::Attributes attr; theme = shcore::Value::parse("{'text':'a'}"); EXPECT_NO_THROW(attr.load(theme.as_map())); EXPECT_EQ("a", attr.text); EXPECT_FALSE(attr.sep); EXPECT_EQ(2, attr.min_width); EXPECT_EQ(0, attr.padding); EXPECT_EQ(Prompt_renderer::Shrinker_type::No_shrink, attr.shrink); } { Prompt_manager::Attributes attr; theme = shcore::Value::parse("{'separator':'#'}"); EXPECT_NO_THROW(attr.load(theme.as_map())); EXPECT_EQ("", attr.text); EXPECT_EQ("#", *attr.sep); EXPECT_EQ(2, attr.min_width); EXPECT_EQ(0, attr.padding); EXPECT_EQ(Prompt_renderer::Shrinker_type::No_shrink, attr.shrink); } { Prompt_manager::Attributes attr; theme = shcore::Value::parse("{'padding':4}"); EXPECT_NO_THROW(attr.load(theme.as_map())); EXPECT_EQ("", attr.text); EXPECT_FALSE(attr.sep); EXPECT_EQ(2, attr.min_width); EXPECT_EQ(4, attr.padding); EXPECT_EQ(Prompt_renderer::Shrinker_type::No_shrink, attr.shrink); } { Prompt_manager::Attributes attr; theme = shcore::Value::parse("{'min_width':40}"); EXPECT_NO_THROW(attr.load(theme.as_map())); EXPECT_EQ("", attr.text); EXPECT_FALSE(attr.sep); EXPECT_EQ(40, attr.min_width); EXPECT_EQ(0, attr.padding); EXPECT_EQ(Prompt_renderer::Shrinker_type::No_shrink, attr.shrink); } { Prompt_manager::Attributes attr; theme = shcore::Value::parse("{'shrink':'none'}"); EXPECT_NO_THROW(attr.load(theme.as_map())); EXPECT_EQ("", attr.text); EXPECT_FALSE(attr.sep); EXPECT_EQ(2, attr.min_width); EXPECT_EQ(0, attr.padding); EXPECT_EQ(Prompt_renderer::Shrinker_type::No_shrink, attr.shrink); } { Prompt_manager::Attributes attr; theme = shcore::Value::parse("{'shrink':'ellipsize'}"); EXPECT_NO_THROW(attr.load(theme.as_map())); EXPECT_EQ("", attr.text); EXPECT_FALSE(attr.sep); EXPECT_EQ(2, attr.min_width); EXPECT_EQ(0, attr.padding); EXPECT_EQ(Prompt_renderer::Shrinker_type::Ellipsize_on_char, attr.shrink); } { Prompt_manager::Attributes attr; theme = shcore::Value::parse("{'shrink':'truncate_on_dot'}"); EXPECT_NO_THROW(attr.load(theme.as_map())); EXPECT_EQ("", attr.text); EXPECT_FALSE(attr.sep); EXPECT_EQ(2, attr.min_width); EXPECT_EQ(0, attr.padding); EXPECT_EQ(Prompt_renderer::Shrinker_type::Truncate_on_dot_from_right, attr.shrink); } { Prompt_manager::Attributes attr; theme = shcore::Value::parse("{'shrink':'eqwewq'}"); EXPECT_THROW(attr.load(theme.as_map()), std::invalid_argument); } { Prompt_manager::Attributes attr; theme = shcore::Value::parse("{'shrink':123}"); EXPECT_THROW(attr.load(theme.as_map()), std::exception); } } // ---------------------------------------------------------------------------- class Shell_prompt_exe : public tests::Command_line_test { public: static void SetUpTestCase() { shcore::create_file( shcore::path::join_path(shcore::get_user_config_path(), "prompt.json"), "{\"segments\":[{\"text\":\"TEST_PROMPT\"}]}"); shcore::create_file("altprompt.json", "{\"segments\":[{\"text\":\"ALT_PROMPT\"}]}"); shcore::create_file("badprompt.json", "\"segments\":[{\"text\":\"ALT_PROMPT\"}]}"); } static void TearDownTestCase() { shcore::delete_file( shcore::path::join_path(shcore::get_user_config_path(), "prompt.json")); shcore::delete_file("altprompt.json"); shcore::delete_file("badprompt.json"); } }; TEST_F(Shell_prompt_exe, environment) { #ifdef HAVE_JS const auto expect_prompt = "mysql-js>"; #else const auto expect_prompt = "mysql-py>"; #endif std::string no_theme_prompt = expect_prompt; // by default this prompt is not necessarily the same as expect_prompt shcore::unsetenv("MYSQLSH_PROMPT_THEME"); execute({_mysqlsh, "--interactive=full", "-e", "1", nullptr}); const auto position = _output.rfind("\n"); if (position != std::string::npos) { no_theme_prompt = _output.substr(position + 1); } // TS_CP#1 set MYSQLSH_PROMPT_THEME environment variable to a file name , it // configures prompt correctly accordingly to file shcore::setenv("MYSQLSH_PROMPT_THEME", "altprompt.json"); execute({_mysqlsh, "--interactive=full", "-e", "1", nullptr}); MY_EXPECT_CMD_OUTPUT_CONTAINS("ALT_PROMPT> 1\n1\nALT_PROMPT> "); shcore::unsetenv("MYSQLSH_PROMPT_THEME"); // TS_CP#6 On startup, if an error is found in the prompt file, an error // message will be printed and a default prompt will be used. // TS_EV#1 invalid values on MYSQLSH_PROMPT_THEME= will force the shell to use // a default prompt with no colors and log an error to the log file and also // to the terminal, during startup. shcore::setenv("MYSQLSH_PROMPT_THEME", "badprompt.json"); execute({_mysqlsh, "--interactive=full", "-e", "1", nullptr}); MY_EXPECT_CMD_OUTPUT_CONTAINS("Error loading prompt theme 'badprompt.json'"); MY_EXPECT_CMD_OUTPUT_CONTAINS(expect_prompt); shcore::unsetenv("MYSQLSH_PROMPT_THEME"); // no prompt theme execute({_mysqlsh, "--interactive=full", "-e", "1", nullptr}); MY_EXPECT_CMD_OUTPUT_CONTAINS(no_theme_prompt); // TS_EV#3 MYSQLSH_TERM_COLOR_MODE= with invalid value will force the shell to // use a default prompt with no colors and log an error to the log file and // also to the terminal, during startup. shcore::setenv("MYSQLSH_TERM_COLOR_MODE", "bla"); execute({_mysqlsh, "--interactive=full", "-e", "1", nullptr}); MY_EXPECT_CMD_OUTPUT_CONTAINS( "MYSQLSH_TERM_COLOR_MODE environment variable set to invalid value. "); MY_EXPECT_CMD_OUTPUT_CONTAINS(expect_prompt); shcore::unsetenv("MYSQLSH_TERM_COLOR_MODE"); } TEST_F(Shell_prompt_exe, histignore) { // TS_CLO#1 (combined with other tests in Cmdline_shell) // TS_CLO#2 execute({_mysqlsh, "--histignore=foobar", "-e", "print(shell.options['history.sql.ignorePattern']);", nullptr}); MY_EXPECT_CMD_OUTPUT_CONTAINS("foobar"); } // the last line output is the prompt we will check #define EXPECT_PROMPT(prompt) \ ASSERT_TRUE(_output.rfind('\n') != std::string::npos); \ EXPECT_EQ(prompt, _output.substr(_output.rfind('\n') + 1)); namespace { size_t second_to_last_line(const std::string &s) { auto p = s.rfind('\n'); if (p != std::string::npos) { p = s.substr(0, p).rfind('\n'); } return p; } } // namespace #define EXPECT_DBL_PROMPT(prompt) \ ASSERT_TRUE(second_to_last_line(_output) != std::string::npos); \ EXPECT_EQ(prompt, _output.substr(second_to_last_line(_output) + 1)); TEST_F(Shell_prompt_exe, prompt_variables) { std::string segs; for (const char *s : {"host", "port", "mode", "Mode", "uri", "user", "schema", "ssl", "date", "env:MYSQLSH_PROMPT_THEME", "sysvar:autocommit", "Sessvar:autocommit", "sessstatus:Mysqlx_ssl_active"}) { segs.append(shcore::str_format( "{\"text\": \"%s=%%%s%%\", \"separator\":\" \"}, ", s, s)); } segs.append(shcore::str_format("{\"text\": \"END\"}")); shcore::create_file("allvars.json", "{\"segments\": [" + segs + "]}"); shcore::setenv("MYSQLSH_PROMPT_THEME", "allvars.json"); using mysqlshdk::utils::fmttime; int rc; std::string uri = _user + "@" + _host; wipe_out(); rc = execute({_mysqlsh, "--interactive=full", "--sql", "-e", "1;", nullptr}); EXPECT_EQ(0, rc); EXPECT_PROMPT( "host= port= mode=sql Mode=SQL uri= user= schema= ssl= date=" + fmttime("%F") + " env:MYSQLSH_PROMPT_THEME=allvars.json " "sysvar:autocommit= Sessvar:autocommit= " "sessstatus:Mysqlx_ssl_active= END> "); wipe_out(); rc = execute({_mysqlsh, "--interactive=full", "--py", "-e", "1", nullptr}); EXPECT_EQ(0, rc); EXPECT_PROMPT( "host= port= mode=py Mode=Py uri= user= schema= ssl= date=" + fmttime("%F") + " env:MYSQLSH_PROMPT_THEME=allvars.json " "sysvar:autocommit= Sessvar:autocommit= " "sessstatus:Mysqlx_ssl_active= END> "); wipe_out(); rc = execute({_mysqlsh, "--interactive=full", _mysql_uri.c_str(), "--ssl-mode=REQUIRED", "--sql", "-e", "set autocommit=0;", nullptr}); EXPECT_EQ(0, rc); EXPECT_PROMPT( "host=" + _host + " port=" + _mysql_port + " mode=sql" + " Mode=SQL uri=" + uri + ":" + _mysql_port + " user=root schema= ssl=SSL date=" + fmttime("%F") + " env:MYSQLSH_PROMPT_THEME=allvars.json sysvar:autocommit=ON " "Sessvar:autocommit=OFF " "sessstatus:Mysqlx_ssl_active= END> "); wipe_out(); rc = execute({_mysqlsh, "--interactive=full", _mysql_uri.c_str(), "--ssl-mode=DISABLED", "--sql", "-e", "set autocommit=0;", nullptr}); EXPECT_EQ(0, rc); EXPECT_PROMPT( "host=" + _host + " port=" + _mysql_port + " mode=sql" + " Mode=SQL uri=" + uri + ":" + _mysql_port + " user=root schema= ssl= date=" + fmttime("%F") + " env:MYSQLSH_PROMPT_THEME=allvars.json sysvar:autocommit=ON " "Sessvar:autocommit=OFF " "sessstatus:Mysqlx_ssl_active= END> "); // This test only works if the test server is listening on default socket path if (0) { wipe_out(); rc = execute({_mysqlsh, "--interactive=full", uri.c_str(), "--password=", "--mysql", "--ssl-mode=DISABLED", "--sql", "-e", "set autocommit=0;", nullptr}); EXPECT_EQ(0, rc); EXPECT_PROMPT( "host=" + _host + " port=" + " mode=sql" + " Mode=SQL uri=" + uri + " user=root schema= ssl= date=" + fmttime("%F") + " env:MYSQLSH_PROMPT_THEME=allvars.json sysvar:autocommit=ON " "Sessvar:autocommit=OFF " "sessstatus:Mysqlx_ssl_active= END> "); } wipe_out(); rc = execute({_mysqlsh, "--interactive=full", "--sql", _uri.c_str(), "--schema=mysql", "--ssl-mode=REQUIRED", "-e", "set autocommit=0;", nullptr}); EXPECT_EQ(0, rc); EXPECT_PROMPT("host=" + _host + " port=" + _port + " mode=sql Mode=SQL uri=" + uri + ":" + _port + " user=root schema=mysql ssl=SSL" + " date=" + fmttime("%F") + " env:MYSQLSH_PROMPT_THEME=allvars.json " "sysvar:autocommit=ON Sessvar:autocommit=OFF " "sessstatus:Mysqlx_ssl_active=ON END> "); if (_port == "33060") { wipe_out(); rc = execute({_mysqlsh, "--interactive=full", "--sql", uri.c_str(), "--password=", "--mysqlx", "--schema=mysql", "--ssl-mode=REQUIRED", "-e", "set autocommit=0;", nullptr}); EXPECT_EQ(0, rc); EXPECT_PROMPT("host=" + _host + " port=" + _port + " mode=sql Mode=SQL uri=" + uri + ":" + _port + " user=root schema=mysql ssl=SSL" + " date=" + fmttime("%F") + " env:MYSQLSH_PROMPT_THEME=allvars.json " "sysvar:autocommit=ON Sessvar:autocommit=OFF " "sessstatus:Mysqlx_ssl_active=ON END> "); } shcore::delete_file("allvars.json"); shcore::unsetenv("MYSQLSH_PROMPT_THEME"); } // Test sample prompts (which in turn would test the whole thing) TEST_F(Shell_prompt_exe, sample_prompt_theme_nocolor) { shcore::setenv("MYSQLSH_PROMPT_THEME", shcore::get_binary_folder() + "/prompt_nocolor.json"); shcore::setenv("MYSQLSH_TERM_COLOR_MODE", "nocolor"); int rc = execute({_mysqlsh, "--interactive=full", _uri.c_str(), "--schema=mysql", "--ssl-mode=REQUIRED", "-e", "1", nullptr}); EXPECT_EQ(0, rc); std::cout << _output << "\n"; #ifdef HAVE_JS EXPECT_PROMPT("MySQL [" + _host + ":" + _port + "+ ssl/mysql] JS> "); #else EXPECT_PROMPT("MySQL [" + _host + ":" + _port + "+ ssl/mysql] Py> "); #endif shcore::unsetenv("MYSQLSH_PROMPT_THEME"); shcore::unsetenv("MYSQLSH_TERM_COLOR_MODE"); } TEST_F(Shell_prompt_exe, sample_prompt_theme_16) { shcore::setenv("MYSQLSH_PROMPT_THEME", shcore::get_binary_folder() + "/prompt_16.json"); shcore::setenv("MYSQLSH_TERM_COLOR_MODE", "16"); int rc = execute({_mysqlsh, "--interactive=full", _uri.c_str(), "--schema=mysql", "--ssl-mode=REQUIRED", "-e", "1", nullptr}); EXPECT_EQ(0, rc); std::cout << _output << "\n"; #ifdef HAVE_JS EXPECT_PROMPT("MySQL \x1B[1m[" + _host + "+ ssl/mysql] \x1B[0mJS> "); #else EXPECT_PROMPT("MySQL \x1B[1m[" + _host + "+ ssl/mysql] \x1B[0mPy> "); #endif shcore::unsetenv("MYSQLSH_PROMPT_THEME"); shcore::unsetenv("MYSQLSH_TERM_COLOR_MODE"); } TEST_F(Shell_prompt_exe, sample_prompt_theme_256) { shcore::setenv("MYSQLSH_PROMPT_THEME", shcore::get_binary_folder() + "/prompt_256.json"); shcore::setenv("MYSQLSH_TERM_COLOR_MODE", "256"); int rc = execute({_mysqlsh, "--interactive=full", _uri.c_str(), "--schema=mysql", "--ssl-mode=REQUIRED", "-e", "1", nullptr}); EXPECT_EQ(0, rc); std::cout << _output << "\n"; #ifdef HAVE_JS EXPECT_PROMPT( "\x1B[48;5;254m\x1B[38;5;23m My\x1B[0m\x1B[48;5;254m\x1B[38;5;166mSQL " "\x1B[0m\x1B[48;5;237m\x1B[38;5;15m " + _host + ":" + _port + "+ ssl " "\x1B[0m\x1B[48;5;242m\x1B[38;5;15m mysql " "\x1B[0m\x1B[48;5;221m\x1B[38;5;0m JS \x1B[0m\x1B[48;5;0m> \x1B[0m"); #else EXPECT_PROMPT( "\x1B[48;5;254m\x1B[38;5;23m My\x1B[0m\x1B[48;5;254m\x1B[38;5;166mSQL " "\x1B[0m\x1B[48;5;237m\x1B[38;5;15m " + _host + ":" + _port + "+ ssl " "\x1B[0m\x1B[48;5;242m\x1B[38;5;15m mysql " "\x1B[0m\x1B[48;5;25m\x1B[38;5;15m Py \x1B[0m\x1B[48;5;0m> \x1B[0m"); #endif shcore::unsetenv("MYSQLSH_PROMPT_THEME"); shcore::unsetenv("MYSQLSH_TERM_COLOR_MODE"); } TEST_F(Shell_prompt_exe, sample_prompt_theme_dbl_256) { shcore::setenv("MYSQLSH_PROMPT_THEME", shcore::get_binary_folder() + "/prompt_dbl_256.json"); shcore::setenv("MYSQLSH_TERM_COLOR_MODE", "256"); int rc = execute({_mysqlsh, "--interactive=full", _uri.c_str(), "--schema=mysql", "--ssl-mode=REQUIRED", "-e", "1", nullptr}); EXPECT_EQ(0, rc); std::cout << _output << "\n"; #ifdef HAVE_JS EXPECT_DBL_PROMPT( "\x1B[48;5;254m\x1B[38;5;23m My\x1B[0m\x1B[48;5;254m\x1B[38;5;166mSQL " "\x1B[0m\x1B[48;5;237m\x1B[38;5;15m " + _host + ":" + _port + "+ ssl " "\x1B[0m\x1B[48;5;242m\x1B[38;5;15m mysql " "\x1B[0m\x1B[48;5;221m\x1B[38;5;0m JS \x1B[0m\n\x1B[48;5;0m > \x1B[0m"); #else EXPECT_DBL_PROMPT( "\x1B[48;5;254m\x1B[38;5;23m My\x1B[0m\x1B[48;5;254m\x1B[38;5;166mSQL " "\x1B[0m\x1B[48;5;237m\x1B[38;5;15m " + _host + ":" + _port + "+ ssl " "\x1B[0m\x1B[48;5;242m\x1B[38;5;15m mysql " "\x1B[0m\x1B[48;5;25m\x1B[38;5;15m Py \x1B[0m\n\x1B[48;5;0m > \x1B[0m"); #endif shcore::unsetenv("MYSQLSH_PROMPT_THEME"); shcore::unsetenv("MYSQLSH_TERM_COLOR_MODE"); } TEST_F(Shell_prompt_exe, sample_prompt_theme_256pl) { shcore::setenv("MYSQLSH_PROMPT_THEME", shcore::get_binary_folder() + "/prompt_256pl.json"); shcore::setenv("MYSQLSH_TERM_COLOR_MODE", "256"); int rc = execute({_mysqlsh, "--interactive=full", _uri.c_str(), "--schema=mysql", "--ssl-mode=REQUIRED", "-e", "1", nullptr}); EXPECT_EQ(0, rc); std::cout << _output << "\n"; #ifdef HAVE_JS EXPECT_PROMPT( "\x1B[48;5;254m\x1B[38;5;23m My\x1B[0m\x1B[48;5;254m\x1B[38;5;166mSQL " "\x1B[48;5;237m\x1B[38;5;254m\xEE\x82\xB0\x1B[0m\x1B[48;5;237m\x1B[38;5;" "15m " + _host + ":" + _port + "+ \xEE\x82\xA2 " "\x1B[48;5;242m\x1B[38;5;237m\xEE\x82\xB0\x1B[0m\x1B[48;5;242m\x1B[38;5;" "15m mysql " "\x1B[48;5;221m\x1B[38;5;242m\xEE\x82\xB0\x1B[0m\x1B[48;5;221m\x1B[38;5;" "0m JS \x1B[0m\x1B[48;5;0m\x1B[38;5;221m\xEE\x82\xB0 \x1B[0m"); #else EXPECT_PROMPT( "\x1B[48;5;254m\x1B[38;5;23m My\x1B[0m\x1B[48;5;254m\x1B[38;5;166mSQL " "\x1B[48;5;237m\x1B[38;5;254m\xEE\x82\xB0\x1B[0m\x1B[48;5;237m\x1B[38;5;" "15m " + _host + ":" + _port + "+ \xEE\x82\xA2 " "\x1B[48;5;242m\x1B[38;5;237m\xEE\x82\xB0\x1B[0m\x1B[48;5;242m\x1B[38;5;" "15m mysql " "\x1B[48;5;25m\x1B[38;5;242m\xEE\x82\xB0\x1B[0m\x1B[48;5;25m\x1B[38;5;" "15m Py \x1B[0m\x1B[48;5;0m\x1B[38;5;25m\xEE\x82\xB0 \x1B[0m"); #endif shcore::unsetenv("MYSQLSH_PROMPT_THEME"); shcore::unsetenv("MYSQLSH_TERM_COLOR_MODE"); } #ifdef HAVE_JS TEST_F(Shell_prompt_exe, bug28314383_js) { static constexpr auto k_file = "close.js"; shcore::setenv("MYSQLSH_PROMPT_THEME", shcore::get_binary_folder() + "/prompt_nocolor.json"); shcore::setenv("MYSQLSH_TERM_COLOR_MODE", "nocolor"); shcore::create_file(k_file, "session.close();\n" "\\connect " + _uri + "?ssl-mode=REQUIRED\n" "session.close();\n"); int rc = execute({_mysqlsh, "--interactive=full", _uri.c_str(), "--schema=mysql", "--ssl-mode=REQUIRED", "--js", nullptr}, nullptr, k_file); EXPECT_EQ(0, rc); std::cout << _output << "\n"; MY_EXPECT_CMD_OUTPUT_CONTAINS("MySQL [" + _host + ":" + _port + "+ ssl/mysql] JS> session.close();\n" "MySQL JS> \\connect " + _uri + "?ssl-mode=REQUIRED\n"); MY_EXPECT_CMD_OUTPUT_CONTAINS("MySQL [" + _host + ":" + _port + "+ ssl] JS> session.close();\n" "MySQL JS> Bye!"); shcore::unsetenv("MYSQLSH_PROMPT_THEME"); shcore::unsetenv("MYSQLSH_TERM_COLOR_MODE"); shcore::delete_file(k_file); } #endif // HAVE_JS #ifdef HAVE_PYTHON TEST_F(Shell_prompt_exe, bug28314383_py) { static constexpr auto k_file = "close.py"; shcore::setenv("MYSQLSH_PROMPT_THEME", shcore::get_binary_folder() + "/prompt_nocolor.json"); shcore::setenv("MYSQLSH_TERM_COLOR_MODE", "nocolor"); shcore::create_file(k_file, "session.close();\n" "\\connect " + _uri + "?ssl-mode=REQUIRED\n" "session.close();\n"); int rc = execute({_mysqlsh, "--interactive=full", _uri.c_str(), "--schema=mysql", "--ssl-mode=REQUIRED", "--py", nullptr}, nullptr, k_file); EXPECT_EQ(0, rc); std::cout << _output << "\n"; MY_EXPECT_CMD_OUTPUT_CONTAINS("MySQL [" + _host + ":" + _port + "+ ssl/mysql] Py> session.close();\n" "MySQL Py> \\connect " + _uri + "?ssl-mode=REQUIRED\n"); MY_EXPECT_CMD_OUTPUT_CONTAINS("MySQL [" + _host + ":" + _port + "+ ssl] Py> session.close();\n" "MySQL Py> Bye!"); shcore::unsetenv("MYSQLSH_PROMPT_THEME"); shcore::unsetenv("MYSQLSH_TERM_COLOR_MODE"); shcore::delete_file(k_file); } #endif // HAVE_PYTHON TEST_F(Shell_prompt_exe, bug30406283) { const std::string prompt_file = "invalid-utf8.json"; const auto expect_prompt = #ifdef HAVE_JS "mysql-js> " #else "mysql-py> " #endif "1"; shcore::setenv("MYSQLSH_PROMPT_THEME", prompt_file); shcore::setenv("MYSQLSH_TERM_COLOR_MODE", "nocolor"); shcore::create_file(prompt_file, "{'prompt':{'text': '\xFF '}}"); EXPECT_EQ(0, execute({_mysqlsh, "--interactive=full", "-e", "1", nullptr})); MY_EXPECT_CMD_OUTPUT_CONTAINS("Error loading prompt theme '" + prompt_file + "': File contains invalid UTF-8 sequence."); MY_EXPECT_CMD_OUTPUT_CONTAINS(expect_prompt); shcore::unsetenv("MYSQLSH_PROMPT_THEME"); shcore::unsetenv("MYSQLSH_TERM_COLOR_MODE"); shcore::delete_file(prompt_file); } #undef EXPECT_PROMPT } // namespace mysqlsh