【问题标题】:How to (unit)test a weakrefence list's memory management function?如何(单元)测试弱引用列表的内存管理功能?
【发布时间】:2017-03-19 13:12:58
【问题描述】:

错误标题的借口:如果有人可以更好地描述它,请这样做。

我有一个WeakList<T> 类,它基本上“是”一个List<WeakReference<T>>(虽然不是从字面意义上说它源自列表,而是它应该对用户完全透明)。

现在的基本思想是“如果引用的变量消失,则忽略项目”,即WeakList<T> 源的一部分是:

public class WeakList<T> : IReadOnlyList<T>, IWeakList<T>, IWeakCollection<T>, IReadOnlyCollection<T>,
    ICollection<T>, IEnumerable<T>, IEnumerable where T : class
{
    private readonly List<WeakReference<T>> _myList;
    public void Add(T item) {
        _myList.Add(new WeakReference<T>(item));
    }
    IEnumerator<T> IEnumerable<T>.GetEnumerator() {
        foreach (var reference in _myList) {
            T elem;
            if (reference.TryGetTarget(out elem)) {
                yield return elem;
            }
        }
    }
}

我仍在考虑List 在这里是否真的有意义,weaklist[n] 并不总是与通过迭代到第 n 个元素相同。 - 有没有一个集合名称,它有一个顺序,但没有基于索引的访问?

但除此之外: 该类还包括一个“清除”功能,旨在“删除不再存在的引用”。

    public void Purge() {
        for (int i = _myList.Count - 1; i >= 0; i--) {
            T elem;
            if (!_myList[i].TryGetTarget(out elem)) {
                _myList.RemoveAt(i);
            }
        }
    }

现在我的问题是:如何在单元测试中测试上述方法(对于 Nunit)?如何设置一个列表,然后确保删除了一些元素,然后调用 purge 并对其进行测试,例如:

[TestFixture]
public class WeakListTests
{
    [Test]
    public static void PurgeTest() {
        var myList = new WeakList<string>;
        string s1 = "hello world 1";
        string s2 = "hello world 2";
        string s3 = "hello world 3";
        myList.Add(s1);
        myList.Add(s2);
        myList.Add(s3);
        // "clear", force s1 & s2 away. (yet keep a reference so I can test it gets removed)
        myList.Purge();
        Assert.That(myList, Does.Not.Contain(s1));
        Assert.That(myList, Does.Not.Contain(s2));
        Assert.That(myList, Does.Contain(s3));

    }
}

评论描述了我在什么时候被卡住了。

【问题讨论】:

    标签: c# unit-testing memory-management garbage-collection nunit


    【解决方案1】:

    你可以这样做(见 cmets):

    [TestFixture]
    public class WeakListTests
    {
        [Test]
        public static void PurgeTest()
        {
            var myList = new WeakList<string>();
            // construct strings like this to prevent interning
            string s1 = new string(new[] { 'h', 'e', 'l', 'l', 'o' });
            string s2 = new string(new[] { 'h', 'e', 'l', 'l', 'o', '2' });
            // this string can be interned, we don't want it to be collected anyway
            string s3 = "hello world 3";            
            myList.Add(s1);
            myList.Add(s2);
            myList.Add(s3);
            // set to null for that to work even in application built with "Debug"
            // in Release it will work without setting to null
            s1 = null;
            s2 = null;
            // force GC collection
            GC.Collect(2, GCCollectionMode.Forced);            
            // now s1 and s2 are away
            myList.Purge();
            // invoke your enumerator
            var left = myList.ToArray();            
            // should contain 1 item and that item should be s3
            Assert.That(left.Length == 1);
            Assert.AreEqual(s3, left[0]);            
        }
    }
    

    【讨论】:

    • 这是规范“保证工作”还是“正常工作”?
    • @paul23 我不明白为什么它不能工作。我们有 2 个超出范围的变量(在方法下面的任何地方都没有使用),我们强制立即进行垃圾收集,它们被收集,弱引用不再存在。但是,即使由于某种原因,在某些未知的条件下,它也不起作用——你的单元测试会失败,你会注意到。如果该测试在出现问题时会默默通过,我会理解您的担忧,但这不会发生。如果它通过了 - 2 个弱引用不存在,1 个存在,正如预期的那样。
    • @paul23 代码的哪一部分让您担心?强制垃圾回收?
    • 是的,强制实际上是对垃圾收集器的保证或建议。 (就像 C++ 中强制内联的工作方式一样)。
    • @paul23 是的,这是保证,但具体收集的内容取决于收集者。由于您正在测试内存管理 - 无论如何您别无他法。在这种情况下,您不需要超强的保证,因为在(假想的)不当行为的情况下,它不会造成任何伤害。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-24
    • 2017-04-12
    • 2011-09-28
    • 1970-01-01
    相关资源
    最近更新 更多