Author Topic: Running Automated testing in AutoCAD getting eLockViolation  (Read 593 times)

0 Members and 1 Guest are viewing this topic.

shupsta2010

  • Mosquito
  • Posts: 10
Running Automated testing in AutoCAD getting eLockViolation
« on: October 08, 2021, 12:40:05 PM »
I'm attempting to use CADBloke's CADTest project to run NUnit test inside AutoCAD. But when I try to have atest method call a method I normally run by command I get an eLockViolation error with this message.

'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'

Do you think there would be any way around this? I think it is because the CADTest project starts a program that isn't inside AutoCAD and that program is accessing data that is being used by AutoCAD?

n.yuan

  • Bull Frog
  • Posts: 322
Re: Running Automated testing in AutoCAD getting eLockViolation
« Reply #1 on: October 10, 2021, 10:38:43 AM »
I guess your test method tests your CAD DLL methods that are used in custom commands (CommandMethod[]), not the CommandMethod itself. When AutoCAD .NET API code modifies anything in drawing database, the document of the database has to be locked. When the code is executed from CommandMethod, the CommandMethod does the document locking automatically when executed in Document context(unless the CommandMethod has its CommandFlags.Session flag set, eg. the code is executed in Application context).

So, for each of your test method, if the CAD .NET code to be tested is to make changes to the database of the current drawing, you need to lock the document first. Something like:

[Test]
void TestSomeMethodInCadDll()
{
   var dwg As Application.DocumentManager.MdiActiveDocument;
   using (dwg.LockDocument())
   {
      // Your test code here
   }
}

Of course, if the CAD .NET code does not change anything in the open drawing, then no need to lock it.

CADbloke

  • Bull Frog
  • Posts: 331
  • Crash Test Dummy
Re: Running Automated testing in AutoCAD getting eLockViolation
« Reply #2 on: October 11, 2021, 06:39:34 AM »
Without any code to look at its hard to tell what went wrong. I havenít used CAD test in a whole but iirc it runs the console & nunit inside the AutoCAD process, in a command. If Normanís answer doesnít work post some code or link to a repo so we can take a look at it.

shupsta2010

  • Mosquito
  • Posts: 10
Re: Running Automated testing in AutoCAD getting eLockViolation
« Reply #3 on: October 11, 2021, 11:14:48 AM »
Thank you for the reply's. I'll try to explain the code here.

Code: [Select]
[SetUp]
        public void SetUp()
        {
            zones = new ShimZones(new List<IZone>());
            IZone zone = new Zone("1", "306406", "H");
            zones.addZone(zone);
           
        }

ShimZones is an object that inherites from the normal Zones class, and simply overrides a serialization method, which was the first place I encountered this problem. So this was my work around.

A Zones object is pretty much just a class with a list of Zone objects, with some helper methods for manipulating that list.

Code: [Select]
public Zone(string zoneNum, string handle, string thermostat)
        {
            this.zoneNum = zoneNum;
            this.handle = handle;
            IZones zones = new Zones(new List<IZone>());
            this.color = getZoneColor(zones, zoneNum);
            this.thermostat = thermostat;
        }

This is the constructor that is called for the Zone item to be added.

In my non-testing code, after the new zone is added to the Zones object, I run this method

Code: [Select]
public void UpdateColor()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = doc.Database;
            Autodesk.AutoCAD.DatabaseServices.TransactionManager tm = db.TransactionManager;

            using (DocumentLock doclock = doc.LockDocument())
            using (Transaction tr = tm.StartTransaction())
            {
                ObjectId newObjectId = this.GetObjectId();
                Polyline btr = (Polyline)tr.GetObject(newObjectId, OpenMode.ForWrite);
                btr.Color = Autodesk.AutoCAD.Colors.Color.FromColorIndex(ColorMethod.ByAci, (short)color);

                tr.Commit();
            }
        }

But this seems to be where the crash and error occurs. But when I run this test method

