VSharp-team / VSharp

Symbolic execution engine for .NET Core
Apache License 2.0
50 stars 32 forks source link

Generate tests for code written in older .NET frameworks #323

Open smhmhmd opened 3 months ago

smhmhmd commented 3 months ago

Hi @MchKosticyn

Nopcommerce-release-3.80 uses the older Windows-based .NET Framework (VS upgrades to .NET Framework 4.8), can VSharp generate tests for such code, I got this error while running VSharp from Visual Studio on Windows-10:

Unhandled exception: System.TypeLoadException: Could not load type 'System.Web.HttpResponseBase' from assembly 'System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
   at System.RuntimeMethodHandle.GetMethodBody(IRuntimeMethodInfo method, RuntimeType declaringType)
   at System.Reflection.RuntimeMethodInfo.GetMethodBody()
   at VSharp.CSharpUtils.ReflectionUtils.<>c__DisplayClass1_0.<EnumerateExplorableMethods>b__0(MethodInfo m) in D:\Users\samiull\Documents\2023\cymbal-testgen\Cymbal-TestGen\VSharp\VSharp.CSharpUtils\ReflectionUtils.cs:line 23
   at System.Linq.Enumerable.WhereArrayIterator`1.MoveNext()
   at VSharp.CSharpUtils.ReflectionUtils.EnumerateExplorableMethods(Type type)+MoveNext() in D:\Users\samiull\Documents\2023\cymbal-testgen\Cymbal-TestGen\VSharp\VSharp.CSharpUtils\ReflectionUtils.cs:line 23
   at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at VSharp.TestGenerator.Cover(Assembly assembly, VSharpOptions options, Boolean renderSingleFile) in D:\Users\samiull\Documents\2023\cymbal-testgen\Cymbal-TestGen\VSharp\VSharp.API\VSharp.cs:line 455
   at VSharp.Runner.RunnerProgram.AllPublicMethodsHandler(FileInfo assemblyPath, Int32 timeout, Int32 solverTimeout, DirectoryInfo output, Boolean renderTests, Boolean runTests, SearchStrategy strat, Verbosity verbosity, UInt32 recursionThreshold, ExplorationMode explorationMode) in D:\Users\samiull\Documents\2023\cymbal-testgen\Cymbal-TestGen\VSharp\VSharp.Runner\RunnerProgram.cs:line 173
   at VSharp.Runner.RunnerProgram.<>c__DisplayClass9_0.<Main>b__9(InvocationContext context) in D:\Users\samiull\Documents\2023\cymbal-testgen\Cymbal-TestGen\VSharp\VSharp.Runner\RunnerProgram.cs:line 451
   at System.CommandLine.Invocation.AnonymousCommandHandler.Invoke(InvocationContext context)
   at System.CommandLine.Invocation.InvocationPipeline.<>c__DisplayClass4_0.<<BuildInvocationChain>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass17_0.<<UseParseErrorReporting>b__0>d.MoveNext()
--- End of stack trace from previous l

Command in Visual Studio is --all-public-methods D:\Users\samiull\Documents\2024\nopCommerce-release-3.80\src\Presentation\Nop.Web\bin\Nop.Core.dll -t 30 -o ..\out -v Trace

smhmhmd commented 3 months ago

.NET Framework reference assemblies probably need to be added. If VSharp can have a command-line parameter to point to reference assemblies, that may help.

smhmhmd commented 3 months ago

Getting method body does work with Mono.Cecil.

using System.Collections.Immutable;
using System.IO;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.CompilerServices;
using Microsoft.Win32.SafeHandles;
using System.Reflection.PortableExecutable;
using System.Xml;
using System.Diagnostics;
using System.Xml.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;

class Program
{
    static void Main(string[] args)
    {

        var inputAssembly =
            @"D:\Users\samiull\Documents\2024\nopCommerce-release-3.80\src\Libraries\Nop.Core\bin\Debug\Nop.Core.dll";
        var path = Path.GetDirectoryName(inputAssembly);

        var assemblyResolver = new DefaultAssemblyResolver();
        var assemblyLocation = Path.GetDirectoryName(inputAssembly);
        assemblyResolver.AddSearchDirectory(assemblyLocation);

        var readerParameters = new ReaderParameters { AssemblyResolver = assemblyResolver };

        var assemblyDefinition = Mono.Cecil.AssemblyDefinition.ReadAssembly(
            @"D:\Users\samiull\Documents\2024\nopCommerce-release-3.80\src\Libraries\Nop.Core\bin\Debug\Nop.Core.dll",
            readerParameters);

        var module = assemblyDefinition.MainModule;
        foreach (var typeDefinition in module.Types)
        {
            Console.WriteLine("Type = " + typeDefinition.ToString());
        }

        var methods = assemblyDefinition.MainModule
            .GetTypes()
            .SelectMany(t => t.Methods
                .Where(m => m.HasBody)
                .Select(m =>  m ));

        foreach (var m in methods)
        {
            var count = m.Body.Instructions.Count;
            Console.WriteLine("Method " + m.Name + " has " + count + " instructions");
        }
    }
}
MchKosticyn commented 3 months ago

Hi @smhmhmd

Before running V#, you should do dotnet publish --self-contained -r (RID) -c Release. You can find your RID in this paper https://learn.microsoft.com/ru-ru/dotnet/core/rid-catalog. This command will create folder 'publish', in which all dependency assemblies will be. Example of needed folder path: /Users/michael/Documents/Work/VSharp/VSharp.TestGenerator/bin/Release/net7.0/osx-arm64/publish/. After that you need to use that path inside V# running command. Example: --all-public-methods /Users/michael/Documents/Work/VSharp/VSharp.TestGenerator/bin/Release/net7.0/osx-arm64/publish/VSharp.TestGenerator.dll .... This will help V# to find dependencies.

smhmhmd commented 3 months ago

Hi @MchKosticyn

I published VSharp to a folder using Visual Studio and also published NopCommerce-3.80 to a folder. The error is still there, will using Mono.Cecil instead of System.Reflection work better ?

samiull@DESKTOP-OS5I16B MINGW64 /d/Users/samiull/Documents/2024/vsharp-publish
$  dotnet VSharp.Runner.dll  --all-public-methods /d/Users/samiull/Documents/2024/nopcommerce-publish/bin/Nop.Web.dll -o ../out/ -t 30
Unhandled exception: System.TypeLoadException: Could not load type 'System.Web.HttpContextBase' from assembly 'System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
   at System.RuntimeMethodHandle.GetMethodBody(IRuntimeMethodInfo method, RuntimeType declaringType)
   at System.Reflection.RuntimeMeth