#include <iostream>
#include <vector>
#include <queue>
#include <climits>
#include <tuple>
using namespace std;
struct Point {
int row, col;
};
bool isValid(int row, int col, int N, int M, vector<vector<char>> &grid, vector<vector<int>> &cost) {
return row >= 0 && row < N && col >= 0 && col < M && grid[row][col] != '#' && cost[row][col] == INT_MAX;
}
int dijkstra(int N, int M, int x, int y, vector<vector<char>> &grid) {
vector<vector<int>> cost(N, vector<int>(M, INT_MAX));
priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, greater<>> pq;
pq.push(make_tuple(0, 0, 0));
cost[0][0] = 0;
// Directions for straight and L-shaped pipes
vector<int> dr = {1, 0};
vector<int> dc = {0, 1};
vector<pair<int, int>> lshape = {{1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
while (!pq.empty()) {
auto [currentCost, r, c] = pq.top();
pq.pop();
if (r == N - 1 && c == M - 1) {
return currentCost;
}
// Move straight
for (int i = 0; i < 2; ++i) {
int new_row = r + dr[i];
int new_col = c + dc[i];
if (isValid(new_row, new_col, N, M, grid, cost) && currentCost + x < cost[new_row][new_col]) {
cost[new_row][new_col] = currentCost + x;
pq.push(make_tuple(cost[new_row][new_col], new_row, new_col));
}
}
// Move L-shaped (avoiding invalid paths)
for (auto [drL, dcL] : lshape) {
int new_row = r + drL;
int new_col = c + dcL;
if (isValid(new_row, new_col, N, M, grid, cost) && currentCost + y < cost[new_row][new_col]) {
cost[new_row][new_col] = currentCost + y;
pq.push(make_tuple(cost[new_row][new_col], new_row, new_col));
}
}
}
return -1;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
while (T--) {
int N, M, x, y;
cin >> N >> M >> x >> y;
vector<vector<char>> grid(N, vector<char>(M));
for (int i = 0; i < N; ++i) {
for (int j = 0; j < M; ++j) {
cin >> grid[i][j];
}
}
int result = dijkstra(N, M, x, y, grid);
cout << result << "\n";
}
return 0;
}