import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (in.hasNext()) {
int t=in.nextInt();
in.nextLine();
while(t>0){
t--;
int n = in.nextInt();
int m = in.nextInt();
in.nextLine();
int[][] board=new int[n][m];
for(int i=0;i<n;i++){
board[i]=Arrays.stream(in.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
}
//列举单层所有可能的情况,用二进制存储实际取出状态,1表示取出0表示舍弃
List<Integer> valid=new ArrayList<>();
for(int i=0;i<(1<<m);i++){
if(((i>>1)&i)==0){//右移一位后与运算,检查是否存在相邻的情况
valid.add(i);
}
}
int len=valid.size();
//遍历原数组每一行,通过比对valid计算每一种情况的单层和
int[][] rowSum=new int[n][len];
for(int i=0;i<n;i++){
for(int j=0;j<len;j++){
int mask=valid.get(j);//第j种取出方式
int sum=0;
//遍历该种情况下的取出数进行sum
for(int k=0;k<m;k++){
if(((mask>>k)&1)==1)sum+=board[i][k];
}
rowSum[i][j]=sum;
}
}
//计算相邻两行选择是否冲突
boolean[][] compatible=new boolean[len][len];
for(int i=0;i<len;i++){
int mask1=valid.get(i);
for(int j=0;j<len;j++){
int mask2=valid.get(j);
if((mask1&mask2)>0)continue;//直接上下相邻
if((((mask1>>1)&mask2)>0))continue;//在上一行的右下,对应着上一行是当前行的左上
if((((mask1<<1)&mask2)>0))continue;//在上一行的左下
compatible[i][j]=true;
}
}
//动态规划多行的结果
int[][] dp=new int[n][len];
//初始化第一行
for(int i=0;i<len;i++){
dp[0][i]=rowSum[0][i];
}
//当前计算的行,初始标记为不可达状态
for(int i=1;i<n;i++){
Arrays.fill(dp[i],Integer.MIN_VALUE);
for(int j=0;j<len;j++){//第i行的取用方式
for(int k=0;k<len;k++){//上一行的取用方式
//只有上一行和当前行的取用方式不冲突时才有效
if(compatible[j][k]){
//dp结果就是上一行结果加上当前行的sum
dp[i][j]=Math.max(dp[i][j],dp[i-1][k]+rowSum[i][j]);
}
}
}
}
int res=0;
for(int i:dp[n-1]){
res=Math.max(res,i);
}
System.out.println(res);
}
}
}
}