MacOS에서 간단한 gStreamer 파일을 실행하려는 데 애로 사항이 많네. 

 

컴파일까지야 어떻게든 했는데, 실행하려고 하니 에러가 발생하네. Dynamic Library를 찾지를 못 하는군. 

생성된 실행 파일을 `otool -L` 로 확인해 보니, gStreamer 의 참조 경로가 이렇게 되어 있네. 

 

```

 @rpath/libgstreamer-1.0.0.dylib (compatibility version 2205.0.0, current version 2205.0.0)

```

 

어쨌든, 원래는 /Library/Framework 폴더에 gStreamer가 복사되어 있는데. 해당 폴더는 또 Dynamic Library 경로에 추가되어 있지를 않고 있다. 뭐 나중을 위해서는 gStreamer Dynamic Library를 Application에 같이 패키징되어야 할 텐데, 디버깅/테스트할 때는 또 그걸 할 수 없으니. 

 

문제는 MacOS에서는 LD_LIBRARY_PATH가 동작하지 않는다. 찾아 보니 어쨌든 DYLD_FALLBACK_LIBRARY_PATH 에 추가하면 되네. Rust 나 Cargo 에서도 별 다른 방법은 없고. 환경 변수로 해당 폴더를 추가해주면 된다. 

 

실행하고 나면 참조되는 gObject 라이브러리 등이 중복되어 있다고 나오는데, 그건 무시하면 되고... 

 

그리고 나서는 다시 SSL Error가 발생하고, 이것 역시 Dynamic Library 경로 문제 같은데. 어쨌든 개발 시에만 발생할 문제로 보인다. 

 

GIO_EXTRA_MODULES 에 gstreamer 라이브러리의 gio/modules 를 할당해주면 문제 없이 실행된다. 


WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,

2023. 3월 현재 기준으로.. 

 

여기 저기에 Conda를 사용해야 하네, 뭐네 여러가지 이야기가 많지만, 그건 작년 기준이고.

현재는 다음 TensorFlow 패키지만 설치한다. 

 

1. TensorFlow MacOS 버전 

   - tensorflow-macos => 2.10 

2. TensorFlow GPU 가속 

   - tensorflow-metal => 0.60 

 

이보다 높은 버전을 설치할 경우는 2.11의 변경 사항 때문에 GPU 가속이 동작하지 않는다. 

GPU 가속을 원하면 2.10, 0.60으로 버전을 맞추어야 한다. 

 

Apple Tensorflow 테스트 스크립트를 실행했을 때. 맥북 M2 기준으로 4배 정도 속도 차이가 난다. 

- CPU로 : 408초

- GPU로 : 90초 

 


WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,

이게 뭔가 독특한게 사용자가 정의한 것과 시스템 환경 변수가 사용 방법이 다르다. 

소스 코드를 뜯어봐야 알겠지만, 사용자가 정의한 환경 변수는 별도의 env 변수에 넣는 걸로 보인다. 

 

