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

Exposing --output and --spill-translation in dafny test, per Issue 3612 #3769

Merged
merged 15 commits into from
Mar 27, 2023
Merged
Show file tree
Hide file tree
Changes from 9 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
4 changes: 3 additions & 1 deletion Source/DafnyCore/Compilers/SinglePassCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1621,7 +1621,9 @@ public static bool HasMain(Program program, out Method mainMethod) {
}
}
}
ReportError(program.Reporter, program.DefaultModule.tok, "Could not find the method named by the -Main option: {0}", null, name);
if (name != RunAllTestsMainMethod.SyntheticTestMainName) {
ReportError(program.Reporter, program.DefaultModule.tok, "Could not find the method named by the -Main option: {0}", null, name);
}
}
foreach (var module in program.CompileModules) {
if (module.IsAbstract) {
Expand Down
1 change: 1 addition & 0 deletions Source/DafnyCore/DafnyOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ public enum ContractTestingMode {
public bool RewriteFocalPredicates = true;
public bool PrintTooltips = false;
public bool PrintStats = false;
public string MethodsToTest = null;
public bool DisallowConstructorCaseWithoutParentheses = false;
public bool PrintFunctionCallGraph = false;
public bool WarnShadowing = false;
Expand Down
8 changes: 8 additions & 0 deletions Source/DafnyCore/Options/CommonOptionBag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ Note that quantifier variable domains (<- <Domain>) are available in both syntax
};
public static readonly Option<bool> VerifyIncludedFiles = new("--verify-included-files",
"Verify code in included files.");
public static readonly Option<bool> UseBaseFileName = new("--use-basename-for-filename",
"When parsing use basename of file for tokens instead of the path supplied on the command line") {
};
public static readonly Option<bool> SpillTranslation = new("--spill-translation",
@"In case the Dafny source code is translated to another language, emit that translation.") {
};
public static readonly Option<bool> WarningAsErrors = new("--warn-as-errors",
"Treat warnings as errors.");
public static readonly Option<bool> WarnMissingConstructorParenthesis = new("--warn-missing-constructor-parentheses",
Expand Down Expand Up @@ -201,6 +207,7 @@ static CommonOptionBag() {
}
});
DafnyOptions.RegisterLegacyBinding(IncludeRuntimeOption, (options, value) => { options.IncludeRuntime = value; });
DafnyOptions.RegisterLegacyBinding(UseBaseFileName, (o, f) => o.UseBaseNameForFileName = f);
DafnyOptions.RegisterLegacyBinding(UseJavadocLikeDocstringRewriterOption,
(options, value) => { options.UseJavadocLikeDocstringRewriter = value; });
DafnyOptions.RegisterLegacyBinding(WarnShadowing, (options, value) => { options.WarnShadowing = value; });
Expand Down Expand Up @@ -252,6 +259,7 @@ static CommonOptionBag() {
}
});
DafnyOptions.RegisterLegacyBinding(SolverLog, (o, v) => o.ProverLogFilePath = v);
DafnyOptions.RegisterLegacyBinding(SpillTranslation, (o, f) => o.SpillTargetCode = f ? 1U : 0U);