Code: [Select]
[Test]
        [Description("Does renumbering the zone work?")]
        public void zone_renumber_to_2()
        {
            zones.RenumberZone(0, "2", "H");
            Assert.IsTrue(zones.zoneList[0].zoneNum == "2");
            Assert.IsTrue(zones.zoneList[0].color == 116);

        }

Code: [Select]
public void RenumberZone(int index, string newNum, string newThermo)
        {
            zoneList[index].zoneNum = newNum;
            zoneList[index].thermostat = newThermo.ToUpper();
            zoneList[index].color = Zone.getZoneColor(this, newNum);
            zoneList[index].UpdateColor();
            SerializeZones();
        }

The UpdateColor() method is run without a problem, and the pline that represents the Zone has it's color changed?

Very strange behavior that one method is able to run the UpdateColor() method without a problem, and another method is not able.

shupsta2010

  • Mosquito
  • Posts: 10
Re: Running Automated testing in AutoCAD getting eLockViolation
« Reply #4 on: October 11, 2021, 04:10:50 PM »
I guess your test method tests your CAD DLL methods that are used in custom commands (CommandMethod[]), not the CommandMethod itself. When AutoCAD .NET API code modifies anything in drawing database, the document of the database has to be locked. When the code is executed from CommandMethod, the CommandMethod does the document locking automatically when executed in Document context(unless the CommandMethod has its CommandFlags.Session flag set, eg. the code is executed in Application context).

So, for each of your test method, if the CAD .NET code to be tested is to make changes to the database of the current drawing, you need to lock the document first. Something like:

[Test]
void TestSomeMethodInCadDll()
{
   var dwg As Application.DocumentManager.MdiActiveDocument;
   using (dwg.LockDocument())
   {
      // Your test code here
   }
}

Of course, if the CAD .NET code does not change anything in the open drawing, then no need to lock it.

I did try this just now, wrapping the SetUp method that I shared in a using statement and locking the document, but had the same result. Also the method that does the color updating already has a using statment to lock the document.

n.yuan

  • Bull Frog
  • Posts: 322
Re: Running Automated testing in AutoCAD getting eLockViolation
« Reply #5 on: October 12, 2021, 10:25:01 AM »
It is indeed a bit of strange.

The only thing that looks a tiny bit of suspicious to me is that in the UpdateColor method: the entity to be changed is identified by "this.GetObjectId()", which in turn tracess back to a Handle of the Zone object. Is there chance that the handle belongs to another opened drawing, rather than the current MdiActiveDocument when the UpdateColor() is called in the test?

I am not a fan to get MdiActiveDocument and lock the document in every CAD operation method, if the class already has means to identify its dependency database. In your case, the method this.GetObjectId() already implies that the Zone class itself knows which database the handle belongs to. So, why you need to get MdiActiveDocument in order to reach a database, and then the TransactionManager? You could do:

var newObjectId=this.GetObjectId()
using (var tran=newObjectId.Database.TransactionManager.StartTransaction())
{
  ....
}

This way, you could leave the document lock completely outside the UpdateColor method and let the calling process to decide whether the locking is necessary. For example, in Document context, there is no need to lock, while in Application context (such as the test here), you need to explicitly lock the doc in your test call.

Also, we all know, if the test involves the AutoCAD process, the test effectively changes from "unit test" to "integration test". and it is hard and time consuming. So I am never of fan to do that for not worth the time, in many cases. But for your Zone class. if "unit test" is required (I know, some bosses/companies demand it), I'd stick to "unit test", not "integration test", that is, I'd consider some mocking dependency to replace AutoCAD by supplying a mocking/empty UpdateColor() method for Zone class's unit test: after all, the unit test for Zone class is to make sure when RenumberZone() is called, something should occur as expected, so, as long as mocking UpdateColor is called, the unit test should pass.


shupsta2010

  • Mosquito
  • Posts: 10
Re: Running Automated testing in AutoCAD getting eLockViolation
« Reply #6 on: October 12, 2021, 03:52:45 PM »
Thank you for your help n.yuan, it's been very valuable.

