| 1 | //go:build cgo |
| 2 | |
| 3 | package jmx |
| 4 | |
| 5 | import ( |
| 6 | "context" |
| 7 | "testing" |
| 8 | |
| 9 | "github.com/netdata/netdata/go/plugins/pkg/confopt" |
| 10 | "github.com/netdata/netdata/go/plugins/plugin/ibm.d/modules/websphere/common" |
| 11 | "github.com/netdata/netdata/go/plugins/plugin/ibm.d/modules/websphere/jmx/contexts" |
| 12 | jmxproto "github.com/netdata/netdata/go/plugins/plugin/ibm.d/protocols/websphere/jmx" |
| 13 | ) |
| 14 | |
| 15 | type fakeClient struct { |
| 16 | jvm *jmxproto.JVMStats |
| 17 | threadPools []jmxproto.ThreadPool |
| 18 | jdbcPools []jmxproto.JDBCPool |
| 19 | jcaPools []jmxproto.JCAPool |
| 20 | jmsDestinations []jmxproto.JMSDestination |
| 21 | applications []jmxproto.ApplicationMetric |
| 22 | jvmErr error |
| 23 | poolErr error |
| 24 | jdbcErr error |
| 25 | jcaErr error |
| 26 | jmsErr error |
| 27 | } |
| 28 | |
| 29 | func (f *fakeClient) Start(context.Context) error { return nil } |
| 30 | func (f *fakeClient) Shutdown() {} |
| 31 | |
| 32 | func (f *fakeClient) FetchJVM(context.Context) (*jmxproto.JVMStats, error) { |
| 33 | if f.jvmErr != nil { |
| 34 | return nil, f.jvmErr |
| 35 | } |
| 36 | return f.jvm, nil |
| 37 | } |
| 38 | |
| 39 | func (f *fakeClient) FetchThreadPools(context.Context, int) ([]jmxproto.ThreadPool, error) { |
| 40 | if f.poolErr != nil { |
| 41 | return nil, f.poolErr |
| 42 | } |
| 43 | return f.threadPools, nil |
| 44 | } |
| 45 | |
| 46 | func (f *fakeClient) FetchJDBCPools(context.Context, int) ([]jmxproto.JDBCPool, error) { |
| 47 | if f.jdbcErr != nil { |
| 48 | return nil, f.jdbcErr |
| 49 | } |
| 50 | return f.jdbcPools, nil |
| 51 | } |
| 52 | |
| 53 | func (f *fakeClient) FetchJMSDestinations(context.Context, int) ([]jmxproto.JMSDestination, error) { |
| 54 | if f.jmsErr != nil { |
| 55 | return nil, f.jmsErr |
| 56 | } |
| 57 | return f.jmsDestinations, nil |
| 58 | } |
| 59 | |
| 60 | func (f *fakeClient) FetchJCAPools(context.Context, int) ([]jmxproto.JCAPool, error) { |
| 61 | if f.jcaErr != nil { |
| 62 | return nil, f.jcaErr |
| 63 | } |
| 64 | return f.jcaPools, nil |
| 65 | } |
| 66 | |
| 67 | func (f *fakeClient) FetchApplications(context.Context, int, bool, bool) ([]jmxproto.ApplicationMetric, error) { |
| 68 | return f.applications, nil |
| 69 | } |
| 70 | |
| 71 | func newTestCollector(t *testing.T, client jmxClient) *Collector { |
| 72 | t.Helper() |
| 73 | |
| 74 | cfg := defaultConfig() |
| 75 | collector := &Collector{ |
| 76 | Config: cfg, |
| 77 | client: client, |
| 78 | identity: common.Identity{}, |
| 79 | } |
| 80 | |
| 81 | collector.Collector.Config = cfg.Config |
| 82 | if err := collector.Collector.Init(context.Background()); err != nil { |
| 83 | t.Fatalf("framework init failed: %v", err) |
| 84 | } |
| 85 | collector.RegisterContexts(contexts.GetAllContexts()...) |
| 86 | collector.SetImpl(collector) |
| 87 | |
| 88 | return collector |
| 89 | } |
| 90 | |
| 91 | func TestCollectorCollectOnceExportsJVMMetrics(t *testing.T) { |
| 92 | client := &fakeClient{ |
| 93 | jvm: &jmxproto.JVMStats{ |
| 94 | Heap: struct { |
| 95 | Used float64 |
| 96 | Committed float64 |
| 97 | Max float64 |
| 98 | }{ |
| 99 | Used: 512, |
| 100 | Committed: 1024, |
| 101 | Max: 2048, |
| 102 | }, |
| 103 | NonHeap: struct { |
| 104 | Used float64 |
| 105 | Committed float64 |
| 106 | }{ |
| 107 | Used: 128, |
| 108 | Committed: 256, |
| 109 | }, |
| 110 | GC: struct { |
| 111 | Count float64 |
| 112 | Time float64 |
| 113 | }{ |
| 114 | Count: 12, |
| 115 | Time: 345, |
| 116 | }, |
| 117 | Threads: struct { |
| 118 | Count float64 |
| 119 | Daemon float64 |
| 120 | Peak float64 |
| 121 | Started float64 |
| 122 | }{ |
| 123 | Count: 44, |
| 124 | Daemon: 30, |
| 125 | Peak: 60, |
| 126 | Started: 100, |
| 127 | }, |
| 128 | Classes: struct { |
| 129 | Loaded float64 |
| 130 | Unloaded float64 |
| 131 | }{ |
| 132 | Loaded: 5000, |
| 133 | Unloaded: 200, |
| 134 | }, |
| 135 | CPU: struct{ ProcessUsage float64 }{ProcessUsage: 42}, |
| 136 | Uptime: 900, |
| 137 | }, |
| 138 | } |
| 139 | |
| 140 | collector := newTestCollector(t, client) |
| 141 | collector.Config.CollectThreadPoolMetrics = confopt.AutoBoolDisabled |
| 142 | |
| 143 | metrics := collector.Collect(context.Background()) |
| 144 | |
| 145 | expects := map[string]int64{ |
| 146 | "websphere_jmx.jvm_heap_memory.used": 512, |
| 147 | "websphere_jmx.jvm_heap_memory.committed": 1024, |
| 148 | "websphere_jmx.jvm_heap_memory.max": 2048, |
| 149 | "websphere_jmx.jvm_heap_usage.usage": 25, |
| 150 | "websphere_jmx.jvm_nonheap_memory.used": 128, |
| 151 | "websphere_jmx.jvm_nonheap_memory.committed": 256, |
| 152 | "websphere_jmx.jvm_gc_count.collections": 12, |
| 153 | "websphere_jmx.jvm_gc_time.time": 345, |
| 154 | "websphere_jmx.jvm_threads.total": 44, |
| 155 | "websphere_jmx.jvm_threads.daemon": 30, |
| 156 | "websphere_jmx.jvm_thread_states.peak": 60, |
| 157 | "websphere_jmx.jvm_thread_states.started": 100, |
| 158 | "websphere_jmx.jvm_classes.loaded": 5000, |
| 159 | "websphere_jmx.jvm_classes.unloaded": 200, |
| 160 | "websphere_jmx.jvm_process_cpu_usage.cpu": 42, |
| 161 | "websphere_jmx.jvm_uptime.uptime": 900, |
| 162 | } |
| 163 | |
| 164 | for key, want := range expects { |
| 165 | if got, ok := metrics[key]; !ok { |
| 166 | t.Errorf("expected metric %s to be present", key) |
| 167 | } else if got != want { |
| 168 | t.Errorf("metric %s mismatch: want %d got %d", key, want, got) |
| 169 | } |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | func TestCollectorCollectsThreadPools(t *testing.T) { |
| 174 | client := &fakeClient{ |
| 175 | jvm: &jmxproto.JVMStats{}, |
| 176 | threadPools: []jmxproto.ThreadPool{ |
| 177 | {Name: "Default", PoolSize: 50, ActiveCount: 5, MaximumPoolSize: 75}, |
| 178 | {Name: "WebContainer", PoolSize: 80, ActiveCount: 12, MaximumPoolSize: 100}, |
| 179 | }, |
| 180 | } |
| 181 | |
| 182 | collector := newTestCollector(t, client) |
| 183 | |
| 184 | metrics := collector.Collect(context.Background()) |
| 185 | |
| 186 | expected := map[string]int64{ |
| 187 | "websphere_jmx.threadpool_size.default.size": 50, |
| 188 | "websphere_jmx.threadpool_size.default.max": 75, |
| 189 | "websphere_jmx.threadpool_active.default.active": 5, |
| 190 | "websphere_jmx.threadpool_size.webcontainer.size": 80, |
| 191 | "websphere_jmx.threadpool_size.webcontainer.max": 100, |
| 192 | "websphere_jmx.threadpool_active.webcontainer.active": 12, |
| 193 | } |
| 194 | |
| 195 | for key, want := range expected { |
| 196 | if got, ok := metrics[key]; !ok { |
| 197 | t.Fatalf("expected metric %s to be present", key) |
| 198 | } else if got != want { |
| 199 | t.Errorf("metric %s mismatch: want %d got %d", key, want, got) |
| 200 | } |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | func TestCollectorCollectsJDBCPools(t *testing.T) { |
| 205 | client := &fakeClient{ |
| 206 | jvm: &jmxproto.JVMStats{}, |
| 207 | jdbcPools: []jmxproto.JDBCPool{ |
| 208 | { |
| 209 | Name: "DefaultDS", |
| 210 | PoolSize: 40, |
| 211 | NumConnectionsUsed: 8, |
| 212 | NumConnectionsFree: 12, |
| 213 | AvgWaitTime: 1.5, |
| 214 | AvgInUseTime: 2.5, |
| 215 | NumConnectionsCreated: 100, |
| 216 | NumConnectionsDestroyed: 90, |
| 217 | WaitingThreadCount: 3, |
| 218 | }, |
| 219 | }, |
| 220 | } |
| 221 | |
| 222 | collector := newTestCollector(t, client) |
| 223 | collector.Config.CollectThreadPoolMetrics = confopt.AutoBoolDisabled |
| 224 | |
| 225 | metrics := collector.Collect(context.Background()) |
| 226 | |
| 227 | expected := map[string]int64{ |
| 228 | "websphere_jmx.jdbc_pool_size.defaultds.size": 40, |
| 229 | "websphere_jmx.jdbc_pool_usage.defaultds.active": 8, |
| 230 | "websphere_jmx.jdbc_pool_usage.defaultds.free": 12, |
| 231 | "websphere_jmx.jdbc_pool_wait_time.defaultds.wait": 1500, |
| 232 | "websphere_jmx.jdbc_pool_use_time.defaultds.use": 2500, |
| 233 | "websphere_jmx.jdbc_pool_connections.defaultds.created": 100, |
| 234 | "websphere_jmx.jdbc_pool_connections.defaultds.destroyed": 90, |
| 235 | "websphere_jmx.jdbc_pool_waiting_threads.defaultds.waiting": 3, |
| 236 | } |
| 237 | |
| 238 | for key, want := range expected { |
| 239 | if got, ok := metrics[key]; !ok { |
| 240 | t.Fatalf("expected metric %s to be present", key) |
| 241 | } else if got != want { |
| 242 | t.Errorf("metric %s mismatch: want %d got %d", key, want, got) |
| 243 | } |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | func TestCollectorCollectsJMSDestinations(t *testing.T) { |
| 248 | client := &fakeClient{ |
| 249 | jvm: &jmxproto.JVMStats{}, |
| 250 | jmsDestinations: []jmxproto.JMSDestination{ |
| 251 | { |
| 252 | Name: "Queue1", |
| 253 | Type: "queue", |
| 254 | MessagesCurrentCount: 12, |
| 255 | MessagesPendingCount: 4, |
| 256 | MessagesAddedCount: 80, |
| 257 | ConsumerCount: 6, |
| 258 | }, |
| 259 | }, |
| 260 | } |
| 261 | |
| 262 | collector := newTestCollector(t, client) |
| 263 | collector.Config.CollectThreadPoolMetrics = confopt.AutoBoolDisabled |
| 264 | collector.Config.CollectJDBCMetrics = confopt.AutoBoolDisabled |
| 265 | |
| 266 | metrics := collector.Collect(context.Background()) |
| 267 | |
| 268 | expected := map[string]int64{ |
| 269 | "websphere_jmx.jms_messages_current.queue1_queue.current": 12, |
| 270 | "websphere_jmx.jms_messages_pending.queue1_queue.pending": 4, |
| 271 | "websphere_jmx.jms_messages_total.queue1_queue.total": 80, |
| 272 | "websphere_jmx.jms_consumers.queue1_queue.consumers": 6, |
| 273 | } |
| 274 | |
| 275 | for key, want := range expected { |
| 276 | if got, ok := metrics[key]; !ok { |
| 277 | t.Fatalf("expected metric %s to be present", key) |
| 278 | } else if got != want { |
| 279 | t.Errorf("metric %s mismatch: want %d got %d", key, want, got) |
| 280 | } |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | func TestCollectorCollectsApplications(t *testing.T) { |
| 285 | client := &fakeClient{ |
| 286 | jvm: &jmxproto.JVMStats{}, |
| 287 | applications: []jmxproto.ApplicationMetric{ |
| 288 | { |
| 289 | Name: "sample-app", |
| 290 | Module: "moduleA", |
| 291 | Requests: 120, |
| 292 | ResponseTime: 0.24, |
| 293 | ActiveSessions: 5, |
| 294 | LiveSessions: 7, |
| 295 | SessionCreates: 60, |
| 296 | SessionInvalidates: 10, |
| 297 | TransactionsCommitted: 40, |
| 298 | TransactionsRolledback: 2, |
| 299 | }, |
| 300 | }, |
| 301 | } |
| 302 | |
| 303 | collector := newTestCollector(t, client) |
| 304 | collector.Config.CollectThreadPoolMetrics = confopt.AutoBoolDisabled |
| 305 | collector.Config.CollectJDBCMetrics = confopt.AutoBoolDisabled |
| 306 | collector.Config.CollectJMSMetrics = confopt.AutoBoolDisabled |
| 307 | collector.Config.CollectWebAppMetrics = confopt.AutoBoolEnabled |
| 308 | collector.Config.CollectSessionMetrics = confopt.AutoBoolEnabled |
| 309 | collector.Config.CollectTransactionMetrics = confopt.AutoBoolEnabled |
| 310 | |
| 311 | metrics := collector.Collect(context.Background()) |
| 312 | |
| 313 | expected := map[string]int64{ |
| 314 | "websphere_jmx.app_requests.sample_app_modulea.requests": 120, |
| 315 | "websphere_jmx.app_response_time.sample_app_modulea.response_time": 240, |
| 316 | "websphere_jmx.app_sessions_active.sample_app_modulea.active": 5, |
| 317 | "websphere_jmx.app_sessions_live.sample_app_modulea.live": 7, |
| 318 | "websphere_jmx.app_session_events.sample_app_modulea.creates": 60, |
| 319 | "websphere_jmx.app_session_events.sample_app_modulea.invalidates": 10, |
| 320 | "websphere_jmx.app_transactions.sample_app_modulea.committed": 40, |
| 321 | "websphere_jmx.app_transactions.sample_app_modulea.rolledback": 2, |
| 322 | } |
| 323 | |
| 324 | for key, want := range expected { |
| 325 | if got, ok := metrics[key]; !ok { |
| 326 | t.Fatalf("expected metric %s to be present", key) |
| 327 | } else if got != want { |
| 328 | t.Errorf("metric %s mismatch: want %d got %d", key, want, got) |
| 329 | } |
| 330 | } |
| 331 | } |
| 332 | func TestCollectorCollectsJCAPools(t *testing.T) { |
| 333 | client := &fakeClient{ |
| 334 | jvm: &jmxproto.JVMStats{}, |
| 335 | jcaPools: []jmxproto.JCAPool{ |
| 336 | { |
| 337 | Name: "JCAPool1", |
| 338 | PoolSize: 20, |
| 339 | NumConnectionsUsed: 6, |
| 340 | NumConnectionsFree: 4, |
| 341 | AvgWaitTime: 1.2, |
| 342 | AvgInUseTime: 3.4, |
| 343 | NumConnectionsCreated: 30, |
| 344 | NumConnectionsDestroyed: 12, |
| 345 | WaitingThreadCount: 2, |
| 346 | }, |
| 347 | }, |
| 348 | } |
| 349 | |
| 350 | collector := newTestCollector(t, client) |
| 351 | collector.Config.CollectThreadPoolMetrics = confopt.AutoBoolDisabled |
| 352 | collector.Config.CollectJDBCMetrics = confopt.AutoBoolDisabled |
| 353 | collector.Config.CollectJMSMetrics = confopt.AutoBoolDisabled |
| 354 | collector.Config.CollectWebAppMetrics = confopt.AutoBoolDisabled |
| 355 | |
| 356 | metrics := collector.Collect(context.Background()) |
| 357 | |
| 358 | expected := map[string]int64{ |
| 359 | "websphere_jmx.jca_pool_size.jcapool1.size": 20, |
| 360 | "websphere_jmx.jca_pool_usage.jcapool1.active": 6, |
| 361 | "websphere_jmx.jca_pool_usage.jcapool1.free": 4, |
| 362 | "websphere_jmx.jca_pool_wait_time.jcapool1.wait": 1200, |
| 363 | "websphere_jmx.jca_pool_use_time.jcapool1.use": 3400, |
| 364 | "websphere_jmx.jca_pool_connections.jcapool1.created": 30, |
| 365 | "websphere_jmx.jca_pool_connections.jcapool1.destroyed": 12, |
| 366 | "websphere_jmx.jca_pool_waiting_threads.jcapool1.waiting": 2, |
| 367 | } |
| 368 | |
| 369 | for key, want := range expected { |
| 370 | if got, ok := metrics[key]; !ok { |
| 371 | t.Fatalf("expected metric %s to be present", key) |
| 372 | } else if got != want { |
| 373 | t.Errorf("metric %s mismatch: want %d got %d", key, want, got) |
| 374 | } |
| 375 | } |
| 376 | } |