import java.io.*;
import java.util.*;
import java.util.Map.Entry;

import javax.lang.model.type.IntersectionType;
 
 
 
public class  gym{
	static ArrayList<Integer>[] adjList;
	static int[] dfs_low, dfs_num, parent;
	static int V, counter, root, rootChildren;
	static boolean[] artPoints;
	static void findArtPointsAndBridges()	//O(V + E)
	{
		for(int i = 0; i < V; ++i)
			if(dfs_num[i] == 0)
			{
				root = i;
				rootChildren = 0;
				dfs(i);
				if(rootChildren <= 1)		//special case
					artPoints[i] = false;
			}
	}
	
	static void dfs(int u)
	{
		dfs_num[u] = dfs_low[u] = ++counter;
		for(int v: adjList[u])
			if(dfs_num[v] == 0)
			{
				parent[v] = u;
				if(u == root)
					++rootChildren;
				dfs(v);
				if(dfs_low[v] >= dfs_num[u])
					artPoints[u] = true;
				if(dfs_low[v] > dfs_num[u]) {
					edges.add(new edge(u,v));
				}
				dfs_low[u] = Math.min(dfs_low[v], dfs_low[u]);
			}
			else
				if(parent[u] != v)
					dfs_low[u] = Math.min(dfs_low[u], dfs_num[v]);
	}
	static class edge implements Comparable<edge>{
		int u,v;
		edge(int x,int y){
			u=Math.min(x, y);v=Math.max(x, y);
		}
		@Override
		public int compareTo(edge o) {
			if(u==o.u) {
				return v-o.v;
			}
			return u-o.u;
		}
		public String toString() {return u+" "+v;}
	}
	static TreeSet<edge>edges;
	static LinkedList<Integer>[]notbridgeEdges;
	static int farthest=0;
	static int depthofFarthest=0;
	static int[]par;
	static boolean[]v;
	static HashSet<Integer> dup;
	static int cnt;
	static void bfs(int i,boolean f) {
		v[i]=true;
		LinkedList<Integer>q=new LinkedList<Integer>();
		q.add(i);
		while(!q.isEmpty()) {
			int cur=q.poll();
			
			LinkedList<Integer>qcompnent=new LinkedList<Integer>();
			qcompnent.add(cur);
			while(!qcompnent.isEmpty()) {
				int curv=qcompnent.poll();
				for(int o:notbridgeEdges[curv]) {
					if(!v[o]) {
						v[o]=true;
						qcompnent.add(o);q.add(o);
						if(!bc.contains(o) && !ac.contains(o)) {
							dup.add(o);
							cnt++;
						}
						if(f) {
							
						}
						else {
							
						}
					}
				}
			}
			
			for(int o:adjList[cur]) {
				if(!v[o]) {
					v[o]=true;
					if(f) {
						
						if(bc.contains(o))continue;
						
					}
					else {
						if(ac.contains(o))continue;
					}
					q.add(o);
					if(f) {
						if(!ac.contains(o)) {
						dup.add(o);
						cnt++;
						}
					}
					else {
						if(!bc.contains(o)) {
							dup.add(o);
							cnt++;
							}
					}
				}
			}
		}
	}
	static int[]d;
	static boolean z;
	static int target;
	static void bfs2(int i,LinkedList<Integer>q) {
		d[i]=0;
		while(!q.isEmpty()) {
			int cur=q.poll();
			if(target==a) {
				if(ac.contains(cur))z=true;
			}
			else {
				if(bc.contains(cur))z=true;
			}
			LinkedList<Integer>qcompnent=new LinkedList<Integer>();
			qcompnent.add(cur);
			while(!qcompnent.isEmpty()) {
				int curv=qcompnent.poll();
				for(int o:notbridgeEdges[curv]) {
					if(d[o]==-1) {
						d[o]=d[cur];
						qcompnent.add(o);q.add(o);
						
					}
				}
			}
			
			for(int o:adjList[cur]) {
				if(d[o]==-1) {
					d[o]=d[cur]+1;
					q.add(o);
					
				}
			}
		}
	}
	static HashSet<Integer>ac,bc;
	static int a,b;
	public static void main(String[] args) throws Exception{
		MScanner sc=new MScanner(System.in);
		PrintWriter pw=new PrintWriter(System.out);
		int t=sc.nextInt();
		while(t-->0) {
		int n=V=sc.nextInt(),m=sc.nextInt();a=sc.nextInt()-1;b=sc.nextInt()-1;
		adjList=new ArrayList[n];for(int i=0;i<n;i++)adjList[i]=new ArrayList<Integer>();
		notbridgeEdges=new LinkedList[n];for(int i=0;i<n;i++)notbridgeEdges[i]=new LinkedList<Integer>();
		dfs_low=new int[n];
		dfs_num=new int[n];
		parent=new int[n];
		artPoints=new boolean[n];
		edge[]in=new edge[m];
		for(int i=0;i<m;i++) {
			int u=sc.nextInt()-1,v=sc.nextInt()-1;
			adjList[u].add(v);adjList[v].add(u);
			in[i]=new edge(u, v);
		}
		edges=new TreeSet<edge>();
		findArtPointsAndBridges();
		LinkedList<Integer>bridge[]=new LinkedList[n];
		for(int i=0;i<n;i++) {
			bridge[i]=new LinkedList<Integer>();
		}
		for(int i=0;i<m;i++) {
			if(!edges.contains(in[i])) {
				int u=in[i].u,v=in[i].v;
				notbridgeEdges[u].add(v);
				notbridgeEdges[v].add(u);
			}
			else {
				int u=in[i].u,v=in[i].v;
				bridge[u].add(v);
				bridge[v].add(u);
			}
		}
		LinkedList<Integer>qcompnent=new LinkedList<Integer>();
		qcompnent.add(a);
		v=new boolean[n];
		bc=new HashSet<Integer>();
		bc.add(b);
		ac=new HashSet<Integer>();
		ac.add(a);
		while(!qcompnent.isEmpty()) {
			int curv=qcompnent.poll();
			for(int o:notbridgeEdges[curv]) {
				if(!v[o]) {
					v[o]=true;
					qcompnent.add(o);
					ac.add(o);
				}
			}
		}
		qcompnent.add(b);
		v=new boolean[n];
		while(!qcompnent.isEmpty()) {
			int curv=qcompnent.poll();
			for(int o:notbridgeEdges[curv]) {
				if(!v[o]) {
					v[o]=true;;
					qcompnent.add(o);
					bc.add(o);
				}
			}
		}
		dup=new HashSet<Integer>();
		v=new boolean[n];
		cnt=0;
		bfs(a,true);
		int cnta=cnt;
		v=new boolean[n];
		cnt=0;
		bfs(b,false);
		int cntb=cnt;
		int inc=0;
		if(artPoints[b]) {
			LinkedList<Integer>newq=new LinkedList<Integer>();
			d=new int[n];;
			Arrays.fill(d, -1);
			for(int i:bridge[b]) {
				newq.add(i);
				d[i]=1;
			}
			z=false;
			target=a;
			bfs2(0,newq);
			//System.out.println("zz");
			if(z) {
				inc+=bc.size()-1;
				cntb+=bc.size()-1;
			}
			
		}
		
		if(artPoints[a]) {
			LinkedList<Integer>newq=new LinkedList<Integer>();
			d=new int[n];;
			Arrays.fill(d, -1);
			for(int i:bridge[a]) {
				newq.add(i);d[i]=1;
			}
			z=false;
			target=b;
			bfs2(0,newq);
			if(z) {
				cnta+=ac.size()-1;
				inc+=ac.size()-1;
			}
		}
		//System.out.println(cnta+" "+cntb+" "+(dup.size()+inc));
		int intersection=cnta+cntb-dup.size()+inc;
		
		cnta-=intersection;
		cntb-=intersection;
		
		pw.println(cnta*1l*cntb);
		}
		pw.flush();
	}
	static class MScanner {
		StringTokenizer st;
		BufferedReader br;
		public MScanner(InputStream system) {
			br = new BufferedReader(new InputStreamReader(system));
		}
 
