Search code examples
c#windows-8windows-store-apps

Avoid Race Condition Creating StorageFolder


I am unit testing a new Win8 Store app and noticed a race condition I want to avoid. So I am looking for a way to avoid this race condition.

I have a class that when instantiated calls a method to be sure it has a local StorageFolder. My unit test simply instantiates the object and tests if the folder is there. Sometime the folder is not and sometimes it is so I believe this to be a race condition because the CreateFolderAsync is asynchronous (obviously).

public class Class1
{
    StorageFolder _localFolder = null;

    public Class1()
    {
        _localFolder = ApplicationData.Current.LocalFolder;
        _setUpStorageFolders();
    }

    public StorageFolder _LocalFolder
    {
        get
        {
            return _localFolder;
        }

    }


    async void _setUpStorageFolders()
    {
        try
        {
            _localFolder = await _localFolder.CreateFolderAsync("TestFolder", CreationCollisionOption.FailIfExists);

        }
        catch (Exception)
        {
            throw;
        }
    }
}

My Unit Test Looks Like This:

 [TestMethod]
    public void _LocalFolder_Test()
    {
        Class1 ke = new Class1();


        // TODO: Fix Race Condition 
        StorageFolder folder = ke._LocalFolder;

        string folderName = folder.Name;

        Assert.IsTrue(folderName == "TestFolder");

    }

Solution

  • As Iboshuizen suggested, I would do this synchronously. This can be done with async, task, and await. There is a gotcha - the setup cannot be done inside the constructor of Class1 because constructors do not support async/ await. Because of this SetUpStorageFolders is now public, and is called from the test method.

    public class Class1
    {
        StorageFolder _localFolder = null;
    
        public Class1()
        {
            _localFolder = ApplicationData.Current.LocalFolder;
                    // call to setup removed here because constructors
                    // do not support async/ await keywords
        }
    
        public StorageFolder _LocalFolder
        {
            get
            {
                return _localFolder;
            }
    
        }
    
          // now public... (note Task return type)
        async public Task SetUpStorageFolders()
        {
            try
            {
                _localFolder = await _localFolder.CreateFolderAsync("TestFolder", CreationCollisionOption.FailIfExists);
    
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
    

    Test:

     // note the signature change here (async + Task)
     [TestMethod]
        async public Task _LocalFolder_Test()
        {
            Class1 ke = new Class1();
            // synchronous call to SetupStorageFolders - note the await
            await ke.SetUpStorageFolders();
    
            StorageFolder folder = ke._LocalFolder;
    
            string folderName = folder.Name;
    
            Assert.IsTrue(folderName == "TestFolder");
        }