Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions SarAsio/configui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,25 @@ void EndpointsPropertySheetPage::onAddEndpoint()
{
_epDialogConfig = EndpointConfig();

// Windows keeps what it knows about an endpoint (the name it shows, the
// user's settings) under its ID, so an ID must never be handed to a
// different endpoint: a deleted endpoint's ID given to a new one made
// Windows keep showing the deleted endpoint's name.
GUID guid;

if (SUCCEEDED(CoCreateGuid(&guid))) {
char id[40];

sprintf_s(id, "ep_%08lx%04hx%04hx%02x%02x%02x%02x%02x%02x%02x%02x",
guid.Data1, guid.Data2, guid.Data3,
guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
_epDialogConfig.id = id;
}

int counter = 1;

do {
while (_epDialogConfig.id.empty()) {
std::ostringstream os;

os << "ep_" << counter;
Expand All @@ -248,7 +264,7 @@ void EndpointsPropertySheetPage::onAddEndpoint()
} else {
counter++;
}
} while (_epDialogConfig.id.empty());
}

auto result = DialogBoxParam(
gDllModule,
Expand Down
111 changes: 111 additions & 0 deletions SarTest/SarTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ int usage()
" Open SAR with no hardware interface configured, the way a DAW sees\n"
" it before first-time setup; fail unless it offers a stereo input\n"
" and output (issues #31, #54).\n"
" SarTest endpoint-names\n"
" Rename an endpoint keeping its ID, then give it a new ID, and report\n"
" the name Windows shows each time (issues #15, #66).\n"
"\n"
"Layout options: --endpoints N (playback/recording pairs, default 2)\n"
" --channels C (per endpoint, default 2) --prefix <name>\n"
Expand Down Expand Up @@ -711,6 +714,112 @@ int cmdNoInterface(const Args& args)
return rc;
}

// Starts a host on the layout, records whether Windows shows the layout's
// endpoints (and, when stale is given, endpoints under those names instead)
// and stops it again.
bool showsEndpoints(const Args& args, const EndpointLayout& layout,
const EndpointLayout *stale, bool *staleShown)
{
if (!prepareConfig(args, layout)) {
return false;
}

AsioHost host(hostOptions(args, layout));

if (!host.load() || !host.open() || !host.start()) {
host.close();
return false;
}

FoundEndpoints found;
bool shown = findEndpoints(layout, args.getInt(L"wait", 30), &found);

found.release();

if (stale) {
*staleShown = findEndpoints(*stale, 5, &found);
found.release();
}

host.close();

if (!waitForEndpointsGone(layout, args.getInt(L"wait-gone", 30))) {
logf("Endpoints lingered after stop");
}

return shown;
}

// Issues #15 and #66: SarAsio's control panel gave a new endpoint the lowest
// unused "ep_N", so a deleted endpoint's ID went to the next one, and Windows
// kept showing the old endpoint's name for it. Windows keys what it stores
// about an endpoint (its name, the user's settings) on the ID. Checks that a
// fresh ID gets the new name, and reports what a reused ID shows.
int cmdEndpointNames(const Args& args)
{
EndpointLayout before = EndpointLayout::fromArgs(args);

before.pairs = 1;
before.prefix = L"SarTest Alpha";
before.idPrefix = L"SarTest-names";

EndpointLayout renamed = before;

renamed.prefix = L"SarTest Beta";

EndpointLayout fresh = renamed;

fresh.idPrefix = L"SarTest-names-fresh";

logf("--- %s, IDs %s ---", narrow(before.prefix).c_str(), narrow(before.idPrefix).c_str());

bool firstShown = showsEndpoints(args, before, nullptr, nullptr);

logf("--- renamed to %s, same IDs ---", narrow(renamed.prefix).c_str());

bool oldNameShown = false;
bool renamedShown = showsEndpoints(args, renamed, &before, &oldNameShown);

logf("--- %s, new IDs %s ---", narrow(fresh.prefix).c_str(), narrow(fresh.idPrefix).c_str());

bool freshShown = showsEndpoints(args, fresh, nullptr, nullptr);

logf("Reused IDs: Windows shows the %s name", renamedShown ? "new" :
oldNameShown ? "old" : "neither");

int rc = 0;
std::string failure;

if (!firstShown) {
rc = 2;
failure = "endpoints never appeared";
} else if (!freshShown) {
rc = 2;
failure = "endpoints with new IDs did not show their names";
}

if (rc != 0) {
logf("%s", failure.c_str());
}

JsonObject result;

result.setString("command", "endpoint-names")
.setBool("passed", rc == 0)
.setInt("exitCode", rc)
.setBool("reusedIdShowsNewName", renamedShown)
.setBool("reusedIdShowsOldName", oldNameShown)
.setBool("newIdShowsNewName", freshShown);

if (!failure.empty()) {
result.setString("failure", failure);
}

writeResults(args, result.str());
logf("%s", rc == 0 ? "PASSED" : "FAILED");
return rc;
}

} // namespace

