Fonte de dados WMI
Verifique se você está familiarizado com a execução básica do TAEF e saiba como criar testes usando-o antes de prosseguir com esta seção.
Fundo
"WMI" significa "Instrumentação de Gerenciamento do Windows". Usando o CIM (Common Information Model), que é o padrão do setor para representar sistemas. A Instrumentação de Gerenciamento do Windows fornece uma maneira unificada de acessar informações de gerenciamento do sistema.
Como isso ajuda meus testes?
Usando o suporte à consulta WMI disponível como o WMI DataSource no TAEF, você pode adicionar uma pré-condição ao teste, bem como obter informações sobre os recursos no computador de teste antes de executar o teste. Aqui estão alguns dos exemplos do tipo de consulta que você pode fazer usando o WMI:
- Verifique se o computador em que o teste está sendo executado é um laptop e execute o teste somente se for um laptop.
- Verifique se um service pack foi instalado no computador de teste e execute o teste somente se ele tiver sido.
- Recupere todas as unidades removíveis e unidades de disco rígido locais no computador de teste e execute o teste para cada uma das unidades que correspondem à consulta.
- Execute o teste somente se o computador de teste não estiver ingressado no domínio OU
- Execute o teste somente se o computador de teste estiver ingressado no domínio e recuperar o nome de domínio.
Isso teria lhe dado alguma ideia sobre onde e como você pode aproveitar o WMI DataSource para seu teste. Vamos examinar como adicionar esse suporte à consulta WMI durante a criação de um teste TAEF.
Os únicos metadados especiais necessários para fazer do teste um teste do WMI DataSource é o "DataSource". A sintaxe datasource deve ter a seguinte aparência:
[DataSource("WMI:<WQL query>")]
Ou no código nativo:
TEST_METHOD_PROPERTY(L"DataSource", L"WMI:<WQL query>")]
Você deve ter notado que o valor datasource começa com "WMI:", que permite que o TAEF saiba que essa é, de fato, a fonte de dados para um teste que depende do resultado da consulta WMI e também o distingue do teste controlado por dados. Essa é uma boa oportunidade para menção que atualmente o TAEF não dá suporte a um teste para ser ambos - um teste controlado por dados, bem como um teste que depende do resultado da consulta WMI.
A próxima pergunta naturalmente é como escrever consultas WQL para o que você está procurando? A sintaxe de consulta WQL é muito semelhante a consultas SQL simplificadas. Há alguns exemplos muito bons de consultas fornecidas em Tarefas WMI para Scripts e Aplicativos. Veja alguns exemplos:
SELECT Description, DesktopInteract, ProcessId FROM Win32_Service WHERE Name='Themes'
Execute o teste no serviço "Temas" depois de descobrir suas propriedades Description, DesktopInteract e ProcessId que você pretende usar em seu teste.
RECURSOS SELECT, CapabilityDescriptions FROM Win32_Printe
Execute o teste para cada impressora conectada a este computador. Permita que o teste acesse os Recursos e CapabilityDescriptions para cada impressora.
SELECT Name, User, Location FROM Win32_StartupCommand
Execute o teste para cada processo que é executado na inicialização do Windows. Para cada processo, permita que o teste saiba qual é o Nome do processo, onde ele está localizado (Local) e como Usuário o processo é executado.
Você pode encontrar mais exemplos na documentação mencionada acima, bem como no arquivo .cs e no arquivo de cabeçalho nos exemplos que você abriu. A sintaxe geral simplificada é a seguinte:
SELECT <comma separated properties> FROM <WMI Class name> [WHERE <add condition on some properties>]
Nos exemplos que você acabou de ver, Win32_Service, Win32_Printer e Win32_StartupCommand são todas classes WMI. Você pode pesquisar as classes WMI em classes WMI.
O TAEF não dá suporte à recuperação de propriedades do sistema.
Por trás da cena, o TAEF executará a consulta para você e confirmará o resultado. Se pelo menos um objeto for retornado como resultado da consulta, o teste será executado para cada objeto retornado. Se a consulta WQL não retornar nenhum objeto, o teste será registrado como Bloqueado com essas informações e a execução passará para o próximo teste.
Verificar ou verificar sua consulta antes de criar o teste parece ser uma ótima ideia e é um processo muito simples:
- Na caixa de diálogo de execução ou em um prompt de comando, invoque "wbemtest.exe"
- Clique no botão "Conectar" no canto superior direito.
- Verifique se o namespace é "root\cimv2" antes de clicar em "Conectar" novamente no canto superior direito.
- Em "IWbemServices", clique em "Consulta"
- Insira sua consulta na caixa de edição exibida e clique em "Aplicar"
OBSERVAÇÃO: o "IWbemService" tem várias outras opções que podem ajudá-lo com sua consulta. Por exemplo, usar "Classes de Enumeração" e alterar o botão de opção para "recursivo" ajudará você a ver todas as classes WMI no sistema.
Recuperando propriedades consultadas usando a consulta WMI
Agora você tem uma ideia de como criar uma consulta WMI para um método de teste e como aplicá-la como metadados durante a criação de um teste. Você também sabe como confirmar se a consulta é válida usando wbemtest.exe. Agora vamos examinar como recuperar os valores de propriedade que você estava procurando.
As noções básicas sobre como recuperar essas informações são muito semelhantes à recuperação de valores para o teste controlado por dados. Por exemplo, no código gerenciado, isso teria a seguinte aparência:
1 namespace WEX.Examples
2 {
3 using Microsoft.VisualStudio.TestTools.UnitTesting;
4 using System;
5 using System.Collections;
6 using System.Data;
7 using WEX.Logging.Interop;
8 using WEX.TestExecution;
9
10 [TestClass]
11 public class CSharpWmiDataSourceExample
12 {
13 [TestMethod]
14 [DataSource("WMI:SELECT Description, DesktopInteract, ProcessId FROM Win32_Service WHERE Name='Themes'")]
15 public void ThemesTest()
16 {
17 String description = (String)m_testContext.DataRow["Description"];
18 Boolean desktopInteract = (Boolean)m_testContext.DataRow["DesktopInteract"];
19 UInt32 processId = (UInt32)m_testContext.DataRow["ProcessId"];
20 Log.Comment("Themes service is running on process " + processId.ToString() + " with desktop interact set to "
+ desktopInteract.ToString());
21 Log.Comment("Themes service description: " + description);
22 }
23 ...
24 public TestContext TestContext
25 {
26 get { return m_testContext; }
27 set { m_testContext = value; }
28 }
29
30 private TestContext m_testContext;
31 }
32}
As linhas 24-30 no exemplo acima são exatamente o que é necessário para um teste controlado por dados gerenciado. Você definiu uma propriedade Privada TestContext e forneceu getter público e setter nela para TAEF definir os valores corretos. Usando a propriedade TestContext privada, você pode recuperar o valor atual de qualquer uma das propriedades do objeto resultante da consulta WMI que você recuperou do TAEF.
O código nativo para recuperar as propriedades WMI é muito semelhante. Assim como acontece com testes nativos controlados por dados, você usará TestData para obter os valores da propriedade. Por exemplo, vamos considerar o teste para obter propriedades da impressora padrão. O arquivo de cabeçalho cria esse teste da seguinte maneira:
1 // Test on the default printer and its driver name
2 BEGIN_TEST_METHOD(DefaultPrinterTest)
3 TEST_METHOD_PROPERTY(L"DataSource",
L"WMI:SELECT DriverName, DeviceId, LanguagesSupported FROM Win32_Printer WHERE Default = True")
4 END_TEST_METHOD()
Para isso, nosso código de recuperação, no arquivo cpp, tem a seguinte aparência:
1 void WmiExample::DefaultPrinterTest()
2 {
3 String deviceId;
4 VERIFY_SUCCEEDED(TestData::TryGetValue(L"DeviceId", deviceId));
5
6 String driverName;
7 VERIFY_SUCCEEDED(TestData::TryGetValue(L"DriverName", driverName));
8
9 TestDataArray<unsigned int> languagesSupported;
10 VERIFY_SUCCEEDED(TestData::TryGetValue(L"LanguagesSupported", languagesSupported));
11
12 Log::Comment(L"The default driver is " + deviceId + L" which is a " + driverName);
13 size_t count = languagesSupported.GetSize();
14 for (size_t i = 0; i < count; i++)
15 {
16 Log::Comment(String().Format(L"Language supported: %d", languagesSupported[i]));
17 }
18 }
Contabilizando possíveis valores de propriedade NULL
A parte a ter em mente é que a consulta WMI nem sempre pode retornar uma propriedade não nula. Pode haver momentos em que o valor da propriedade WMI retornado é "nulo". Se você acha que a propriedade que você está procurando pode ser "nula" em alguns cenários, marcar para ela antes de verificar ou tentar usá-la.
No código de teste gerenciado, por exemplo, TestContext armazenará os valores nulos como um objeto do tipo DBNull. Você deve marcar se o objeto for do tipo DBNull antes de tentar converter o valor resultante para o tipo que você espera que seja. Vamos dar uma olhada:
1 namespace WEX.Examples
2 {
3 using Microsoft.VisualStudio.TestTools.UnitTesting;
4 using System;
5 using System.Collections;
6 using System.Data;
7 using WEX.Logging.Interop;
8 using WEX.TestExecution;
9
10 [TestClass]
11 public class CSharpWmiDataSourceExample
12 {
13 [TestMethod]
14 [DataSource("WMI:SELECT MaximumComponentLength, Availability, DeviceId, DriveType, Compressed
FROM Win32_LogicalDisk WHERE DriveType=2 Or DriveType=3")]
15 public void LogicalDiskTest()
16 {
17 UInt32 driveType = (UInt32)m_testContext.DataRow["DriveType"];
18 Log.Comment("DeviceId is " + m_testContext.DataRow["DeviceId"]);
19 Log.Comment("DriveType is " + driveType.ToString());
20
21 object nullCheckCompressed = m_testContext.DataRow["Compressed"];
22 Log.Comment("Compressed's type is: " + nullCheckCompressed.GetType().ToString());
23 if (nullCheckCompressed.GetType() == typeof(DBNull))
24 {
25 Log.Comment("Compressed is NULL");
26 }
27 else
28 {
29 Boolean compressed = (Boolean)nullCheckCompressed;
30 Log.Comment("Compressed is " + compressed.ToString());
31 }
32
33 object nullCheckMaxComponentLength = m_testContext.DataRow["MaximumComponentLength"];
34 if (nullCheckMaxComponentLength.GetType() == typeof(DBNull))
35 {
36 Log.Comment("MaxComponentLength is NULL");
37 }
38 else
39 {
40 UInt32 maxComponentLength = (UInt32)nullCheckMaxComponentLength;
41 Log.Comment("MaxComponentLength is " + maxComponentLength.ToString());
42 }
43
44 object nullCheckAvailability = m_testContext.DataRow["Availability"];
45 if (nullCheckAvailability.GetType() == typeof(DBNull))
46 {
47 Log.Comment("Availability is NULL");
48 }
49 else
50 {
51 UInt32 availability = (UInt32)nullCheckAvailability;
52 Log.Comment("Availability is " + availability.ToString());
53 }
54 }
55 ...
56 public TestContext TestContext
57 {
58 get { return m_testContext; }
59 set { m_testContext = value; }
60 }
61
62 private TestContext m_testContext;
63 }
64}
Por exemplo, no teste acima, "Compactado", "MaximumComponentLength" e "Availability" podem ser nulos em alguns cenários (quando a consulta retorna unidades removíveis como unidades de disquete). Você deseja garantir que o teste se comporte adequadamente nesses casos. Nesse sentido, recupere o valor da propriedade como um objeto e marcar se ele for do tipo "DBNull". Se for, significa que o valor da propriedade retornado era nulo. Se não for, o valor retornado não era nulo e, portanto, válido, portanto, converta-o para os tipos apropriados e use-o para o teste.
O mesmo acontece com as APIs de recuperação nativas também – o valor da propriedade retornado pode ser NULL. Isso significa que você precisa marcar se o TestData recuperou com êxito o valor sem usar uma chamada de verificação (já que não é possível recuperar pode ser porque o valor é nulo). Por exemplo, você pode ter um método de teste que depende de uma consulta WMI:
1 // Test on only local (drive type = 3) or removable (drive type = 2) harddrive
2 BEGIN_TEST_METHOD(LocalOrRemovableHardDriveTest)
3 TEST_METHOD_PROPERTY(L"DataSource", L"WMI:SELECT DeviceId, DriveType, Availability,
MaximumComponentLength FROM Win32_LogicalDisk WHERE DriveType=2 OR DriveType=3")
4 END_TEST_METHOD()
Você pode ter "Availability e "MaximumComponentLength" retornados como valores NULL. Portanto, escreva o teste para considerar isso da seguinte forma:
1 void WmiExample::LocalOrRemovableHardDriveTest()
2 {
3 String deviceId;
4 VERIFY_SUCCEEDED(TestData::TryGetValue(L"DeviceId", deviceId));
5 int driveType;
6 VERIFY_SUCCEEDED(TestData::TryGetValue(L"DriveType", driveType));
7
8 unsigned int maxComponentLength;
9 if (SUCCEEDED(TestData::TryGetValue(L"MaximumComponentLength", maxComponentLength)))
10 {
11 Log::Comment(String().Format(L"MaximumComponentLength: %d", maxComponentLength));
12 }
13
14 unsigned int availability;
15 if (SUCCEEDED(TestData::TryGetValue(L"Availability", availability)))
16 {
17 Log::Comment(String().Format(L"Availability: %d", availability));
18 }
19
20 Log::Comment(L"DeviceId: " + deviceId);
21 Log::Comment(String().Format(L"DriveType: %d", driveType));
22 }