Git 急に Fetch やPull ができなくなったら。。

こんなエラーが出るようになったら、

error: cannot lock ref ‘refs/remotes/origin/xxx’: ‘refs/remotes/origin/xxx’

リモート上の削除されたブランチをローカルで追跡できなくなっているのである。
そういう時は、クリーンする処置が必要で、prune というのを実行する。

  git remote prune リポジトリ

が書式である。
自分は以下を実際に実行させて解決した。

git remote prune origin

厳格な日付読み取りで良い方法は。。

存在しない日付の読み取り

String strdate = "20230931";
LocalDate dt = LocalDate.parse(strdate, DateTimeFormatter.ofPattern("yyyyMMdd"));
2023-09-30

まあ、これは想定される好ましくない方法
DateTimeFormatter に、ResolverStyle.LENIENT を指定すると

String strdate = "20230931";
LocalDate dt = LocalDate.parse(strdate, DateTimeFormatter.ofPattern("yyyyMMdd")
               .withResolverStyle(ResolverStyle.LENIENT));
2023-10-01

DateTimeFormatter に、ResolverStyle.STRICT を指定すると

String strdate = "20230931";
LocalDate dt = LocalDate.parse(strdate, DateTimeFormatter.ofPattern("yyyyMMdd")
               .withResolverStyle(ResolverStyle.STRICT));

java.time.DateTimeException: Unable to obtain LocalDate from TemporalAccessor:
{YearOfEra=2023, DayOfMonth=31, MonthOfYear=9},ISO of type java.time.format.Parsed

年を yyyy ではなくて、uuuu を指定すると、、

String strdate = "20230931";
LocalDate dt = LocalDate.parse(strdate, DateTimeFormatter.ofPattern("uuuuMMdd")
               .withResolverStyle(ResolverStyle.STRICT));

java.time.DateTimeException: Invalid date 'SEPTEMBER 31'

LocalDate.parse(strdate, DateTimeFormatter.ofPattern("yyyyMMdd")
.withResolverStyle(ResolverStyle.STRICT));
でもいいじゃないかと思ってしまうが、それはダメで、
正常な日付、String strdate = "20230930"; で、ResolverStyle.STRICT を指定すると
java.time.DateTimeException: Unable to obtain LocalDate from TemporalAccessor:
{YearOfEra=2023, DayOfMonth=30, MonthOfYear=9},ISO of type java.time.format.Parsed
になってしまう。

やはり、
  DateTimeFormatter.ofPattern("uuuuMMdd").withResolverStyle(ResolverStyle.STRICT)
を使うべきなのだと。。。

そして、30日、31日を指定した 2月30日、2月31日は、ResolverStyle.SMART なら、
2月28日、閏年なら、2月29日に補正してくれる。

以下、DateFormatter を直接使うことは少ないかな?

LocalDate dt = DateTimeFormatter.ofPattern("uuuuMMdd")
              .withResolverStyle(ResolverStyle.STRICT)
              .parse("20221001", LocalDate::from);
String res = DateTimeFormatter.ofPattern("uuuuMMdd")
            .withResolverStyle(ResolverStyle.STRICT)
            .toFormat()
            .format(dt);

JUnit の mockito thenAnswer を lambda で

先日書いた、
JUnit mockito の return を遅らせたい(時間を掛けたい) - Oboe吹きプログラマの黙示録

よく考えたら thenAnswer は、lambda で書けましたね。。

Mockito.when(foo.getAgent(Mockito.contains("a"))).thenAnswer(new Answer<String>() {
    @Override
    public String answer(InvocationOnMock invocation){
        System.out.println("invocation.getArgument(0) = "+invocation.getArgument(0));
        return "A";
    }
});

というのを、

Mockito.when(foo.getAgent(Mockito.contains("a"))).thenAnswer(i->{
      System.out.println("i.getArgument(0) = "+i.getArgument(0));
      return "A";
});

下のようにここまで省略すると逆に意味がなく、

Mockito.when(foo.getAgent(Mockito.contains("a"))).thenAnswer(i->"A");

{ }で書いた方が if 制御文などを差し込める。

JUnit mockito の return を遅らせたい(時間を掛けたい)

変なテスト要求仕様であるし、こんなこと通常しないかもしれないが、リターン値を
when で書くメソッドの引数の条件以外の条件で分岐させる時に応用できるかもしれない。

