1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 11 февр. 2019 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #ifndef TEST_MAIN_CONFIG_H_
23 #define TEST_MAIN_CONFIG_H_
24 
25 #include <core/types.h>
26 #include <core/stdlib/stdio.h>
27 #include <core/io/charset.h>
28 #include <test/main/types.h>
29 #include <data/cvector.h>
30 #include <unistd.h>
31 #include <errno.h>
32 
33 
34 namespace lsp
35 {
36     enum test_mode_t
37     {
38         UNKNOWN,
39         PTEST,
40         UTEST,
41         MTEST
42     };
43 
44     typedef struct config_t
45     {
46         public:
47             mode_t                      mode;
48             bool                        fork;
49             bool                        verbose;
50             bool                        debug;
51             bool                        list_all;
52             bool                        mtrace;
53             bool                        ilist;
54             bool                        sysinfo;
55             bool                        is_child;
56             size_t                      threads;
57             const char                 *executable;
58             const char                 *outfile;
59             const char                 *tracepath;
60             cvector<char>               list;
61             cvector<char>               ignore;
62             cvector<char>               args;
63 
64 #ifdef PLATFORM_WINDOWS
65             size_t                      utf8_argc;
66             char                      **utf8_argv;
67 #endif /* PLATFORM_WINDOWS */
68 
69         public:
70             explicit config_t();
~config_tconfig_t71             ~config_t()         { clear(); }
72 
73             status_t        parse(FILE *out, int argc, const char **argv);
74             status_t        print_usage(FILE *out, bool detailed=false);
75             void            clear();
76 
77     } config_t;
78 
print_usage(FILE * out,bool detailed)79     status_t config_t::print_usage(FILE *out, bool detailed)
80     {
81         fputs("USAGE: {utest|ptest|mtest} [args...] [test name...]\n", out);
82         if (!detailed)
83             return STATUS_INSUFFICIENT;
84 
85         fputs("  First argument:\n", out);
86         fputs("    utest                 Unit testing subsystem\n", out);
87         fputs("    ptest                 Performance testing subsystem\n", out);
88         fputs("    mtest                 Manual testing subsystem\n", out);
89         fputs("  Additional arguments:\n", out);
90         fputs("    -a, --args [args...]  Pass arguments to test\n", out);
91         fputs("    -d, --debug           Disable time restrictions for unit tests\n", out);
92         fputs("                          for debugging purposes\n", out);
93         fputs("    -e, --execute         Launch tests specified after this switch\n", out);
94         fputs("    -f, --fork            Fork child processes (opposite to --nofork)\n", out);
95         fputs("    -h, --help            Display help\n", out);
96         fputs("    -i, --ignore          Ignore tests specified after this switch\n", out);
97         fputs("    -j, --jobs            Set number of job workers for unit tests\n", out);
98         fputs("    -l, --list            List all available tests\n", out);
99     #ifdef PLATFORM_LINUX
100         fputs("    -mt, --mtrace         Enable mtrace log\n", out);
101     #endif /* PLATFORM_LINUX */
102         fputs("    -nf, --nofork         Do not fork child processes (for better \n", out);
103         fputs("                          debugging capabilities)\n", out);
104     #ifdef PLATFORM_LINUX
105         fputs("    -nt, --nomtrace       Disable mtrace log\n", out);
106     #endif /* PLATFORM_LINUX */
107         fputs("    -nsi, --nosysinfo     Do not output system information\n", out);
108         fputs("    -o, --outfile file    Output performance test statistics to specified file\n", out);
109         fputs("    -s, --silent          Do not output additional information from tests\n", out);
110         fputs("    -si, --sysinfo        Output system information\n", out);
111         fputs("    -t, --tracepath path  Override default trace path with specified value\n", out);
112         fputs("    -v, --verbose         Output additional information from tests\n", out);
113 
114         return STATUS_INSUFFICIENT;
115     }
116 
parse(FILE * out,int argc,const char ** argv)117     status_t config_t::parse(FILE *out, int argc, const char **argv)
118     {
119         clear();
120 
121 #if defined(PLATFORM_WINDOWS)
122         // Get number of processors for system
123         SYSTEM_INFO     os_sysinfo;
124         GetSystemInfo(&os_sysinfo);
125         threads         = os_sysinfo.dwNumberOfProcessors;
126 
127         // Get command line
128         LPWSTR cmdline  = GetCommandLineW();
129         int nargs = 0;
130         LPWSTR *arglist = CommandLineToArgvW(cmdline, &nargs);
131         if ((arglist == NULL) || (nargs < 1))
132         {
133             fprintf(stderr, "Error obtaining command-line arguments\n");
134             fflush(stderr);
135             return STATUS_UNKNOWN_ERR;
136         }
137 
138         // Convert UTF-16-encoded command line arguments to UTF-8-encoded
139         utf8_argc       = nargs;
140         utf8_argv       = reinterpret_cast<char **>(malloc(nargs * sizeof(char *)));
141         for (size_t i=0; i<utf8_argc; ++i)
142             utf8_argv[i]        = NULL;
143         for (size_t i=0; i<utf8_argc; ++i)
144         {
145             utf8_argv[i]        = lsp::utf16_to_utf8(arglist[i]);
146             if (utf8_argv[i] == NULL)
147                 return STATUS_NO_MEM;
148         }
149 
150         LocalFree(arglist);
151 
152         // Patch arguments
153         argc        = utf8_argc;
154         argv        = const_cast<const char **>(utf8_argv);
155 #else
156         threads     = sysconf(_SC_NPROCESSORS_ONLN);
157 #endif /* PLATFORM_WINDOWS */
158 
159         if (argc < 2)
160             return print_usage(out);
161 
162         executable  = argv[0];
163 
164         if (!strcmp(argv[1], "ptest"))
165             mode    = PTEST;
166         else if (!strcmp(argv[1], "utest"))
167             mode    = UTEST;
168         else if (!strcmp(argv[1], "mtest"))
169             mode    = MTEST;
170         else if ((!strcmp(argv[1], "--help")) || ((!strcmp(argv[1], "-h"))))
171             return print_usage(out, true);
172         else
173             return print_usage(out);
174 
175         for (int i=2; i<argc; ++i)
176         {
177             if ((!strcmp(argv[i], "--nofork")) || (!strcmp(argv[i], "-nf")))
178                 fork        = false;
179             else if ((!strcmp(argv[i], "--fork")) || (!strcmp(argv[i], "-f")))
180                 fork        = true;
181             else if ((!strcmp(argv[i], "--verbose")) || (!strcmp(argv[i], "-v")))
182                 verbose     = true;
183             else if ((!strcmp(argv[i], "--silent")) || (!strcmp(argv[i], "-s")))
184                 verbose     = false;
185             else if ((!strcmp(argv[i], "--sysinfo")) || (!strcmp(argv[i], "-si")))
186                 sysinfo     = true;
187             else if ((!strcmp(argv[i], "--nosysinfo")) || (!strcmp(argv[i], "-nsi")))
188                 sysinfo     = false;
189             else if ((!strcmp(argv[i], "--debug")) || (!strcmp(argv[i], "-d")))
190                 debug       = true;
191             else if ((!strcmp(argv[i], "--list")) || (!strcmp(argv[i], "-l")))
192                 list_all    = true;
193     #ifdef PLATFORM_LINUX
194             else if ((!strcmp(argv[i], "--mtrace")) || (!strcmp(argv[i], "-mt")))
195                 mtrace      = true;
196             else if ((!strcmp(argv[i], "--nomtrace")) || (!strcmp(argv[i], "-nt")))
197                 mtrace      = false;
198     #endif /* PLATFORM_LINUX */
199             else if ((!strcmp(argv[i], "--tracepath")) || (!strcmp(argv[i], "-t")))
200             {
201                 if ((++i) >= argc)
202                 {
203                     fprintf(stderr, "Not specified trace path\n");
204                     return STATUS_INVALID_VALUE;
205                 }
206                 tracepath   = argv[i];
207             }
208             else if ((!strcmp(argv[i], "--outfile")) || (!strcmp(argv[i], "-o")))
209             {
210                 if ((++i) >= argc)
211                 {
212                     fprintf(stderr, "Not specified name of output file\n");
213                     return STATUS_INVALID_VALUE;
214                 }
215                 outfile     = argv[i];
216             }
217             else if ((!strcmp(argv[i], "--args")) || (!strcmp(argv[i], "-a")))
218             {
219                 while (++i < argc)
220                     args.add(const_cast<char *>(argv[i]));
221             }
222             else if ((!strcmp(argv[i], "--jobs")) || (!strcmp(argv[i], "-j")))
223             {
224                 if ((++i) >= argc)
225                 {
226                     fprintf(stderr, "Not specified number of jobs for --jobs parameter\n");
227                     return STATUS_INVALID_VALUE;
228                 }
229 
230                 errno           = 0;
231                 char *end       = NULL;
232                 long jobs       = strtol(argv[i], &end, 10);
233                 if ((errno != 0) || ((*end) != '\0') || (jobs <= 0))
234                 {
235                     fprintf(stderr, "Invalid value for --jobs parameter: %s\n", argv[i]);
236                     return STATUS_INVALID_VALUE;
237                 }
238                 threads         = size_t(jobs);
239             }
240             else if ((!strcmp(argv[i], "--help")) || ((!strcmp(argv[i], "-h"))))
241                 return print_usage(out, true);
242             else if ((!strcmp(argv[i], "--ignore")) || ((!strcmp(argv[i], "-i"))))
243                 ilist           = true;
244             else if ((!strcmp(argv[i], "--execute")) || ((!strcmp(argv[i], "-e"))))
245                 ilist           = false;
246 #ifdef PLATFORM_WINDOWS
247             else if (!strcmp(argv[i], "--run-as-nested-process"))
248                 is_child        = true;
249 #endif /* PLATFORM_WINDOWS */
250             else
251             {
252                 if (ilist)
253                     ignore.add(const_cast<char *>(argv[i]));
254                 else
255                     list.add(const_cast<char *>(argv[i]));
256             }
257         }
258 
259         return 0;
260     }
261 
config_t()262     config_t::config_t()
263     {
264         mode        = UNKNOWN;
265         fork        = true;
266         verbose     = false;
267         debug       = false;
268         list_all    = false;
269         mtrace      = false;
270         ilist       = false;
271         sysinfo     = true;
272         is_child    = false;
273         executable  = NULL;
274         tracepath   = "/tmp/lsp-plugins-trace";
275         outfile     = NULL;
276         threads     = 1;
277 
278 #if defined(PLATFORM_WINDOWS)
279         utf8_argc   = 0;
280         utf8_argv   = NULL;
281 #endif
282     }
283 
clear()284     void config_t::clear()
285     {
286 #if defined(PLATFORM_WINDOWS)
287         if (utf8_argv != NULL)
288         {
289             for (size_t i=0; i<utf8_argc; ++i)
290             {
291                 if (utf8_argv[i] != NULL)
292                     free(utf8_argv[i]);
293             }
294             free(utf8_argv);
295 
296             utf8_argv       = NULL;
297             utf8_argc       = 0;
298         }
299 #endif /* PLATFORM_WINDOWS */
300         list.flush();
301         ignore.flush();
302         args.flush();
303     }
304 }
305 
306 #endif /* TEST_MAIN_CONFIG_H_ */
307