1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "gtest/gtest.h"
7 
8 #include "mozilla/AssembleCmdLine.h"
9 #include "mozilla/CmdLineAndEnvUtils.h"
10 #include "mozilla/UniquePtrExtensions.h"
11 #include "WinRemoteMessage.h"
12 
13 using namespace mozilla;
14 
15 template <typename T>
16 struct TestCase {
17   const T* mArgs[4];
18   const wchar_t* mExpected;
19 };
20 
21 #define ALPHA_IN_UTF8 "\xe3\x82\xa2\xe3\x83\xab\xe3\x83\x95\xe3\x82\xa1"
22 #define OMEGA_IN_UTF8 "\xe3\x82\xaa\xe3\x83\xa1\xe3\x82\xac"
23 #define ALPHA_IN_UTF16 L"\u30A2\u30EB\u30D5\u30A1"
24 #define OMEGA_IN_UTF16 L"\u30AA\u30E1\u30AC"
25 #define UPPER_CYRILLIC_P_IN_UTF8 "\xd0\xa0"
26 #define LOWER_CYRILLIC_P_IN_UTF8 "\xd1\x80"
27 #define UPPER_CYRILLIC_P_IN_UTF16 L"\u0420"
28 #define LOWER_CYRILLIC_P_IN_UTF16 L"\u0440"
29 
30 TestCase<char> testCases[] = {
31     // Copied from TestXREMakeCommandLineWin.ini
32     {{"a:\\", nullptr}, L"a:\\"},
33     {{"a:\"", nullptr}, L"a:\\\""},
34     {{"a:\\b c", nullptr}, L"\"a:\\b c\""},
35     {{"a:\\b c\"", nullptr}, L"\"a:\\b c\\\"\""},
36     {{"a:\\b c\\d e", nullptr}, L"\"a:\\b c\\d e\""},
37     {{"a:\\b c\\d e\"", nullptr}, L"\"a:\\b c\\d e\\\"\""},
38     {{"a:\\", nullptr}, L"a:\\"},
39     {{"a:\"", "b:\\c d", nullptr}, L"a:\\\" \"b:\\c d\""},
40     {{"a", "b:\" c:\\d", "e", nullptr}, L"a \"b:\\\" c:\\d\" e"},
41     {{"abc", "d", "e", nullptr}, L"abc d e"},
42     {{"a b c", "d", "e", nullptr}, L"\"a b c\" d e"},
43     {{"a\\\\\\b", "de fg", "h", nullptr}, L"a\\\\\\b \"de fg\" h"},
44     {{"a", "b", nullptr}, L"a b"},
45     {{"a\tb", nullptr}, L"\"a\tb\""},
46     {{"a\\\"b", "c", "d", nullptr}, L"a\\\\\\\"b c d"},
47     {{"a\\\"b", "c", nullptr}, L"a\\\\\\\"b c"},
48     {{"a\\\\\\b c", nullptr}, L"\"a\\\\\\b c\""},
49     {{"\"a", nullptr}, L"\\\"a"},
50     {{"\\a", nullptr}, L"\\a"},
51     {{"\\\\\\a", nullptr}, L"\\\\\\a"},
52     {{"\\\\\\\"a", nullptr}, L"\\\\\\\\\\\\\\\"a"},
53     {{"a\\\"b c\" d e", nullptr}, L"\"a\\\\\\\"b c\\\" d e\""},
54     {{"a\\\\\"b", "c d e", nullptr}, L"a\\\\\\\\\\\"b \"c d e\""},
55     {{"a:\\b", "c\\" ALPHA_IN_UTF8, OMEGA_IN_UTF8 "\\d", nullptr},
56      L"a:\\b c\\" ALPHA_IN_UTF16 L" " OMEGA_IN_UTF16 L"\\d"},
57     {{"a:\\b", "c\\" ALPHA_IN_UTF8 " " OMEGA_IN_UTF8 "\\d", nullptr},
58      L"a:\\b \"c\\" ALPHA_IN_UTF16 L" " OMEGA_IN_UTF16 L"\\d\""},
59     {{ALPHA_IN_UTF8, OMEGA_IN_UTF8, nullptr},
60      ALPHA_IN_UTF16 L" " OMEGA_IN_UTF16},
61 
62     // More single-argument cases
63     {{"", nullptr}, L""},
64     {{"a\fb", nullptr}, L"\"a\fb\""},
65     {{"a\nb", nullptr}, L"\"a\nb\""},
66     {{"a\rb", nullptr}, L"\"a\rb\""},
67     {{"a\vb", nullptr}, L"\"a\vb\""},
68     {{"\"a\" \"b\"", nullptr}, L"\"\\\"a\\\" \\\"b\\\"\""},
69     {{"\"a\\b\" \"c\\d\"", nullptr}, L"\"\\\"a\\b\\\" \\\"c\\d\\\"\""},
70     {{"\\\\ \\\\", nullptr}, L"\"\\\\ \\\\\\\\\""},
71     {{"\"\" \"\"", nullptr}, L"\"\\\"\\\" \\\"\\\"\""},
72     {{ALPHA_IN_UTF8 "\\" OMEGA_IN_UTF8, nullptr},
73      ALPHA_IN_UTF16 L"\\" OMEGA_IN_UTF16},
74     {{ALPHA_IN_UTF8 " " OMEGA_IN_UTF8, nullptr},
75      L"\"" ALPHA_IN_UTF16 L" " OMEGA_IN_UTF16 L"\""},
76 };
77 
TEST(AssembleCommandLineWin,assembleCmdLine)78 TEST(AssembleCommandLineWin, assembleCmdLine)
79 {
80   for (const auto& testCase : testCases) {
81     UniqueFreePtr<wchar_t> assembled;
82     wchar_t* assembledRaw = nullptr;
83     EXPECT_EQ(assembleCmdLine(testCase.mArgs, &assembledRaw, CP_UTF8), 0);
84     assembled.reset(assembledRaw);
85 
86     EXPECT_STREQ(assembled.get(), testCase.mExpected);
87   }
88 }
89 
TEST(CommandLineParserWin,HandleCommandLine)90 TEST(CommandLineParserWin, HandleCommandLine)
91 {
92   CommandLineParserWin<char> parser;
93   for (const auto& testCase : testCases) {
94     NS_ConvertUTF16toUTF8 utf8(testCase.mExpected);
95     parser.HandleCommandLine(utf8);
96 
97     if (utf8.Length() == 0) {
98       EXPECT_EQ(parser.Argc(), 0);
99       continue;
100     }
101 
102     for (int i = 0; i < parser.Argc(); ++i) {
103       EXPECT_NE(testCase.mArgs[i], nullptr);
104       EXPECT_STREQ(parser.Argv()[i], testCase.mArgs[i]);
105     }
106     EXPECT_EQ(testCase.mArgs[parser.Argc()], nullptr);
107   }
108 }
109 
TEST(WinRemoteMessage,SendReceive)110 TEST(WinRemoteMessage, SendReceive)
111 {
112   const char kCommandline[] =
113       "dummy.exe /arg1 --arg2 \"3rd arg\" "
114       "4th=\"" UPPER_CYRILLIC_P_IN_UTF8 " " LOWER_CYRILLIC_P_IN_UTF8 "\"";
115   const wchar_t kCommandlineW[] =
116       L"dummy.exe /arg1 --arg2 \"3rd arg\" "
117       L"4th=\"" UPPER_CYRILLIC_P_IN_UTF16 L" " LOWER_CYRILLIC_P_IN_UTF16 L"\"";
118   const wchar_t* kExpectedArgsW[] = {
119       L"-arg1", L"-arg2", L"3rd arg",
120       L"4th=" UPPER_CYRILLIC_P_IN_UTF16 L" " LOWER_CYRILLIC_P_IN_UTF16};
121 
122   char workingDirA[MAX_PATH];
123   wchar_t workingDirW[MAX_PATH];
124   EXPECT_NE(getcwd(workingDirA, MAX_PATH), nullptr);
125   EXPECT_NE(_wgetcwd(workingDirW, MAX_PATH), nullptr);
126 
127   WinRemoteMessageSender v0(kCommandline);
128   WinRemoteMessageSender v1(kCommandline, workingDirA);
129   WinRemoteMessageSender v2(kCommandlineW, workingDirW);
130 
131   WinRemoteMessageReceiver receiver;
132   int32_t len;
133   nsAutoString arg;
134   nsCOMPtr<nsIFile> workingDir;
135 
136   receiver.Parse(v0.CopyData());
137   EXPECT_TRUE(NS_SUCCEEDED(receiver.CommandLineRunner()->GetLength(&len)));
138   EXPECT_EQ(len, ArrayLength(kExpectedArgsW));
139   for (int i = 0; i < ArrayLength(kExpectedArgsW); ++i) {
140     EXPECT_TRUE(
141         NS_SUCCEEDED(receiver.CommandLineRunner()->GetArgument(i, arg)));
142     EXPECT_STREQ(arg.get(), kExpectedArgsW[i]);
143   }
144   EXPECT_EQ(receiver.CommandLineRunner()->GetWorkingDirectory(
145                 getter_AddRefs(workingDir)),
146             NS_ERROR_NOT_INITIALIZED);
147 
148   receiver.Parse(v1.CopyData());
149   EXPECT_TRUE(NS_SUCCEEDED(receiver.CommandLineRunner()->GetLength(&len)));
150   EXPECT_EQ(len, ArrayLength(kExpectedArgsW));
151   for (int i = 0; i < ArrayLength(kExpectedArgsW); ++i) {
152     EXPECT_TRUE(
153         NS_SUCCEEDED(receiver.CommandLineRunner()->GetArgument(i, arg)));
154     EXPECT_STREQ(arg.get(), kExpectedArgsW[i]);
155   }
156   EXPECT_TRUE(NS_SUCCEEDED(receiver.CommandLineRunner()->GetWorkingDirectory(
157       getter_AddRefs(workingDir))));
158   EXPECT_TRUE(NS_SUCCEEDED(workingDir->GetPath(arg)));
159   EXPECT_STREQ(arg.get(), workingDirW);
160 
161   receiver.Parse(v2.CopyData());
162   EXPECT_TRUE(NS_SUCCEEDED(receiver.CommandLineRunner()->GetLength(&len)));
163   EXPECT_EQ(len, ArrayLength(kExpectedArgsW));
164   for (int i = 0; i < ArrayLength(kExpectedArgsW); ++i) {
165     EXPECT_TRUE(
166         NS_SUCCEEDED(receiver.CommandLineRunner()->GetArgument(i, arg)));
167     EXPECT_STREQ(arg.get(), kExpectedArgsW[i]);
168   }
169   EXPECT_TRUE(NS_SUCCEEDED(receiver.CommandLineRunner()->GetWorkingDirectory(
170       getter_AddRefs(workingDir))));
171   EXPECT_TRUE(NS_SUCCEEDED(workingDir->GetPath(arg)));
172   EXPECT_STREQ(arg.get(), workingDirW);
173 }
174