Unit test common runtime language detected invalid program when calling method.GetCustomAttributes

Unit test common runtime language detected invalid program when calling method.GetCustomAttributes

nwbos
Enthusiast Enthusiast
713 Views
5 Replies
Message 1 of 6

Unit test common runtime language detected invalid program when calling method.GetCustomAttributes

nwbos
Enthusiast
Enthusiast

I want to define a unit test, Using Xunit and FluentAssertions.

In this test i want to list all command names from the methods in our code for example

 

 

[CommandMethod("SOME-COMMAND")]
public void SomeCommand()
{
  // some method
}

[CommandMethod("SOME-COMMAND-1")]
public void SomeCommandOne()
{
  // some method
}

 

 

I want result the names of the command like

 

SOME-COMMAND
SOME-COMMAND-1

 

This list will i use to compare all the commands in our UI with our Code.

 

I started with the following code

 

var assembly = Assembly.GetAssembly(typeof(EditorCommands));
var allTypes = AllTypes.From(assembly); // FluentAssertions.Types
var allMethods = allTypes.SelectMany(x => x.GetMethods()).ToArray();
foreach (var method in allMethods)
{
    var attributes = method.GetCustomAttributes(typeof(CommandMethodAttribute), false);

    foreach (var attribute in attributes)
    {
        var commandAttribute = attribute as CommandMethodAttribute;
        var commandName = commandAttribute.GlobalName;
    }
}

 

At line 11 in the sample code above i geth the following Exception

 

System.InvalidProgramException: 'Common Language Runtime detected an invalid program.'

 

msedge_z98xLS9juq.png

Somehow it seems accoremgd.dll is protected as AutoCAD is not running, how can I get this working without running AutoCAD.

0 Likes
714 Views
5 Replies
Replies (5)
Message 2 of 6

norman.yuan
Mentor
Mentor

If the the classes you are unit-testing is the AutoCAD .NET plugin DLL, which has references to the AutoCAD .NET API assemblies (accoremgd/acdbmgd/acmgd.dll), then your DLL cannot be unit-tested in by the testing EXE, because the AutoCAD .NET API assemblies CAN ONLY BE used inside AutoCAD process. That is why you get "InvalidProgram" exception. That is, you cannot unit-test AutoCAD .NET plugin DLL as long as the DLL assembly has references to Acad .NET API assemblies. You can only do integration test (e.g. testing the AutoCAD process with the custom plugin DLL loaded).

 

If you really want to unit-test some code in the Acad plugin, you need to split your code to multiple DLLs, and place all code that do not deal AutoCAD at all (e.g. no references to Acad .NET API assemblies) in a DLL project and all Acad operation related in another DLL project. The former will be "unit-test"-able. However, in reality, such a separation in many Acad plugin developments is quite tedious/redundant/impractical. The extra effort may not be worth it in many cases, if not most, IMO. The reason? The .NET APIs are mostly the wrappers of underline C/C++ code that drives AutoCAD. Unless Autodesk (or other parties) can provide an easy "mocking AutoCAD" for testing (which will never happen), there is no such thing as true "unit-test" for Acad plugin

 

If you search this forum, you would find a few thread talking this topic. There are third party "test environments" to make testing Acad plugin code a bit easier, but is is not strictly "unit-test".

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 3 of 6

nshupeFMPE3
Advocate
Advocate

One thing I have started to do is write a command that tests objects in model space for certain conditions. I'm doing this as a way to quality control the drawings we produce to make sure the blocks we use contain data that is consistent with out business rules. 
But I image you could take the same idea and turn it into an integration test of sorts like mentioned above. 
I too tried to go down this road of unit testing plugin code and also ran into the same problems.

0 Likes
Message 4 of 6

drobertsY4GL3
Enthusiast
Enthusiast

That helped tremendously to articulate why I have been struggling to utilize unit testing in my AutoCAD API projects. 

 

If I wanted to unit test the following method,  would one valid solution be to create an IDocument interface that mocked all of the methods called in the AutoCAD Document class and pass the interface in instead?

 

 

public static List<ColorKey> LogCurrentColors(Document document)
{
PromptSelectionResult selection = document.Editor.SelectAll();
SelectionSet selectionSet = selection.Value;
ObjectId[] objectIDs = selectionSet.GetObjectIds();
List<ColorKey> colorKeys = new List<ColorKey>();

// Need to lock the document before modifying the database when using a modeless dialog?
using (DocumentLock docLock = document.LockDocument())
{
    using (Transaction transaction = document.Database.TransactionManager.StartOpenCloseTransaction())
    {
        foreach (ObjectId objectID in objectIDs)
        {
            DBObject dbObject = transaction.GetObject(objectID, OpenMode.ForWrite);

            if (dbObject.GetType().IsSubclassOf(typeof(Entity)))
            {
                var entity = dbObject as Entity;
                colorKeys.Add(new ColorKey(entity.Id, entity.Color));
            }
        }
        transaction.Commit();
    }
}
return colorKeys;
}

 

 

 

0 Likes
Message 5 of 6

nshupeFMPE3
Advocate
Advocate

I dont think its really feasible. Because If you are mocking the Document with an IDocument and then mocking out all the methods your mocking out
-the database property
-the transaction manager of that database
-the transaction
Essentially there is nothing left of the AutoCAD API. 
And really that is what our code it doing, it is interfacing with the AutoCAD API and producing some result. 

I think if you really want to test your functions you need to do it by hand, or create some sort of testing CAD file.

For example if you had a function that changed all closed polylines on a certain layer to red, you could make a CAD file that had lines of different layers and colors, and then run a command like Test() or something that calls the logic you are trying to test. 

Then you would either want to implement logic in that Test() method to check that the method you called did what it is suppose to do, maybe GetAll() the polylines and check their layer and color etc. Or you verify yourself and see that the method did what it should.

But all that seems like a lot unfortunately, and doesn't help you catch bugs while your coding like Unit Tests should do.

0 Likes
Message 6 of 6

drobertsY4GL3
Enthusiast
Enthusiast

Good. I just needed some confirmation that I'm on the right track with integration tests instead of trying to force unit testing to happen with the API. Thanks for the feedback.

0 Likes