通常、String 1個の引数メソッドのモック
Mockito.when(テスト対象Class.メソッド(Mockito.anyString()).thenReturn(期待値);

thenReturn ではなく、thenAnswer を使ってAnswer<T> をオーバライドで書く。

// 5秒遅延して "A" を返す。
Mockito.when(テスト対象Class.メソッド(Mockito.contains(”a"))
.thenAnswer(new Answer<String>() {
    @Override
    public String answer(InvocationOnMock invocation){
        // 第1引数は、invocation.getArgument(0)
        try{ Thread.sleep(5000); }catch(InterruptedException e){}
        return "A";
    }
});

遅延以外の要求 → when で書くメソッドの引数の条件もこのように、
Answer<T> をオーバライドで書くことで、invocation.getArgument(0) で引数も参照可能で、
return 値を更に場合分けもできるはずだ。

JUnit 実行用の log4j2 設定を指定する

JUnit テストケースコードで、log4j2log4j.configurationFile 属性を書きかえれば良い。
以前、
oboe2uran.hatenablog.com
を書いて悩んだが、
開発 IDEEclipseを使っているのであれば、、
test/resources/ の下に、JUnit で実行する時のための
log4j2.xml を用意して使用すれば良いだけで、main/resources/ に配置する log4j2.xml
とは、異なるファイル名で用意して指定すれば良いのである。

test/resources/test-log4j2.xml を用意したとして、、
JUnit テストケースプログラム

public class UserSendTest {
    @Rule  public TestName testName = new TestName();

    @BeforeClass
    public static void initialize(){
        System.setProperty("log4j.configurationFile","test-log4j2.xml");
    }

    @After
    public void after(){
        File logbackupdir = new File("/result/" + testName.getMethodName());
        logbackupdir.mkdirs();
        LogManager.shutdown();
        try{
            Files.move(Paths.get("/work/console.log"),
                       Paths.get(logbackupdir.getAbsolutePath() + "/console.log"),
                       StandardCopyOption.REPLACE_EXISTING);
        }catch(IOException e){
            e.printStackTrace();
        }
    }

@BeforeClass で、System.setProperty で設定する。
@After で、テストケースメソッド毎のログを別のディレクトリに退避させている。
退避させるために、一旦、 LogManager.shutdown(); を実行するが、次のテストケースでログ出力メソッドが
呼び出されば、自動で設定どおりにログ出力をする。

(例として、、)
main/resources/log4j2.xml は、、

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="off">
  <Properties>
    <Property name="format1">[%d{yyyy-MM-dd HH:mm:ss.SSS}],[%-5p], %c, %m%n</Property>
    <Property name="logfile">./log/batch.log</Property>
    <Property name="logfile-archive">./log/batchg_%d{yyyy-MM-dd}-%i.log</Property>
  </Properties>
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout>
        <pattern>${format1}</pattern>
      </PatternLayout>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="TRACE">
      <AppenderRef ref="Console" level="INFO"/>
    </Root>
    <Logger name="software.amazon.awssdk" level="WARN" />
    <Logger name="software.amazon.awssdk.request" level="INFO" />
    <Logger name="org.apache.ibatis.transaction" level="WARN" />
    <Logger name="org.mybatis.guice.transactional" level="WARN" />
    <Logger name="jp.mytarget" level="INFO" />
  </Loggers>
</Configuration>

test/resources/test-log4j2.xml は、、

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="off">
  <Properties>
    <Property name="format1">[%d{yyyy-MM-dd HH:mm:ss.SSS}],[%-5p], %c, %m%n</Property>
    <Property name="logfile">./log/batch.log</Property>
    <Property name="logfile-archive">./log/batchg_%d{yyyy-MM-dd}-%i.log</Property>
  </Properties>
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout>
        <pattern>${format1}</pattern>
      </PatternLayout>
    </Console>
    <File name="file" fileName="/work/console.log">
      <PatternLayout>
        <pattern>${format1}</pattern>
      </PatternLayout>
    </File>
  </Appenders>
  <Loggers>
    <Root level="TRACE">
      <AppenderRef ref="Console" level="DEBUG"/>
      <AppenderRef ref="file" level="DEBUG"/>
    </Root>
    <Logger name="software.amazon.awssdk" level="WARN" />
    <Logger name="software.amazon.awssdk.request" level="INFO" />
    <Logger name="org.apache.ibatis.transaction" level="WARN" />
    <Logger name="org.mybatis.guice.transactional" level="WARN" />
    <Logger name="jp.mytarget" level="DEBUG" />
  </Loggers>
</Configuration>

JUnit で、実行中のメソッド名を取得

JUnit で、@Before 付与したテストケースの前に動くメソッド内で、
これからテスト実行するテストケースメソッド名を取得する。

JUnit で、@After付与したテストケースの後に動くメソッド内で、
テスト実行したテストケースメソッド名を取得する。
という場合は、
@Rule を付けた org.junit.rules.TestName をテストクラスで、初期化生成するようにして、
TestName#getMethodName() で取得することができる。

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
public class UserSendTest {
     @Rule  public TestName testName = new TestName();
@Before
public void before(){
    System.out.println("これからテストするテストケース  = "+ testName.getMethodName() );
}
@After
public void after(){
    System.out.println("テストしたテストケース   = "+ testName.getMethodName() );
}

スレッドプールの状況

先日書いたサンプル、
oboe2uran.hatenablog.com
のようなケースで、待ち合わせ時にスレッドプールがどういう状況かを知りたい場合がある。

単純であるが、Executors.newFixedThreadPool で作成したものを ThreadPoolExecutor でキャストすれば良い。

ThreadPoolExecutor es = (ThreadPoolExecutor)Executors.newFixedThreadPool(4);

これで、

es.getPoolSize();     // プール内の現在のスレッド数
es.getCompletedTaskCount();  // これまでに実行が完了したタスクのおよその総数
es.getActiveCount();   // アクティブにタスクを実行しているスレッドのおよその数