DafnyOptions.RegisterLegacyBinding(EnforceDeterminism, (options, value) => {
options.ForbidNondeterminism = value;
Expand Down
11 changes: 0 additions & 11 deletions Source/DafnyCore/Options/DeveloperOptionBag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,6 @@ namespace Microsoft.Dafny;

public class DeveloperOptionBag {

public static readonly Option<bool> SpillTranslation = new("--spill-translation",
@"In case the Dafny source code is translated to another language, emit that translation.") {
IsHidden = true
};

public static readonly Option<bool> UseBaseFileName = new("--use-basename-for-filename",
"When parsing use basename of file for tokens instead of the path supplied on the command line") {
};

public static readonly Option<string> BoogiePrint = new("--bprint",
@"
Print Boogie program translated from Dafny
Expand Down Expand Up @@ -44,7 +35,6 @@ Print Dafny program after resolving it.
};

static DeveloperOptionBag() {
DafnyOptions.RegisterLegacyBinding(SpillTranslation, (o, f) => o.SpillTargetCode = f ? 1U : 0U);

DafnyOptions.RegisterLegacyBinding(ResolvedPrint, (options, value) => {
options.DafnyPrintResolvedFile = value;
Expand All @@ -58,7 +48,6 @@ static DeveloperOptionBag() {
options.FileTimestamp);
});

DafnyOptions.RegisterLegacyBinding(UseBaseFileName, (o, f) => o.UseBaseNameForFileName = f);
DafnyOptions.RegisterLegacyBinding(BoogiePrint, (options, f) => {
options.PrintFile = f;
options.ExpandFilename(options.PrintFile, x => options.PrintFile = x, options.LogPrefix,
Expand Down
5 changes: 2 additions & 3 deletions Source/DafnyCore/Options/ICommandSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ static ICommandSpec() {
CommonOptionBag.Check,
CommonOptionBag.Verbose,
CommonOptionBag.FormatPrint,
DeveloperOptionBag.UseBaseFileName
}.Concat(ParserOptions);

public static IReadOnlyList<Option> VerificationOptions = new Option[] {
Expand Down Expand Up @@ -53,12 +52,11 @@ static ICommandSpec() {

public static IReadOnlyList<Option> ExecutionOptions = new Option[] {
CommonOptionBag.Target,
DeveloperOptionBag.SpillTranslation
CommonOptionBag.SpillTranslation
}.Concat(TranslationOptions).ToList();

public static IReadOnlyList<Option> ConsoleOutputOptions = new List<Option>(new Option[] {
DafnyConsolePrinter.ShowSnippets,
DeveloperOptionBag.UseBaseFileName,
DeveloperOptionBag.Print,
DeveloperOptionBag.ResolvedPrint,
DeveloperOptionBag.BoogiePrint,
Expand All @@ -75,6 +73,7 @@ static ICommandSpec() {
Function.FunctionSyntaxOption,
CommonOptionBag.QuantifierSyntax,
CommonOptionBag.UnicodeCharacters,
CommonOptionBag.UseBaseFileName,
CommonOptionBag.TypeSystemRefresh,
CommonOptionBag.ErrorLimit,
});
Expand Down
19 changes: 13 additions & 6 deletions Source/DafnyCore/Resolver/RunAllTestsMainMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@
using System.Diagnostics.Contracts;
using System.Linq;
using Microsoft.Boogie;
using System.Text.RegularExpressions;

namespace Microsoft.Dafny;

public class RunAllTestsMainMethod : IRewriter {

/** The name used for Main when executing tests. Should be a name that cannot be a Dafny name,
that Dafny will not use as a mangled Dafny name for any backend, and that is not likely
to be chosen by the user as an extern name. **/
public static string SyntheticTestMainName = "_Test__Main_";

public RunAllTestsMainMethod(ErrorReporter reporter) : base(reporter) {
}

Expand All @@ -19,17 +25,12 @@ public RunAllTestsMainMethod(ErrorReporter reporter) : base(reporter) {
/// errors much earlier in the pipeline.
/// </summary>
internal override void PreResolve(Program program) {
var hasMain = Compilers.SinglePassCompiler.HasMain(program, out var mainMethod);
if (hasMain) {
Reporter.Error(MessageSource.Rewriter, mainMethod.tok, "Cannot use /runAllTests on a program with a main method");
return;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code at the end of this file still calls hasMain and an assertion fails when running with /testContracts. I wonder whether the implementation of HasMain need to be updated to account for the possibility of the new synthetic name you're using?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was a satisfyingly simple fix. :)


// Verifying the method isn't yet possible since the translation of try/recover statments is not implemented,
// and would be low-value anyway.
var noVerifyAttribute = new Attributes("verify", new List<Expression> { new LiteralExpr(Token.NoToken, false) }, null);

mainMethod = new Method(RangeToken.NoToken, new Name("Main"), false, false,
var mainMethod = new Method(RangeToken.NoToken, new Name(SyntheticTestMainName), false, false,
new List<TypeParameter>(), new List<Formal>(), new List<Formal>(),
new List<AttributedExpression>(),
new Specification<FrameExpression>(new List<FrameExpression>(), null),
Expand Down Expand Up @@ -98,6 +99,12 @@ internal override void PostResolve(Program program) {
foreach (var moduleDefinition in program.CompileModules) {
foreach (var callable in ModuleDefinition.AllCallables(moduleDefinition.TopLevelDecls)) {
if ((callable is Method method) && Attributes.Contains(method.Attributes, "test")) {
var regex = program.Options.MethodsToTest;
if (regex != null) {
string name = method.FullDafnyName;
if (!Regex.Match(name, regex).Success) continue;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have braces, as the CI complains about.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

}

// print "TestMethod: ";
mainMethodStatements.Add(Statement.CreatePrintStmt(tok,
Expression.CreateStringLiteral(tok, $"{method.FullDafnyName}: ")));
Expand Down
12 changes: 12 additions & 0 deletions Source/DafnyDriver/Commands/TestCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,21 @@ namespace Microsoft.Dafny;
public class TestCommand : ICommandSpec {
public IEnumerable<Option> Options =>
new Option[] {
CommonOptionBag.Output,
MethodsToTest,
}.Concat(ICommandSpec.ExecutionOptions).
Concat(ICommandSpec.ConsoleOutputOptions).
Concat(ICommandSpec.ResolverOptions);

public static readonly Option<string> MethodsToTest = new("--methods-to-test",
"A regex (over fully qualified names) selecting which methods or functions to test") {
};

static TestCommand() {
DafnyOptions.RegisterLegacyBinding(MethodsToTest, (o, v) => { o.MethodsToTest = v; });
}


public Command Create() {
var result = new Command("test", "(Experimental) Execute every method in the program that's annotated with the {:test} attribute.");
result.AddArgument(ICommandSpec.FilesArgument);
Expand All @@ -24,5 +35,6 @@ public void PostProcess(DafnyOptions dafnyOptions, Options options, InvocationCo
dafnyOptions.RunAllTests = true;
dafnyOptions.ForceCompile = dafnyOptions.Get(BoogieOptionBag.NoVerify);
dafnyOptions.CompileVerbose = false;
dafnyOptions.MainMethod = RunAllTestsMainMethod.SyntheticTestMainName;
}
}
27 changes: 27 additions & 0 deletions Test/git-issues/git-issue-3221.dfy
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// RUN: %baredafny test "%s" > "%t"
// RUN: %baredafny test --methods-to-test='q' "%s" >> "%t"
// RUN: %baredafny test --methods-to-test='m' "%s" >> "%t"
// RUN: %baredafny test --methods-to-test='M.*' "%s" >> "%t"
// RUN: %baredafny test --methods-to-test='^.$' "%s" >> "%t"
// RUN: %diff "%s.expect" "%t"

method {:test} m() {
print "Testing m\n";
}
method {:test} q() {
print "Testing q\n";
}
method {:test} pp() {
print "Testing pp\n";
}

module M {

method {:test} m() {
print "Testing M.m\n";
}
method {:test} t() {
print "Testing M.t\n";
}
}

34 changes: 34 additions & 0 deletions Test/git-issues/git-issue-3221.dfy.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

Dafny program verifier finished with 0 verified, 0 errors
M.m: Testing M.m
PASSED
M.t: Testing M.t
PASSED
m: Testing m
PASSED
q: Testing q
PASSED
pp: Testing pp
PASSED

Dafny program verifier finished with 0 verified, 0 errors
q: Testing q
PASSED

Dafny program verifier finished with 0 verified, 0 errors
M.m: Testing M.m
PASSED
m: Testing m
PASSED

Dafny program verifier finished with 0 verified, 0 errors
M.m: Testing M.m
PASSED
M.t: Testing M.t
PASSED

Dafny program verifier finished with 0 verified, 0 errors
m: Testing m
PASSED
q: Testing q
PASSED
11 changes: 11 additions & 0 deletions Test/git-issues/git-issue-3744.dfy
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// RUN: %baredafny test "%s" > "%t"
// RUN: %baredafny run "%s" >> "%t"
// RUN: %diff "%s.expect" "%t"

method Main() {
print "Main\n";
}

method {:test} Test() {
print "Test\n";
}
7 changes: 7 additions & 0 deletions Test/git-issues/git-issue-3744.dfy.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

Dafny program verifier finished with 0 verified, 0 errors
Test: Test
PASSED

Dafny program verifier finished with 0 verified, 0 errors
Main
20 changes: 18 additions & 2 deletions docs/DafnyRef/UserGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -434,11 +434,16 @@ errors or if --check is stipulated and at least one file is not the same as its

#### 13.5.1.10. `dafny test` {#sec-dafny-test}

This _experimental_ command (verifies and compiles the program and) runs every method in the program that is annotated with the `{:test}` attribute.
This command (verifies and compiles the program and) runs every method in the program that is annotated with the `{:test}` attribute.
Verification can be disabled using the `--no-verify` option. `dafny test` also accepts all other options of the `dafny build` command.
In particular, it accepts the `--target` option that specifies the programming language used in the build and execution phases.

There are currently no other options specific to the `dafny test` command.
`dafny test` also accepts these options:

- `-spill-translation` - (default disabled) when enabled the compilation artifacts are retained
- `--output` - gives the folder and filename root for compilation artifacts
- `--methods-to-test` - the value is a (.NET) regular expression that is matched against the fully
qualified name of the method; only those methods that match are tested

The order in which the tests are run is not specified.

Expand Down Expand Up @@ -479,6 +484,17 @@ Hi!
PASSED
```

and this command-line
```bash
dafny test --no-verify --methods-to-test='m' t.dfy
```
produces this output text:
```text
m: mm
Hi!
PASSED
```

#### 13.5.1.11. `dafny generate-tests` {#sec-dafny-generate-tests}

This _experimental_ command (verifies the program and) then generates unit test code (as Dafny source code) that provides
Expand Down
1 change: 1 addition & 0 deletions docs/dev/news/3221.fix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* dafny test accepts a --methods-to-test option whose value is a regular expression selecting which tests to include in the test run
2 changes: 2 additions & 0 deletions docs/dev/news/3612.feat
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
* Exposes the --output and --spill-translation options for the dafny test command

1 change: 1 addition & 0 deletions docs/dev/news/3744.fix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Main and {:test} methods may now be in the same program