1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2020-2021. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(kernel_test_lib).
22
23-export([init_per_suite/1,
24         end_per_suite/1]).
25-export([tc_try/3]).
26-export([socket_type/1,
27	 listen/3,
28         connect/4, connect/5,
29         open/3,
30         is_socket_backend/1,
31         inet_backend_opts/1,
32         explicit_inet_backend/0,
33         test_inet_backends/0]).
34-export([start_slave_node/2, start_slave_node/3,
35         start_node/3, start_node/4,
36         stop_node/1]).
37-export([f/2,
38         print/1, print/2]).
39-export([good_hosts/1,
40         lookup/3]).
41
42-include("kernel_test_lib.hrl").
43
44
45%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
46
47init_per_suite([{allow_skip, Allow}|Config]) ->
48    init_per_suite(Allow, Config);
49init_per_suite(Config) ->
50    init_per_suite(true, Config).
51
52init_per_suite(AllowSkip, Config) when is_boolean(AllowSkip) ->
53
54    print("kernel environment: "
55          "~n   (kernel) app:  ~p"
56          "~n   (all)    init: ~p"
57          "~n   (kernel) init: ~p",
58          [application:get_all_env(kernel),
59           init:get_arguments(),
60           case init:get_argument(kernel) of
61               {ok, Args} -> Args;
62               error -> undefined
63           end]),
64
65    ct:timetrap(timer:minutes(2)),
66
67    try analyze_and_print_host_info() of
68        {Factor, HostInfo} when (AllowSkip =:= true) andalso
69                                is_integer(Factor) ->
70            try maybe_skip(HostInfo) of
71                true ->
72                    {skip, "Unstable host and/or os (or combo thererof)"};
73                false ->
74                    kernel_test_global_sys_monitor:start(),
75                    [{kernel_factor, Factor} | Config]
76            catch
77                throw:{skip, _} = SKIP ->
78                    SKIP
79            end;
80
81        {Factor, _HostInfo} when (AllowSkip =:= false) andalso
82                                 is_integer(Factor) ->
83            [{kernel_factor, Factor} | Config]
84
85    catch
86        throw:{skip, _} = SKIP ->
87            SKIP
88    end.
89
90
91end_per_suite(Config) when is_list(Config) ->
92    kernel_test_global_sys_monitor:stop(),
93    Config.
94
95analyze_and_print_host_info() ->
96    {OsFam, OsName} = os:type(),
97    Version         =
98        case os:version() of
99            {Maj, Min, Rel} ->
100                f("~w.~w.~w", [Maj, Min, Rel]);
101            VStr ->
102                VStr
103        end,
104    case {OsFam, OsName} of
105        {unix, linux} ->
106            analyze_and_print_linux_host_info(Version);
107        {unix, openbsd} ->
108            analyze_and_print_openbsd_host_info(Version);
109        {unix, freebsd} ->
110            analyze_and_print_freebsd_host_info(Version);
111        {unix, netbsd} ->
112            analyze_and_print_netbsd_host_info(Version);
113        {unix, darwin} ->
114            analyze_and_print_darwin_host_info(Version);
115        {unix, sunos} ->
116            analyze_and_print_solaris_host_info(Version);
117        {win32, nt} ->
118            analyze_and_print_win_host_info(Version);
119        _ ->
120            io:format("OS Family: ~p"
121                      "~n   OS Type:               ~p"
122                      "~n   Version:               ~p"
123                      "~n   Num Online Schedulers: ~s"
124                      "~n", [OsFam, OsName, Version, str_num_schedulers()]),
125            {num_schedulers_to_factor(), []}
126    end.
127
128linux_which_distro(Version) ->
129    case file:read_file_info("/etc/issue") of
130        {ok, _} ->
131            case [string:trim(S) ||
132                     S <- string:tokens(os:cmd("cat /etc/issue"), [$\n])] of
133                [DistroStr|_] ->
134                    io:format("Linux: ~s"
135                              "~n   ~s"
136                              "~n",
137                              [Version, DistroStr]),
138                    case DistroStr of
139                        "Wind River Linux" ++ _ ->
140                            wind_river;
141                        "MontaVista" ++ _ ->
142                            montavista;
143                        "Yellow Dog" ++ _ ->
144                            yellow_dog;
145                        _ ->
146                            other
147                    end;
148                X ->
149                    io:format("Linux: ~s"
150                              "~n   ~p"
151                              "~n",
152                              [Version, X]),
153                    other
154            end;
155        _ ->
156            io:format("Linux: ~s"
157                      "~n", [Version]),
158            other
159    end.
160
161analyze_and_print_linux_host_info(Version) ->
162    Distro =
163        case file:read_file_info("/etc/issue") of
164            {ok, _} ->
165                linux_which_distro(Version);
166            _ ->
167                io:format("Linux: ~s"
168                          "~n", [Version]),
169                other
170        end,
171    Factor =
172        case (catch linux_which_cpuinfo(Distro)) of
173            {ok, {CPU, BogoMIPS}} ->
174                io:format("CPU: "
175                          "~n   Model:                 ~s"
176                          "~n   BogoMIPS:              ~w"
177                          "~n   Num Online Schedulers: ~s"
178                          "~n", [CPU, BogoMIPS, str_num_schedulers()]),
179                if
180                    (BogoMIPS > 20000) ->
181                        1;
182                    (BogoMIPS > 10000) ->
183                        2;
184                    (BogoMIPS > 5000) ->
185                        3;
186                    (BogoMIPS > 2000) ->
187                        5;
188                    (BogoMIPS > 1000) ->
189                        8;
190                    true ->
191                        10
192                end;
193            {ok, CPU} ->
194                io:format("CPU: "
195                          "~n   Model:                 ~s"
196                          "~n   Num Online Schedulers: ~s"
197                          "~n", [CPU, str_num_schedulers()]),
198                NumChed = erlang:system_info(schedulers),
199                if
200                    (NumChed > 2) ->
201                        2;
202                    true ->
203                        5
204                end;
205            _ ->
206                5
207        end,
208    %% Check if we need to adjust the factor because of the memory
209    try linux_which_meminfo() of
210        AddFactor ->
211            io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]),
212            {Factor + AddFactor, []}
213    catch
214        _:_:_ ->
215            io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]),
216            {Factor, []}
217    end.
218
219linux_cpuinfo_lookup(Key) when is_list(Key) ->
220    linux_info_lookup(Key, "/proc/cpuinfo").
221
222linux_cpuinfo_cpu() ->
223    case linux_cpuinfo_lookup("cpu") of
224        [Model] ->
225            Model;
226        _ ->
227            "-"
228    end.
229
230linux_cpuinfo_motherboard() ->
231    case linux_cpuinfo_lookup("motherboard") of
232        [MB] ->
233            MB;
234        _ ->
235            "-"
236    end.
237
238linux_cpuinfo_bogomips() ->
239    case linux_cpuinfo_lookup("bogomips") of
240        BMips when is_list(BMips) ->
241            try lists:sum([bogomips_to_int(BM) || BM <- BMips])
242            catch
243                _:_:_ ->
244                    "-"
245            end;
246        _X ->
247            "-"
248    end.
249
250linux_cpuinfo_total_bogomips() ->
251    case linux_cpuinfo_lookup("total bogomips") of
252        [TBM] ->
253            try bogomips_to_int(TBM)
254            catch
255                _:_:_ ->
256                    "-"
257            end;
258        _ ->
259            "-"
260    end.
261
262bogomips_to_int(BM) ->
263    try list_to_float(BM) of
264        F ->
265            floor(F)
266    catch
267        _:_:_ ->
268            try list_to_integer(BM) of
269                I ->
270                    I
271            catch
272                _:_:_ ->
273                    throw(noinfo)
274            end
275    end.
276
277linux_cpuinfo_model() ->
278    case linux_cpuinfo_lookup("model") of
279        [M] ->
280            M;
281        _X ->
282            "-"
283    end.
284
285linux_cpuinfo_platform() ->
286    case linux_cpuinfo_lookup("platform") of
287        [P] ->
288            P;
289        _ ->
290            "-"
291    end.
292
293linux_cpuinfo_model_name() ->
294    case linux_cpuinfo_lookup("model name") of
295        [P|_] ->
296            P;
297        _ ->
298            "-"
299    end.
300
301linux_cpuinfo_processor() ->
302    case linux_cpuinfo_lookup("Processor") of
303        [P] ->
304            P;
305        _ ->
306            "-"
307    end.
308
309linux_which_cpuinfo(montavista) ->
310    CPU =
311        case linux_cpuinfo_cpu() of
312            "-" ->
313                throw(noinfo);
314            Model ->
315                case linux_cpuinfo_motherboard() of
316                    "-" ->
317                        Model;
318                    MB ->
319                        Model ++ " (" ++ MB ++ ")"
320                end
321        end,
322    case linux_cpuinfo_bogomips() of
323        "-" ->
324            {ok, CPU};
325        BMips ->
326            {ok, {CPU, BMips}}
327    end;
328
329linux_which_cpuinfo(yellow_dog) ->
330    CPU =
331        case linux_cpuinfo_cpu() of
332            "-" ->
333                throw(noinfo);
334            Model ->
335                case linux_cpuinfo_motherboard() of
336                    "-" ->
337                        Model;
338                    MB ->
339                        Model ++ " (" ++ MB ++ ")"
340                end
341        end,
342    {ok, CPU};
343
344linux_which_cpuinfo(wind_river) ->
345    CPU =
346        case linux_cpuinfo_model() of
347            "-" ->
348                throw(noinfo);
349            Model ->
350                case linux_cpuinfo_platform() of
351                    "-" ->
352                        Model;
353                    Platform ->
354                        Model ++ " (" ++ Platform ++ ")"
355                end
356        end,
357    case linux_cpuinfo_total_bogomips() of
358        "-" ->
359            {ok, CPU};
360        BMips ->
361            {ok, {CPU, BMips}}
362    end;
363
364%% Check for x86 (Intel or AMD)
365linux_which_cpuinfo(other) ->
366    CPU =
367        case linux_cpuinfo_model_name() of
368            "-" ->
369                %% ARM (at least some distros...)
370                case linux_cpuinfo_processor() of
371                    "-" ->
372			case linux_cpuinfo_model() of
373			    "-" ->
374				%% Ok, we give up
375				throw(noinfo);
376			    Model ->
377				Model
378			end;
379                    Proc ->
380                        Proc
381                end;
382            ModelName ->
383                ModelName
384        end,
385    case linux_cpuinfo_bogomips() of
386        "-" ->
387            {ok, CPU};
388        BMips ->
389            {ok, {CPU, BMips}}
390    end.
391
392linux_meminfo_lookup(Key) when is_list(Key) ->
393    linux_info_lookup(Key, "/proc/meminfo").
394
395linux_meminfo_memtotal() ->
396    case linux_meminfo_lookup("MemTotal") of
397        [X] ->
398            X;
399        _ ->
400            "-"
401    end.
402
403%% We *add* the value this return to the Factor.
404linux_which_meminfo() ->
405    case linux_meminfo_memtotal() of
406        "-" ->
407            0;
408        MemTotal ->
409            io:format("Memory:"
410                      "~n   ~s"
411                      "~n", [MemTotal]),
412            case string:tokens(MemTotal, [$ ]) of
413                [MemSzStr, MemUnit] ->
414                    MemSz2 = list_to_integer(MemSzStr),
415                    MemSz3 =
416                        case string:to_lower(MemUnit) of
417                            "kb" ->
418                                MemSz2;
419                            "mb" ->
420                                MemSz2*1024;
421                            "gb" ->
422                                MemSz2*1024*1024;
423                            _ ->
424                                throw(noinfo)
425                        end,
426                    if
427                        (MemSz3 >= 8388608) ->
428                            0;
429                        (MemSz3 >= 4194304) ->
430                            1;
431                        (MemSz3 >= 2097152) ->
432                            3;
433                        true ->
434                            5
435                    end;
436                _X ->
437                    0
438            end
439    end.
440
441%% Just to be clear: This is ***not*** scientific...
442analyze_and_print_openbsd_host_info(Version) ->
443    io:format("OpenBSD:"
444              "~n   Version: ~p"
445              "~n", [Version]),
446    Extract =
447        fun(Key) ->
448                string:tokens(string:trim(os:cmd("sysctl " ++ Key)), [$=])
449        end,
450    try
451        begin
452            CPU =
453                case Extract("hw.model") of
454                    ["hw.model", Model] ->
455                        string:trim(Model);
456                    _ ->
457                        "-"
458                end,
459            CPUSpeed =
460                case Extract("hw.cpuspeed") of
461                    ["hw.cpuspeed", Speed] ->
462                        list_to_integer(Speed);
463                    _ ->
464                        -1
465                end,
466            NCPU =
467                case Extract("hw.ncpufound") of
468                    ["hw.ncpufound", N] ->
469                        list_to_integer(N);
470                    _ ->
471                        -1
472                end,
473            Memory =
474                case Extract("hw.physmem") of
475                    ["hw.physmem", PhysMem] ->
476                        list_to_integer(PhysMem) div 1024;
477                    _ ->
478                        -1
479                end,
480            io:format("CPU:"
481                      "~n   Model: ~s"
482                      "~n   Speed: ~w"
483                      "~n   N:     ~w"
484                      "~nMemory:"
485                      "~n   ~w KB"
486                      "~n", [CPU, CPUSpeed, NCPU, Memory]),
487            io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]),
488            CPUFactor =
489                if
490                    (CPUSpeed =:= -1) ->
491                        1;
492                    (CPUSpeed >= 2000) ->
493                        if
494                            (NCPU >= 4) ->
495                                1;
496                            (NCPU >= 2) ->
497                                2;
498                            true ->
499                                3
500                        end;
501                    true ->
502                        if
503                            (NCPU >= 4) ->
504                                2;
505                            (NCPU >= 2) ->
506                                3;
507                            true ->
508                                4
509                        end
510                end,
511            MemAddFactor =
512                if
513                    (Memory =:= -1) ->
514                        0;
515                    (Memory >= 8388608) ->
516                        0;
517                    (Memory >= 4194304) ->
518                        1;
519                    (Memory >= 2097152) ->
520                        2;
521                    true ->
522                        3
523                end,
524            {CPUFactor + MemAddFactor, []}
525        end
526    catch
527        _:_:_ ->
528            io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]),
529            {2, []}
530    end.
531
532analyze_and_print_freebsd_host_info(Version) ->
533    io:format("FreeBSD:"
534              "~n   Version: ~p"
535              "~n", [Version]),
536    %% This test require that the program 'sysctl' is in the path.
537    %% First test with 'which sysctl', if that does not work
538    %% try with 'which /sbin/sysctl'. If that does not work either,
539    %% we skip the test...
540    try
541        begin
542            SysCtl =
543                case string:trim(os:cmd("which sysctl")) of
544                    [] ->
545                        case string:trim(os:cmd("which /sbin/sysctl")) of
546                            [] ->
547                                throw(sysctl);
548                            SC2 ->
549                                SC2
550                        end;
551                    SC1 ->
552                        SC1
553                end,
554            Extract =
555                fun(Key) ->
556                        string:tokens(string:trim(os:cmd(SysCtl ++ " " ++ Key)),
557                                      [$:])
558                end,
559            CPU      = analyze_freebsd_cpu(Extract),
560            CPUSpeed = analyze_freebsd_cpu_speed(Extract),
561            NCPU     = analyze_freebsd_ncpu(Extract),
562            Memory   = analyze_freebsd_memory(Extract),
563            io:format("CPU:"
564                      "~n   Model:                 ~s"
565                      "~n   Speed:                 ~w"
566                      "~n   N:                     ~w"
567                      "~n   Num Online Schedulers: ~s"
568                      "~nMemory:"
569                      "~n   ~w KB"
570                      "~n",
571                      [CPU, CPUSpeed, NCPU, str_num_schedulers(), Memory]),
572            io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]),
573            CPUFactor =
574                if
575                    (CPUSpeed =:= -1) ->
576                        1;
577                    (CPUSpeed >= 2000) ->
578                        if
579                            (NCPU >= 4) ->
580                                1;
581                            (NCPU >= 2) ->
582                                2;
583                            true ->
584                                3
585                        end;
586                    true ->
587                        if
588                            (NCPU =:= -1) ->
589                                1;
590                            (NCPU >= 4) ->
591                                2;
592                            (NCPU >= 2) ->
593                                3;
594                            true ->
595                                4
596                        end
597                end,
598            MemAddFactor =
599                if
600                    (Memory =:= -1) ->
601                        0;
602                    (Memory >= 8388608) ->
603                        0;
604                    (Memory >= 4194304) ->
605                        1;
606                    (Memory >= 2097152) ->
607                        2;
608                    true ->
609                        3
610                end,
611            {CPUFactor + MemAddFactor, []}
612        end
613    catch
614        _:_:_ ->
615            io:format("CPU:"
616                      "~n   Num Online Schedulers: ~s"
617                      "~n", [str_num_schedulers()]),
618            io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]),
619            Factor = case erlang:system_info(schedulers) of
620                         1 ->
621                             10;
622                         2 ->
623                             5;
624                         _ ->
625                             2
626                     end,
627            {Factor, []}
628    end.
629
630
631analyze_freebsd_cpu(Extract) ->
632    analyze_freebsd_item(Extract, "hw.model", fun(X) -> X end, "-").
633
634analyze_freebsd_cpu_speed(Extract) ->
635    analyze_freebsd_item(Extract,
636                         "hw.clockrate",
637                         fun(X) -> list_to_integer(X) end,
638                         -1).
639
640analyze_freebsd_ncpu(Extract) ->
641    analyze_freebsd_item(Extract,
642                         "hw.ncpu",
643                         fun(X) -> list_to_integer(X) end,
644                         -1).
645
646analyze_freebsd_memory(Extract) ->
647    analyze_freebsd_item(Extract,
648                         "hw.physmem",
649                         fun(X) -> list_to_integer(X) div 1024 end,
650                         -1).
651
652analyze_freebsd_item(Extract, Key, Process, Default) ->
653    try
654        begin
655            case Extract(Key) of
656                [Key, Model] ->
657                    Process(string:trim(Model));
658                _ ->
659                    Default
660            end
661        end
662    catch
663        _:_:_ ->
664            Default
665    end.
666
667analyze_and_print_netbsd_host_info(Version) ->
668    io:format("NetBSD:"
669              "~n   Version: ~p"
670              "~n", [Version]),
671    %% This test require that the program 'sysctl' is in the path.
672    %% First test with 'which sysctl', if that does not work
673    %% try with 'which /sbin/sysctl'. If that does not work either,
674    %% we skip the test...
675    try
676        begin
677            SysCtl =
678                case string:trim(os:cmd("which sysctl")) of
679                    [] ->
680                        case string:trim(os:cmd("which /sbin/sysctl")) of
681                            [] ->
682                                throw(sysctl);
683                            SC2 ->
684                                SC2
685                        end;
686                    SC1 ->
687                        SC1
688                end,
689            Extract =
690                fun(Key) ->
691                        [string:trim(S) ||
692                            S <-
693                                string:tokens(string:trim(os:cmd(SysCtl ++ " " ++ Key)),
694                                              [$=])]
695                end,
696            CPU      = analyze_netbsd_cpu(Extract),
697            Machine  = analyze_netbsd_machine(Extract),
698            Arch     = analyze_netbsd_machine_arch(Extract),
699            CPUSpeed = analyze_netbsd_cpu_speed(Extract),
700            NCPU     = analyze_netbsd_ncpu(Extract),
701            Memory   = analyze_netbsd_memory(Extract),
702            io:format("CPU:"
703                      "~n   Model:          ~s (~s, ~s)"
704                      "~n   Speed:          ~w MHz"
705                      "~n   N:              ~w"
706                      "~n   Num Schedulers: ~w"
707                      "~nMemory:"
708                      "~n   ~w KB"
709                      "~n",
710                      [CPU, Machine, Arch, CPUSpeed, NCPU,
711                       erlang:system_info(schedulers), Memory]),
712            CPUFactor =
713                if
714                    (CPUSpeed =:= -1) ->
715                        1;
716                    (CPUSpeed >= 2000) ->
717                        if
718                            (NCPU >= 4) ->
719                                1;
720                            (NCPU >= 2) ->
721                                2;
722                            true ->
723                                3
724                        end;
725                    true ->
726                        if
727                            (NCPU =:= -1) ->
728                                1;
729                            (NCPU >= 4) ->
730                                2;
731                            (NCPU >= 2) ->
732                                3;
733                            true ->
734                                4
735                        end
736                end,
737            MemAddFactor =
738                if
739                    (Memory =:= -1) ->
740                        0;
741                    (Memory >= 8388608) ->
742                        0;
743                    (Memory >= 4194304) ->
744                        1;
745                    (Memory >= 2097152) ->
746                        2;
747                    true ->
748                        3
749                end,
750            {CPUFactor + MemAddFactor, []}
751        end
752    catch
753        _:_:_ ->
754            io:format("CPU:"
755                      "~n   Num Schedulers: ~w"
756                      "~n", [erlang:system_info(schedulers)]),
757            Factor = case erlang:system_info(schedulers) of
758                         1 ->
759                             10;
760                         2 ->
761                             5;
762                         _ ->
763                             2
764                     end,
765            {Factor, []}
766    end.
767
768analyze_netbsd_cpu(Extract) ->
769    analyze_netbsd_item(Extract, "hw.model", fun(X) -> X end, "-").
770
771analyze_netbsd_machine(Extract) ->
772    analyze_netbsd_item(Extract, "hw.machine", fun(X) -> X end, "-").
773
774analyze_netbsd_machine_arch(Extract) ->
775    analyze_netbsd_item(Extract, "hw.machine_arch", fun(X) -> X end, "-").
776
777analyze_netbsd_cpu_speed(Extract) ->
778    analyze_netbsd_item(Extract, "machdep.dmi.processor-frequency",
779                        fun(X) -> case string:tokens(X, [$\ ]) of
780                                      [MHz, "MHz"] ->
781                                          list_to_integer(MHz);
782                                      _ ->
783                                          -1
784                                  end
785                        end, "-").
786
787analyze_netbsd_ncpu(Extract) ->
788    analyze_netbsd_item(Extract,
789                        "hw.ncpu",
790                        fun(X) -> list_to_integer(X) end,
791                        -1).
792
793analyze_netbsd_memory(Extract) ->
794    analyze_netbsd_item(Extract,
795                        "hw.physmem64",
796                        fun(X) -> list_to_integer(X) div 1024 end,
797                        -1).
798
799analyze_netbsd_item(Extract, Key, Process, Default) ->
800    analyze_freebsd_item(Extract, Key, Process, Default).
801
802%% Model Identifier: Macmini7,1
803%%       Processor Name: Intel Core i5
804%%       Processor Speed: 2,6 GHz
805%%       Number of Processors: 1
806%%       Total Number of Cores: 2
807%%       L2 Cache (per Core): 256 KB
808%%       L3 Cache: 3 MB
809%%       Hyper-Threading Technology: Enabled
810%%       Memory: 16 GB
811
812analyze_and_print_darwin_host_info(Version) ->
813    %% This stuff is for macOS.
814    %% If we ever tested on a pure darwin machine,
815    %% we need to find some other way to find some info...
816    %% Also, I suppose its possible that we for some other
817    %% reason *fail* to get the info...
818    case analyze_darwin_software_info() of
819        [] ->
820            io:format("Darwin:"
821                      "~n   Version:               ~s"
822                      "~n   Num Online Schedulers: ~s"
823                      "~n", [Version, str_num_schedulers()]),
824            {num_schedulers_to_factor(), []};
825        SwInfo when  is_list(SwInfo) ->
826            SystemVersion = analyze_darwin_sw_system_version(SwInfo),
827            KernelVersion = analyze_darwin_sw_kernel_version(SwInfo),
828            HwInfo        = analyze_darwin_hardware_info(),
829            ModelName     = analyze_darwin_hw_model_name(HwInfo),
830            ModelId       = analyze_darwin_hw_model_identifier(HwInfo),
831            ProcName      = analyze_darwin_hw_processor_name(HwInfo),
832            ProcSpeed     = analyze_darwin_hw_processor_speed(HwInfo),
833            NumProc       = analyze_darwin_hw_number_of_processors(HwInfo),
834            NumCores      = analyze_darwin_hw_total_number_of_cores(HwInfo),
835            Memory        = analyze_darwin_hw_memory(HwInfo),
836            io:format("Darwin:"
837                      "~n   System Version:        ~s"
838                      "~n   Kernel Version:        ~s"
839                      "~n   Model:                 ~s (~s)"
840                      "~n   Processor:             ~s (~s, ~s, ~s)"
841                      "~n   Memory:                ~s"
842                      "~n   Num Online Schedulers: ~s"
843                      "~n", [SystemVersion, KernelVersion,
844                             ModelName, ModelId,
845                             ProcName, ProcSpeed, NumProc, NumCores,
846                             Memory,
847                             str_num_schedulers()]),
848            CPUFactor = analyze_darwin_cpu_to_factor(ProcName,
849                                                     ProcSpeed,
850                                                     NumProc,
851                                                     NumCores),
852            MemFactor = analyze_darwin_memory_to_factor(Memory),
853            if (MemFactor =:= 1) ->
854                    {CPUFactor, []};
855               true ->
856                    {CPUFactor + MemFactor, []}
857            end
858    end.
859
860analyze_darwin_sw_system_version(SwInfo) ->
861    proplists:get_value("system version", SwInfo, "-").
862
863analyze_darwin_sw_kernel_version(SwInfo) ->
864    proplists:get_value("kernel version", SwInfo, "-").
865
866analyze_darwin_software_info() ->
867    analyze_darwin_system_profiler("SPSoftwareDataType").
868
869analyze_darwin_hw_model_name(HwInfo) ->
870    proplists:get_value("model name", HwInfo, "-").
871
872analyze_darwin_hw_model_identifier(HwInfo) ->
873    proplists:get_value("model identifier", HwInfo, "-").
874
875analyze_darwin_hw_processor_name(HwInfo) ->
876    proplists:get_value("processor name", HwInfo, "-").
877
878analyze_darwin_hw_processor_speed(HwInfo) ->
879    proplists:get_value("processor speed", HwInfo, "-").
880
881analyze_darwin_hw_number_of_processors(HwInfo) ->
882    proplists:get_value("number of processors", HwInfo, "-").
883
884analyze_darwin_hw_total_number_of_cores(HwInfo) ->
885    proplists:get_value("total number of cores", HwInfo, "-").
886
887analyze_darwin_hw_memory(HwInfo) ->
888    proplists:get_value("memory", HwInfo, "-").
889
890analyze_darwin_hardware_info() ->
891    analyze_darwin_system_profiler("SPHardwareDataType").
892
893%% This basically has the structure: "Key: Value"
894%% But could also be (for example):
895%%    "Something:" (which we ignore)
896%%    "Key: Value1:Value2"
897analyze_darwin_system_profiler(DataType) ->
898    %% First, make sure the program actually exist:
899    case os:cmd("which system_profiler") of
900        [] ->
901            [];
902        _ ->
903            D0 = os:cmd("system_profiler " ++ DataType),
904            D1 = string:tokens(D0, [$\n]),
905            D2 = [string:trim(S1) || S1 <- D1],
906            D3 = [string:tokens(S2, [$:]) || S2 <- D2],
907            analyze_darwin_system_profiler2(D3)
908    end.
909
910analyze_darwin_system_profiler2(L) ->
911    analyze_darwin_system_profiler2(L, []).
912
913analyze_darwin_system_profiler2([], Acc) ->
914    [{string:to_lower(K), V} || {K, V} <- lists:reverse(Acc)];
915analyze_darwin_system_profiler2([[_]|T], Acc) ->
916    analyze_darwin_system_profiler2(T, Acc);
917analyze_darwin_system_profiler2([[H1,H2]|T], Acc) ->
918    analyze_darwin_system_profiler2(T, [{H1, string:trim(H2)}|Acc]);
919analyze_darwin_system_profiler2([[H|TH0]|T], Acc) ->
920    %% Some value parts has ':' in them, so put them together
921    TH1 = colonize(TH0),
922    analyze_darwin_system_profiler2(T, [{H, string:trim(TH1)}|Acc]).
923
924%% This is only called if the length is at least 2
925colonize([L1, L2]) ->
926    L1 ++ ":" ++ L2;
927colonize([H|T]) ->
928    H ++ ":" ++ colonize(T).
929
930%% The memory looks like this "<size> <unit>". Example: "2 GB"
931analyze_darwin_memory_to_factor(Mem) ->
932    case [string:to_lower(S) || S <- string:tokens(Mem, [$\ ])] of
933        [_SzStr, "tb"] ->
934            1;
935        [SzStr, "gb"] ->
936            try list_to_integer(SzStr) of
937                Sz when Sz < 2 ->
938                    20;
939                Sz when Sz < 4 ->
940                    10;
941                Sz when Sz < 8 ->
942                    5;
943                Sz when Sz < 16 ->
944                    2;
945                _ ->
946                    1
947            catch
948                _:_:_ ->
949                    20
950            end;
951        [_SzStr, "mb"] ->
952            20;
953        _ ->
954            20
955    end.
956
957%% The speed is a string: "<speed> <unit>"
958%% the speed may be a float, which we transforms into an integer of MHz.
959%% To calculate a factor based on processor speed, number of procs
960%% and number of cores is ... not an exact ... science ...
961analyze_darwin_cpu_to_factor(_ProcName,
962                             ProcSpeedStr, NumProcStr, NumCoresStr) ->
963    Speed =
964        case [string:to_lower(S) || S <- string:tokens(ProcSpeedStr, [$\ ])] of
965            [SpeedStr, "mhz"] ->
966                try list_to_integer(SpeedStr) of
967                    SpeedI ->
968                        SpeedI
969                catch
970                    _:_:_ ->
971                        try list_to_float(SpeedStr) of
972                            SpeedF ->
973                                trunc(SpeedF)
974                        catch
975                            _:_:_ ->
976                                -1
977                        end
978                end;
979            [SpeedStr, "ghz"] ->
980                try list_to_float(SpeedStr) of
981                    SpeedF ->
982                        trunc(1000*SpeedF)
983                catch
984                    _:_:_ ->
985                        try list_to_integer(SpeedStr) of
986                            SpeedI ->
987                                1000*SpeedI
988                        catch
989                            _:_:_ ->
990                                -1
991                        end
992                end;
993            _ ->
994                -1
995        end,
996    NumProc = try list_to_integer(NumProcStr) of
997                  NumProcI ->
998                      NumProcI
999              catch
1000                  _:_:_ ->
1001                      1
1002              end,
1003    NumCores = try list_to_integer(NumCoresStr) of
1004                   NumCoresI ->
1005                       NumCoresI
1006               catch
1007                   _:_:_ ->
1008                       1
1009               end,
1010    if
1011        (Speed > 3000) ->
1012            if
1013                (NumProc =:= 1) ->
1014                    if
1015                        (NumCores < 2) ->
1016                            5;
1017                        (NumCores < 4) ->
1018                            3;
1019                        (NumCores < 6) ->
1020                            2;
1021                        true ->
1022                            1
1023                    end;
1024                true ->
1025                    if
1026                        (NumCores < 4) ->
1027                            2;
1028                        true ->
1029                            1
1030                    end
1031            end;
1032        (Speed > 2000) ->
1033            if
1034                (NumProc =:= 1) ->
1035                    if
1036                        (NumCores < 2) ->
1037                            8;
1038                        (NumCores < 4) ->
1039                            5;
1040                        (NumCores < 6) ->
1041                            3;
1042                        true ->
1043                            1
1044                    end;
1045                true ->
1046                    if
1047                        (NumCores < 4) ->
1048                            5;
1049                        (NumCores < 8) ->
1050                            2;
1051                        true ->
1052                            1
1053                    end
1054            end;
1055        true ->
1056            if
1057                (NumProc =:= 1) ->
1058                    if
1059                        (NumCores < 2) ->
1060                            10;
1061                        (NumCores < 4) ->
1062                            7;
1063                        (NumCores < 6) ->
1064                            5;
1065                        (NumCores < 8) ->
1066                            3;
1067                        true ->
1068                            1
1069                    end;
1070                true ->
1071                    if
1072                        (NumCores < 4) ->
1073                            8;
1074                        (NumCores < 8) ->
1075                            4;
1076                        true ->
1077                            1
1078                    end
1079            end
1080    end.
1081
1082analyze_and_print_solaris_host_info(Version) ->
1083    Release =
1084        case file:read_file_info("/etc/release") of
1085            {ok, _} ->
1086                case [string:trim(S) || S <- string:tokens(os:cmd("cat /etc/release"), [$\n])] of
1087                    [Rel | _] ->
1088                        Rel;
1089                    _ ->
1090                        "-"
1091                end;
1092            _ ->
1093                "-"
1094        end,
1095    %% Display the firmware device tree root properties (prtconf -b)
1096    Props = [list_to_tuple([string:trim(PS) || PS <- Prop]) ||
1097                Prop <- [string:tokens(S, [$:]) ||
1098                            S <- string:tokens(os:cmd("prtconf -b"), [$\n])]],
1099    BannerName = case lists:keysearch("banner-name", 1, Props) of
1100                     {value, {_, BN}} ->
1101                         string:trim(BN);
1102                     _ ->
1103                         "-"
1104                 end,
1105    InstructionSet =
1106        case string:trim(os:cmd("isainfo -k")) of
1107            "Pseudo-terminal will not" ++ _ ->
1108                "-";
1109            IS ->
1110                IS
1111        end,
1112    PtrConf = [list_to_tuple([string:trim(S) || S <- Items]) || Items <- [string:tokens(S, [$:]) || S <- string:tokens(os:cmd("prtconf"), [$\n])], length(Items) > 1],
1113    SysConf =
1114        case lists:keysearch("System Configuration", 1, PtrConf) of
1115            {value, {_, SC}} ->
1116                SC;
1117            _ ->
1118                "-"
1119        end,
1120    %% Because we count the lines of the output (which may contain
1121    %% any number of extra crap lines) we need to ensure we only
1122    %% count the "proper" stdout. So send it to a tmp file first
1123    %% and then count its number of lines...
1124    NumPhysCPU =
1125       try
1126            begin
1127                File1 = f("/tmp/psrinfo_p.~s.~w", [os:getpid(), os:system_time()]),
1128                os:cmd("psrinfo -p > " ++ File1),
1129                string:trim(os:cmd("cat " ++ File1))
1130            end
1131        catch
1132                _:_:_ ->
1133                    "-"
1134        end,
1135    %% Because we count the lines of the output (which may contain
1136    %% any number of extra crap lines) we need to ensure we only
1137    %% count the "proper" stdout. So send it to a tmp file first
1138    %% and then count its number of lines...
1139    NumVCPU =
1140        try
1141            begin
1142                File2 = f("/tmp/psrinfo.~s.~w", [os:getpid(), os:system_time()]),
1143                os:cmd("psrinfo > " ++ File2),
1144                [NumVCPUStr | _] = string:tokens(os:cmd("wc -l " ++ File2), [$\ ]),
1145                NumVCPUStr
1146            end
1147        catch
1148            _:_:_ ->
1149                "-"
1150        end,
1151    MemSz =
1152        case lists:keysearch("Memory size", 1, PtrConf) of
1153            {value, {_, MS}} ->
1154                MS;
1155            _ ->
1156                "-"
1157        end,
1158    io:format("Solaris: ~s"
1159              "~n   Release:                ~s"
1160              "~n   Banner Name:            ~s"
1161              "~n   Instruction Set:        ~s"
1162              "~n   CPUs:                   ~s (~s)"
1163              "~n   System Config:          ~s"
1164              "~n   Memory Size:            ~s"
1165              "~n   Num Online Schedulers:  ~s"
1166              "~n~n", [Version, Release, BannerName, InstructionSet,
1167                       NumPhysCPU, NumVCPU,
1168                       SysConf, MemSz,
1169                       str_num_schedulers()]),
1170    io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]),
1171    MemFactor =
1172        try string:tokens(MemSz, [$ ]) of
1173            [SzStr, "Mega" ++ _] ->
1174                try list_to_integer(SzStr) of
1175                    Sz when Sz > 8192 ->
1176                        0;
1177                    Sz when Sz > 4096 ->
1178                        1;
1179                    Sz when Sz > 2048 ->
1180                        2;
1181                    _ ->
1182                        5
1183                catch
1184                    _:_:_ ->
1185                        10
1186                end;
1187            [SzStr, "Giga" ++ _] ->
1188                try list_to_integer(SzStr) of
1189                    Sz when Sz > 8 ->
1190                        0;
1191                    Sz when Sz > 4 ->
1192                        1;
1193                    Sz when Sz > 2 ->
1194                        2;
1195                    _ ->
1196                        5
1197                catch
1198                    _:_:_ ->
1199                        10
1200                end;
1201            _ ->
1202                10
1203        catch
1204            _:_:_ ->
1205                10
1206        end,
1207    {try erlang:system_info(schedulers) of
1208         1 ->
1209             10;
1210         2 ->
1211             5;
1212         N when (N =< 6) ->
1213             2;
1214         _ ->
1215             1
1216     catch
1217         _:_:_ ->
1218             10
1219     end + MemFactor, []}.
1220
1221analyze_and_print_win_host_info(Version) ->
1222    SysInfo    = which_win_system_info(),
1223    OsName     = win_sys_info_lookup(os_name,             SysInfo),
1224    OsVersion  = win_sys_info_lookup(os_version,          SysInfo),
1225    SysMan     = win_sys_info_lookup(system_manufacturer, SysInfo),
1226    SysMod     = win_sys_info_lookup(system_model,        SysInfo),
1227    NumProcs   = win_sys_info_lookup(num_processors,      SysInfo),
1228    TotPhysMem = win_sys_info_lookup(total_phys_memory,   SysInfo),
1229    io:format("Windows: ~s"
1230              "~n   OS Version:             ~s (~p)"
1231              "~n   System Manufacturer:    ~s"
1232              "~n   System Model:           ~s"
1233              "~n   Number of Processor(s): ~s"
1234              "~n   Total Physical Memory:  ~s"
1235              "~n   Num Online Schedulers:  ~s"
1236              "~n~n", [OsName, OsVersion, Version,
1237                       SysMan, SysMod, NumProcs, TotPhysMem,
1238                       str_num_schedulers()]),
1239    io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]),
1240    MemFactor =
1241        try
1242            begin
1243                [MStr, MUnit|_] =
1244                    string:tokens(lists:delete($,, TotPhysMem), [$\ ]),
1245                case string:to_lower(MUnit) of
1246                    "gb" ->
1247                        try list_to_integer(MStr) of
1248                            M when M > 8 ->
1249                                0;
1250                            M when M > 4 ->
1251                                1;
1252                            M when M > 2 ->
1253                                2;
1254                            _ ->
1255                                5
1256                        catch
1257                            _:_:_ ->
1258                                10
1259                        end;
1260                    "mb" ->
1261                        try list_to_integer(MStr) of
1262                            M when M > 8192 ->
1263                                0;
1264                            M when M > 4096 ->
1265                                1;
1266                            M when M > 2048 ->
1267                                2;
1268                            _ ->
1269                                5
1270                        catch
1271                            _:_:_ ->
1272                                10
1273                        end;
1274                    _ ->
1275                        10
1276                end
1277            end
1278        catch
1279            _:_:_ ->
1280                10
1281        end,
1282    CPUFactor =
1283        case erlang:system_info(schedulers) of
1284            1 ->
1285                10;
1286            2 ->
1287                5;
1288            _ ->
1289                2
1290        end,
1291    {CPUFactor + MemFactor, SysInfo}.
1292
1293win_sys_info_lookup(Key, SysInfo) ->
1294    win_sys_info_lookup(Key, SysInfo, "-").
1295
1296win_sys_info_lookup(Key, SysInfo, Def) ->
1297    case lists:keysearch(Key, 1, SysInfo) of
1298        {value, {Key, Value}} ->
1299            Value;
1300        false ->
1301            Def
1302    end.
1303
1304%% This function only extracts the prop we actually care about!
1305which_win_system_info() ->
1306    F = fun() ->
1307                try
1308                    begin
1309                        SysInfo = os:cmd("systeminfo"),
1310                        process_win_system_info(
1311                          string:tokens(SysInfo, [$\r, $\n]), [])
1312                    end
1313                catch
1314                    C:E:S ->
1315                        io:format("Failed get or process System info: "
1316                                  "   Error Class: ~p"
1317                                  "   Error:       ~p"
1318                                  "   Stack:       ~p"
1319                                  "~n", [C, E, S]),
1320                        []
1321                end
1322        end,
1323    proxy_call(F, timer:minutes(1), []).
1324
1325process_win_system_info([], Acc) ->
1326    Acc;
1327process_win_system_info([H|T], Acc) ->
1328    case string:tokens(H, [$:]) of
1329        [Key, Value] ->
1330            case string:to_lower(Key) of
1331                "os name" ->
1332                    process_win_system_info(T,
1333                                            [{os_name, string:trim(Value)}|Acc]);
1334                "os version" ->
1335                    process_win_system_info(T,
1336                                            [{os_version, string:trim(Value)}|Acc]);
1337                "system manufacturer" ->
1338                    process_win_system_info(T,
1339                                            [{system_manufacturer, string:trim(Value)}|Acc]);
1340                "system model" ->
1341                    process_win_system_info(T,
1342                                            [{system_model, string:trim(Value)}|Acc]);
1343                "processor(s)" ->
1344                    [NumProcStr|_] = string:tokens(Value, [$\ ]),
1345                    T2 = lists:nthtail(list_to_integer(NumProcStr), T),
1346                    process_win_system_info(T2,
1347                                            [{num_processors, NumProcStr}|Acc]);
1348                "total physical memory" ->
1349                    process_win_system_info(T,
1350                                            [{total_phys_memory, string:trim(Value)}|Acc]);
1351                _ ->
1352                    process_win_system_info(T, Acc)
1353            end;
1354        _ ->
1355            process_win_system_info(T, Acc)
1356    end.
1357
1358str_num_schedulers() ->
1359    try erlang:system_info(schedulers_online) of
1360        N -> f("~w", [N])
1361    catch
1362        _:_:_ -> "-"
1363    end.
1364
1365num_schedulers_to_factor() ->
1366    try erlang:system_info(schedulers_online) of
1367        1 ->
1368            10;
1369        2 ->
1370            5;
1371        N when (N =< 6) ->
1372            2;
1373        _ ->
1374            1
1375    catch
1376        _:_:_ ->
1377            10
1378    end.
1379
1380
1381linux_info_lookup(Key, File) ->
1382    LKey = string:to_lower(Key),
1383    try [string:trim(S) || S <- string:tokens(os:cmd("grep -i " ++ "\"" ++ LKey ++ "\"" ++ " " ++ File), [$:,$\n])] of
1384        Info ->
1385            linux_info_lookup_collect(LKey, Info, [])
1386    catch
1387        _:_:_ ->
1388            "-"
1389    end.
1390
1391linux_info_lookup_collect(_Key, [], Values) ->
1392    lists:reverse(Values);
1393linux_info_lookup_collect(Key, [Key, Value|Rest], Values) ->
1394    linux_info_lookup_collect(Key, Rest, [Value|Values]);
1395linux_info_lookup_collect(Key1, [Key2, Value|Rest], Values) ->
1396    case string:to_lower(Key2) of
1397	Key1 ->
1398	    linux_info_lookup_collect(Key1, Rest, [Value|Values]);
1399	_ ->
1400	    lists:reverse(Values)
1401    end;
1402linux_info_lookup_collect(_, _, Values) ->
1403    lists:reverse(Values).
1404
1405maybe_skip(_HostInfo) ->
1406
1407    %% We have some crap machines that causes random test case failures
1408    %% for no obvious reason. So, attempt to identify those without actually
1409    %% checking for the host name...
1410
1411    LinuxVersionVerify =
1412        fun(V) when (V > {3,6,11}) ->
1413                false; % OK - No skip
1414           (V) when (V =:= {3,6,11}) ->
1415                case string:trim(os:cmd("cat /etc/issue")) of
1416                    "Fedora release 16 " ++ _ -> % Stone age Fedora => Skip
1417                        true;
1418                    _ ->
1419                        false
1420                end;
1421           (V) when (V =:= {3,4,20}) ->
1422                case string:trim(os:cmd("cat /etc/issue")) of
1423                    "Wind River Linux 5.0.1.0" ++ _ -> % *Old* Wind River => skip
1424                        true;
1425                    _ ->
1426                        false
1427                end;
1428           (V) when (V =:= {2,6,32}) ->
1429                case string:trim(os:cmd("cat /etc/issue")) of
1430                    "Debian GNU/Linux 6.0 " ++ _ -> % Stone age Debian => Skip
1431                        true;
1432                    _ ->
1433                        false
1434                end;
1435           (V) when (V > {2,6,24}) ->
1436                false; % OK - No skip
1437           (V) when (V =:= {2,6,10}) ->
1438                case string:trim(os:cmd("cat /etc/issue")) of
1439                    "MontaVista" ++ _ -> % Stone age MontaVista => Skip
1440                        %% The real problem is that the machine is *very* slow
1441                        true;
1442                    _ ->
1443                        false
1444                end;
1445           (_) ->
1446                %% We are specifically checking for
1447                %% a *really* old gento...
1448                case string:find(string:strip(os:cmd("uname -a")), "gentoo") of
1449                    nomatch ->
1450                        false;
1451                    _ -> % Stone age gentoo => Skip
1452                        true
1453                end
1454        end,
1455    DarwinVersionVerify =
1456        fun(V) when (V > {9, 8, 0}) ->
1457                %% This version is OK: No Skip
1458                false;
1459           (_V) ->
1460                %% This version is *not* ok: Skip
1461                true
1462        end,
1463    SkipWindowsOnVirtual =
1464        %% fun() ->
1465        %%         SysMan = win_sys_info_lookup(system_manufacturer, HostInfo),
1466        %%         case string:to_lower(SysMan) of
1467        %%             "vmware" ++ _ ->
1468        %%                 true;
1469        %%             _ ->
1470        %%                 false
1471        %%         end
1472        %% end,
1473        fun() ->
1474                %% The host has been replaced and the VM has been reinstalled
1475                %% so for now we give it a chance...
1476                false
1477        end,
1478    COND = [{unix,  [{linux, LinuxVersionVerify},
1479                     {darwin, DarwinVersionVerify}]},
1480            {win32, SkipWindowsOnVirtual}],
1481    os_cond_skip(COND).
1482
1483os_cond_skip(any) ->
1484    true;
1485os_cond_skip(Skippable) when is_list(Skippable) ->
1486    os_cond_skip(Skippable, os:type());
1487os_cond_skip(_Crap) ->
1488    false.
1489
1490os_cond_skip(Skippable, {OsFam, OsName}) ->
1491    os_cond_skip(Skippable, OsFam, OsName);
1492os_cond_skip(Skippable, OsFam) ->
1493    os_cond_skip(Skippable, OsFam, undefined).
1494
1495os_cond_skip(Skippable, OsFam, OsName) ->
1496    %% Check if the entire family is to be skipped
1497    %% Example: [win32, unix]
1498    case lists:member(OsFam, Skippable) of
1499        true ->
1500            true;
1501        false ->
1502            %% Example: [{unix, freebsd}] | [{unix, [freebsd, darwin]}]
1503            case lists:keysearch(OsFam, 1, Skippable) of
1504                {value, {OsFam, OsName}} ->
1505                    true;
1506                {value, {OsFam, OsNames}} when is_list(OsNames) ->
1507                    %% OsNames is a list of:
1508                    %%    [atom()|{atom(), function/0 | function/1}]
1509                    case lists:member(OsName, OsNames) of
1510                        true ->
1511                            true;
1512                        false ->
1513                            os_cond_skip_check(OsName, OsNames)
1514                    end;
1515                {value, {OsFam, Check}} when is_function(Check, 0) ->
1516                    Check();
1517                {value, {OsFam, Check}} when is_function(Check, 1) ->
1518                    Check(os:version());
1519                _ ->
1520                    false
1521            end
1522    end.
1523
1524%% Performs a check via a provided fun with arity 0 or 1.
1525%% The argument is the result of os:version().
1526os_cond_skip_check(OsName, OsNames) ->
1527    case lists:keysearch(OsName, 1, OsNames) of
1528        {value, {OsName, Check}} when is_function(Check, 0) ->
1529            Check();
1530        {value, {OsName, Check}} when is_function(Check, 1) ->
1531            Check(os:version());
1532        _ ->
1533            false
1534    end.
1535
1536
1537%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1538
1539lookup(Key, Config, Default) ->
1540    case lists:keysearch(Key, 1, Config) of
1541        {value, {Key, Val}} ->
1542            Val;
1543        _ ->
1544            Default
1545    end.
1546
1547
1548%% Extract N number of hosts from the config.
1549%% Prepend with the own host.
1550%% If not N number of hosts are available, issue a skip exit.
1551good_hosts(N) when is_integer(N) andalso (N > 0) ->
1552    GoodHosts         = ct:get_config(test_hosts, []),
1553    {ok, CurrentHost} = inet:gethostname(),
1554    GoodHosts2        = [CurrentHost] ++ (GoodHosts -- [CurrentHost]),
1555    good_hosts2(GoodHosts2, N).
1556
1557good_hosts2(GoodHosts, N) ->
1558    good_hosts2(GoodHosts, N, []).
1559
1560good_hosts2(_GoodHosts, N, Acc) when (N =:= 0) ->
1561    lists:reverse(Acc);
1562good_hosts2([], _N, _Acc) ->
1563    ?SKIPE("Not enough good hosts");
1564good_hosts2([H|T], N, Acc) ->
1565    case inet:gethostbyname(H) of
1566        {ok, _} ->
1567            good_hosts2(T, N-1, [H|Acc]);
1568        {error, _} ->
1569            ?P("Host not found: ~p", [H]),
1570            good_hosts2(T, N, Acc)
1571    end.
1572
1573
1574
1575%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1576
1577set_tc_name(N) when is_atom(N) ->
1578    set_tc_name(atom_to_list(N));
1579set_tc_name(N) when is_list(N) ->
1580    put(tc_name, N).
1581
1582%% get_tc_name() ->
1583%%     get(tc_name).
1584
1585tc_begin(TC) ->
1586    OldVal = process_flag(trap_exit, true),
1587    put(old_trap_exit, OldVal),
1588    set_tc_name(TC),
1589    tc_print("begin ***",
1590             "~n----------------------------------------------------~n", "").
1591
1592tc_end(Result) when is_list(Result) ->
1593    OldVal = erase(old_trap_exit),
1594    process_flag(trap_exit, OldVal),
1595    tc_print("done: ~s", [Result],
1596             "", "----------------------------------------------------~n~n"),
1597    ok.
1598
1599%% *** tc_try/2,3 ***
1600%% Case: Basically the test case name
1601%% Cond: A fun that is evaluated before the actual test case
1602%%       The point of this is that it can performs checks to
1603%%       see if we shall run the test case at all.
1604%%       For instance, the test case may only work in specific
1605%%       conditions.
1606%% TC:   The test case fun
1607tc_try(Case, Cond, TC)
1608  when is_atom(Case) andalso
1609       is_function(Cond, 0) andalso
1610       is_function(TC, 0) ->
1611    tc_begin(Case),
1612    try Cond() of
1613        ok ->
1614            try
1615                begin
1616                    TC(),
1617                    ?SLEEP(?SECS(1)),
1618                    tc_end("ok")
1619                end
1620            catch
1621                C:{skip, _} = SKIP when ((C =:= throw) orelse (C =:= exit)) ->
1622                    %% i("catched[tc] (skip): "
1623                    %%   "~n   C:    ~p"
1624                    %%   "~n   SKIP: ~p"
1625                    %%   "~n", [C, SKIP]),
1626                    tc_end( f("skipping(catched,~w,tc)", [C]) ),
1627                    SKIP;
1628                C:E:S ->
1629                    %% i("catched[tc]: "
1630                    %%   "~n   C: ~p"
1631                    %%   "~n   E: ~p"
1632                    %%   "~n   S: ~p"
1633                    %%    "~n", [C, E, S]),
1634                    tc_end( f("failed(catched,~w,tc)", [C]) ),
1635                    erlang:raise(C, E, S)
1636            end;
1637        {skip, _} = SKIP ->
1638            tc_end("skipping(tc)"),
1639            SKIP;
1640        {error, Reason} ->
1641            tc_end("failed(tc)"),
1642            exit({tc_cond_failed, Reason})
1643    catch
1644        C:{skip, _} = SKIP when ((C =:= throw) orelse (C =:= exit)) ->
1645            %% i("catched[cond] (skip): "
1646            %%   "~n   C:    ~p"
1647            %%   "~n   SKIP: ~p"
1648            %%   "~n", [C, SKIP]),
1649            tc_end( f("skipping(catched,~w,cond)", [C]) ),
1650            SKIP;
1651        C:E:S ->
1652            %% i("catched[cond]: "
1653            %%   "~n   C: ~p"
1654            %%   "~n   E: ~p"
1655            %%   "~n   S: ~p"
1656            %%   "~n", [C, E, S]),
1657            tc_end( f("failed(catched,~w,cond)", [C]) ),
1658            erlang:raise(C, E, S)
1659    end.
1660
1661
1662tc_print(F, Before, After) ->
1663    tc_print(F, [], Before, After).
1664
1665tc_print(F, A, Before, After) ->
1666    Name = tc_which_name(),
1667    FStr = f("*** [~s][~s][~p] " ++ F ++ "~n",
1668             [formated_timestamp(),Name,self()|A]),
1669    io:format(user, Before ++ FStr ++ After, []).
1670
1671tc_which_name() ->
1672    case get(tc_name) of
1673        undefined ->
1674            case get(sname) of
1675                undefined ->
1676                    "";
1677                SName when is_list(SName) ->
1678                    SName
1679            end;
1680        Name when is_list(Name) ->
1681            Name
1682    end.
1683
1684
1685%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1686
1687start_slave_node(Name, Args) ->
1688    start_slave_node(Name, Args, []).
1689
1690start_slave_node(Name, Args, Opts) ->
1691    start_node(Name, slave, Args, Opts).
1692
1693
1694start_node(Name, Type, Args) ->
1695    start_node(Name, Type, Args, []).
1696
1697start_node(Name, Type, Args, Opts) ->
1698    Pa = filename:dirname(code:which(?MODULE)),
1699    A = Args ++
1700        " -pa " ++ Pa ++
1701        " -s " ++ atom_to_list(kernel_test_sys_monitor) ++ " start" ++
1702        " -s global sync",
1703    case test_server:start_node(Name, Type, [{args, A}|Opts]) of
1704        {ok, _Node} = OK ->
1705            global:sync(),
1706	    OK;
1707        ERROR ->
1708            ERROR
1709    end.
1710
1711stop_node(Node) ->
1712    test_server:stop_node(Node).
1713
1714
1715%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1716
1717timetrap_scale_factor() ->
1718    case (catch test_server:timetrap_scale_factor()) of
1719        {'EXIT', _} ->
1720            1;
1721        N ->
1722            N
1723    end.
1724
1725
1726proxy_call(F, Timeout, Default)
1727  when is_function(F, 0) andalso is_integer(Timeout) andalso (Timeout > 0) ->
1728    {P, M} = erlang:spawn_monitor(fun() -> exit(F()) end),
1729    receive
1730        {'DOWN', M, process, P, Reply} ->
1731            Reply
1732    after Timeout ->
1733            erlang:demonitor(M, [flush]),
1734            exit(P, kill),
1735            Default
1736    end.
1737
1738
1739
1740%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1741
1742socket_type(Config) ->
1743    case is_socket_backend(Config) of
1744	true ->
1745	    socket;
1746	false ->
1747	    port
1748    end.
1749
1750%% gen_tcp wrappers
1751
1752listen(Config, Port, Opts) ->
1753    InetBackendOpts = inet_backend_opts(Config),
1754    gen_tcp:listen(Port, InetBackendOpts ++ Opts).
1755
1756connect(Config, Host, Port, Opts) ->
1757    InetBackendOpts = inet_backend_opts(Config),
1758    gen_tcp:connect(Host, Port, InetBackendOpts ++ Opts).
1759
1760connect(Config, Host, Port, Opts, Timeout) ->
1761    InetBackendOpts = inet_backend_opts(Config),
1762    gen_tcp:connect(Host, Port, InetBackendOpts ++ Opts, Timeout).
1763
1764
1765%% gen_udp wrappers
1766
1767open(Config, Port, Opts) ->
1768    InetBackendOpts = inet_backend_opts(Config),
1769    gen_udp:open(Port, InetBackendOpts ++ Opts).
1770
1771
1772inet_backend_opts(Config) when is_list(Config) ->
1773    case lists:keysearch(socket_create_opts, 1, Config) of
1774        {value, {socket_create_opts, InetBackendOpts}} ->
1775            InetBackendOpts;
1776        false ->
1777            []
1778    end.
1779
1780is_socket_backend(Config) when is_list(Config) ->
1781    case lists:keysearch(socket_create_opts, 1, Config) of
1782        {value, {socket_create_opts, [{inet_backend, socket}]}} ->
1783            true;
1784        _ ->
1785            false
1786    end.
1787
1788
1789explicit_inet_backend() ->
1790    case application:get_all_env(kernel) of
1791        Env when is_list(Env) ->
1792            case lists:keysearch(inet_backend, 1, Env) of
1793                {value, {inet_backend, _}} ->
1794                    true;
1795                _ ->
1796                    false
1797            end;
1798        _ ->
1799            false
1800    end.
1801
1802
1803test_inet_backends() ->
1804    case application:get_all_env(kernel) of
1805        Env when is_list(Env) ->
1806            case lists:keysearch(test_inet_backends, 1, Env) of
1807                {value, {test_inet_backends, true}} ->
1808                    true;
1809                _ ->
1810                    false
1811            end;
1812        _ ->
1813            false
1814    end.
1815
1816
1817%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1818
1819f(F, A) ->
1820    lists:flatten(io_lib:format(F, A)).
1821
1822formated_timestamp() ->
1823    format_timestamp(os:timestamp()).
1824
1825format_timestamp({_N1, _N2, N3} = TS) ->
1826    {_Date, Time}   = calendar:now_to_local_time(TS),
1827    {Hour, Min, Sec} = Time,
1828    FormatTS = io_lib:format("~.2.0w:~.2.0w:~.2.0w.~.3.0w",
1829                             [Hour, Min, Sec, N3 div 1000]),
1830    lists:flatten(FormatTS).
1831
1832print(F) ->
1833    print(F, []).
1834
1835print(F, A) ->
1836    io:format("~s ~p " ++ F ++ "~n", [formated_timestamp(), self() | A]).
1837