Skip to content

Commit

Permalink
Merge pull request #5 from Microsoft/develop
Browse files Browse the repository at this point in the history
Create second release complete with usage information
  • Loading branch information
heaths authored Feb 24, 2017
2 parents af4ee7f + d1e0d8c commit c2cffab
Show file tree
Hide file tree
Showing 18 changed files with 422 additions and 84 deletions.
84 changes: 62 additions & 22 deletions docker/Tests/vswhere.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,97 @@
# Licensed under the MIT license. See LICENSE.txt in the project root for license information.

Describe 'vswhere' {
Context 'format: text (default)' {
It 'returns 2 instances' {
Context '(no arguments)' {
It 'returns 2 instances using "text"' {
$instanceIds = C:\bin\vswhere.exe | Select-String 'instanceId: \w+'
$instanceIds.Count | Should Be 2
}

It '-all returns 3 instances' {
It 'returns 2 instances using "json"' {
$instances = C:\bin\vswhere.exe -format json | ConvertFrom-Json
$instances.Count | Should Be 2
}
}

Context '-all' {
It 'returns 3 instances using "text"' {
$instanceIds = C:\bin\vswhere.exe -all | Select-String 'instanceId: \w+'
$instanceIds.Count | Should Be 3
}

It '-products returns 1 instance' {
It 'returns 3 instances using "json"' {
$instances = C:\bin\vswhere.exe -all -format json | ConvertFrom-Json
$instances.Count | Should Be 3
}
}

Context '-products' {
It 'returns 1 instance using "text"' {
$instanceIds = C:\bin\vswhere.exe -products microsoft.visualstudio.product.buildtools | Select-String 'instanceId: \w+'
$instanceIds.Count | Should Be 1
$instanceIds.Matches[0].Value | Should Be 'instanceId: 4'
}

It 'returns 1 instance using "json"' {
$instances = C:\bin\vswhere.exe -products microsoft.visualstudio.product.buildtools -format json | ConvertFrom-Json
$instances.Count | Should Be 1
$instances[0].instanceId | Should Be 4
}
}

It '-requires returns 1 instance' {
Context '-requires' {
It 'returns 1 instance using "text"' {
$instanceIds = C:\bin\vswhere.exe -requires microsoft.visualstudio.workload.nativedesktop | Select-String 'instanceId: \w+'
$instanceIds.Count | Should Be 1
$instanceIds.Matches[0].Value | Should Be 'instanceId: 2'
}

It '-version returns 1 instance' {
$instanceIds = C:\bin\vswhere.exe -version '(15.0.26116,]' | Select-String 'instanceId: \w+'
$instanceIds.Count | Should Be 1
It 'returns 1 instance using "json"' {
$instances = C:\bin\vswhere.exe -requires microsoft.visualstudio.workload.nativedesktop -format json | ConvertFrom-Json
$instances.Count | Should Be 1
$instances[0].instanceId | Should Be 2
}
}

Context 'format: json' {
It 'returns 2 instances' {
$instances = C:\bin\vswhere.exe -format json | ConvertFrom-Json
$instances.Count | Should Be 2
Context '-version' {
It 'returns 1 instance using "text"' {
$instanceIds = C:\bin\vswhere.exe -version '(15.0.26116,]' | Select-String 'instanceId: \w+'
$instanceIds.Count | Should Be 1
$instanceIds.Matches[0].Value | Should Be 'instanceId: 2'
}

It '-all returns 3 instances' {
$instances = C:\bin\vswhere.exe -all -format json | ConvertFrom-Json
$instances.Count | Should Be 3
It 'returns 1 instance using "json"' {
$instances = C:\bin\vswhere.exe -version '(15.0.26116,]' -format json | ConvertFrom-Json
$instances.Count | Should Be 1
$instances[0].instanceId | Should Be 2
}
}

It '-products returns 1 instance' {
$instance = C:\bin\vswhere.exe -products microsoft.visualstudio.product.buildtools -format json | ConvertFrom-Json
$instance.Count | Should Be 1
Context '-latest' {
It 'returns 1 instance using "text"' {
$instanceIds = C:\bin\vswhere.exe -latest | Select-String 'instanceId: \w+'
$instanceIds.Count | Should Be 1
$instanceIds.Matches[0].Value | Should Be 'instanceId: 2'
}

It '-requires returns 1 instance' {
$instances = C:\bin\vswhere.exe -requires microsoft.visualstudio.workload.nativedesktop -format json | ConvertFrom-Json
It 'returns 1 instance using "json"' {
$instances = C:\bin\vswhere.exe -latest -format json | ConvertFrom-Json
$instances.Count | Should Be 1
$instances[0].instanceId | Should Be 2
}
}

Context '-latest -all' {
It 'returns 1 instance using "text"' {
$instanceIds = C:\bin\vswhere.exe -latest -all | Select-String 'instanceId: \w+'
$instanceIds.Count | Should Be 1
$instanceIds.Matches[0].Value | Should Be 'instanceId: 3'
}

It '-version returns 1 instance' {
$instances = C:\bin\vswhere.exe -version '(15.0.26116,]' -format json | ConvertFrom-Json
It 'returns 1 instance using "json"' {
$instances = C:\bin\vswhere.exe -latest -all -format json | ConvertFrom-Json
$instances.Count | Should Be 1
$instances[0].instanceId | Should Be 3
}
}
}
15 changes: 15 additions & 0 deletions src/vswhere.lib/CommandArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,21 @@ void CommandArgs::Parse(_In_ vector<CommandParser::Token> args)
}
}

void CommandArgs::Usage(_In_ std::wostream& out) const
{
auto pos = m_path.find_last_of(L"\\");
auto path = ++pos != wstring::npos ? m_path.substr(pos) : m_path;
out << ResourceManager::FormatString(IDS_USAGE, path.c_str()) << endl;

for (const auto& formatter : Formatter::Formatters)
{
UINT nID;

tie(nID, ignore) = formatter.second;
out << ResourceManager::FormatString(nID, formatter.first.c_str()) << endl;
}
}

static bool ArgumentEquals(_In_ const wstring& name, _In_ LPCWSTR expect)
{
_ASSERT(expect && *expect);
Expand Down
2 changes: 1 addition & 1 deletion src/vswhere.lib/CommandArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class CommandArgs

void Parse(_In_ LPCWSTR wszCommandLine);
void Parse(_In_ int argc, _In_ LPCWSTR argv[]);
void Usage(_In_ std::wostream& out) noexcept;
void Usage(_In_ std::wostream& out) const;

private:
static const std::vector<std::wstring> s_Products;
Expand Down
17 changes: 8 additions & 9 deletions src/vswhere.lib/Formatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,19 @@ Formatter::Formatter()

Formatter::FormatterMap Formatter::Formatters =
{
{ L"json", JsonFormatter::Create },
{ L"text", TextFormatter::Create },
{ L"json", make_tuple(IDS_FORMAT_TEXT, JsonFormatter::Create) },
{ L"text", make_tuple(IDS_FORMAT_JSON, TextFormatter::Create) },
};

std::unique_ptr<Formatter> Formatter::Create(const std::wstring& type)
{
auto it = Formatters.find(type);
if (it != Formatters.end())
{
return it->second();
FormatterFactory factory;
tie(ignore, factory) = it->second;

return factory();
}

throw win32_error(ERROR_NOT_SUPPORTED);
Expand All @@ -50,15 +53,11 @@ void Formatter::Write(_In_ std::wostream& out, _In_ ISetupInstance* pInstance)
EndDocument(out);
}

void Formatter::Write(_In_ std::wostream& out, _In_ std::vector<ISetupInstance*> instances)
void Formatter::Write(_In_ std::wostream& out, _In_ std::vector<ISetupInstancePtr> instances)
{
StartDocument(out);
StartArray(out);

HRESULT hr = S_OK;
ISetupInstance* pInstance[1] = {};
unsigned long fetched = 0;

for (const auto& instance : instances)
{
WriteInternal(out, instance);
Expand All @@ -68,7 +67,7 @@ void Formatter::Write(_In_ std::wostream& out, _In_ std::vector<ISetupInstance*>
EndDocument(out);
}

wstring Formatter::FormatDateISO9601(_In_ const FILETIME& value)
wstring Formatter::FormatDateISO8601(_In_ const FILETIME& value)
{
SYSTEMTIME st = {};

Expand Down
7 changes: 4 additions & 3 deletions src/vswhere.lib/Formatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
class Formatter
{
public:
typedef std::map<std::wstring, std::function<std::unique_ptr<Formatter>()>, ci_less> FormatterMap;
typedef std::function<std::unique_ptr<Formatter>()> FormatterFactory;
typedef std::map<std::wstring, std::tuple<UINT, FormatterFactory>, ci_less> FormatterMap;

static std::unique_ptr<Formatter> Create(const std::wstring& type);
static FormatterMap Formatters;
Expand All @@ -19,7 +20,7 @@ class Formatter
}

void Write(_In_ std::wostream& out, _In_ ISetupInstance* pInstance);
void Write(_In_ std::wostream& out, _In_ std::vector<ISetupInstance*> instances);
void Write(_In_ std::wostream& out, _In_ std::vector<ISetupInstancePtr> instances);

virtual bool ShowLogo() const
{
Expand All @@ -32,7 +33,7 @@ class Formatter

Formatter();

static std::wstring FormatDateISO9601(_In_ const FILETIME& value);
static std::wstring FormatDateISO8601(_In_ const FILETIME& value);

virtual void StartDocument(_In_ std::wostream& out) {}
virtual void StartArray(_In_ std::wostream& out) {}
Expand Down
100 changes: 85 additions & 15 deletions src/vswhere.lib/InstanceSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,16 @@
using namespace std;
using std::placeholders::_1;

const ci_equal InstanceSelector::s_comparer;

InstanceSelector::InstanceSelector(_In_ const CommandArgs& args, _In_opt_ ISetupHelper* pHelper) :
m_args(args)
m_args(args),
m_ullMinimumVersion(0),
m_ullMaximumVersion(0)
{
// Get the ISetupHelper (if implemented) if a version range is specified.
auto version = args.get_Version();
if (!version.empty())
m_helper = pHelper;
if (m_helper)
{
m_helper = pHelper;
if (m_helper)
auto version = args.get_Version();
if (!version.empty())
{
auto hr = m_helper->ParseVersionRange(version.c_str(), &m_ullMinimumVersion, &m_ullMaximumVersion);
if (FAILED(hr))
Expand All @@ -31,30 +30,102 @@ InstanceSelector::InstanceSelector(_In_ const CommandArgs& args, _In_opt_ ISetup
}
}

vector<ISetupInstance*> InstanceSelector::Select(_In_ IEnumSetupInstances* pEnum) const
bool InstanceSelector::Less(const ISetupInstancePtr& a, const ISetupInstancePtr& b) const
{
static ci_equal equal;
static ci_less less;

bstr_t bstrVersionA, bstrVersionB;
ULONGLONG ullVersionA, ullVersionB;
FILETIME ftDateA, ftDateB;

// Compare versions.
auto hrA = a->GetInstallationVersion(bstrVersionA.GetAddress());
auto hrB = b->GetInstallationVersion(bstrVersionB.GetAddress());
if (SUCCEEDED(hrA) && SUCCEEDED(hrB))
{
if (m_helper)
{
hrA = m_helper->ParseVersion(bstrVersionA, &ullVersionA);
hrB = m_helper->ParseVersion(bstrVersionB, &ullVersionB);
if (SUCCEEDED(hrA) && SUCCEEDED(hrB))
{
if (ullVersionA != ullVersionB)
{
return ullVersionA < ullVersionB;
}
}
else
{
// a is less than b if we can't parse version for a but can for b.
return SUCCEEDED(hrB);
}
}
}
else
{
// a is less than b if we can't get version for a but can for b.
return SUCCEEDED(hrB);
}

// Compare dates.
hrA = a->GetInstallDate(&ftDateA);
hrB = b->GetInstallDate(&ftDateB);
if (SUCCEEDED(hrA) && SUCCEEDED(hrB))
{
auto result = ::CompareFileTime(&ftDateA, &ftDateB);
if (0 == result)
{
auto message = ResourceManager::GetString(IDS_E_UNEXPECTEDDATE);
throw win32_error(E_UNEXPECTED, message);
}

return 0 > result;
}
else
{
// a is less than b if we can't get date for a but can for b.
return SUCCEEDED(hrB);
}
}

vector<ISetupInstancePtr> InstanceSelector::Select(_In_ IEnumSetupInstances* pEnum) const
{
_ASSERT(pEnum);

HRESULT hr = S_OK;
unsigned long celtFetched = 0;
ISetupInstance* pInstances[1] = {};

vector<ISetupInstance*> instances;
vector<ISetupInstancePtr> instances;
do
{
celtFetched = 0;

hr = pEnum->Next(1, pInstances, &celtFetched);
if (SUCCEEDED(hr) && celtFetched)
{
auto pInstance = pInstances[0];
if (IsMatch(pInstance))
ISetupInstancePtr instance(pInstances[0], false);
if (IsMatch(instance))
{
instances.push_back(pInstance);
instances.push_back(instance);
}
}
} while (SUCCEEDED(hr) && celtFetched);

if (m_args.get_Latest() && 1 < instances.size())
{
sort(instances.begin(), instances.end(), [&](const ISetupInstancePtr& a, const ISetupInstancePtr& b) -> bool
{
return Less(a, b);
});

return vector<ISetupInstancePtr>
{
instances.back(),
};
}

return instances;
}

Expand Down Expand Up @@ -168,8 +239,7 @@ bool InstanceSelector::IsVersionMatch(_In_ ISetupInstance* pInstance) const
{
_ASSERT(pInstance);

// m_helper will be NULL if no version range was specified or the interface was not yet implemented.
if (!m_helper)
if (!HasVersionRange())
{
return true;
}
Expand Down
8 changes: 6 additions & 2 deletions src/vswhere.lib/InstanceSelector.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,20 @@ class InstanceSelector
{
}

std::vector<ISetupInstance*> Select(_In_ IEnumSetupInstances* pEnum) const;
bool Less(const ISetupInstancePtr& a, const ISetupInstancePtr& b) const;
std::vector<ISetupInstancePtr> Select(_In_ IEnumSetupInstances* pEnum) const;

private:
static std::wstring GetId(_In_ ISetupPackageReference* pPackageReference);
bool IsMatch(_In_ ISetupInstance* pInstance) const;
bool IsProductMatch(_In_ ISetupInstance2* pInstance) const;
bool IsWorkloadMatch(_In_ ISetupInstance2* pInstance) const;
bool IsVersionMatch(_In_ ISetupInstance* pInstance) const;
bool HasVersionRange() const
{
return m_ullMinimumVersion != 0 && m_ullMaximumVersion != 0;
}

static const ci_equal s_comparer;
const CommandArgs& m_args;
ULONGLONG m_ullMinimumVersion;
ULONGLONG m_ullMaximumVersion;
Expand Down
Loading

0 comments on commit c2cffab

Please sign in to comment.