int wmain(int argc, wchar_t **argv)
Expand Down Expand Up @@ -748,6 +857,8 @@ int wmain(int argc, wchar_t **argv)
rc = cmdControlPanel(args);
} else if (args.command == L"no-interface") {
rc = cmdNoInterface(args);
} else if (args.command == L"endpoint-names") {
rc = cmdEndpointNames(args);
} else {
rc = usage();
}
Expand Down
4 changes: 2 additions & 2 deletions SarTest/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,12 +201,12 @@ std::wstring EndpointLayout::recordingName(int pair) const

std::string EndpointLayout::playbackId(int pair) const
{
return narrow(prefix) + "-out-" + std::to_string(pair + 1);
return narrow(idPrefix.empty() ? prefix : idPrefix) + "-out-" + std::to_string(pair + 1);
}

std::string EndpointLayout::recordingId(int pair) const
{
return narrow(prefix) + "-in-" + std::to_string(pair + 1);
return narrow(idPrefix.empty() ? prefix : idPrefix) + "-in-" + std::to_string(pair + 1);
}

EndpointLayout EndpointLayout::fromArgs(const Args& args)
Expand Down
2 changes: 2 additions & 0 deletions SarTest/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ struct EndpointLayout
int pairs = 2;
int channels = 2;
std::wstring prefix = L"SarTest";
// Prefix of the endpoint IDs; the name prefix when empty.
std::wstring idPrefix;

std::wstring playbackName(int pair) const;
std::wstring recordingName(int pair) const;
Expand Down
2 changes: 2 additions & 0 deletions tools/test/Invoke-SarTest.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ if ($Scenarios -contains 'issues') {
@('control-panel', '--endpoints', 1) + $common) -Timeout 120
$summary.scenarios += Invoke-Scenario 'issue-54-no-interface' (
@('no-interface') + $common) -Timeout 120
$summary.scenarios += Invoke-Scenario 'issue-15-endpoint-names' (
@('endpoint-names') + $common) -Timeout 300
}

if ($Scenarios -contains 'matrix') {
Expand Down
4 changes: 4 additions & 0 deletions tools/test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ host, presses the hardware interface's Configure button and checks that the
clock never had a second instance alive. It needs an interactive desktop.
`SarTest.exe no-interface` (#31, #54) opens SAR with no hardware interface
configured and checks that it still offers a stereo input and output.
`SarTest.exe endpoint-names` (#15, #66) renames an endpoint keeping its ID,
then gives it a new ID, and checks that the new ID shows the new name; the
results record what Windows shows for the reused ID.
`Invoke-SarTest.ps1 -Scenarios` selects any of `issues`, `matrix`, `race`
and `kill`.

Expand All @@ -103,6 +106,7 @@ SarTest run [--iterations K] [--duration S]
SarTest race [--cycles K] [--up S] [--down MS] [--openers T] [--hold MS]
SarTest control-panel
SarTest no-interface
SarTest endpoint-names
```

`install` creates the SAR software device node if needed, installs the
Expand Down
Loading