그래서 사용 방법이 달라진다. 웃긴 건 이걸 적어 놓은 게 아무 데도 없다는 거다. -.-

 

 

    environment {
        commitHash = ""         // 
    }

	pipeline {
    
    	...
        
    	stage('Some') {
        
        	script {
            	echo "${commitHash}" 		// Error 
                echo "${env.commitHash}" 	// Work well
                
                echo "${BRANCH_NAME}"		// work well
        
        
        }
        
        ...
    }

	

WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,

회사 보안 정책으로 인해서 여러 가지 보안 SW를 설치하고 사용합니다. 그 중 잘 만든 것도 있고, 쓰레기 같은 프로그램도 있습니다. 쓰레기 류 중의 하나가 V3죠. 

 

NPM과 Gradle을 CI를 돌리거나, NPM Install을 하게 되면, 대량의 파일 복사를 하게 됩니다. 특히 NPM은 Directory나 파일의 Link를 Rename 해 놓은 후, 나중에 영구 삭제하는 과정을 거치는 걸로 보입니다. 

 

문제는 NPM이 파일을 처리하고 나서 바로 Rename에 들어가게 되는데, NPM이 처리한 파일을 V3가 붙잡고 있습니다. 그래서 rename하는 과정에 예외가 발생합니다. 아주 거지 같죠. 

 

회사 직원들이 Jenkins가 자꾸 실패가 뜬다는 말에 살펴 보니, 또 V3가 문제인 것 같습니다. 


WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,

UI 표시가 제대로 되지 않아서 문제점을 찾다보니 객체가 이유없이 생성, 파괴를 반복한다. 

문제는 해당 객체가 State를 유지하고 있다는 거다. Stateless하면 문제 없을 텐데 상태가 있다 보니 재생성되면 UI 요소가 사라져 버린다. 내가 짠 부분이 아니라 수정하진 않는다. 

 

<ng-container *ngFor="let server of servers$|async;">
  .... something ..... 
</ng-container>

문제가 되는 부분을 보니 ngFor와 NgRx Store가 같이 쓰인다. 두 가지가 복합되서 문제가 발생했다. 

1. NgRx Store에서 State를 가져오는데, 그 때마다 Object Reference가 변경된다. 

2. ngFor는 자기 하부의 DOM을 Object Reference에 의거해서 관리한다. 

두 가지가 조합되면 ngFor는 자기 하부의 모든 DOM을 싹 날리고 재생성하는 일을 반복한다. 

 

그래서 아래와 같이 변경한다. 이렇게 하면 ngFor는 trackBy에 의거해서 자신의 하부의 DOM을 생성한다. 

<ng-container *ngFor="let server of servers$|async; trackBy: trackByGuid">
  .... something ..... 
</ng-container>



  public trackByGuid(index: number, server: Server) {
    return server.guid;
  }

 


WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,

Angular에서 주기적 polling 같은 걸 rxjs, ngrx 같은 걸로 구현해 놓으면 테스트에 실패가 발생한다. 마지막에 다음과 같은 에러가 발생하면서 문제가 발생한다. 주기적 Timer 들이 MicroTask로 계속 남아 있으면서 에러가 발생하는 걸로 보인다. 

    8 periodic timer(s) still in the queue.

      at node_modules/zone.js/dist/fake-async-test.js:617:31
      at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:386:30)
      at ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/proxy.js:117:43)
      at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:385:36)
      at Zone.run (node_modules/zone.js/dist/zone.js:143:47)

이걸 해결하려면 test의 마지막에 discardPeriodicTasks()를 호출해주면, 정리 작업이 완료된다. 

 

또 한 가지 에러는 테스트 안의 expect의 assertion이 fail하면, 코드에 아무 문제가 없는 데도 아래와 같은 에러가 발생한다. 테스트가 정상적으로 완료되면 아래 에러는 없어진다. 

    Error: Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.

 


WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,

Jest에 Angular FlexLayout이 적용된 컴포넌트를 테스트하니 아래와 같은 에러가 발생한다. 

    TypeError: getComputedStyle(...).getPropertyValue is not a function

      at StyleUtils.lookupStyle (node_modules/@angular/src/lib/core/style-utils/style-utils.ts:94:47)
      at StyleUtils.hasWrap (node_modules/@angular/src/lib/core/style-utils/style-utils.ts:65:17)
      at DefaultFlexDirective.BaseDirective2.hasWrap (node_modules/@angular/src/lib/core/base/base2.ts:137:24)

 

한참을 찾아봐도 에러 원인을 알 수가 없다. 당연히 나지 말아야 할 에러니까.

 

찾다 보니 Jest는 DOM 관련된 모듈로 JSDOM을 사용한다. 문제는 JSDOM이 getComputedStyle에 대한 구현이 없는 모양이다. 그래서 jest-preset-angular에서는 setup-jest.ts에 다음과 같이 mocking을 해놨다. 위에서 발생한 에러를 쳐다보면 당연히 구현이 없으니 에러가 발생한다. 

Object.defineProperty(window, 'getComputedStyle', {
  value: () => ['-webkit-appearance'],
});

그래서 다음과 같이 mocking한 걸 변경한다. 

