1
+ using System . Collections . Concurrent ;
2
+ using System . Reflection ;
3
+ using NHibernate . Proxy ;
4
+ using NHibernate . Proxy . DynamicProxy ;
5
+ using NUnit . Framework ;
6
+
7
+ namespace NHibernate . Test . NHSpecificTest . NH3954
8
+ {
9
+ [ TestFixture , Explicit ( "Demonstrates bug impact on cache, but which tests will fail is a bit unpredictable" ) ]
10
+ public class ProxyCacheFixture
11
+ {
12
+ private ProxyCache _cache ;
13
+ private ConcurrentDictionary < ProxyCacheEntry , System . Type > _internalCache ;
14
+ private int _hashCode1 ;
15
+ private int _hashCode2 ;
16
+
17
+ private static readonly FieldInfo InternalCacheField =
18
+ typeof ( ProxyCache ) . GetField ( "cache" , BindingFlags . Static | BindingFlags . NonPublic ) ;
19
+
20
+ [ SetUp ]
21
+ public void SetUp ( )
22
+ {
23
+ _cache = new ProxyCache ( ) ;
24
+
25
+ _internalCache = ( ConcurrentDictionary < ProxyCacheEntry , System . Type > ) InternalCacheField . GetValue ( null ) ;
26
+
27
+ _cache . StoreProxyType ( typeof ( Entity1FakeProxy ) , typeof ( Entity1 ) ) ;
28
+ _cache . StoreProxyType ( typeof ( Entity2FakeProxy ) , typeof ( Entity2 ) , typeof ( INHibernateProxy ) ) ;
29
+ _cache . StoreProxyType ( typeof ( Entity3FakeProxy ) , typeof ( Entity3 ) ) ;
30
+ _cache . StoreProxyType ( typeof ( Entity4FakeProxy ) , typeof ( Entity4 ) , typeof ( IProxy ) ) ;
31
+ _cache . StoreProxyType ( typeof ( Entity5FakeProxy ) , typeof ( Entity5 ) , typeof ( INHibernateProxy ) , typeof ( IProxy ) ) ;
32
+
33
+ // Artificially inject other entries with same hashcodes
34
+ _hashCode1 = new ProxyCacheEntry ( typeof ( Entity1 ) , null ) . GetHashCode ( ) ;
35
+ Inject ( new ProxyCacheEntry ( typeof ( Entity1 ) , new [ ] { typeof ( INHibernateProxy ) } ) , _hashCode1 , typeof ( Entity1FakeProxy2 ) ) ;
36
+ Inject ( new ProxyCacheEntry ( typeof ( Entity3 ) , null ) , _hashCode1 , typeof ( Entity3FakeProxy2 ) ) ;
37
+
38
+ _hashCode2 = new ProxyCacheEntry ( typeof ( Entity2 ) , new [ ] { typeof ( INHibernateProxy ) } ) . GetHashCode ( ) ;
39
+ Inject ( new ProxyCacheEntry ( typeof ( Entity2 ) , null ) , _hashCode2 , typeof ( Entity2FakeProxy2 ) ) ;
40
+ Inject ( new ProxyCacheEntry ( typeof ( Entity4 ) , new [ ] { typeof ( IProxy ) } ) , _hashCode2 , typeof ( Entity4FakeProxy2 ) ) ;
41
+ Inject ( new ProxyCacheEntry ( typeof ( Entity5 ) , new [ ] { typeof ( INHibernateProxy ) , typeof ( IProxy ) } ) , _hashCode2 , typeof ( Entity5FakeProxy2 ) ) ;
42
+ }
43
+
44
+ private void Inject ( ProxyCacheEntry entryToTweak , int hashcode , System . Type result )
45
+ {
46
+ TweakEntry ( entryToTweak , hashcode ) ;
47
+ _internalCache [ entryToTweak ] = result ;
48
+ }
49
+
50
+ private static readonly FieldInfo HashCodeField =
51
+ typeof ( ProxyCacheEntry ) . GetField ( "hashCode" , BindingFlags . Instance | BindingFlags . NonPublic ) ;
52
+
53
+ /// <summary>
54
+ /// Allows to simulate a hashcode collision. Issue would be unpractical to test otherwise.
55
+ /// Hashcode collision must be supported for avoiding unexpected and hard to reproduce failures.
56
+ /// </summary>
57
+ private void TweakEntry ( ProxyCacheEntry entryToTweak , int hashcode )
58
+ {
59
+ // Though hashCode is a readonly field, this works at the time of this writing. If it starts breaking and cannot be fixed,
60
+ // ignore those tests or throw them away.
61
+ HashCodeField . SetValue ( entryToTweak , hashcode ) ;
62
+ }
63
+
64
+ [ Test ]
65
+ public void ProxyCacheEntity1FakeProxy ( )
66
+ {
67
+ var result = _cache . GetProxyType ( typeof ( Entity1 ) ) ;
68
+ Assert . AreEqual ( typeof ( Entity1FakeProxy ) , result ) ;
69
+ }
70
+
71
+ [ Test ]
72
+ public void ProxyCacheEntity1FakeProxy2 ( )
73
+ {
74
+ var entry = new ProxyCacheEntry ( typeof ( Entity1 ) , new [ ] { typeof ( INHibernateProxy ) } ) ;
75
+ TweakEntry ( entry , _hashCode1 ) ;
76
+ var result = _internalCache [ entry ] ;
77
+ Assert . AreEqual ( typeof ( Entity1FakeProxy2 ) , result ) ;
78
+ }
79
+
80
+ [ Test ]
81
+ public void ProxyCacheEntity2FakeProxy ( )
82
+ {
83
+ var result = _cache . GetProxyType ( typeof ( Entity2 ) , typeof ( INHibernateProxy ) ) ;
84
+ Assert . AreEqual ( typeof ( Entity2FakeProxy ) , result ) ;
85
+ }
86
+
87
+ [ Test ]
88
+ public void ProxyCacheEntity2FakeProxy2 ( )
89
+ {
90
+ var entry = new ProxyCacheEntry ( typeof ( Entity2 ) , null ) ;
91
+ TweakEntry ( entry , _hashCode2 ) ;
92
+ var result = _internalCache [ entry ] ;
93
+ Assert . AreEqual ( typeof ( Entity2FakeProxy2 ) , result ) ;
94
+ }
95
+
96
+ [ Test ]
97
+ public void ProxyCacheEntity3FakeProxy ( )
98
+ {
99
+ var result = _cache . GetProxyType ( typeof ( Entity3 ) ) ;
100
+ Assert . AreEqual ( typeof ( Entity3FakeProxy ) , result ) ;
101
+ }
102
+
103
+ [ Test ]
104
+ public void ProxyCacheEntity3FakeProxy2 ( )
105
+ {
106
+ var entry = new ProxyCacheEntry ( typeof ( Entity3 ) , null ) ;
107
+ TweakEntry ( entry , _hashCode1 ) ;
108
+ var result = _internalCache [ entry ] ;
109
+ Assert . AreEqual ( typeof ( Entity3FakeProxy2 ) , result ) ;
110
+ }
111
+
112
+ [ Test ]
113
+ public void ProxyCacheEntity4FakeProxy ( )
114
+ {
115
+ var result = _cache . GetProxyType ( typeof ( Entity4 ) , typeof ( IProxy ) ) ;
116
+ Assert . AreEqual ( typeof ( Entity4FakeProxy ) , result ) ;
117
+ }
118
+
119
+ [ Test ]
120
+ public void ProxyCacheEntity4FakeProxy2 ( )
121
+ {
122
+ var entry = new ProxyCacheEntry ( typeof ( Entity4 ) , new [ ] { typeof ( IProxy ) } ) ;
123
+ TweakEntry ( entry , _hashCode2 ) ;
124
+ var result = _internalCache [ entry ] ;
125
+ Assert . AreEqual ( typeof ( Entity4FakeProxy2 ) , result ) ;
126
+ }
127
+
128
+ [ Test ]
129
+ public void ProxyCacheEntity5FakeProxy ( )
130
+ {
131
+ var result = _cache . GetProxyType ( typeof ( Entity5 ) , typeof ( IProxy ) , typeof ( INHibernateProxy ) ) ;
132
+ Assert . AreEqual ( typeof ( Entity5FakeProxy ) , result ) ;
133
+ }
134
+
135
+ [ Test ]
136
+ public void ProxyCacheEntity5FakeProxy2 ( )
137
+ {
138
+ // Interfaces order inverted on purpose: must be supported.
139
+ var entry = new ProxyCacheEntry ( typeof ( Entity5 ) , new [ ] { typeof ( IProxy ) , typeof ( INHibernateProxy ) } ) ;
140
+ TweakEntry ( entry , _hashCode2 ) ;
141
+ var result = _internalCache [ entry ] ;
142
+ Assert . AreEqual ( typeof ( Entity5FakeProxy2 ) , result ) ;
143
+ }
144
+
145
+ [ Test ]
146
+ public void ProxyCacheNone ( )
147
+ {
148
+ // Beware not testing the lookup failure of any combination, even tweaked, actually added in cache.
149
+ // (Otherwise the test may starts failing unexpectedly sometimes, as the original bug ...)
150
+ // This one was not added in anyway.
151
+ System . Type result ;
152
+ Assert . IsFalse ( _cache . TryGetProxyType ( typeof ( Entity2 ) , new [ ] { typeof ( IProxy ) } , out result ) ) ;
153
+ }
154
+ }
155
+ }
0 commit comments