1+ using System ;
2+ using System . Collections ;
3+ using System . Collections . Concurrent ;
4+ using System . Collections . Generic ;
5+ using System . Linq ;
6+ using System . Reflection ;
7+ using NHibernate . Linq ;
8+ using NHibernate . Proxy ;
9+ using NHibernate . Proxy . DynamicProxy ;
10+ using NUnit . Framework ;
11+
12+ namespace NHibernate . Test . NHSpecificTest . NH3954
13+ {
14+ [ TestFixture ]
15+ public class Fixture
16+ {
17+ private ProxyCache _cache ;
18+ private ConcurrentDictionary < ProxyCacheEntry , System . Type > _internalCache ;
19+ private static readonly FieldInfo InternalCacheField =
20+ typeof ( ProxyCache ) . GetField ( "cache" , BindingFlags . Static | BindingFlags . NonPublic ) ;
21+
22+ [ SetUp ]
23+ public void SetUp ( )
24+ {
25+ _cache = new ProxyCache ( ) ;
26+
27+ _internalCache = ( ConcurrentDictionary < ProxyCacheEntry , System . Type > ) InternalCacheField . GetValue ( _cache ) ;
28+
29+ _cache . StoreProxyType ( typeof ( Entity1FakeProxy ) , typeof ( Entity1 ) ) ;
30+ _cache . StoreProxyType ( typeof ( Entity2FakeProxy ) , typeof ( Entity2 ) , typeof ( INHibernateProxy ) ) ;
31+ _cache . StoreProxyType ( typeof ( Entity3FakeProxy ) , typeof ( Entity3 ) ) ;
32+ _cache . StoreProxyType ( typeof ( Entity4FakeProxy ) , typeof ( Entity4 ) , typeof ( IProxy ) ) ;
33+ _cache . StoreProxyType ( typeof ( Entity5FakeProxy ) , typeof ( Entity5 ) , typeof ( INHibernateProxy ) , typeof ( IProxy ) ) ;
34+
35+ // Artificially inject other entries with same hashcodes
36+ Inject ( new ProxyCacheEntry ( typeof ( Entity1 ) , null ) , typeof ( Entity1FakeProxy2 ) , typeof ( Entity1 ) , typeof ( INHibernateProxy ) ) ;
37+ Inject ( new ProxyCacheEntry ( typeof ( Entity1 ) , null ) , typeof ( Entity3FakeProxy2 ) , typeof ( Entity3 ) ) ;
38+
39+ Inject ( new ProxyCacheEntry ( typeof ( Entity2 ) , new [ ] { typeof ( INHibernateProxy ) } ) , typeof ( Entity2FakeProxy2 ) , typeof ( Entity2 ) ) ;
40+ Inject ( new ProxyCacheEntry ( typeof ( Entity2 ) , new [ ] { typeof ( INHibernateProxy ) } ) , typeof ( Entity4FakeProxy2 ) , typeof ( Entity4 ) , typeof ( IProxy ) ) ;
41+ Inject ( new ProxyCacheEntry ( typeof ( Entity2 ) , new [ ] { typeof ( INHibernateProxy ) } ) , typeof ( Entity5FakeProxy2 ) , typeof ( Entity5 ) , typeof ( INHibernateProxy ) , typeof ( IProxy ) ) ;
42+ }
43+
44+ private void Inject ( ProxyCacheEntry entryToTweak , System . Type result , System . Type baseType , params System . Type [ ] baseInterfaces )
45+ {
46+ TweakEntry ( entryToTweak , baseType , baseInterfaces ) ;
47+ _internalCache [ entryToTweak ] = result ;
48+ }
49+
50+ private static readonly MethodInfo BaseTypeSetter = typeof ( ProxyCacheEntry ) . GetProperty ( "BaseType" ) . GetSetMethod ( true ) ;
51+ private static readonly MethodInfo InterfacesSetter = typeof ( ProxyCacheEntry ) . GetProperty ( "Interfaces" ) . GetSetMethod ( true ) ;
52+ private static readonly FieldInfo UniqueInterfacesField =
53+ typeof ( ProxyCacheEntry ) . GetField ( "_uniqueInterfaces" , BindingFlags . Instance | BindingFlags . NonPublic ) ;
54+
55+ /// <summary>
56+ /// Transforms the entry to some other one but preserves its hashcode. This allows to reproduce the issue, which otherwise
57+ /// is too unlikely for being tested.
58+ /// </summary>
59+ private void TweakEntry ( ProxyCacheEntry entryToTweak , System . Type baseType , params System . Type [ ] baseInterfaces )
60+ {
61+ BaseTypeSetter . Invoke ( entryToTweak , new object [ ] { baseType } ) ;
62+ InterfacesSetter . Invoke ( entryToTweak , new object [ ] { baseInterfaces ?? new System . Type [ 0 ] } ) ;
63+ UniqueInterfacesField . SetValue ( entryToTweak , new HashSet < System . Type > ( baseInterfaces ?? new System . Type [ 0 ] ) ) ;
64+ }
65+
66+ [ TearDown ]
67+ public void TearDown ( )
68+ {
69+ _cache = null ;
70+ _internalCache = null ;
71+ }
72+
73+ [ Test ]
74+ public void ProxyCacheEntity1FakeProxy ( )
75+ {
76+ var result = _cache . GetProxyType ( typeof ( Entity1 ) ) ;
77+ Assert . AreEqual ( typeof ( Entity1FakeProxy ) , result ) ;
78+ }
79+
80+ [ Test ]
81+ public void ProxyCacheEntity1FakeProxy2 ( )
82+ {
83+ var entry = new ProxyCacheEntry ( typeof ( Entity1 ) , null ) ;
84+ TweakEntry ( entry , typeof ( Entity1 ) , typeof ( INHibernateProxy ) ) ;
85+ var result = _internalCache [ entry ] ;
86+ Assert . AreEqual ( typeof ( Entity1FakeProxy2 ) , result ) ;
87+ }
88+
89+ [ Test ]
90+ public void ProxyCacheEntity2FakeProxy ( )
91+ {
92+ var result = _cache . GetProxyType ( typeof ( Entity2 ) , typeof ( INHibernateProxy ) ) ;
93+ Assert . AreEqual ( typeof ( Entity2FakeProxy ) , result ) ;
94+ }
95+
96+ [ Test ]
97+ public void ProxyCacheEntity2FakeProxy2 ( )
98+ {
99+ var entry = new ProxyCacheEntry ( typeof ( Entity2 ) , new [ ] { typeof ( INHibernateProxy ) } ) ;
100+ TweakEntry ( entry , typeof ( Entity2 ) ) ;
101+ var result = _internalCache [ entry ] ;
102+ Assert . AreEqual ( typeof ( Entity2FakeProxy2 ) , result ) ;
103+ }
104+
105+ [ Test ]
106+ public void ProxyCacheEntity3FakeProxy ( )
107+ {
108+ var result = _cache . GetProxyType ( typeof ( Entity3 ) ) ;
109+ Assert . AreEqual ( typeof ( Entity3FakeProxy ) , result ) ;
110+ }
111+
112+ [ Test ]
113+ public void ProxyCacheEntity3FakeProxy2 ( )
114+ {
115+ var entry = new ProxyCacheEntry ( typeof ( Entity1 ) , null ) ;
116+ TweakEntry ( entry , typeof ( Entity3 ) ) ;
117+ var result = _internalCache [ entry ] ;
118+ Assert . AreEqual ( typeof ( Entity3FakeProxy2 ) , result ) ;
119+ }
120+
121+ [ Test ]
122+ public void ProxyCacheEntity4FakeProxy ( )
123+ {
124+ var result = _cache . GetProxyType ( typeof ( Entity4 ) , typeof ( IProxy ) ) ;
125+ Assert . AreEqual ( typeof ( Entity4FakeProxy ) , result ) ;
126+ // Interfaces order voluntarily inverted.
127+ }
128+
129+ [ Test ]
130+ public void ProxyCacheEntity4FakeProxy2 ( )
131+ {
132+ var entry = new ProxyCacheEntry ( typeof ( Entity2 ) , new [ ] { typeof ( INHibernateProxy ) } ) ;
133+ TweakEntry ( entry , typeof ( Entity4 ) , typeof ( IProxy ) ) ;
134+ var result = _internalCache [ entry ] ;
135+ Assert . AreEqual ( typeof ( Entity4FakeProxy2 ) , result ) ;
136+ }
137+
138+ [ Test ]
139+ public void ProxyCacheEntity5FakeProxy ( )
140+ {
141+ var result = _cache . GetProxyType ( typeof ( Entity5 ) , typeof ( IProxy ) , typeof ( INHibernateProxy ) ) ;
142+ Assert . AreEqual ( typeof ( Entity5FakeProxy ) , result ) ;
143+ }
144+
145+ [ Test ]
146+ public void ProxyCacheEntity5FakeProxy2 ( )
147+ {
148+ var entry = new ProxyCacheEntry ( typeof ( Entity2 ) , new [ ] { typeof ( INHibernateProxy ) } ) ;
149+ TweakEntry ( entry , typeof ( Entity5 ) , typeof ( IProxy ) , typeof ( INHibernateProxy ) ) ;
150+ var result = _internalCache [ entry ] ;
151+ Assert . AreEqual ( typeof ( Entity5FakeProxy2 ) , result ) ;
152+ }
153+
154+ [ Test ]
155+ public void ProxyCacheNone ( )
156+ {
157+ // Beware not testing the lookup failure of any combination, even tweaked, actually added in cache.
158+ // (Otherwise the test may starts failing unexpectedly sometimes, as the original bug ...)
159+ // This one was not added in anyway.
160+ System . Type result ;
161+ Assert . IsFalse ( _cache . TryGetProxyType ( typeof ( Entity2 ) , new [ ] { typeof ( IProxy ) } , out result ) ) ;
162+ }
163+ }
164+ }
0 commit comments