NUnit & Project.OpenAsync()

1021
7
09-24-2020 01:22 PM
AlanStewart
Occasional Contributor

I'm pretty new to Pro SDK development and async coding, so this is probably a rookie mistake.

I'm attempting to us NUnit to do testing of our add-in. I plan to develop a public API to the add-in, so plan for NUnit tests to use that public API. I have already constructed and tested an NUnit test suite and a simple API that does no Pro SDK calls; they work as expected. So I know NUnit is set up correctly and can successfully call methods in the public API class. However, when I create the first test and API method that call a Pro SDK method, Project.OpenAsync() to open a project file, it fails with a NullReferenceException. You will see in the code below that the path being passed to Project.OpenAsync() has already been vetted as valid. ArcGIS Pro has no problem opening said path either. So the input appears good. What could I be missing?

The NUnit test suite:

using System.Threading;
using System.Threading.Tasks;
using ArcGIS.Core.Hosting;
using NUnit.Framework;
using publisher_arcgispro;

namespace NUnit.Tests
{
    [TestFixture]
    public class GeoPdfExportTestSuite
    {
        [SetUp]
        public void TestSetUp()
        {
        }

        [TearDown]
        public void TestTearDown()
        {
        }

        [Test(ExpectedResult = 0), Description("basic export"), Apartment(ApartmentState.STA)]
        public async Task<int> Test1()
        {
            string projectPath = @"C:\devel\Pro test data\subtypes.aprx";
            string layoutPath = "layout/layout.xml";


            Host.Initialize();


            int result = await API.GeoPdfExport.ExportLayout(projectPath,
                                                             layoutPath);
            return result;
        }
    }
}

The public API:

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using ArcGIS.Desktop.Core;
using ArcGIS.Desktop.Framework.Threading.Tasks;
    
namespace publisher_arcgispro
{
    public class API
    {
        public class GeoPdfExport
        {
            public static int dummy()
            {
                return 1;
            }

            public static async Task<int> ExportLayout(string projectPath,
                                                       string layoutPath)
            {
                FileInfo info = new FileInfo(projectPath);
                if (!info.Exists)
                    return 1;

                Project project = await Project.OpenAsync(projectPath);
                Item item = project.FindItem(layoutPath);

                return 0;
            }
        }
    }
}

0 Kudos
7 Replies
AlanStewart
Occasional Contributor

I had to turn my attention away to other tasks for a while, this problem is still alive. I've realized I probably need to be using QueuedTask.Run(). So I wrote another test. This test does not throw the exception, but it never returns from the CreatAsync() call. Does Host.Initialize() just not work for accessing projects? The only working case I've seen on this forum using the Pro SDK and NUnit3 does not access a project. If that's that case then apparently I am dead in the water and cannot automate running tests on our Jenkins server.

[Test(ExpectedResult = 0), Description("basic export"), Apartment(ApartmentState.STA)]
public async Task<int> Test0()
{
Host.Initialize();

Project project = await QueuedTask.Run<Project>(() => Project.CreateAsync());
if (project == null)
return 1;

return 0;
}

0 Kudos
AlanStewart
Occasional Contributor

I've added another test which does not access a project. It is patterned after the code in unit-testing-with-a-database-file-for-arcgis-pro-sdk. It also seems to hang once the QueuedTask.Run() call is made. I'm stumped.

        [Test(ExpectedResult = 0), Description("basic export"), Apartment(ApartmentState.STA)]
        public async Task<int> Test1()
        {
            string gpkgPath = "C:/devel/Pro test data/Map Frame.gpkg";
            string tableName = "main.Detailed_Streets";
            if (!File.Exists(gpkgPath))
                return 1;

            Uri gpkgUri = new Uri(gpkgPath);

            Host.Initialize();

            Database database;
            await QueuedTask.Run(() =>
            {
                using (database = new Database(new SQLiteConnectionPath(gpkgUri)))
                {
                    Table table =
                        database.OpenTable(database.GetQueryDescription(tableName));
                    if (table == null)
                        throw new Exception("no table");
                }
                if (database == null)
                    throw new Exception("no database");
            });

            return 0;
        }

0 Kudos
GintautasKmieliauskas
Regular Contributor

Hi Alan,

Have you read this:

https://community.esri.com/thread/225771-corehost-licensing-queuedworkerrun-vs-queuedtaskrun

"note: ArcGIS.Desktop.Framework.dll is not supported for use in CoreHost so QueuedTask's use in CoreHost is actually moot"

0 Kudos
AlanStewart
Occasional Contributor

Thanks, Gintautas, I had not seen that. It seems to be relevant to Pro 1.2 though and I'm at 2.4 an not trying to run without Pro installed. Helpful though and lead to other helpful links, at least in expanding my understanding of CoreHost.

After switching to QueuedWorker instead of QueuedTask the Test1() test above, which does the SQLite access, passed. But an updated Test0() test, which attempts Project.CreateAsync(), still throws NullReferenceException. It now reads:

       [Test(ExpectedResult = 0), Description("basic export"), Apartment(ApartmentState.STA)]
        public async Task<int> Test0()
        {
            Host.Initialize();

            await QueuedWorker.Run(() =>
            {
                Task<Project> task = Project.CreateAsync();
                task.Wait();
                Project project = task.Result;
                if (project == null)
                    throw new Exception("no project");
            });

            return 0;
        }
0 Kudos
GintautasKmieliauskas
Regular Contributor

Hi Alan,

"CoreHost applications don't have access to any of the ArcGIS.Desktop libraries. Project belongs to ArcGIS.Desktop.Core."

https://community.esri.com/thread/229393-arcgisdesktopcoregeoprocessing-in-standalone-arcgis-pro-sdk...

0 Kudos
AlanStewart
Occasional Contributor

Thanks, Gintautas, I had gathered that, the link directly confirms it. I'm looking in to a Python solution.

0 Kudos
AlanStewart
Occasional Contributor

I constructed a Python toolbox, which works fine as long as the clr package code is commented out. Clearly the  ArcGIS.Desktop is accessible at least to arcpy. But if the clr code is uncommented the tool fails at the first reference to ArcGIS.Desktop. This same clr code works fine in the Python window in Pro.

For brevity only the execute() function is shown.

    def execute(self, parameters, messages):
        workspace_path = parameters[0].valueAsText
        layout_name = parameters[1].valueAsText
        pdf_path = parameters[2].valueAsText

        print("Executing Export_TerraGo")
        print("Workspace path is " + workspace_path)
        print("Layout name is " + layout_name)
        print("PDF path is " + pdf_path)

        ## prove that the ArcGIS.Desktop environment is available
        
        project = arcpy.mp.ArcGISProject(workspace_path)
        layout = project.listLayouts(layout_name)[0]
        print("Layout \'" + layout.name + "\' is accessible")

        ## access ArcGIS.Desktop objects compatible with C# add-in

        import sys
        import os
        import clr
        clr.AddReference("ArcGIS.Core")
        clr.AddReference("ArcGIS.Desktop.Framework")
        clr.AddReference("ArcGIS.Desktop.Resources")

        import ArcGIS.Core as AC
        import ArcGIS.Desktop.Core as ADC
        import ArcGIS.Desktop.Layouts as ADL
        import ArcGIS.Desktop.Mapping as ADM

        print("success")

0 Kudos