Object.defineProperty(window, 'getComputedStyle', {
  value: () => ({
      getPropertyValue: (prop) => {
          return '';
      }
  })
});

 

References

 


WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,
m: expect.objectContaining({ type: ServerActions.InitialServersLoaded.type })

이 조합으로 검증을 하면 귀찮은 일이 많이 생긴다. 아래 같이 InitialServerLoaded Action을 발산할 때 Action에 Field가 여러 개고 랜덤으로 생성되는 값이면 아주 귀찮아진다. 

 

      it('무조건, IntialServersLoadedOk를 발산해야 한다', () => {
        jest.spyOn(metricsApiSvc, 'getServerMetric')
          .mockImplementation(() => of( mockResponse ));

        testScheduler.run(helpers => {
          const { cold, hot, expectObservable, expectSubscriptions, flush } = helpers;
          actions$ = hot('-a----a-', { a: ServerActions.InitialServersRequesting() });

          expectObservable(spectator.service.initialServersRequesting$).toBe(
            '-(ml)-l-',
            {
              l: ServerActions.InitialServersLoadedOk(),
              m: ServerActions.InitialServersLoaded({servers: {
              	id: 355232,
                desc: 'desc'
              })
            }
          );
        })
      });

RxJs Marble Testing를 Jest와 같이 사용하게 되면, Assert에 toEqual이 사용된다. 그리고 Jest에서는 다행히도 toEqual 내에서 사용 가능한 Matcher가 존재한다. 그중 하나가 expect.any이다. 그래서 다음과 같이 변경 가능하다. 

// ...

m: ServerActions.InitialServersLoaded({servers: {
	id: expect.any(number),
    desc: expect.any(string)
})

// ...

그런데 이것도 필드 수가 많아지면 피곤해진다. type만 비교하고 싶은데 말이지. 그럴 때는 다음과 같이.. 

m: expect.objectContaining({ type: ServerActions.InitialServersLoaded.type })

 


WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,

NgRx로 Redux Pattern의 Application을 작성하는 중이다. Unit Test는 어느 정도 틀을 만들어 완료를 했고, IntegrationTest를 작성하는 중이다.

 

Unit Test에서 NgRx의 MockStore 기능을 사용하여 테스트를 수행했기에 역시나 MockStore를 사용하려고 하는데, 뻔한 곳에서 에러가 난다. 가만히 생각해 보니, MockStore는 Action이 Stream에 보이기는 하지만, Store의 State를 변경하지를 않는다. 

 

Integration Test를 하려면 State에 변화가 있어야 하는데, MockStore로는 불가능하다. Real Store를 사용해서 작성해야 할 듯.... 


WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,

가끔 프로그래밍하다 보면 새로운 걸 불현듯 깨닫는 때가 있다. 당연히 이미 알고 있어야 할 걸 말이지. 

 

오늘은 Unit Test를 하다가 Constructor와 OnInit의 용도 차이를 깨닫게 된다. 

OnInit은 Override 안 하면 안 생기니, 아무 생각 없이 Constructor에다 초기화 코드를 때려 넣었다. 

 

만들어낸 Unit Test를 하려고 보니 아.. 뭐냐.. 왜 이렇게 에러가 많이 생기는 거냐? 

Constructor에다 초기화 코드를 다 때려 넣었더니 Mocking을 안 하면 아예 동작을 안 한다. ㅋㅋ 

 

Assign와 DI만 빼고 다 옮겼더니 갑자기 Unit Test로 작업할 양이 확 줄면서, 동작하기 시작한다. 

왜 그랬을까나... 왜 몰랐을까나... 


WRITTEN BY
HanDDol
여행이란 건 말이지. 첫 걸음을 내딜 때는 모든 게 낯설고.. 그리고 점점 더 낯선 세상에 익숙해지면서 세상의 모든 곳이 고향처럼 느껴진다. 고향으로 돌아오는 여행의 마지막 걸음에는 나의 고향이 더 이상 익숙한 곳이 아닌 낯선 곳임을 알게 된다.

,