1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <vector>
6
7#include "base/memory/scoped_ptr.h"
8#include "gpu/config/gpu_control_list.h"
9#include "gpu/config/gpu_info.h"
10#include "testing/gtest/include/gtest/gtest.h"
11
12const char kOsVersion[] = "10.6.4";
13const uint32 kIntelVendorId = 0x8086;
14const uint32 kNvidiaVendorId = 0x10de;
15const uint32 kAmdVendorId = 0x10de;
16
17#define LONG_STRING_CONST(...) #__VA_ARGS__
18
19#define EXPECT_EMPTY_SET(feature_set) EXPECT_EQ(0u, feature_set.size())
20#define EXPECT_SINGLE_FEATURE(feature_set, feature) \
21    EXPECT_TRUE(feature_set.size() == 1 && feature_set.count(feature) == 1)
22
23namespace gpu {
24
25enum TestFeatureType {
26  TEST_FEATURE_0 = 1,
27  TEST_FEATURE_1 = 1 << 2,
28  TEST_FEATURE_2 = 1 << 3,
29};
30
31class GpuControlListTest : public testing::Test {
32 public:
33  GpuControlListTest() { }
34
35  virtual ~GpuControlListTest() { }
36
37  const GPUInfo& gpu_info() const {
38    return gpu_info_;
39  }
40
41  GpuControlList* Create() {
42    GpuControlList* rt = new GpuControlList();
43    rt->AddSupportedFeature("test_feature_0", TEST_FEATURE_0);
44    rt->AddSupportedFeature("test_feature_1", TEST_FEATURE_1);
45    rt->AddSupportedFeature("test_feature_2", TEST_FEATURE_2);
46    return rt;
47  }
48
49 protected:
50  virtual void SetUp() {
51    gpu_info_.gpu.vendor_id = kNvidiaVendorId;
52    gpu_info_.gpu.device_id = 0x0640;
53    gpu_info_.driver_vendor = "NVIDIA";
54    gpu_info_.driver_version = "1.6.18";
55    gpu_info_.driver_date = "7-14-2009";
56    gpu_info_.machine_model_name = "MacBookPro";
57    gpu_info_.machine_model_version = "7.1";
58    gpu_info_.gl_vendor = "NVIDIA Corporation";
59    gpu_info_.gl_renderer = "NVIDIA GeForce GT 120 OpenGL Engine";
60    gpu_info_.performance_stats.graphics = 5.0;
61    gpu_info_.performance_stats.gaming = 5.0;
62    gpu_info_.performance_stats.overall = 5.0;
63  }
64
65  virtual void TearDown() {
66  }
67
68 private:
69  GPUInfo gpu_info_;
70};
71
72TEST_F(GpuControlListTest, DefaultControlListSettings) {
73  scoped_ptr<GpuControlList> control_list(Create());
74  // Default control list settings: all feature are allowed.
75  std::set<int> features = control_list->MakeDecision(
76      GpuControlList::kOsMacosx, kOsVersion, gpu_info());
77  EXPECT_EMPTY_SET(features);
78}
79
80TEST_F(GpuControlListTest, EmptyControlList) {
81  // Empty list: all features are allowed.
82  const std::string empty_list_json = LONG_STRING_CONST(
83      {
84        "name": "gpu control list",
85        "version": "2.5",
86        "entries": [
87        ]
88      }
89  );
90  scoped_ptr<GpuControlList> control_list(Create());
91
92  EXPECT_TRUE(control_list->LoadList(empty_list_json,
93                                     GpuControlList::kAllOs));
94  EXPECT_EQ("2.5", control_list->version());
95  std::set<int> features = control_list->MakeDecision(
96      GpuControlList::kOsMacosx, kOsVersion, gpu_info());
97  EXPECT_EMPTY_SET(features);
98}
99
100TEST_F(GpuControlListTest, DetailedEntryAndInvalidJson) {
101  // exact setting.
102  const std::string exact_list_json = LONG_STRING_CONST(
103      {
104        "name": "gpu control list",
105        "version": "0.1",
106        "entries": [
107          {
108            "id": 5,
109            "os": {
110              "type": "macosx",
111              "version": {
112                "op": "=",
113                "value": "10.6.4"
114              }
115            },
116            "vendor_id": "0x10de",
117            "device_id": ["0x0640"],
118            "driver_version": {
119              "op": "=",
120              "value": "1.6.18"
121            },
122            "features": [
123              "test_feature_0"
124            ]
125          }
126        ]
127      }
128  );
129  scoped_ptr<GpuControlList> control_list(Create());
130
131  EXPECT_TRUE(control_list->LoadList(exact_list_json, GpuControlList::kAllOs));
132  std::set<int> features = control_list->MakeDecision(
133      GpuControlList::kOsMacosx, kOsVersion, gpu_info());
134  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
135
136  // Invalid json input should not change the current control_list settings.
137  const std::string invalid_json = "invalid";
138
139  EXPECT_FALSE(control_list->LoadList(invalid_json, GpuControlList::kAllOs));
140  features = control_list->MakeDecision(
141      GpuControlList::kOsMacosx, kOsVersion, gpu_info());
142  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
143  std::vector<uint32> entries;
144  control_list->GetDecisionEntries(&entries, false);
145  ASSERT_EQ(1u, entries.size());
146  EXPECT_EQ(5u, entries[0]);
147  EXPECT_EQ(5u, control_list->max_entry_id());
148}
149
150TEST_F(GpuControlListTest, VendorOnAllOsEntry) {
151  // ControlList a vendor on all OS.
152  const std::string vendor_json = LONG_STRING_CONST(
153      {
154        "name": "gpu control list",
155        "version": "0.1",
156        "entries": [
157          {
158            "id": 1,
159            "vendor_id": "0x10de",
160            "features": [
161              "test_feature_0"
162            ]
163          }
164        ]
165      }
166  );
167  scoped_ptr<GpuControlList> control_list(Create());
168
169  // ControlList entries won't be filtered to the current OS only upon loading.
170  EXPECT_TRUE(control_list->LoadList(vendor_json, GpuControlList::kAllOs));
171  std::set<int> features = control_list->MakeDecision(
172      GpuControlList::kOsMacosx, kOsVersion, gpu_info());
173  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
174  features = control_list->MakeDecision(
175      GpuControlList::kOsWin, kOsVersion, gpu_info());
176  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
177  features = control_list->MakeDecision(
178      GpuControlList::kOsLinux, kOsVersion, gpu_info());
179  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
180#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_MACOSX) || \
181    defined(OS_OPENBSD)
182  // ControlList entries will be filtered to the current OS only upon loading.
183  EXPECT_TRUE(control_list->LoadList(
184      vendor_json, GpuControlList::kCurrentOsOnly));
185  features = control_list->MakeDecision(
186      GpuControlList::kOsMacosx, kOsVersion, gpu_info());
187  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
188  features = control_list->MakeDecision(
189      GpuControlList::kOsWin, kOsVersion, gpu_info());
190  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
191  features = control_list->MakeDecision(
192      GpuControlList::kOsLinux, kOsVersion, gpu_info());
193  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
194#endif
195}
196
197TEST_F(GpuControlListTest, UnknownField) {
198  const std::string unknown_field_json = LONG_STRING_CONST(
199      {
200        "name": "gpu control list",
201        "version": "0.1",
202        "entries": [
203          {
204            "id": 1,
205            "unknown_field": 0,
206            "features": [
207              "test_feature_1"
208            ]
209          },
210          {
211            "id": 2,
212            "features": [
213              "test_feature_0"
214            ]
215          }
216        ]
217      }
218  );
219  scoped_ptr<GpuControlList> control_list(Create());
220
221  EXPECT_FALSE(control_list->LoadList(
222      unknown_field_json, GpuControlList::kAllOs));
223}
224
225TEST_F(GpuControlListTest, UnknownExceptionField) {
226  const std::string unknown_exception_field_json = LONG_STRING_CONST(
227      {
228        "name": "gpu control list",
229        "version": "0.1",
230        "entries": [
231          {
232            "id": 1,
233            "unknown_field": 0,
234            "features": [
235              "test_feature_2"
236            ]
237          },
238          {
239            "id": 2,
240            "exceptions": [
241              {
242                "unknown_field": 0
243              }
244            ],
245            "features": [
246              "test_feature_1"
247            ]
248          },
249          {
250            "id": 3,
251            "features": [
252              "test_feature_0"
253            ]
254          }
255        ]
256      }
257  );
258  scoped_ptr<GpuControlList> control_list(Create());
259
260  EXPECT_FALSE(control_list->LoadList(
261      unknown_exception_field_json, GpuControlList::kAllOs));
262}
263
264TEST_F(GpuControlListTest, DisabledEntry) {
265  const std::string disabled_json = LONG_STRING_CONST(
266      {
267        "name": "gpu control list",
268        "version": "0.1",
269        "entries": [
270          {
271            "id": 1,
272            "disabled": true,
273            "features": [
274              "test_feature_0"
275            ]
276          }
277        ]
278      }
279  );
280  scoped_ptr<GpuControlList> control_list(Create());
281  EXPECT_TRUE(control_list->LoadList(disabled_json, GpuControlList::kAllOs));
282  std::set<int> features = control_list->MakeDecision(
283      GpuControlList::kOsWin, kOsVersion, gpu_info());
284  EXPECT_EMPTY_SET(features);
285  std::vector<uint32> flag_entries;
286  control_list->GetDecisionEntries(&flag_entries, false);
287  EXPECT_EQ(0u, flag_entries.size());
288  control_list->GetDecisionEntries(&flag_entries, true);
289  EXPECT_EQ(1u, flag_entries.size());
290}
291
292TEST_F(GpuControlListTest, NeedsMoreInfoForExceptions) {
293  const std::string json = LONG_STRING_CONST(
294      {
295        "name": "gpu control list",
296        "version": "0.1",
297        "entries": [
298          {
299            "id": 1,
300            "os": {
301              "type": "linux"
302            },
303            "vendor_id": "0x8086",
304            "exceptions": [
305              {
306                "gl_renderer": {
307                  "op": "contains",
308                  "value": "mesa"
309                }
310              }
311            ],
312            "features": [
313              "test_feature_0"
314            ]
315          }
316        ]
317      }
318  );
319  GPUInfo gpu_info;
320  gpu_info.gpu.vendor_id = kIntelVendorId;
321
322  scoped_ptr<GpuControlList> control_list(Create());
323  EXPECT_TRUE(control_list->LoadList(json, GpuControlList::kAllOs));
324
325  // The case this entry does not apply.
326  std::set<int> features = control_list->MakeDecision(
327      GpuControlList::kOsMacosx, kOsVersion, gpu_info);
328  EXPECT_EMPTY_SET(features);
329  EXPECT_FALSE(control_list->needs_more_info());
330
331  // The case this entry might apply, but need more info.
332  features = control_list->MakeDecision(
333      GpuControlList::kOsLinux, kOsVersion, gpu_info);
334  EXPECT_EMPTY_SET(features);
335  EXPECT_TRUE(control_list->needs_more_info());
336
337  // The case we have full info, and the exception applies (so the entry
338  // does not apply).
339  gpu_info.gl_renderer = "mesa";
340  features = control_list->MakeDecision(
341      GpuControlList::kOsLinux, kOsVersion, gpu_info);
342  EXPECT_EMPTY_SET(features);
343  EXPECT_FALSE(control_list->needs_more_info());
344
345  // The case we have full info, and this entry applies.
346  gpu_info.gl_renderer = "my renderer";
347  features = control_list->MakeDecision(GpuControlList::kOsLinux, kOsVersion,
348      gpu_info);
349  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
350  EXPECT_FALSE(control_list->needs_more_info());
351}
352
353TEST_F(GpuControlListTest, IgnorableEntries) {
354  // If an entry will not change the control_list decisions, then it should not
355  // trigger the needs_more_info flag.
356  const std::string json = LONG_STRING_CONST(
357      {
358        "name": "gpu control list",
359        "version": "0.1",
360        "entries": [
361          {
362            "id": 1,
363            "os": {
364              "type": "linux"
365            },
366            "vendor_id": "0x8086",
367            "features": [
368              "test_feature_0"
369            ]
370          },
371          {
372            "id": 2,
373            "os": {
374              "type": "linux"
375            },
376            "vendor_id": "0x8086",
377            "driver_version": {
378              "op": "<",
379              "value": "10.7"
380            },
381            "features": [
382              "test_feature_0"
383            ]
384          }
385        ]
386      }
387  );
388  GPUInfo gpu_info;
389  gpu_info.gpu.vendor_id = kIntelVendorId;
390
391  scoped_ptr<GpuControlList> control_list(Create());
392  EXPECT_TRUE(control_list->LoadList(json, GpuControlList::kAllOs));
393  std::set<int> features = control_list->MakeDecision(
394      GpuControlList::kOsLinux, kOsVersion, gpu_info);
395  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
396  EXPECT_FALSE(control_list->needs_more_info());
397}
398
399TEST_F(GpuControlListTest, ExceptionWithoutVendorId) {
400  const std::string json = LONG_STRING_CONST(
401      {
402        "name": "gpu control list",
403        "version": "0.1",
404        "entries": [
405          {
406            "id": 1,
407            "os": {
408              "type": "linux"
409            },
410            "vendor_id": "0x8086",
411            "exceptions": [
412              {
413                "device_id": ["0x2a06"],
414                "driver_version": {
415                  "op": ">=",
416                  "value": "8.1"
417                }
418              },
419              {
420                "device_id": ["0x2a02"],
421                "driver_version": {
422                  "op": ">=",
423                  "value": "9.1"
424                }
425              }
426            ],
427            "features": [
428              "test_feature_0"
429            ]
430          }
431        ]
432      }
433  );
434  GPUInfo gpu_info;
435  gpu_info.gpu.vendor_id = kIntelVendorId;
436  gpu_info.gpu.device_id = 0x2a02;
437  gpu_info.driver_version = "9.1";
438
439  scoped_ptr<GpuControlList> control_list(Create());
440  EXPECT_TRUE(control_list->LoadList(json, GpuControlList::kAllOs));
441
442  std::set<int> features = control_list->MakeDecision(
443      GpuControlList::kOsLinux, kOsVersion, gpu_info);
444  EXPECT_EMPTY_SET(features);
445
446  gpu_info.driver_version = "9.0";
447  features = control_list->MakeDecision(
448      GpuControlList::kOsLinux, kOsVersion, gpu_info);
449  EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
450}
451
452TEST_F(GpuControlListTest, AMDSwitchable) {
453  GPUInfo gpu_info;
454  gpu_info.amd_switchable = true;
455  gpu_info.gpu.vendor_id = kAmdVendorId;
456  gpu_info.gpu.device_id = 0x6760;
457  GPUInfo::GPUDevice integrated_gpu;
458  integrated_gpu.vendor_id = kIntelVendorId;
459  integrated_gpu.device_id = 0x0116;
460  gpu_info.secondary_gpus.push_back(integrated_gpu);
461
462  {  // amd_switchable_discrete entry
463    const std::string json= LONG_STRING_CONST(
464        {
465          "name": "gpu control list",
466          "version": "0.1",
467          "entries": [
468            {
469              "id": 1,
470              "os": {
471                "type": "win"
472              },
473              "multi_gpu_style": "amd_switchable_discrete",
474              "features": [
475                "test_feature_0"
476              ]
477            }
478          ]
479        }
480    );
481
482    scoped_ptr<GpuControlList> control_list(Create());
483    EXPECT_TRUE(control_list->LoadList(json, GpuControlList::kAllOs));
484
485    // Integrated GPU is active
486    gpu_info.gpu.active = false;
487    gpu_info.secondary_gpus[0].active = true;
488    std::set<int> features = control_list->MakeDecision(
489        GpuControlList::kOsWin, kOsVersion, gpu_info);
490    EXPECT_EMPTY_SET(features);
491
492    // Discrete GPU is active
493    gpu_info.gpu.active = true;
494    gpu_info.secondary_gpus[0].active = false;
495    features = control_list->MakeDecision(
496        GpuControlList::kOsWin, kOsVersion, gpu_info);
497    EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
498  }
499
500  {  // amd_switchable_integrated entry
501    const std::string json= LONG_STRING_CONST(
502        {
503          "name": "gpu control list",
504          "version": "0.1",
505          "entries": [
506            {
507              "id": 1,
508              "os": {
509                "type": "win"
510              },
511              "multi_gpu_style": "amd_switchable_integrated",
512              "features": [
513                "test_feature_0"
514              ]
515            }
516          ]
517        }
518    );
519
520    scoped_ptr<GpuControlList> control_list(Create());
521    EXPECT_TRUE(control_list->LoadList(json, GpuControlList::kAllOs));
522
523    // Discrete GPU is active
524    gpu_info.gpu.active = true;
525    gpu_info.secondary_gpus[0].active = false;
526    std::set<int> features = control_list->MakeDecision(
527        GpuControlList::kOsWin, kOsVersion, gpu_info);
528    EXPECT_EMPTY_SET(features);
529
530    // Integrated GPU is active
531    gpu_info.gpu.active = false;
532    gpu_info.secondary_gpus[0].active = true;
533    features = control_list->MakeDecision(
534        GpuControlList::kOsWin, kOsVersion, gpu_info);
535    EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0);
536
537    // For non AMD switchable
538    gpu_info.amd_switchable = false;
539    features = control_list->MakeDecision(
540        GpuControlList::kOsWin, kOsVersion, gpu_info);
541    EXPECT_EMPTY_SET(features);
542  }
543}
544
545}  // namespace gpu
546
547