I made the changes to UpdateColor as you described

Code: [Select]
public void UpdateColor()
        {
            ObjectId newObjectId = this.GetObjectId();
           
            using (Transaction tr = newObjectId.Database.TransactionManager.StartTransaction())
            {
               
                Polyline btr = (Polyline)tr.GetObject(newObjectId, OpenMode.ForWrite);
                btr.Color = Autodesk.AutoCAD.Colors.Color.FromColorIndex(ColorMethod.ByAci, (short)color);

                tr.Commit();
            }
        }

And made some changes to GetObjectId where it now looks like this

Code: [Select]
public ObjectId GetObjectId()
        {
            Document doc = Active.Document;
            ObjectId newObjectId = new ObjectId();
           
            using (doc.Database.TransactionManager.StartTransaction())
            {
                long l = Int64.Parse(handle, System.Globalization.NumberStyles.AllowHexSpecifier);
                Handle h = new Handle(l);
                newObjectId = Active.Database.GetObjectId(false, h, 0);
            }
            return newObjectId;
        }

Now in the Setup funcion for the tests, there is no crash if I lock the document

Code: [Select]
[SetUp]
        public void SetUp()
        {
            using (Active.Document.LockDocument())
            {
                zones = new ShimZones(new List<IZone>());
                IZone zone = new Zone("1", "306406", "H");
                zones.addZone(zone);
            }
               
        }

But in a later test, even though I'm locking the Document, the same crash occurs?

Code: [Select]
[Test]
        [Description("Does renumbering the zone work?")]
        public void zone_renumber_to_2()
        {
            using (Active.Document.LockDocument())
            {
                zones.RenumberZone(0, "2", "H");
                Assert.IsTrue(zones.zoneList[0].zoneNum == "2");
                Assert.IsTrue(zones.zoneList[0].color == 116);
            }
           

        }

How should I be storing the handle and database information for that handle? I'm inheriting this code and am trying to improve it, while learning how to code for AutoCAD plugins.

Thanks again!

n.yuan

  • Bull Frog
  • Posts: 322
Re: Running Automated testing in AutoCAD getting eLockViolation
« Reply #7 on: October 13, 2021, 09:05:20 AM »
You probably have done it already, by I ask it anyway: did you debug the code with a break point set at this line in UpdateColor() method:

Polyline btr = (Polyline)tr.GetObject(newObjectId, OpenMode.ForWrite);

If the exception is indeed raised by this line, then I do not know what else can be done, because the document is locked prior to this "write" change :idiot2:


shupsta2010

  • Mosquito
  • Posts: 10
Re: Running Automated testing in AutoCAD getting eLockViolation
« Reply #8 on: October 13, 2021, 10:31:32 AM »
I did watch the program run using breakpoints, and the error did not occur in the UpdateColor() method, but in the GetObjectId() method which is called by the UpdateColor() method at the start

n.yuan

  • Bull Frog
  • Posts: 322
Re: Running Automated testing in AutoCAD getting eLockViolation
« Reply #9 on: October 14, 2021, 08:50:03 AM »
The exception is raised in GetObjectId() method? Which line?

One thing to note is: here is no need to start a transaction in that method.


shupsta2010

  • Mosquito
  • Posts: 10
Re: Running Automated testing in AutoCAD getting eLockViolation
« Reply #10 on: October 14, 2021, 11:53:07 AM »
Thank you n.yuan! Removing that Transaction in GetObjectId() fixed it! I guess I thought creating a new ObjectId Object would warrant a Transaction.

n.yuan

  • Bull Frog
  • Posts: 322
Re: Running Automated testing in AutoCAD getting eLockViolation
« Reply #11 on: October 15, 2021, 08:41:37 AM »
Glad to hear the issue is fixed because of my "not-so-sure" prompt. It is still quite a surprise and a puzzle: why an useless transaction, or an opened transaction within an DocumentLock scope still raises eLockViolation, yet another Transaction immediately opened after it does not cause the exception? Really strange!