Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

first draft of alire test runner #1850

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions src/alr/alr-commands-test2.adb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
with Alire.Directories;
with Alr.Test_Runner;

package body Alr.Commands.Test2 is
overriding procedure Execute
(Cmd : in out Command; Args : AAA.Strings.Vector)
is
use Alire.Directories;
use GNAT.Strings;

Subfolder : constant String :=
(if Cmd.Directory.all /= "" then Cmd.Directory.all else "tests");
begin
Cmd.Requires_Workspace;
Cmd.Set (Alire.Roots.Load_Root (Cmd.Root.Path / Subfolder));
Cmd.Requires_Workspace (Sync => True);

declare
G : Guard (Enter_Folder (Cmd.Root.Path));
pragma Unreferenced (G);
begin
Alr.Test_Runner.Run
(Cmd.Root, AAA.Strings.Empty_Vector, Integer'Max (Cmd.Jobs, 0));
end;
end Execute;

----------------------
-- Long_Description --
----------------------

overriding function Long_Description
(Cmd : Command) return AAA.Strings.Vector is
(AAA.Strings.Empty_Vector.Append
("Run tests in a predefined format using the (experimental) " &
"default alire test runner"));

--------------------
-- Setup_Switches --
--------------------

overriding procedure Setup_Switches
(Cmd : in out Command;
Config : in out CLIC.Subcommand.Switches_Configuration)
is
use CLIC.Subcommand;
begin
Define_Switch
(Config, Cmd.Jobs'Access, "-j:", "--jobs=",
"Run up to N tests in parallel, or as many as processors " &
"if 0 (default)", Default => 0, Argument => "N");

Define_Switch
(Config, Cmd.Directory'Access, Long_Switch => "--dir=",
Help => "Run tests from the given folder (default: tests)",
Argument => "<dir>");
end Setup_Switches;
end Alr.Commands.Test2;
35 changes: 35 additions & 0 deletions src/alr/alr-commands-test2.ads
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
with AAA.Strings;
with GNAT.Strings;

package Alr.Commands.Test2 is

type Command is new Commands.Command with private;

overriding function Name
(Cmd : Command) return CLIC.Subcommand.Identifier is
("test2");

overriding procedure Execute
(Cmd : in out Command; Args : AAA.Strings.Vector);

overriding function Long_Description
(Cmd : Command) return AAA.Strings.Vector;

overriding procedure Setup_Switches
(Cmd : in out Command;
Config : in out CLIC.Subcommand.Switches_Configuration);

overriding function Short_Description (Cmd : Command) return String is
("Test the current crate with the default alire test runner");

overriding function Usage_Custom_Parameters (Cmd : Command) return String is
("");

private

type Command is new Commands.Command with record
Jobs : aliased Integer;
Directory : aliased GNAT.Strings.String_Access;
end record;

end Alr.Commands.Test2;
2 changes: 2 additions & 0 deletions src/alr/alr-commands.adb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ with Alr.Commands.Search;
with Alr.Commands.Settings;
with Alr.Commands.Show;
with Alr.Commands.Test;
with Alr.Commands.Test2;
with Alr.Commands.Toolchain;
with Alr.Commands.Update;
with Alr.Commands.Version;
Expand Down Expand Up @@ -770,6 +771,7 @@ begin
Sub_Cmd.Register ("Testing", new Action.Command);
Sub_Cmd.Register ("Testing", new Dev.Command);
Sub_Cmd.Register ("Testing", new Test.Command);
Sub_Cmd.Register ("Testing", new Test2.Command);

-- Help topics --
Sub_Cmd.Register (new Topics.Aliases.Topic);
Expand Down
213 changes: 213 additions & 0 deletions src/alr/alr-test_runner.adb
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
with Ada.Command_Line;
with Ada.Containers.Indefinite_Ordered_Maps;
with Ada.Text_IO;
with GNAT.OS_Lib;
with System.Multiprocessors;

with Alire;
with Alire.Directories; use Alire.Directories;
with Alire.OS_Lib;
with Alire.Utils.Text_Files; use Alire.Utils;

with CLIC.TTY;

package body Alr.Test_Runner is

protected Driver is
-- Protected driver for synchronising stats and output

procedure Pass (Msg : String);
-- Report a passing test with a message

procedure Fail (Msg : String; Output : AAA.Strings.Vector);
-- Report a failing test with a message and its output

function Total_Count return Natural;
-- Get the total number of tests that have been run

function Fail_Count return Natural;
-- Get the number of failed tests
private
Passed : Natural := 0;
Failed : Natural := 0;
end Driver;

protected body Driver is
procedure Pass (Msg : String) is
begin
Passed := Passed + 1;
Alr.Trace.Always ("[ " & CLIC.TTY.OK ("PASS") & " ] " & Msg);
end Pass;

procedure Fail (Msg : String; Output : AAA.Strings.Vector) is
begin
Failed := Failed + 1;
Alr.Trace.Always ("[ " & CLIC.TTY.Error ("FAIL") & " ] " & Msg);
if not Output.Is_Empty then
Alr.Trace.Info ("*** Test output ***");
for L of Output loop
Alr.Trace.Info (CLIC.TTY.Dim (L));
end loop;
Alr.Trace.Info ("*** End Test output ***");
end if;
end Fail;

function Total_Count return Natural is (Passed + Failed);
function Fail_Count return Natural is (Failed);
end Driver;

procedure Create_Gpr_List
(Root : Alire.Roots.Root; List : AAA.Strings.Vector)
-- Create a gpr file containing a list of the test files
-- (named `Test_Files`).

is
File_Path : constant Alire.Absolute_Path :=
Root.Path / "config" / (Root.Name.As_String & "_list_config.gpr");
File : Text_Files.File := Text_Files.Create (File_Path);
Lines : access AAA.Strings.Vector renames File.Lines;
First : Boolean := True;

Indent : constant String := " ";

Root_Name : constant String :=
AAA.Strings.To_Mixed_Case (Root.Name.As_String);
begin
Touch (File_Path, True);

Lines.Append_Line ("abstract project " & Root_Name & "_List_Config is");
Lines.Append_Line (Indent & "Test_Files := (");

for Name of List loop
Lines.Append_Line (Indent & Indent);
if First then
Lines.Append_To_Last_Line (" ");
First := False;
else
Lines.Append_To_Last_Line (",");
end if;
Lines.Append_To_Last_Line ("""" & Name & ".adb""");
end loop;

Lines.Append_Line (Indent & ");");
Lines.Append_Line ("end " & Root_Name & "_List_Config;");
end Create_Gpr_List;

procedure Run_All_Tests
(Root : Alire.Roots.Root; Test_List : AAA.Strings.Vector; Jobs : Positive)
is
use GNAT.OS_Lib;

function Cmp (A, B : Process_Id) return Boolean is
(Pid_To_Integer (A) < Pid_To_Integer (B));

package Map is new Ada.Containers.Indefinite_Ordered_Maps
(Process_Id, String, "<" => Cmp);

Running_Tests : Map.Map := Map.Empty_Map;
Output_Files : Map.Map := Map.Empty_Map;

procedure Spawn_Test (Test_Name : String) is
Exe_Name : constant String := Test_Name & Alire.OS_Lib.Exe_Suffix;
Filename : constant String := "output_" & Test_Name & ".tmp";

Args : constant Argument_List := (1 .. 0 => <>);
Pid : Process_Id;
begin
Pid := Non_Blocking_Spawn (Root.Path / "bin" / Exe_Name,
Args, Filename, Err_To_Out => True);
if Pid = Invalid_Pid then
Driver.Fail (Test_Name & " (failed to start!)",
AAA.Strings.Empty_Vector);
else
Running_Tests.Insert (Pid, Test_Name);
Output_Files.Insert (Pid, Filename);
end if;
end Spawn_Test;

Pid : Process_Id;
Success : Boolean;

Remaining : AAA.Strings.Vector := Test_List;

begin

-- start the first `Jobs` tests
for I in 1 .. Natural'Min (Jobs, Natural (Test_List.Length)) loop
Spawn_Test (Remaining.First_Element);
Remaining := Remaining.Tail;
end loop;

loop
-- wait for one test to finish
Wait_Process (Pid, Success);

if Pid = Invalid_Pid then
-- if no process was running, end the loop
exit;
end if;

if Success then
Driver.Pass (Running_Tests (Pid));
else
declare
use Alire.Utils.Text_Files;
Output : File := Load (Output_Files (Pid), False);
begin
Driver.Fail (Running_Tests (Pid), Output.Lines.all);
end;
end if;

Delete_File (Output_Files (Pid), Success);
Running_Tests.Delete (Pid);
Output_Files.Delete (Pid);

if not Remaining.Is_Empty then
-- start up a new test
Spawn_Test (Remaining.First_Element);
Remaining := Remaining.Tail;
end if;
end loop;
end Run_All_Tests;

procedure Run
(Root : in out Alire.Roots.Root;
Args : AAA.Strings.Vector := AAA.Strings.Empty_Vector;
Jobs : Natural := 0)
is
Job_Count : constant Positive :=
(if Jobs = 0 then Positive (System.Multiprocessors.Number_Of_CPUs)
else Jobs);
Path : constant Alire.Absolute_Path := Root.Path;
Test_List : AAA.Strings.Vector;

procedure Append (Dir_Entry : Adirs.Directory_Entry_Type) is
-- Helper function to append all .adb files in a folder
-- to the `Test_List` vector

Name : constant String := Adirs.Simple_Name (Dir_Entry);
begin
if Name'Length > 4 and then Name (Name'Last - 3 .. Name'Last) = ".adb"
then
Test_List.Append (Name (Name'First .. Name'Last - 4));
end if;
end Append;

begin
Adirs.Search (Path / "src", "", Process => Append'Access);
Create_Gpr_List (Root, Test_List);

Alr.Trace.Info ("Building tests");
if Alire.Roots.Build (Root, Args, Build_All_Deps => True) then
Alr.Trace.Info ("Running" & Test_List.Length'Image & " tests");
Run_All_Tests (Root, Test_List, Job_Count);

Alr.Trace.Info ("Total:" & Driver.Total_Count'Image & " tests");
Ada.Text_IO.Flush;
if Driver.Fail_Count /= 0 then
Alr.Trace.Error ("failed" & Driver.Fail_Count'Image & " test");
Ada.Command_Line.Set_Exit_Status (Ada.Command_Line.Failure);
end if;
end if;
end Run;
end Alr.Test_Runner;
12 changes: 12 additions & 0 deletions src/alr/alr-test_runner.ads
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
with Alire.Roots;

with AAA.Strings;

package Alr.Test_Runner is
procedure Run
(Root : in out Alire.Roots.Root;
Args : AAA.Strings.Vector := AAA.Strings.Empty_Vector;
Jobs : Natural := 0);
-- Run all .adb files in the `src` folder of the given root as
-- separate tests.
end Alr.Test_Runner;
Loading