OLD | NEW |
1 // Copyright 2013 Canonical Ltd. | 1 // Copyright 2013 Canonical Ltd. |
2 // Licensed under the AGPLv3, see LICENCE file for details. | 2 // Licensed under the AGPLv3, see LICENCE file for details. |
3 | 3 |
4 package azure | 4 package azure |
5 | 5 |
6 import ( | 6 import ( |
7 . "launchpad.net/gocheck" | 7 . "launchpad.net/gocheck" |
| 8 "launchpad.net/juju-core/environs/config" |
8 "sync" | 9 "sync" |
9 ) | 10 ) |
10 | 11 |
11 type EnvironSuite struct { | 12 type EnvironSuite struct { |
12 ProviderSuite | 13 ProviderSuite |
13 } | 14 } |
14 | 15 |
15 var _ = Suite(new(EnvironSuite)) | 16 var _ = Suite(new(EnvironSuite)) |
16 | 17 |
| 18 func makeEnviron(c *C) *azureEnviron { |
| 19 attrs := makeAzureConfigMap(c) |
| 20 cfg, err := config.New(attrs) |
| 21 c.Assert(err, IsNil) |
| 22 ecfg, err := azureEnvironProvider{}.newConfig(cfg) |
| 23 c.Assert(err, IsNil) |
| 24 return &azureEnviron{ |
| 25 name: "env", |
| 26 ecfg: ecfg, |
| 27 } |
| 28 } |
| 29 |
| 30 // A note on locking tests. Proper locking is hard to test for. Tests here |
| 31 // use a fixed pattern to verify that a function obeys a particular lock: |
| 32 // |
| 33 // 1. Create a channel for the function's result. |
| 34 // 2. Grab the lock. |
| 35 // 3. Launch goroutine 1: invoke the function and report result to the channel. |
| 36 // 4. Launch goroutine 2: modify the object and then release the lock. |
| 37 // 5. Retrieve result from the channel. |
| 38 // 6. Test that the result reflects goroutine 2's modification. |
| 39 // 7. Test that the lock was released in the end. |
| 40 // |
| 41 // If the function obeys the lock, it can't complete until goroutine 2 has |
| 42 // completed. If it doesn't, it can. The pattern aims for this scenario: |
| 43 // |
| 44 // The mainline code blocks on the channel. |
| 45 // Goroutine 1 starts. It invokes the function you want to test. |
| 46 // The function tries to grab the lock, and blocks. |
| 47 // Goroutine 2 starts. It releases the lock and exits. |
| 48 // The function in goroutine 1 is now unblocked. |
| 49 // |
| 50 // It would be simpler to have just one goroutine (and skip the channel), and |
| 51 // release the lock inline. But then the ordering depends on a more |
| 52 // fundamental choice within the language implementation: it may choose to |
| 53 // start running a goroutine immediately at the "go" statement, or it may |
| 54 // continue executing the inline code and postpone execution of the goroutine |
| 55 // until the inline code blocks. |
| 56 // |
| 57 // The pattern is still not a full guarantee that the lock is obeyed. The |
| 58 // language implementation might choose to run the goroutines in LIFO order, |
| 59 // and then the locking would not be exercised. The lock would simply be |
| 60 // available by the time goroutine 1 ran, and the test would never fail unless |
| 61 // the function you're testing neglected to release the lock. But as long as |
| 62 // there is a reasonable chance of the first goroutine starting before the |
| 63 // second, there is a chance of exposing a function that disobeys the lock. |
| 64 |
17 func (EnvironSuite) TestGetSnapshot(c *C) { | 65 func (EnvironSuite) TestGetSnapshot(c *C) { |
18 original := azureEnviron{name: "this-env", ecfg: new(azureEnvironConfig)
} | 66 original := azureEnviron{name: "this-env", ecfg: new(azureEnvironConfig)
} |
19 snapshot := original.getSnapshot() | 67 snapshot := original.getSnapshot() |
20 | 68 |
21 // The snapshot is identical to the original. | 69 // The snapshot is identical to the original. |
22 c.Check(*snapshot, DeepEquals, original) | 70 c.Check(*snapshot, DeepEquals, original) |
23 | 71 |
24 // However, they are distinct objects. | 72 // However, they are distinct objects. |
25 c.Check(snapshot, Not(Equals), &original) | 73 c.Check(snapshot, Not(Equals), &original) |
26 | 74 |
27 // It's a shallow copy; they still share pointers. | 75 // It's a shallow copy; they still share pointers. |
28 c.Check(snapshot.ecfg, Equals, original.ecfg) | 76 c.Check(snapshot.ecfg, Equals, original.ecfg) |
29 | 77 |
30 // Neither object is locked at the end of the copy. | 78 // Neither object is locked at the end of the copy. |
31 c.Check(original.Mutex, Equals, sync.Mutex{}) | 79 c.Check(original.Mutex, Equals, sync.Mutex{}) |
32 c.Check(snapshot.Mutex, Equals, sync.Mutex{}) | 80 c.Check(snapshot.Mutex, Equals, sync.Mutex{}) |
33 } | 81 } |
34 | 82 |
| 83 func (EnvironSuite) TestGetSnapshotLocksEnviron(c *C) { |
| 84 // This tests follows the locking-test pattern. See comment above. |
| 85 // If you want to change how this works, you probably want to update |
| 86 // any other tests with the same pattern as well. |
| 87 original := azureEnviron{name: "old-name"} |
| 88 // 1. Result comes out of this channel. |
| 89 snaps := make(chan *azureEnviron) |
| 90 // 2. Stop a well-behaved getSnapshot from running (for now). |
| 91 original.Lock() |
| 92 // 3. Goroutine 1: ask for a snapshot. The point of the test is that |
| 93 // this blocks until we release our lock. |
| 94 go func() { |
| 95 snaps <- original.getSnapshot() |
| 96 }() |
| 97 // 4. Goroutine 2: release the lock. The getSnapshot call can't |
| 98 // complete until we've done this. |
| 99 go func() { |
| 100 original.name = "new-name" |
| 101 original.Unlock() |
| 102 }() |
| 103 // 5. Let the goroutines do their work. |
| 104 snapshot := <-snaps |
| 105 // 6. Test: the snapshot was made only after the lock was released. |
| 106 c.Check(snapshot.name, Equals, "new-name") |
| 107 // 7. Test: getSnapshot released the lock. |
| 108 c.Check(original.Mutex, Equals, sync.Mutex{}) |
| 109 } |
| 110 |
35 func (EnvironSuite) TestName(c *C) { | 111 func (EnvironSuite) TestName(c *C) { |
36 env := azureEnviron{name: "foo"} | 112 env := azureEnviron{name: "foo"} |
37 c.Check(env.Name(), Equals, env.name) | 113 c.Check(env.Name(), Equals, env.name) |
38 } | 114 } |
| 115 |
| 116 func (EnvironSuite) TestConfigReturnsConfig(c *C) { |
| 117 cfg := new(config.Config) |
| 118 ecfg := azureEnvironConfig{Config: cfg} |
| 119 env := azureEnviron{ecfg: &ecfg} |
| 120 c.Check(env.Config(), Equals, cfg) |
| 121 } |
| 122 |
| 123 func (EnvironSuite) TestConfigLocksEnviron(c *C) { |
| 124 // This tests follows the locking-test pattern. See comment above. |
| 125 // If you want to change how this works, you probably want to update |
| 126 // any other tests with the same pattern as well. |
| 127 env := azureEnviron{name: "env", ecfg: new(azureEnvironConfig)} |
| 128 newConfig := new(config.Config) |
| 129 // 1. Create results channel. |
| 130 configs := make(chan *config.Config) |
| 131 // 2. Stop a well-behaved Config() from running, for now. |
| 132 env.Lock() |
| 133 // 3. Goroutine 1: call Config(). We want to test that this locks. |
| 134 go func() { |
| 135 configs <- env.Config() |
| 136 }() |
| 137 // 4. Goroutine 2: change the Environ object, and release the lock. |
| 138 go func() { |
| 139 env.ecfg = &azureEnvironConfig{Config: newConfig} |
| 140 env.Unlock() |
| 141 }() |
| 142 // 5. Let the goroutines do their work. |
| 143 config := <-configs |
| 144 // 6. Test that goroutine 2 completed before Config did. |
| 145 c.Check(config, Equals, newConfig) |
| 146 // 7. Test: Config() released the lock. |
| 147 c.Check(env.Mutex, Equals, sync.Mutex{}) |
| 148 } |
| 149 |
| 150 // TODO: Temporarily deactivating this code. Passing certificate in-memory |
| 151 // may require gwacl change. |
| 152 /* |
| 153 func (EnvironSuite) TestGetManagementAPI(c *C) { |
| 154 env := makeEnviron(c) |
| 155 management, err := env.getManagementAPI() |
| 156 c.Assert(err, IsNil) |
| 157 c.Check(management, NotNil) |
| 158 } |
| 159 */ |
| 160 |
| 161 func (EnvironSuite) TestGetStorageContext(c *C) { |
| 162 env := makeEnviron(c) |
| 163 storage, err := env.getStorageContext() |
| 164 c.Assert(err, IsNil) |
| 165 c.Assert(storage, NotNil) |
| 166 c.Check(storage.Account, Equals, env.ecfg.StorageAccountName()) |
| 167 c.Check(storage.Key, Equals, env.ecfg.StorageAccountKey()) |
| 168 } |
OLD | NEW |