		public MScanner(String file) throws Exception {
			br = new BufferedReader(new FileReader(file));
		}
 
		public String next() throws IOException {
			while (st == null || !st.hasMoreTokens())
				st = new StringTokenizer(br.readLine());
			return st.nextToken();
		}
		public int[] takearr(int n) throws IOException {
	        int[]in=new int[n];for(int i=0;i<n;i++)in[i]=nextInt();
	        return in;
		}
		public long[] takearrl(int n) throws IOException {
	        long[]in=new long[n];for(int i=0;i<n;i++)in[i]=nextLong();
	        return in;
		}
		public Integer[] takearrobj(int n) throws IOException {
	        Integer[]in=new Integer[n];for(int i=0;i<n;i++)in[i]=nextInt();
	        return in;
		}
		public Long[] takearrlobj(int n) throws IOException {
	        Long[]in=new Long[n];for(int i=0;i<n;i++)in[i]=nextLong();
	        return in;
		}
		public String nextLine() throws IOException {
			return br.readLine();
		}
 
		public int nextInt() throws IOException {
			return Integer.parseInt(next());
		}
 
		public double nextDouble() throws IOException {
			return Double.parseDouble(next());
		}
 
		public char nextChar() throws IOException {
			return next().charAt(0);
		}
 
		public Long nextLong() throws IOException {
			return Long.parseLong(next());
		}
 
		public boolean ready() throws IOException {
			return br.ready();
		}
 
		public void waitForInput() throws InterruptedException {
			Thread.sleep(3000);
		}
	}
}